def __init__(self, *, bus, handle_clocking=True): """ Parameters: """ # If this looks more like a ULPI bus than a UTMI bus, translate it. if hasattr(bus, 'dir'): self.utmi = UTMITranslator(ulpi=bus, handle_clocking=handle_clocking) self.bus_busy = self.utmi.busy self.translator = self.utmi self.always_fs = False self.data_clock = 60e6 # If this looks more like raw I/O connections than a UTMI bus, create a pure-gatware # PHY to drive the raw I/O signals. elif hasattr(bus, 'd_n'): self.utmi = GatewarePHY(io=bus) self.bus_busy = Const(0) self.translator = self.utmi self.always_fs = True self.data_clock = 12e6 # Otherwise, use it directly. # Note that since a true UTMI interface has separate Tx/Rx/control # interfaces, we don't need to care about bus 'busyness'; so we'll # set it to a const zero. else: self.utmi = bus self.bus_busy = Const(0) self.translator = None self.always_fs = True self.data_clock = 12e6 # # I/O port # self.connect = Signal() self.low_speed_only = Signal() self.full_speed_only = Signal() self.frame_number = Signal(11) self.microframe_number = Signal(3) self.sof_detected = Signal() self.new_frame = Signal() self.reset_detected = Signal() self.speed = Signal(2) self.suspended = Signal() self.tx_activity_led = Signal() self.rx_activity_led = Signal() # # Internals. # self._endpoints = []
def populate_ulpi_registers(self, m): """ Creates translator objects that map our control signals to ULPI registers. """ # Function control. function_control = Cat(self.xcvr_select, self.term_select, self.op_mode, Const(0), ~self.suspend, Const(0)) self.add_composite_register(m, 0x04, function_control, reset_value=0b01000001) # OTG control. otg_control = Cat( self.id_pullup, self.dp_pulldown, self.dm_pulldown, self.dischrg_vbus, self.chrg_vbus, Const(0), Const(0), self.use_external_vbus_indicator ) self.add_composite_register(m, 0x0A, otg_control, reset_value=0b00000110)
def elaborate(self, platform): m = Module() locked = Signal() # Create our domains... m.domains.sync = ClockDomain() m.domains.usb = ClockDomain() m.domains.usb_io = ClockDomain() m.domains.fast = ClockDomain() # ... create our 48 MHz IO and 12 MHz USB clock... clk48 = Signal() clk12 = Signal() m.submodules.pll = Instance("SB_PLL40_2F_CORE", i_REFERENCECLK = platform.request(platform.default_clk), i_RESETB = Const(1), i_BYPASS = Const(0), o_PLLOUTCOREA = clk48, o_PLLOUTCOREB = clk12, o_LOCK = locked, # Create a 48 MHz PLL clock... p_FEEDBACK_PATH = "SIMPLE", p_PLLOUT_SELECT_PORTA = "GENCLK", p_PLLOUT_SELECT_PORTB = "SHIFTREG_0deg", p_DIVR = 0, p_DIVF = 47, p_DIVQ = 4, p_FILTER_RANGE = 1, ) # ... and constrain them to their new frequencies. platform.add_clock_constraint(clk48, 48e6) platform.add_clock_constraint(clk12, 12e6) # We'll use our 48MHz clock for everything _except_ the usb domain... m.d.comb += [ ClockSignal("usb") .eq(clk12), ClockSignal("sync") .eq(clk48), ClockSignal("usb_io") .eq(clk48), ClockSignal("fast") .eq(clk48), ResetSignal("usb") .eq(~locked), ResetSignal("sync") .eq(~locked), ResetSignal("usb_io") .eq(~locked), ResetSignal("fast") .eq(~locked) ] return m
def trap(cause: Optional[Union[TrapCause, IrqCause]], interrupt=False): fetch_with_new_pc(Cat(Const(0, 2), self.csr_unit.mtvec.base)) if cause is None: return assert isinstance(cause, TrapCause) or isinstance(cause, IrqCause) e = exception_unit notifiers = e.irq_cause_map if interrupt else e.trap_cause_map m.d.comb += notifiers[cause].eq(1)
def elaborate(self, platform): m = Module() ac = Signal(self.width+1) ac_next = Signal.like(ac) temp = Signal.like(ac) q1 = Signal(self.width) q1_next = Signal.like(q1) i = Signal(range(self.width)) # combinatorial with m.If(ac >= self.y): m.d.comb += [temp.eq(ac-self.y), Cat(q1_next, ac_next).eq( Cat(1, q1, temp[0:self.width-1]))] with m.Else(): m.d.comb += [Cat(q1_next, ac_next).eq(Cat(q1, ac) << 1)] # synchronized with m.If(self.start): m.d.sync += [self.valid.eq(0), i.eq(0)] with m.If(self.y == 0): m.d.sync += [self.busy.eq(0), self.dbz.eq(1)] with m.Else(): m.d.sync += [self.busy.eq(1), self.dbz.eq(0), Cat(q1, ac).eq(Cat(Const(0, 1), self.x, Const(0, self.width)))] with m.Elif(self.busy): with m.If(i == self.width-1): m.d.sync += [self.busy.eq(0), self.valid.eq(1), i.eq(0), self.q.eq(q1_next), self.r.eq(ac_next >> 1)] with m.Else(): m.d.sync += [i.eq(i+1), ac.eq(ac_next), q1.eq(q1_next)] return m
def uart_gen_serial_record(platform: Platform, m: Module): if platform: serial = platform.request("uart") debug = platform.request("debug") m.d.comb += [ debug.eq(Cat( serial.tx, Const(0, 1), # GND )) ] else: serial = Record(Layout([("tx", 1)]), name="UART_SERIAL") self.serial = serial # TODO this is obfuscated, but we need those signals for simulation testbench return serial
def build_input_fetcher(self, m, stop): # Create fetchers f0 = self.create_fetcher(m, stop, 'f0', Mode0InputFetcher) f1 = self.create_fetcher(m, stop, 'f1', Mode1InputFetcher) # Additional config for fetcher1 repeats = (self.config.output_channel_depth // Const(Constants.SYS_ARRAY_WIDTH)) m.d.comb += [ f1.num_pixels_x.eq(self.config.num_pixels_x), f1.pixel_advance_x.eq(self.config.pixel_advance_x), f1.pixel_advance_y.eq(self.config.pixel_advance_y), f1.depth.eq(self.config.input_channel_depth >> 4), f1.num_repeats.eq(repeats), ] # Create RamMux and connect to LRAMs m.submodules['ram_mux'] = ram_mux = RamMux() mode = self.config.mode for i in range(4): # Connect to ram mux addr and data ports m.d.comb += self.lram_addr[i].eq(ram_mux.lram_addr[i]) m.d.comb += ram_mux.lram_data[i].eq(self.lram_data[i]) m.d.comb += f0.ram_mux_data[i].eq(ram_mux.data_out[i]) m.d.comb += f1.ram_mux_data[i].eq(ram_mux.data_out[i]) m.d.comb += ram_mux.addr_in[i].eq( Mux(mode, f1.ram_mux_addr[i], f0.ram_mux_addr[i])) # phase input depends on mode m.d.comb += ram_mux.phase.eq( Mux(mode, f1.ram_mux_phase, f0.ram_mux_phase)) # Router fetcher outputs depending on mode mode_first = Mux(mode, f1.first, f0.first) mode_last = Mux(mode, f1.last, f0.last) mode_data = [ Mux(mode, f1.data_out[i], f0.data_out[i]) for i in range(4) ] return (mode_first, mode_last, mode_data)
def elaborate(self, platform): m = Module() width = self._width # Instead of using a counter, we will use a sentinel bit in the shift # register to indicate when it is full. shift_reg = Signal(width + 1, reset=0b1) m.d.comb += self.o_data.eq(shift_reg[0:width]) m.d.usb_io += self.o_put.eq(shift_reg[width - 1] & ~shift_reg[width] & self.i_valid), with m.If(self.reset): m.d.usb_io += shift_reg.eq(1) with m.If(self.i_valid): with m.If(shift_reg[width]): m.d.usb_io += shift_reg.eq(Cat(self.i_data, Const(1))) with m.Else(): m.d.usb_io += shift_reg.eq(Cat(self.i_data, shift_reg[0:width])), return m
def build_multipliers(self, m, accumulator): a0 = self.input_a.word_select(0, self._a_shape.width) b0 = self.input_b.word_select(0, self._b_shape.width) a1 = self.input_a.word_select(1, self._a_shape.width) b1 = self.input_b.word_select(1, self._b_shape.width) a2 = self.input_a.word_select(2, self._a_shape.width) b2 = self.input_b.word_select(2, self._b_shape.width) a3 = self.input_a.word_select(3, self._a_shape.width) b3 = self.input_b.word_select(3, self._b_shape.width) # Explicitly instantiate the DSP macro m.submodules.dsp = Instance( "MULTADDSUB9X9WIDE", i_CLK=ClockSignal(), i_CEA0A1=Const(1), i_CEA2A3=Const(1), i_CEB0B1=Const(1), i_CEB2B3=Const(1), i_CEC=Const(1), i_CEPIPE=Const(1), i_CEOUT=Const(1), i_CECTRL=Const(1), i_RSTA0A1=ResetSignal(), i_RSTA2A3=ResetSignal(), i_RSTB0B1=ResetSignal(), i_RSTB2B3=ResetSignal(), i_RSTC=ResetSignal(), i_RSTCTRL=ResetSignal(), i_RSTPIPE=ResetSignal(), i_RSTOUT=ResetSignal(), i_SIGNED=Const(1), i_ADDSUB=Const(0, unsigned(4)), i_A0=a0, i_B0=Cat(b0, b0[7]), i_A1=a1, i_B1=Cat(b1, b1[7]), i_A2=a2, i_B2=Cat(b2, b2[7]), i_A3=a3, i_B3=Cat(b3, b3[7]), i_C=Const(0, unsigned(54)), i_LOADC=self.input_first, o_Z=accumulator, p_REGINPUTAB0="BYPASS", p_REGINPUTAB1="BYPASS", p_REGINPUTAB2="BYPASS", p_REGINPUTAB3="BYPASS", p_REGINPUTC="BYPASS", p_REGADDSUB="BYPASS", p_REGLOADC="BYPASS", p_REGLOADC2="REGISTER", p_REGPIPELINE="REGISTER", p_REGOUTPUT="REGISTER", p_RESETMODE="SYNC", p_GSR="ENABLED", )
def elaborate(self, platform): self.m = m = Module() comb = m.d.comb sync = m.d.sync # CPU units used. logic = m.submodules.logic = LogicUnit() adder = m.submodules.adder = AdderUnit() shifter = m.submodules.shifter = ShifterUnit() compare = m.submodules.compare = CompareUnit() self.current_priv_mode = Signal(PrivModeBits, reset=PrivModeBits.MACHINE) csr_unit = self.csr_unit = m.submodules.csr_unit = CsrUnit( # TODO does '==' below produces the same synth result as .all()? in_machine_mode=self.current_priv_mode == PrivModeBits.MACHINE) exception_unit = self.exception_unit = m.submodules.exception_unit = ExceptionUnit( csr_unit=csr_unit, current_priv_mode=self.current_priv_mode) arbiter = self.arbiter = m.submodules.arbiter = MemoryArbiter( mem_config=self.mem_config, with_addr_translation=True, csr_unit=csr_unit, # SATP register exception_unit=exception_unit, # current privilege mode ) mem_unit = m.submodules.mem_unit = MemoryUnit(mem_port=arbiter.port( priority=0)) ibus = arbiter.port(priority=2) if self.with_debug: m.submodules.debug = self.debug = DebugUnit(self) self.debug_bus = arbiter.port(priority=1) # Current decoding state signals. instr = self.instr = Signal(32) funct3 = self.funct3 = Signal(3) funct7 = self.funct7 = Signal(7) rd = self.rd = Signal(5) rs1 = Signal(5) rs2 = Signal(5) rs1val = Signal(32) rs2val = Signal(32) rdval = Signal(32) # calculated by unit, stored to register file imm = Signal(signed(12)) csr_idx = Signal(12) uimm = Signal(20) opcode = self.opcode = Signal(InstrType) pc = self.pc = Signal(32, reset=CODE_START_ADDR) # at most one active_unit at any time active_unit = ActiveUnit() # Register file. Contains two read ports (for rs1, rs2) and one write port. regs = Memory(width=32, depth=32, init=self.reg_init) reg_read_port1 = m.submodules.reg_read_port1 = regs.read_port() reg_read_port2 = m.submodules.reg_read_port2 = regs.read_port() reg_write_port = (self.reg_write_port ) = m.submodules.reg_write_port = regs.write_port() # Timer management. mtime = self.mtime = Signal(32) sync += mtime.eq(mtime + 1) comb += csr_unit.mtime.eq(mtime) self.halt = Signal() with m.If(csr_unit.mstatus.mie & csr_unit.mie.mtie): with m.If(mtime == csr_unit.mtimecmp): # 'halt' signal needs to be cleared when CPU jumps to trap handler. sync += [ self.halt.eq(1), ] comb += [ exception_unit.m_instruction.eq(instr), exception_unit.m_pc.eq(pc), # TODO more ] # TODO # DebugModule is able to read and write GPR values. # if self.with_debug: # comb += self.halt.eq(self.debug.HALT) # else: # comb += self.halt.eq(0) # with m.If(self.halt): # comb += [ # reg_read_port1.addr.eq(self.gprf_debug_addr), # reg_write_port.addr.eq(self.gprf_debug_addr), # reg_write_port.en.eq(self.gprf_debug_write_en) # ] # with m.If(self.gprf_debug_write_en): # comb += reg_write_port.data.eq(self.gprf_debug_data) # with m.Else(): # comb += self.gprf_debug_data.eq(reg_read_port1.data) with m.If(0): pass with m.Else(): comb += [ reg_read_port1.addr.eq(rs1), reg_read_port2.addr.eq(rs2), reg_write_port.addr.eq(rd), reg_write_port.data.eq(rdval), # reg_write_port.en set later rs1val.eq(reg_read_port1.data), rs2val.eq(reg_read_port2.data), ] comb += [ # following is not true for all instrutions, but in specific cases will be overwritten later imm.eq(instr[20:32]), csr_idx.eq(instr[20:32]), uimm.eq(instr[12:]), ] # drive input signals of actually used unit. with m.If(active_unit.logic): comb += [ logic.funct3.eq(funct3), logic.src1.eq(rs1val), logic.src2.eq(Mux(opcode == InstrType.OP_IMM, imm, rs2val)), ] with m.Elif(active_unit.adder): comb += [ adder.src1.eq(rs1val), adder.src2.eq(Mux(opcode == InstrType.OP_IMM, imm, rs2val)), ] with m.Elif(active_unit.shifter): comb += [ shifter.funct3.eq(funct3), shifter.funct7.eq(funct7), shifter.src1.eq(rs1val), shifter.shift.eq( Mux(opcode == InstrType.OP_IMM, imm[0:5].as_unsigned(), rs2val[0:5])), ] with m.Elif(active_unit.mem_unit): comb += [ mem_unit.en.eq(1), mem_unit.funct3.eq(funct3), mem_unit.src1.eq(rs1val), mem_unit.src2.eq(rs2val), mem_unit.store.eq(opcode == InstrType.STORE), mem_unit.offset.eq( Mux(opcode == InstrType.LOAD, imm, Cat(rd, imm[5:12]))), ] with m.Elif(active_unit.compare): comb += [ compare.funct3.eq(funct3), # Compare Unit uses Adder for carry and overflow flags. adder.src1.eq(rs1val), adder.src2.eq(Mux(opcode == InstrType.OP_IMM, imm, rs2val)), # adder.sub set somewhere below ] with m.Elif(active_unit.branch): comb += [ compare.funct3.eq(funct3), # Compare Unit uses Adder for carry and overflow flags. adder.src1.eq(rs1val), adder.src2.eq(rs2val), # adder.sub set somewhere below ] with m.Elif(active_unit.csr): comb += [ csr_unit.func3.eq(funct3), csr_unit.csr_idx.eq(csr_idx), csr_unit.rs1.eq(rs1), csr_unit.rs1val.eq(rs1val), csr_unit.rd.eq(rd), csr_unit.en.eq(1), ] comb += [ compare.negative.eq(adder.res[-1]), compare.overflow.eq(adder.overflow), compare.carry.eq(adder.carry), compare.zero.eq(adder.res == 0), ] # Decoding state (with redundancy - instr. type not known yet). # We use 'ibus.read_data' instead of 'instr' (that is driven by sync domain) # for getting registers to save 1 cycle. comb += [ opcode.eq(instr[0:7]), rd.eq(instr[7:12]), funct3.eq(instr[12:15]), rs1.eq(instr[15:20]), rs2.eq(instr[20:25]), funct7.eq(instr[25:32]), ] def fetch_with_new_pc(pc: Signal): m.next = "FETCH" m.d.sync += active_unit.eq(0) m.d.sync += self.pc.eq(pc) def trap(cause: Optional[Union[TrapCause, IrqCause]], interrupt=False): fetch_with_new_pc(Cat(Const(0, 2), self.csr_unit.mtvec.base)) if cause is None: return assert isinstance(cause, TrapCause) or isinstance(cause, IrqCause) e = exception_unit notifiers = e.irq_cause_map if interrupt else e.trap_cause_map m.d.comb += notifiers[cause].eq(1) self.fetch = Signal() interconnect_error = Signal() comb += interconnect_error.eq(exception_unit.m_store_error | exception_unit.m_fetch_error | exception_unit.m_load_error) with m.FSM(): with m.State("FETCH"): with m.If(self.halt): sync += self.halt.eq(0) trap(IrqCause.M_TIMER_INTERRUPT, interrupt=True) with m.Else(): with m.If(pc & 0b11): trap(TrapCause.FETCH_MISALIGNED) with m.Else(): comb += [ ibus.en.eq(1), ibus.store.eq(0), ibus.addr.eq(pc), ibus.mask.eq(0b1111), ibus.is_fetch.eq(1), ] with m.If(interconnect_error): trap(cause=None) with m.If(ibus.ack): sync += [ instr.eq(ibus.read_data), ] m.next = "DECODE" with m.State("DECODE"): comb += self.fetch.eq( 1 ) # only for simulation, notify that 'instr' ready to use. m.next = "EXECUTE" # here, we have registers already fetched into rs1val, rs2val. with m.If(instr & 0b11 != 0b11): trap(TrapCause.ILLEGAL_INSTRUCTION) with m.If(match_logic_unit(opcode, funct3, funct7)): sync += [ active_unit.logic.eq(1), ] with m.Elif(match_adder_unit(opcode, funct3, funct7)): sync += [ active_unit.adder.eq(1), adder.sub.eq((opcode == InstrType.ALU) & (funct7 == Funct7.SUB)), ] with m.Elif(match_shifter_unit(opcode, funct3, funct7)): sync += [ active_unit.shifter.eq(1), ] with m.Elif(match_loadstore_unit(opcode, funct3, funct7)): sync += [ active_unit.mem_unit.eq(1), ] with m.Elif(match_compare_unit(opcode, funct3, funct7)): sync += [ active_unit.compare.eq(1), adder.sub.eq(1), ] with m.Elif(match_lui(opcode, funct3, funct7)): sync += [ active_unit.lui.eq(1), ] comb += [ reg_read_port1.addr.eq(rd), # rd will be available in next cycle in rs1val ] with m.Elif(match_auipc(opcode, funct3, funct7)): sync += [ active_unit.auipc.eq(1), ] with m.Elif(match_jal(opcode, funct3, funct7)): sync += [ active_unit.jal.eq(1), ] with m.Elif(match_jalr(opcode, funct3, funct7)): sync += [ active_unit.jalr.eq(1), ] with m.Elif(match_branch(opcode, funct3, funct7)): sync += [ active_unit.branch.eq(1), adder.sub.eq(1), ] with m.Elif(match_csr(opcode, funct3, funct7)): sync += [active_unit.csr.eq(1)] with m.Elif(match_mret(opcode, funct3, funct7)): sync += [active_unit.mret.eq(1)] with m.Elif(match_sfence_vma(opcode, funct3, funct7)): pass # sfence.vma with m.Elif(opcode == 0b0001111): pass # fence with m.Else(): trap(TrapCause.ILLEGAL_INSTRUCTION) with m.State("EXECUTE"): with m.If(active_unit.logic): sync += [ rdval.eq(logic.res), ] with m.Elif(active_unit.adder): sync += [ rdval.eq(adder.res), ] with m.Elif(active_unit.shifter): sync += [ rdval.eq(shifter.res), ] with m.Elif(active_unit.mem_unit): sync += [ rdval.eq(mem_unit.res), ] with m.Elif(active_unit.compare): sync += [ rdval.eq(compare.condition_met), ] with m.Elif(active_unit.lui): sync += [ rdval.eq(Cat(Const(0, 12), uimm)), ] with m.Elif(active_unit.auipc): sync += [ rdval.eq(pc + Cat(Const(0, 12), uimm)), ] with m.Elif(active_unit.jal | active_unit.jalr): sync += [ rdval.eq(pc + 4), ] with m.Elif(active_unit.csr): sync += [rdval.eq(csr_unit.rd_val)] # control flow mux - all traps need to be here, otherwise it will overwrite m.next statement. with m.If(active_unit.mem_unit): with m.If(mem_unit.ack): m.next = "WRITEBACK" sync += active_unit.eq(0) with m.Else(): m.next = "EXECUTE" with m.If(interconnect_error): # NOTE: # the order of that 'If' is important. # In case of error overwrite m.next above. trap(cause=None) with m.Elif(active_unit.csr): with m.If(csr_unit.illegal_insn): trap(TrapCause.ILLEGAL_INSTRUCTION) with m.Else(): with m.If(csr_unit.vld): m.next = "WRITEBACK" sync += active_unit.eq(0) with m.Else(): m.next = "EXECUTE" with m.Elif(active_unit.mret): comb += exception_unit.m_mret.eq(1) fetch_with_new_pc(exception_unit.mepc) with m.Else(): # all units not specified by default take 1 cycle m.next = "WRITEBACK" sync += active_unit.eq(0) jal_offset = Signal(signed(21)) comb += jal_offset.eq( Cat( Const(0, 1), instr[21:31], instr[20], instr[12:20], instr[31], ).as_signed()) pc_addend = Signal(signed(32)) sync += pc_addend.eq(Mux(active_unit.jal, jal_offset, 4)) branch_addend = Signal(signed(13)) comb += branch_addend.eq( Cat( Const(0, 1), instr[8:12], instr[25:31], instr[7], instr[31], ).as_signed() # TODO is it ok that it's signed? ) with m.If(active_unit.branch): with m.If(compare.condition_met): sync += pc_addend.eq(branch_addend) new_pc = Signal(32) is_jalr_latch = Signal() # that's bad workaround with m.If(active_unit.jalr): sync += is_jalr_latch.eq(1) sync += new_pc.eq(rs1val.as_signed() + imm) with m.State("WRITEBACK"): with m.If(is_jalr_latch): sync += pc.eq(new_pc) with m.Else(): sync += pc.eq(pc + pc_addend) sync += is_jalr_latch.eq(0) # Here, rdval is already calculated. If neccessary, put it into register file. should_write_rd = self.should_write_rd = Signal() writeback = self.writeback = Signal() # for riscv-dv simulation: # detect that instruction does not perform register write to avoid infinite loop # by checking writeback & should_write_rd # TODO it will break for trap-causing instructions. comb += writeback.eq(1) comb += should_write_rd.eq( reduce( or_, [ match_shifter_unit(opcode, funct3, funct7), match_adder_unit(opcode, funct3, funct7), match_logic_unit(opcode, funct3, funct7), match_load(opcode, funct3, funct7), match_compare_unit(opcode, funct3, funct7), match_lui(opcode, funct3, funct7), match_auipc(opcode, funct3, funct7), match_jal(opcode, funct3, funct7), match_jalr(opcode, funct3, funct7), match_csr(opcode, funct3, funct7), ], ) & (rd != 0)) with m.If(should_write_rd): comb += reg_write_port.en.eq(True) m.next = "FETCH" return m
def pack(values, shape): return Cat(Const(v, shape) for v in values)
def elaborate(self, platform) -> Module: m = Module() sync = m.d.sync adat = m.d.adat comb = m.d.comb samples_write_port = self.mem.write_port() samples_read_port = self.mem.read_port(domain='comb') m.submodules += [samples_write_port, samples_read_port] # the highest bit in the FIFO marks a frame border frame_border_flag = 24 m.submodules.transmit_fifo = transmit_fifo = AsyncFIFO(width=25, depth=self._fifo_depth, w_domain="sync", r_domain="adat") # needed for output processing m.submodules.nrzi_encoder = nrzi_encoder = NRZIEncoder() transmitted_frame = Signal(30) transmit_counter = Signal(5) comb += [ self.ready_out .eq(transmit_fifo.w_rdy), self.fifo_level_out .eq(transmit_fifo.w_level), self.adat_out .eq(nrzi_encoder.nrzi_out), nrzi_encoder.data_in .eq(transmitted_frame.bit_select(transmit_counter, 1)), self.underflow_out .eq(0) ] # # Fill the transmit FIFO in the sync domain # channel_counter = Signal(3) # make sure, en is only asserted when explicitly strobed comb += samples_write_port.en.eq(0) write_frame_border = [ transmit_fifo.w_data .eq((1 << frame_border_flag) | self.user_data_in), transmit_fifo.w_en .eq(1) ] with m.FSM(): with m.State("DATA"): with m.If(self.ready_out): with m.If(self.valid_in): comb += [ samples_write_port.data.eq(self.sample_in), samples_write_port.addr.eq(self.addr_in), samples_write_port.en.eq(1) ] with m.If(self.last_in): sync += channel_counter.eq(0) comb += write_frame_border m.next = "COMMIT" # underflow: repeat last frame with m.Elif(transmit_fifo.w_level == 0): sync += channel_counter.eq(0) comb += self.underflow_out.eq(1) comb += write_frame_border m.next = "COMMIT" with m.State("COMMIT"): with m.If(transmit_fifo.w_rdy): comb += [ self.ready_out.eq(0), samples_read_port.addr .eq(channel_counter), transmit_fifo.w_data .eq(samples_read_port.data), transmit_fifo.w_en .eq(1) ] sync += channel_counter.eq(channel_counter + 1) with m.If(channel_counter == 7): m.next = "DATA" # # Read the FIFO and send data in the adat domain # # 4b/5b coding: Every 24 bit channel has 6 nibbles. # 1 bit before the sync pad and one bit before the user data nibble filler_bits = [Const(1, 1) for _ in range(7)] adat += transmit_counter.eq(transmit_counter - 1) comb += transmit_fifo.r_en.eq(0) with m.If(transmit_counter == 0): with m.If(transmit_fifo.r_rdy): comb += transmit_fifo.r_en.eq(1) with m.If(transmit_fifo.r_data[frame_border_flag] == 0): adat += [ transmit_counter.eq(29), # generate the adat data for one channel 0b1dddd1dddd1dddd1dddd1dddd1dddd where d is the PCM audio data transmitted_frame.eq(Cat(zip(list(self.chunks(transmit_fifo.r_data[:25], 4)), filler_bits))) ] with m.Else(): adat += [ transmit_counter.eq(15), # generate the adat sync_pad along with the user_bits 0b100000000001uuuu where u is user_data transmitted_frame.eq((1 << 15) | (1 << 4) | transmit_fifo.r_data[:5]) ] with m.Else(): # this should not happen: panic / stop transmitting. adat += [ transmitted_frame.eq(0x00), transmit_counter.eq(4) ] return m
def elaborate(self, platform): m = Module() sync = m.d.sync comb = m.d.comb cfg = self.mem_config # TODO XXX self.no_match on decoder m.submodules.bridge = GenericInterfaceToWishboneMasterBridge( generic_bus=self.generic_bus, wb_bus=self.wb_bus) self.decoder = m.submodules.decoder = WishboneBusAddressDecoder( wb_bus=self.wb_bus, word_size=cfg.word_size) self.initialize_mmio_devices(self.decoder, m) pe = m.submodules.pe = self.pe = PriorityEncoder(width=len(self.ports)) sorted_ports = [port for priority, port in sorted(self.ports.items())] # force 'elaborate' invocation for all mmio modules. for mmio_module, addr_space in self.mmio_cfg: setattr(m.submodules, addr_space.basename, mmio_module) addr_translation_en = self.addr_translation_en = Signal() bus_free_to_latch = self.bus_free_to_latch = Signal(reset=1) if self.with_addr_translation: m.d.comb += addr_translation_en.eq(self.csr_unit.satp.mode & ( self.exception_unit.current_priv_mode == PrivModeBits.USER)) else: m.d.comb += addr_translation_en.eq(False) with m.If(~addr_translation_en): # when translation enabled, 'bus_free_to_latch' is low during page-walk. # with no translation it's simpler - just look at the main bus. m.d.comb += bus_free_to_latch.eq(~self.generic_bus.busy) with m.If(bus_free_to_latch): # no transaction in-progress for i, p in enumerate(sorted_ports): m.d.sync += pe.i[i].eq(p.en) virtual_req_bus_latch = LoadStoreInterface() phys_addr = self.phys_addr = Signal(32) # translation-unit controller signals. start_translation = self.start_translation = Signal() translation_ack = self.translation_ack = Signal() gb = self.generic_bus with m.If(self.decoder.no_match & self.wb_bus.cyc): m.d.comb += self.exception_unit.badaddr.eq(gb.addr) with m.If(gb.store): m.d.comb += self.exception_unit.m_store_error.eq(1) with m.Elif(gb.is_fetch): m.d.comb += self.exception_unit.m_fetch_error.eq(1) with m.Else(): m.d.comb += self.exception_unit.m_load_error.eq(1) with m.If(~pe.none): # transaction request occured for i, priority in enumerate(sorted_ports): with m.If(pe.o == i): bus_owner_port = sorted_ports[i] with m.If(~addr_translation_en): # simple case, no need to calculate physical address comb += gb.connect(bus_owner_port) with m.Else(): # page-walk performs multiple memory operations - will reconnect 'generic_bus' multiple times with m.FSM(): first = self.first = Signal( ) # TODO get rid of that with m.State("TRANSLATE"): comb += [ start_translation.eq(1), bus_free_to_latch.eq(0), ] sync += virtual_req_bus_latch.connect( bus_owner_port, exclude=[ name for name, _, dir in generic_bus_layout if dir == DIR_FANOUT ]) with m.If( translation_ack ): # wait for 'phys_addr' to be set by page-walk algorithm. m.next = "REQ" sync += first.eq(1) with m.State("REQ"): comb += gb.connect(bus_owner_port, exclude=["addr"]) comb += gb.addr.eq( phys_addr) # found by page-walk with m.If(first): sync += first.eq(0) with m.Else(): # without 'first' signal '~gb.busy' would be high at the very beginning with m.If(~gb.busy): comb += bus_free_to_latch.eq(1) m.next = "TRANSLATE" req_is_write = Signal() pte = self.pte = Record(pte_layout) vaddr = Record(virt_addr_layout) comb += [ req_is_write.eq(virtual_req_bus_latch.store), vaddr.eq(virtual_req_bus_latch.addr), ] @unique class Issue(IntEnum): OK = 0 PAGE_INVALID = 1 WRITABLE_NOT_READABLE = 2 LACK_PERMISSIONS = 3 FIRST_ACCESS = 4 MISALIGNED_SUPERPAGE = 5 LEAF_IS_NO_LEAF = 6 self.error_code = Signal(Issue) def error(code: Issue): m.d.sync += self.error_code.eq(code) # Code below implements algorithm 4.3.2 in Risc-V Privileged specification, v1.10 sv32_i = Signal(reset=1) root_ppn = self.root_ppn = Signal(22) if not self.with_addr_translation: return m with m.FSM(): with m.State("IDLE"): with m.If(start_translation): sync += sv32_i.eq(1) sync += root_ppn.eq(self.csr_unit.satp.ppn) m.next = "TRANSLATE" with m.State("TRANSLATE"): vpn = self.vpn = Signal(10) comb += vpn.eq(Mux( sv32_i, vaddr.vpn1, vaddr.vpn0, )) comb += [ gb.en.eq(1), gb.addr.eq(Cat(Const(0, 2), vpn, root_ppn)), gb.store.eq(0), gb.mask.eq(0b1111), # TODO use -1 ] with m.If(gb.ack): sync += pte.eq(gb.read_data) m.next = "PROCESS_PTE" with m.State("PROCESS_PTE"): with m.If(~pte.v): error(Issue.PAGE_INVALID) with m.If(pte.w & ~pte.r): error(Issue.WRITABLE_NOT_READABLE) is_leaf = lambda pte: pte.r | pte.x with m.If(is_leaf(pte)): with m.If(~pte.u & (self.exception_unit.current_priv_mode == PrivModeBits.USER)): error(Issue.LACK_PERMISSIONS) with m.If(~pte.a | (req_is_write & ~pte.d)): error(Issue.FIRST_ACCESS) with m.If(sv32_i.bool() & pte.ppn0.bool()): error(Issue.MISALIGNED_SUPERPAGE) # phys_addr could be 34 bits long, but our interconnect is 32-bit long. # below statement cuts lowest two bits of r-value. sync += phys_addr.eq( Cat(vaddr.page_offset, pte.ppn0, pte.ppn1)) with m.Else(): # not a leaf with m.If(sv32_i == 0): error(Issue.LEAF_IS_NO_LEAF) sync += root_ppn.eq( Cat(pte.ppn0, pte.ppn1)) # pte a is pointer to the next level m.next = "NEXT" with m.State("NEXT"): # Note that we cannot check 'sv32_i == 0', becuase superpages can be present. with m.If(is_leaf(pte)): sync += sv32_i.eq(1) comb += translation_ack.eq( 1) # notify that 'phys_addr' signal is set m.next = "IDLE" with m.Else(): sync += sv32_i.eq(0) m.next = "TRANSLATE" return m