def __init__(self, size, init=None): self.source = source = stream.Endpoint(spi_phy_data_layout) self.sink = sink = stream.Endpoint(spi_phy_ctl_layout) mem = Memory(32, size // 4, init=init) read_addr = Signal(32) self.cs_n = Signal() read_port = mem.get_port(async_read=True) self.comb += read_port.adr.eq(read_addr) self.specials += mem, read_port commands = { CMD: [NextValue(read_addr, sink.data[2:31])], # word addressed memory READ: [NextState("DATA")], } self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm.act( "IDLE", sink.ready.eq(1), If( sink.ready & sink.valid, Case(sink.cmd, commands), ), ) fsm.act( "DATA", source.valid.eq(1), source.data.eq(read_port.dat_r), If( source.ready & source.valid, NextValue(read_addr, read_addr + 1), NextState("IDLE"), ), )
def __init__(self, endpoint, address_decoder): self.wishbone = wishbone.Interface() # # # port = endpoint.crossbar.get_slave_port(address_decoder) self.submodules.fsm = fsm = FSM() fsm.act( "IDLE", If(port.sink.stb & port.sink.sop, If( port.sink.we, NextState("WRITE"), ).Else(NextState("READ"))).Else(port.sink.ack.eq( port.sink.stb))) fsm.act("WRITE", self.wishbone.adr.eq(port.sink.adr[2:]), self.wishbone.dat_w.eq(port.sink.dat[:32]), self.wishbone.sel.eq(0xf), self.wishbone.stb.eq(1), self.wishbone.we.eq(1), self.wishbone.cyc.eq(1), If(self.wishbone.ack, port.sink.ack.eq(1), NextState("IDLE"))) fsm.act("READ", self.wishbone.adr.eq(port.sink.adr[2:]), self.wishbone.stb.eq(1), self.wishbone.we.eq(0), self.wishbone.cyc.eq(1), If(self.wishbone.ack, NextState("COMPLETION"))) self.sync += \ If(self.wishbone.stb & self.wishbone.ack, port.source.dat.eq(self.wishbone.dat_r), ) fsm.act("COMPLETION", port.source.stb.eq(1), port.source.sop.eq(1), port.source.eop.eq(1), port.source.len.eq(1), port.source.err.eq(0), port.source.tag.eq(port.sink.tag), port.source.adr.eq(port.sink.adr), port.source.cmp_id.eq(endpoint.phy.id), port.source.req_id.eq(port.sink.req_id), If(port.source.ack, port.sink.ack.eq(1), NextState("IDLE")))
def __init__(self): self.busy = Signal() self.sink = Endpoint([('d', 8)]) self.source = Endpoint(CMD_REC) sm = FSM(reset_state="IDLE") self.submodules += sm # Basic checksum token = self.source.payload token_next = Record(CMD_REC) self.sync += token.eq(token_next) self.comb += token_next.eq(token) sm.act( "IDLE", self.sink.ack.eq(1), If(self.sink.stb, If(self.sink.payload.d == 0x55, NextState("ADRH")))) def parse_state(st, to, *update): sm.act(st, self.sink.ack.eq(1), If(self.sink.stb, NextState(to), *update)) parse_state('ADRH', 'ADRL', token_next.wr.eq(self.sink.payload.d[7]), token_next.a[8:14].eq(self.sink.payload.d[:6])) parse_state('ADRL', 'DATA', token_next.a[0:8].eq(self.sink.payload.d)), parse_state('DATA', 'CKSUM', token_next.d.eq(self.sink.payload.d)), sm.act("CKSUM", self.sink.ack.eq(1), If(self.sink.stb, NextState('ISSUE'))) sm.act("ISSUE", self.source.stb.eq(1), If(self.source.ack, NextState('IDLE')))
def delay_gen(next_state, width=xfer_width, bits=xfer_len, op=[]): res = [ If( delay_cnt == (bits - width + latency * width), source.valid.eq(1), If( source.ready, NextValue(delay_cnt, 0), NextState(next_state), *op, ), ).Else(NextValue(delay_cnt, delay_cnt + width), ) ] return res
def __init__(self, pads): STATUS_FULL = 1 STATUS_EMPTY = 2 self.shift_reg = shift_reg = CSRStorage(8, write_from_dev=True) self.status = status = CSRStorage(2, reset=STATUS_EMPTY, write_from_dev=True) self.slave_addr = slave_addr = CSRStorage(7) ### scl_raw = Signal() sda_i = Signal() sda_raw = Signal() sda_drv = Signal() scl_drv = Signal() _sda_drv_reg = Signal() self._sda_i_async = _sda_i_async = Signal() self._scl_i_async = _scl_i_async = Signal() _scl_drv_reg = Signal() self.sync += _sda_drv_reg.eq(sda_drv) self.sync += _scl_drv_reg.eq(scl_drv) self.comb += [ pads.scl.w.eq(0), pads.scl.oe.eq(_scl_drv_reg), _scl_i_async.eq(pads.scl.r), pads.sda.w.eq(0), pads.sda.oe.eq(_sda_drv_reg), _sda_i_async.eq(pads.sda.r), ] self.specials += [ MultiReg(_scl_i_async, scl_raw), MultiReg(_sda_i_async, sda_raw), ] # for debug self.scl = scl_raw self.sda_i = sda_i self.sda_o = Signal() self.comb += self.sda_o.eq(~_sda_drv_reg) self.sda_oe = _sda_drv_reg shift_reg_full = Signal() shift_reg_empty = Signal() scl_i = Signal() samp_count = Signal(3) samp_carry = Signal() self.sync += [ Cat(samp_count, samp_carry).eq(samp_count + 1), If(samp_carry, scl_i.eq(scl_raw), sda_i.eq(sda_raw)) ] scl_r = Signal() sda_r = Signal() scl_rising = Signal() scl_falling = Signal() sda_rising = Signal() sda_falling = Signal() self.sync += [scl_r.eq(scl_i), sda_r.eq(sda_i)] self.comb += [ shift_reg_full.eq(status.storage[0]), shift_reg_empty.eq(status.storage[1]), scl_rising.eq(scl_i & ~scl_r), scl_falling.eq(~scl_i & scl_r), sda_rising.eq(sda_i & ~sda_r), sda_falling.eq(~sda_i & sda_r) ] start = Signal() self.comb += start.eq(scl_i & sda_falling) din = Signal(8) counter = Signal(max=9) counter_reset = Signal() self.sync += [ If(start | counter_reset, counter.eq(0)), If( scl_rising, If(counter == 8, counter.eq(0)).Else(counter.eq(counter + 1), din.eq(Cat(sda_i, din[:7])))) ] self.din = din self.counter = counter is_read = Signal() update_is_read = Signal() self.sync += If(update_is_read, is_read.eq(din[0])) data_bit = Signal() zero_drv = Signal() data_drv = Signal() pause_drv = Signal() self.comb += scl_drv.eq(pause_drv) self.comb += If(zero_drv, sda_drv.eq(1)).Elif(data_drv, sda_drv.eq(~data_bit)) data_drv_en = Signal() data_drv_stop = Signal() self.sync += If(data_drv_en, data_drv.eq(1)).Elif(data_drv_stop, data_drv.eq(0)) self.sync += If( data_drv_en, chooser(shift_reg.storage, counter, data_bit, 8, reverse=True)) self.submodules.fsm = fsm = FSM() fsm.act( "WAIT_START", data_drv_stop.eq(1), ) fsm.act( "RCV_ADDRESS", data_drv_stop.eq(1), If( counter == 8, If( din[1:] == slave_addr.storage, update_is_read.eq(1), NextState("ACK_ADDRESS0"), ).Else(NextState("WAIT_START"), ))) fsm.act( "ACK_ADDRESS0", counter_reset.eq(1), If(~scl_i, NextState("ACK_ADDRESS1")), ) fsm.act( "ACK_ADDRESS1", counter_reset.eq(1), zero_drv.eq(1), If(scl_i, NextState("ACK_ADDRESS2")), ) fsm.act("ACK_ADDRESS2", counter_reset.eq(1), zero_drv.eq(1), If(~scl_i, NextState("PAUSE"))) fsm.act( "PAUSE", counter_reset.eq(1), pause_drv.eq(1), If( ~shift_reg_empty & is_read, counter_reset.eq(1), NextState("DO_READ"), ).Elif( ~shift_reg_full & ~is_read, NextState("DO_WRITE"), )) fsm.act( "DO_READ", If( ~scl_i, If( counter == 8, data_drv_stop.eq(1), status.we.eq(1), status.dat_w.eq(STATUS_EMPTY), NextState("ACK_READ0"), ).Else(data_drv_en.eq(1), ))) fsm.act( "ACK_READ0", counter_reset.eq(1), If( scl_rising, If( sda_i, NextState("WAIT_START"), ).Else(NextState("ACK_READ1"), ))) fsm.act("ACK_READ1", counter_reset.eq(1), If( scl_falling, NextState("PAUSE"), )) fsm.act( "DO_WRITE", If( counter == 8, shift_reg.dat_w.eq(din), shift_reg.we.eq(1), NextState("ACK_WRITE0"), )) fsm.act( "ACK_WRITE0", counter_reset.eq(1), If(~scl_i, NextState("ACK_WRITE1")), ) fsm.act( "ACK_WRITE1", counter_reset.eq(1), zero_drv.eq(1), If(scl_i, NextState("ACK_WRITE2")), ) fsm.act( "ACK_WRITE2", counter_reset.eq(1), zero_drv.eq(1), If( ~scl_i, NextState("PAUSE"), status.we.eq(1), status.dat_w.eq(STATUS_FULL), )) for state in fsm.actions.keys(): fsm.act(state, If(start, NextState("RCV_ADDRESS"))) for state in fsm.actions.keys(): fsm.act(state, If(self.slave_addr.re, NextState("WAIT_START")))
def skip(state): return [ self.sink.ack.eq(1), NextState(state) ]
def write_hdr(statename, nextname, hdr_offs, val): self.fsm.act(statename, NextState(nextname), wrport.adr.eq(self.produce_header.v + hdr_offs), wrport.dat_w.eq(val), wrport.we.eq(1))
def __init__(self, master, slave): dw_from = len(master.dat_r) dw_to = len(slave.dat_w) ratio = dw_to // dw_from ratiobits = log2_int(ratio) # # # write = Signal() evict = Signal() refill = Signal() read = Signal() address = FlipFlop(30) self.submodules += address self.comb += address.d.eq(master.adr) counter = Signal(max=ratio) counter_ce = Signal() counter_reset = Signal() self.sync += \ If(counter_reset, counter.eq(0) ).Elif(counter_ce, counter.eq(counter + 1) ) counter_offset = Signal(max=ratio) counter_done = Signal() self.comb += [ counter_offset.eq(address.q), counter_done.eq((counter + counter_offset) == ratio - 1) ] cached_data = Signal(dw_to) cached_sel = Signal(dw_to // 8) end_of_burst = Signal() self.comb += end_of_burst.eq(~master.cyc | (master.stb & master.cyc & master.ack & ((master.cti == 7) | counter_done))) need_refill = FlipFlop(reset=1) self.submodules += need_refill self.comb += [need_refill.reset.eq(end_of_burst), need_refill.d.eq(0)] # Main FSM self.submodules.fsm = fsm = FSM() fsm.act( "IDLE", counter_reset.eq(1), If( master.stb & master.cyc, address.ce.eq(1), If(master.we, NextState("WRITE")).Else( If(need_refill.q, NextState("REFILL")).Else(NextState("READ"))))) fsm.act( "WRITE", If(master.stb & master.cyc, write.eq(1), counter_ce.eq(1), master.ack.eq(1), If(counter_done, NextState("EVICT"))).Elif(~master.cyc, NextState("EVICT"))) fsm.act("EVICT", evict.eq(1), slave.stb.eq(1), slave.we.eq(1), slave.cyc.eq(1), slave.dat_w.eq(cached_data), slave.sel.eq(cached_sel), If(slave.ack, NextState("IDLE"))) fsm.act("REFILL", refill.eq(1), slave.stb.eq(1), slave.cyc.eq(1), If(slave.ack, need_refill.ce.eq(1), NextState("READ"))) fsm.act("READ", read.eq(1), If(master.stb & master.cyc, master.ack.eq(1)), NextState("IDLE")) # Address self.comb += [ slave.cti.eq( 7), # we are not able to generate bursts since up-converting slave.adr.eq(address.q[ratiobits:]) ] # Datapath cached_datas = [FlipFlop(dw_from) for i in range(ratio)] cached_sels = [FlipFlop(dw_from // 8) for i in range(ratio)] self.submodules += cached_datas, cached_sels cases = {} for i in range(ratio): write_sel = Signal() cases[i] = write_sel.eq(1) self.comb += [ cached_sels[i].reset.eq(counter_reset), If( write, cached_datas[i].d.eq(master.dat_w), ).Else(cached_datas[i].d.eq(slave.dat_r[dw_from * i:dw_from * (i + 1)])), cached_sels[i].d.eq(master.sel), If((write & write_sel) | refill, cached_datas[i].ce.eq(1), cached_sels[i].ce.eq(1)) ] self.comb += Case(counter + counter_offset, cases) cases = {} for i in range(ratio): cases[i] = master.dat_r.eq(cached_datas[i].q) self.comb += Case(address.q[:ratiobits], cases) self.comb += [ cached_data.eq(Cat([cached_data.q for cached_data in cached_datas])), cached_sel.eq(Cat([cached_sel.q for cached_sel in cached_sels])) ]
def __init__(self, dram_port, nslots): bus_aw = dram_port.aw bus_dw = dram_port.dw alignment_bits = bits_for(bus_dw // 8) - 1 fifo_word_width = bus_dw self.frame = stream.Endpoint([("sof", 1), ("pixels", fifo_word_width)]) self._frame_size = CSRStorage(bus_aw + alignment_bits) self.submodules._slot_array = _SlotArray(nslots, bus_aw, alignment_bits) self.ev = self._slot_array.ev # # # # address generator + maximum memory word count to prevent DMA buffer # overrun reset_words = Signal() count_word = Signal() last_word = Signal() current_address = Signal(bus_aw) mwords_remaining = Signal(bus_aw) self.comb += [ self._slot_array.address_reached.eq(current_address), last_word.eq(mwords_remaining == 1) ] self.sync += [ If(reset_words, current_address.eq(self._slot_array.address), mwords_remaining.eq( self._frame_size.storage[alignment_bits:])).Elif( count_word, current_address.eq(current_address + 1), mwords_remaining.eq(mwords_remaining - 1)) ] memory_word = Signal(bus_dw) pixbits = [] for i in range(bus_dw // 16): pixbits.append(self.frame.pixels) self.comb += memory_word.eq(Cat(*pixbits)) # bus accessor self.submodules._bus_accessor = LiteDRAMDMAWriter(dram_port) self.comb += [ self._bus_accessor.sink.address.eq(current_address), self._bus_accessor.sink.data.eq(memory_word) ] # control FSM fsm = FSM() self.submodules += fsm fsm.act( "WAIT_SOF", reset_words.eq(1), self.frame.ready.eq(~self._slot_array.address_valid | ~self.frame.sof), If( self._slot_array.address_valid & self.frame.sof & self.frame.valid, NextState("TRANSFER_PIXELS"))) fsm.act( "TRANSFER_PIXELS", self.frame.ready.eq(self._bus_accessor.sink.ready), If( self.frame.valid, self._bus_accessor.sink.valid.eq(1), If(self._bus_accessor.sink.ready, count_word.eq(1), If(last_word, NextState("EOF"))))) fsm.act( "EOF", If(~dram_port.wdata.valid, self._slot_array.address_done.eq(1), NextState("WAIT_SOF")))
def __init__(self, ulpi_bus): self.ulpi_bus = ulpi_bus fsm = FSM() self.submodules += fsm self.WantRx = Signal() self.RxByte = Signal(8) self.RxCmd = Signal() self.NextCycleRx = Signal() self.RegWriteValid = Signal() self.RegAddrW = Signal(6) self.RegDataW = Signal(8) self.RegRead = Signal() self.RegAddrR = Signal(6) self.RegDataR = Signal(8) self.StateTX = Signal() SetRegAddrR = Signal() SetRegAddrW = Signal() SetRegDataW = Signal() fsm.act("TXCMD", If(self.WantRx, NextState("TA1") ).Elif(ulpi_bus.do[6:8] == 0b10, NextState("RW0") # REGW ).Elif(ulpi_bus.do[6:8] == 0b11, NextState("RR0") # REGR ).Else(NextState("TXCMD"), ulpi_bus.dir.eq(0), self.StateTX.eq(1) )) fsm.act("TA1", NextState("RX"), ulpi_bus.dir.eq(1), self.NextCycleRx.eq(1) ) fsm.act("RX", If(self.WantRx, NextState("RX"), ulpi_bus.dir.eq(1), ulpi_bus.di.eq(self.RxByte), ulpi_bus.nxt.eq(self.RxCmd), self.NextCycleRx.eq(1) ).Else(NextState("TA2") )) fsm.act("TA2", NextState("TXCMD"), ulpi_bus.dir.eq(0) ) fsm.act("RW0", If(self.WantRx, NextState("TA1") ).Else( NextState("RW1"), ulpi_bus.dir.eq(0), ulpi_bus.nxt.eq(1), SetRegAddrW.eq(1) )) fsm.act("RW1", NextState("RW2"), ulpi_bus.dir.eq(0), ulpi_bus.nxt.eq(1), SetRegDataW.eq(1) ) fsm.act("RW2", NextState("TXCMD"), self.RegWriteValid.eq(ulpi_bus.stp)) fsm.act("RR0", If(self.WantRx, NextState("TA1") ).Else( NextState("RR1"), SetRegAddrR.eq(1), ulpi_bus.nxt.eq(1), )) fsm.act("RR1", ulpi_bus.dir.eq(1), If(self.WantRx, NextState("RX"), ulpi_bus.nxt.eq(1), # indicating abort ).Else( NextState("RRD") )) fsm.act("RRD", ulpi_bus.dir.eq(1), ulpi_bus.di.eq(self.RegDataR), NextState("TA2" )) self.sync += If(SetRegAddrR, self.RegAddrR.eq(ulpi_bus.do[0:6])) self.sync += self.RegRead.eq(SetRegAddrR) self.sync += If(SetRegAddrW, self.RegAddrW.eq(ulpi_bus.do[0:6])) self.sync += If(SetRegDataW, self.RegDataW.eq(ulpi_bus.do))
def __init__(self, usb_core, clk_freq=12000000, magic_packet=0x43, cdc=False): self.wishbone = wishbone.Interface() self.background = ModuleDoc(title="USB Wishbone Bridge", body=""" This bridge provides a transparent bridge to the target device's Wishbone bus over USB. It can operate without interfering with the device's USB stack. It is simple enough to be able to work even if the USB stack is not enumerated, though the host may not cooperate.""" ) self.protocol = ModuleDoc(title="USB Wishbone Debug Protocol", body=""" The protocol transfers four bytes a time in big-endian (i.e. USB) order. It uses SETUP packets with the special type (0x43) as an `attention` word. This is then followed by an ``OUT`` packet. .. wavedrom:: :caption: Write to Wishbone { "signal": [ ["Request", { "name": 'data', "wave": 'x222...22x', "data": '0x43 0x00 [ADDRESS] 0x04 0x00' }, { "name": 'data bits', "wave": 'xxx2222xxx', "data": '7:0 15:8 23:16 31:24'}, { "name": 'usb meaning', "wave": 'x222.2.2.x', "data": 'bReq bTyp wValue wIndex wLength' }, { "name": 'usb byte', "wave": 'x22222222x', "data": '1 2 3 4 5 6 7 8' } ], {}, ["Payload", { "name": 'data', "wave": 'x3...x', "data": '[DATA]'}, { "name": 'data bits', "wave": 'x3333x', "data": '7:0 15:8 23:16 31:24'}, { "name": 'usb meaning', "wave": 'x3...x', "data": 'OUT' }, { "name": 'usb byte', "wave": 'x3333x', "data": '1 2 3 4'} ] ]} To read data from the device, set the top bit of the `bRequestType`, followed by an ``IN`` packet. .. wavedrom:: :caption: Read from Wishbone { "signal": [ ['Request', { "name": 'data', "wave": 'x222...22x', "data": '0xC3 0x00 [ADDRESS] 0x04 0x00' }, { "name": 'data bits', "wave": 'xxx2222xxx', "data": '7:0 15:8 23:16 31:24'}, { "name": 'usb meaning', "wave": 'x222.2.2.x', "data": 'bReq bTyp wValue wIndex wLength' }, { "name": 'usb byte', "wave": 'x22222222x', "data": '1 2 3 4 5 6 7 8' } ], {}, ["Payload", { "name": 'data', "wave": 'x5...x', "data": '[DATA]'}, { "name": 'data bits', "wave": 'x5555x', "data": '7:0 15:8 23:16 31:24'}, { "name": 'usb meaning', "wave": 'x5...x', "data": 'IN' }, { "name": 'usb byte', "wave": 'x5555x', "data": '1 2 3 4'} ] ]} """) # # # byte_counter = Signal(3, reset_less=True) byte_counter_reset = Signal() byte_counter_ce = Signal() self.sync.usb_12 += \ If(byte_counter_reset, byte_counter.eq(0) ).Elif(byte_counter_ce, byte_counter.eq(byte_counter + 1) ) # Unlike the UART or Ethernet bridges, we explicitly only # support two commands: reading and writing. This gets # integrated into the USB protocol, so it's not really a # state. 1 is "USB Device to Host", and is therefore a "read", # while 0 is "USB Host to Device", and is therefore a "write". cmd = Signal(1, reset_less=True) cmd_ce = Signal() # Add a bridge to allow this module (in the usb_12 domain) to access # the main Wishbone bridge (potentially in some other domain). # Ensure this bridge is placed in the "sys" domain. send_to_wishbone = Signal() reply_from_wishbone = Signal() transfer_active = Signal() if cdc: self.submodules.wb_cd_bridge = wb_cd_bridge = FSM( reset_state="IDLE") self.submodules.usb_to_wb = usb_to_wb = PulseSynchronizer( "usb_12", "sys") self.submodules.wb_to_uwb = wb_to_usb = PulseSynchronizer( "sys", "usb_12") send_to_wishbone = usb_to_wb.i reply_from_wishbone = wb_to_usb.o else: self.comb += [ If( send_to_wishbone | transfer_active, self.wishbone.stb.eq(1), self.wishbone.we.eq(~cmd), self.wishbone.cyc.eq(1), ), reply_from_wishbone.eq(self.wishbone.ack | self.wishbone.err), ] # Instead of self.source and self.sink, we let the wrapping # module handle packing and unpacking the data. self.sink_data = Signal(8) # True when the "sink" value has data self.sink_valid = Signal() self.send_ack = Signal() # Indicates whether a "debug" packet is currently being processed self.n_debug_in_progress = Signal(reset=1) address = Signal(32, reset_less=True) address_ce = Signal() data = Signal(32, reset_less=True) rd_data = Signal(32, reset_less=True) rx_data_ce = Signal() # wishbone_response = Signal(32, reset_less=True) self.sync.usb_12 += [ If(cmd_ce, cmd.eq(usb_core.data_recv_payload[7:8])), If(address_ce, address.eq(Cat(address[8:32], usb_core.data_recv_payload))), If(rx_data_ce, data.eq(Cat(data[8:32], usb_core.data_recv_payload))) ] # The Litex Wishbone `dat_r` line is a shared medium, meaning the value # changes often. Capture our own copy of this data when a wishbone ACK # occurs. self.sync.sys += [ If(self.wishbone.ack, rd_data.eq(self.wishbone.dat_r)) ] fsm = ResetInserter()(ClockDomainsRenamer("usb_12")( FSM(reset_state="IDLE"))) self.submodules += fsm fsm.act( "IDLE", self.n_debug_in_progress.eq(1), If( usb_core.data_recv_put, If( usb_core.tok == PID.SETUP, If( usb_core.endp == 0, # If we get a SETUP packet with a "Vendor" type # going to this device, treat that as a DEBUG packet. cmd_ce.eq(1), byte_counter_reset.eq(1), If( usb_core.data_recv_payload[0:7] == magic_packet, NextState("RECEIVE_ADDRESS"), ).Else( # Wait for the end of the packet, to avoid # messing with normal USB operation NextState("WAIT_PKT_END"), ), )))) # The target address comes as the wValue and wIndex in the SETUP # packet. Once we get that data, we're ready to do the operation. fsm.act( "RECEIVE_ADDRESS", self.n_debug_in_progress.eq(0), If( usb_core.data_recv_put, byte_counter_ce.eq(1), If( (byte_counter >= 1), If( (byte_counter <= 4), address_ce.eq(1), ), ), ), # We don't need to explicitly ACK the SETUP packet, because # they're always acknowledged implicitly. Wait until the # packet ends (i.e. until we've sent the ACK packet) before # moving to the next state. If( usb_core.end, byte_counter_reset.eq(1), If( cmd, send_to_wishbone.eq(1), NextState("READ_DATA"), ).Else(NextState("RECEIVE_DATA"), ), ), ) fsm.act( "RECEIVE_DATA", # Set the "ACK" bit to 1, so we acknowledge the packet # once it comes in, and so that we're in a position to # receive data. self.send_ack.eq(usb_core.endp == 0), self.n_debug_in_progress.eq(0), If( usb_core.endp == 0, If( usb_core.data_recv_put, rx_data_ce.eq(1), byte_counter_ce.eq(1), If( byte_counter == 3, NextState("WAIT_RECEIVE_DATA_END"), ).Elif( usb_core.end, send_to_wishbone.eq(1), NextState("WRITE_DATA"), )))) fsm.act( "WAIT_RECEIVE_DATA_END", self.n_debug_in_progress.eq(0), self.send_ack.eq(1), # Wait for the end of the USB packet, if # it hasn't come already. If(usb_core.end, send_to_wishbone.eq(1), NextState("WRITE_DATA"))) if cdc: wb_cd_bridge.act( "IDLE", If( usb_to_wb.o, NextState("DO_OP"), ), ) wb_cd_bridge.act( "DO_OP", self.wishbone.stb.eq(1), self.wishbone.we.eq(~cmd), self.wishbone.cyc.eq(1), If( self.wishbone.ack | self.wishbone.err, NextState("IDLE"), wb_to_usb.i.eq(1), ), ) self.comb += [ # Trim off the last two bits of the address, because wishbone addresses # are word-based, and a word is 32-bits. Therefore, the last two bits # should always be zero. self.wishbone.adr.eq(address[2:]), self.wishbone.dat_w.eq(data), self.wishbone.sel.eq(2**len(self.wishbone.sel) - 1) ] fsm.act("WRITE_DATA", self.n_debug_in_progress.eq(0), transfer_active.eq(1), If( reply_from_wishbone, NextState("WAIT_SEND_ACK_START"), )) fsm.act("READ_DATA", self.n_debug_in_progress.eq(0), transfer_active.eq(1), If(reply_from_wishbone, NextState("SEND_DATA_WAIT_START"))) fsm.act( "SEND_DATA_WAIT_START", self.n_debug_in_progress.eq(0), byte_counter_reset.eq(1), If( usb_core.start, NextState("SEND_DATA"), ), ) self.comb += \ chooser(rd_data, byte_counter, self.sink_data, n=4, reverse=False) fsm.act( "SEND_DATA", self.n_debug_in_progress.eq(0), If( usb_core.endp != 0, NextState("SEND_DATA_WAIT_START"), ), # Keep sink_valid high during the packet, which indicates we have data # to send. This also causes an "ACK" to be transmitted. self.sink_valid.eq(usb_core.endp == 0), If( usb_core.data_send_get, byte_counter_ce.eq(1), ), If(byte_counter == 4, NextState("WAIT_SEND_ACK_START")), If(usb_core.end, NextState("WAIT_SEND_ACK_START"))) # To validate the transaction was successful, the host will now # send an "IN" request. Acknowledge that by setting # self.send_ack, without putting anything in self.sink_data. fsm.act( "WAIT_SEND_ACK_START", self.n_debug_in_progress.eq(0), If(usb_core.start, NextState("SEND_ACK")), ) # Send the ACK. If the endpoint number is incorrect, go back and # wait again. fsm.act( "SEND_ACK", self.n_debug_in_progress.eq(0), If(usb_core.endp != 0, NextState("WAIT_SEND_ACK_START")), # If(usb_core.retry, # If(cmd, # byte_counter_reset.eq(1), # NextState("SEND_DATA"), # ), # ), self.send_ack.eq(usb_core.endp == 0), If( usb_core.end, NextState("IDLE"), )) fsm.act("WAIT_PKT_END", self.n_debug_in_progress.eq(1), If( usb_core.end, NextState("IDLE"), ))
def __init__(self, usb_core, magic_packet=0x43): self.wishbone = wishbone.Interface() self.background = ModuleDoc(title="USB Wishbone Bridge", body=""" This bridge provides a transparent bridge to the target device's Wishbone bus over USB. It can operate without interfering with the device's USB stack. It is simple enough to be able to work even if the USB stack is not enumerated, though the host may not cooperate.""") self.protocol = ModuleDoc(title="USB Wishbone Debug Protocol", body=""" The protocol transfers four bytes a time in big-endian (i.e. USB) order. It uses SETUP packets with the special type (0x43) as an `attention` word. This is then followed by an ``OUT`` packet. .. wavedrom:: :caption: Write to Wishbone { "signal": [ ["Request", { "name": 'data', "wave": 'x222...22x', "data": '0x43 0x00 [ADDRESS] 0x04 0x00' }, { "name": 'data bits', "wave": 'xxx2222xxx', "data": '7:0 15:8 23:16 31:24'}, { "name": 'usb meaning', "wave": 'x222.2.2.x', "data": 'bReq bTyp wValue wIndex wLength' }, { "name": 'usb byte', "wave": 'x22222222x', "data": '1 2 3 4 5 6 7 8' } ], {}, ["Payload", { "name": 'data', "wave": 'x3...x', "data": '[DATA]'}, { "name": 'data bits', "wave": 'x3333x', "data": '7:0 15:8 23:16 31:24'}, { "name": 'usb meaning', "wave": 'x3...x', "data": 'OUT' }, { "name": 'usb byte', "wave": 'x3333x', "data": '1 2 3 4'} ] ]} To read data from the device, set the top bit of the `bRequestType`, followed by an ``IN`` packet. .. wavedrom:: :caption: Read from Wishbone { "signal": [ ['Request', { "name": 'data', "wave": 'x222...22x', "data": '0xC3 0x00 [ADDRESS] 0x04 0x00' }, { "name": 'data bits', "wave": 'xxx2222xxx', "data": '7:0 15:8 23:16 31:24'}, { "name": 'usb meaning', "wave": 'x222.2.2.x', "data": 'bReq bTyp wValue wIndex wLength' }, { "name": 'usb byte', "wave": 'x22222222x', "data": '1 2 3 4 5 6 7 8' } ], {}, ["Payload", { "name": 'data', "wave": 'x5...x', "data": '[DATA]'}, { "name": 'data bits', "wave": 'x5555x', "data": '7:0 15:8 23:16 31:24'}, { "name": 'usb meaning', "wave": 'x5...x', "data": 'IN' }, { "name": 'usb byte', "wave": 'x5555x', "data": '1 2 3 4'} ] ]} """) # # # # Unlike the UART or Ethernet bridges, we explicitly only # support two commands: reading and writing. This gets # integrated into the USB protocol, so it's not really a # state. 1 is "USB Device to Host", and is therefore a "read", # while 0 is "USB Host to Device", and is therefore a "write". self.cmd = cmd = Signal(1, reset_less=True) cmd_ce = Signal() self.data_phase = Signal() # Instead of self.source and self.sink, we let the wrapping # module handle packing and unpacking the data. self.sink_data = Signal(8) # True when the "sink" value has data self.sink_valid = Signal() self.send_ack = Signal() # Indicates whether a "debug" packet is currently being processed self.n_debug_in_progress = Signal(reset=1) byte_counter = Signal(17, reset_less=True) # up to 64k + 1 byte_counter_reset = Signal() byte_counter_ce = Signal() self.sync.usb_12 += \ If(byte_counter_reset, byte_counter.eq(0) ).Elif(byte_counter_ce, byte_counter.eq(byte_counter + 1) ) burst_counter = Signal(7, reset_less=True) # up to 64 + 1 burst_counter_ce = Signal() self.sync.usb_12 += \ If(usb_core.start, burst_counter.eq(0) ).Elif(burst_counter_ce, burst_counter.eq(burst_counter + 1) ) self.address = address = Signal(32, reset_less=True) address_ce = Signal() address_inc = Signal() self.length = length = Signal(16, reset_less=True) length_ce = Signal() self.data = data = Signal(32) self.rd_data = Signal(32) rx_data_ce = Signal() self.sync.usb_12 += [ If(cmd_ce, cmd.eq(usb_core.data_recv_payload[7:8])), If(address_ce, address.eq(Cat(address[8:32], usb_core.data_recv_payload)), ), If(length_ce, length.eq(Cat(length[8:16],usb_core.data_recv_payload))), If(rx_data_ce, data.eq(Cat(data[8:32], usb_core.data_recv_payload)) ) ] # Add a bridge to allow this module (in the usb_12 domain) to access # the main Wishbone bridge (potentially in some other domain). # Ensure this bridge is placed in the "sys" domain. self.cmd_sys = cmd_sys = Signal() self.specials += MultiReg(cmd, cmd_sys) prefetch_go = Signal() prefetch_go_sys = Signal() self.specials += MultiReg(prefetch_go, prefetch_go_sys) ### cross clock domains using a FIFO. also makes burst access possible. self.submodules.write_fifo = ClockDomainsRenamer({"write": "usb_12", "read": "sys"})(AsyncFIFOBuffered(width=32, depth=64//4)) self.submodules.read_fifo = ClockDomainsRenamer({"write": "sys", "read": "usb_12"})(AsyncFIFOBuffered(width=32, depth=64//4)) self.comb += [ # clk12 domain self.write_fifo.din.eq(data), # data coming from USB interface self.rd_data.eq(self.read_fifo.dout), # data going to USB interface # sys domain self.read_fifo.din.eq(self.wishbone.dat_r), self.wishbone.dat_w.eq(self.write_fifo.dout), ] self.submodules.address_synchronizer = BusSynchronizer(32, "usb_12", "sys") self.comb += self.address_synchronizer.i.eq(self.address), self.submodules.length_synchronizer = BusSynchronizer(16, "usb_12", "sys") self.length_sys = Signal(16) self.comb += [self.length_synchronizer.i.eq(self.length), self.length_sys.eq(self.length_synchronizer.o)] self.burstcount = Signal(16) addr_to_wb = Signal(32) self.sync += addr_to_wb.eq(self.address_synchronizer.o + self.burstcount) # must register this to meet timing self.comb += self.wishbone.adr.eq(addr_to_wb[2:]) self.comb += self.wishbone.cti.eq(0), # classic cycle wbmanager = FSM(reset_state="IDLE") # in sys domain self.submodules += wbmanager wbmanager.act("IDLE", NextValue(self.burstcount, 0), If(prefetch_go_sys & cmd_sys, # 0xC3 (bit set) == read NextState("READER") ).Elif(prefetch_go_sys & ~cmd_sys, NextState("WRITER") ), If(self.write_fifo.readable, # clear entries in write fifo in case of e.g. error condition or previous abort self.write_fifo.re.eq(1), ) ) wb_cyc = Signal() self.comb += self.wishbone.cyc.eq(wb_cyc) # & ~(self.wishbone.ack | self.wishbone.err)) # not needed, but a reminder wbmanager.act("READER", If(self.burstcount < self.length_sys, If(self.read_fifo.writable, NextValue(self.wishbone.stb, 1), NextValue(self.wishbone.we, 0), NextValue(wb_cyc, 1), NextState("READER_WAIT") ) ).Else( NextState("WAIT_DONE") ) ) wbmanager.act("READER_WAIT", If(self.wishbone.ack | self.wishbone.err, NextValue(self.wishbone.stb, 0), NextValue(wb_cyc, 0), self.read_fifo.we.eq(1), NextValue(self.burstcount, self.burstcount + 4), NextState("READER_ADDR_WAIT"), ) ) wbmanager.act("READER_ADDR_WAIT", # one cycle dead state to allow the burst count address to propagate NextState("READER") ) wbmanager.act("WRITER", If(self.burstcount < self.length_sys, If(self.write_fifo.readable, NextValue(self.wishbone.stb, 1), NextValue(self.wishbone.we, 1), NextValue(wb_cyc, 1), NextState("WRITER_WAIT"), ) ).Else( NextState("WAIT_DONE") ) ) wbmanager.act("WRITER_WAIT", If(self.wishbone.ack | self.wishbone.err, NextValue(self.wishbone.stb, 0), NextValue(self.wishbone.we, 0), NextValue(wb_cyc, 0), self.write_fifo.re.eq(1), NextValue(self.burstcount, self.burstcount + 4), NextState("WRITER") ) ) wbmanager.act("WAIT_DONE", If(~prefetch_go_sys, NextState("IDLE") ) ) self.comb += self.wishbone.sel.eq(2 ** len(self.wishbone.sel) - 1) not_first_byte=Signal() fsm = ResetInserter()(ClockDomainsRenamer("usb_12")(FSM(reset_state="IDLE"))) self.submodules += fsm fsm.act("IDLE", NextValue(prefetch_go, 0), NextValue(not_first_byte, 0), NextValue(self.data_phase, 0), self.n_debug_in_progress.eq(1), # drain any excess entries in read FIFO, in case we are recovering from e.g. an error condition If(self.read_fifo.readable, self.read_fifo.re.eq(1), ), If(usb_core.data_recv_put, If(usb_core.tok == PID.SETUP, If(usb_core.endp == 0, # If we get a SETUP packet with a "Vendor" type # going to this device, treat that as a DEBUG packet. cmd_ce.eq(1), byte_counter_reset.eq(1), If(usb_core.data_recv_payload[0:7] == magic_packet, NextState("RECEIVE_ADDRESS"), ).Else( # Wait for the end of the packet, to avoid # messing with normal USB operation NextState("WAIT_PKT_END"), ), ) ) ) ) # The target address comes as the wValue and wIndex in the SETUP # packet. Once we get that data, we're ready to do the operation. fsm.act("RECEIVE_ADDRESS", self.n_debug_in_progress.eq(0), If(usb_core.data_recv_put, byte_counter_ce.eq(1), If((byte_counter >= 1), If((byte_counter <= 4), address_ce.eq(1), ).Elif((byte_counter <= 6), length_ce.eq(1), ) ), ), If(byte_counter == 8, NextValue(prefetch_go, 1), ), # We don't need to explicitly ACK the SETUP packet, because # they're always acknowledged implicitly. Wait until the # packet ends (i.e. until we've sent the ACK packet) before # moving to the next state. If(usb_core.end, byte_counter_reset.eq(1), If(cmd, NextState("READ_DATA"), ).Else( NextState("RECEIVE_DATA"), ), ), ) #################### WRITE MACHINE fsm.act("RECEIVE_DATA", # Set the "ACK" bit to 1, so we acknowledge the packet # once it comes in, and so that we're in a position to # receive data. self.send_ack.eq(usb_core.endp == 0), self.n_debug_in_progress.eq(0), If(usb_core.endp == 0, If(usb_core.data_recv_put, rx_data_ce.eq(1), If(burst_counter < 64, byte_counter_ce.eq(1), ), burst_counter_ce.eq(1), If((burst_counter <= 64) & ((burst_counter & 3) == 0) & (burst_counter != 0), self.write_fifo.we.eq(1), address_inc.eq(1), ), If(byte_counter == (length - 1) | (((byte_counter & 0x3F) == 0x3F) & not_first_byte), NextState("WAIT_RECEIVE_DATA_END"), ).Elif(usb_core.end, NextState("WRITE_DATA"), ) ) ) ) fsm.act("WAIT_RECEIVE_DATA_END", self.n_debug_in_progress.eq(0), self.send_ack.eq(1), # Wait for the end of the USB packet, if # it hasn't come already. If(usb_core.end, self.write_fifo.we.eq(1), NextState("WRITE_DATA") ) ) fsm.act("WRITE_DATA", self.n_debug_in_progress.eq(0), If(self.write_fifo.writable, NextState("WAIT_SEND_ACK_START_WRITE"), ) ) fsm.act("WAIT_SEND_ACK_START_WRITE", self.n_debug_in_progress.eq(0), If(usb_core.start, NextState("SEND_ACK_WRITE") ), ) # Send the ACK. If the endpoint number is incorrect, go back and # wait again. fsm.act("SEND_ACK_WRITE", self.n_debug_in_progress.eq(0), If(usb_core.endp != 0, NextState("WAIT_SEND_ACK_START") ), self.send_ack.eq(usb_core.endp == 0), If(usb_core.end, If( byte_counter != length, NextValue(not_first_byte, 0), NextValue(self.data_phase, ~self.data_phase), NextState("RECEIVE_DATA"), ).Else( NextState("IDLE"), ) ), ) ############### READ MACHINE fsm.act("READ_DATA", self.n_debug_in_progress.eq(0), If(self.read_fifo.readable, NextState("SEND_DATA_WAIT_START"), ) ) fsm.act("SEND_DATA_WAIT_START", self.n_debug_in_progress.eq(0), If(usb_core.start, NextState("SEND_DATA"), ), ) fsm.act("SEND_DATA_BURST_WAIT", self.n_debug_in_progress.eq(0), self.sink_valid.eq(usb_core.endp == 0), If(self.read_fifo.readable, NextState("SEND_DATA"), ) ) self.sync.usb_12 += \ chooser(self.rd_data, byte_counter[0:2], self.sink_data, n=4, reverse=False) fsm.act("SEND_DATA", self.n_debug_in_progress.eq(0), If(usb_core.endp != 0, NextState("SEND_DATA_WAIT_START"), ), # Keep sink_valid high during the packet, which indicates we have data # to send. This also causes an "ACK" to be transmitted. self.sink_valid.eq(usb_core.endp == 0), If(usb_core.data_send_get, NextValue(not_first_byte, 1), byte_counter_ce.eq(1), If( ((byte_counter & 3) == 3) & ((byte_counter + 1) != length), self.read_fifo.re.eq(1), # advance the read fifo by one position address_inc.eq(1), NextState("SEND_DATA_BURST_WAIT"), ) ), If( (byte_counter == length) | (((byte_counter & 0x3F) == 0x00) & not_first_byte), NextState("WAIT_SEND_ACK_START") ), If(usb_core.end, NextState("WAIT_SEND_ACK_START") ) ) # To validate the transaction was successful, the host will now # send an "IN" request. Acknowledge that by setting # self.send_ack, without putting anything in self.sink_data. fsm.act("WAIT_SEND_ACK_START", self.n_debug_in_progress.eq(0), If(usb_core.start, NextState("SEND_ACK") ), If(usb_core.end & (byte_counter != length), #byte_counter_ce.eq(1), #address_inc.eq(1), NextValue(not_first_byte, 0), NextValue(self.data_phase, ~self.data_phase), NextState("READ_DATA"), ), ) # Send the ACK. If the endpoint number is incorrect, go back and # wait again. fsm.act("SEND_ACK", self.n_debug_in_progress.eq(0), If(usb_core.endp != 0, NextState("WAIT_SEND_ACK_START") ), # If(usb_core.retry, # If(cmd, # byte_counter_reset.eq(1), # NextState("SEND_DATA"), # ), # ), self.send_ack.eq(usb_core.endp == 0), If(usb_core.end, self.read_fifo.re.eq(1), # drain the last entry in the read fifo NextState("IDLE"), ) ) fsm.act("WAIT_PKT_END", self.n_debug_in_progress.eq(1), If(usb_core.end, NextState("IDLE"), ) )
def __init__(self, hostif, mem_size): width = flen(hostif.d_write) assert width == 16 self.start = Signal() self.sel_test = Signal(4) self.busy = Signal() self.lat_test = Signal(4) self.ok = Signal() self.status = Signal() self.submodules.fsm = FSM() self.comb += self.busy.eq(~self.fsm.ongoing("IDLE")) pat = Signal(width) self.lfsr = Signal(32) self.sync += [ If( self.fsm.ongoing("IDLE") & self.start, self.lat_test.eq(self.sel_test), self.ok.eq(1), ).Elif((self.fsm.ongoing("WAIT_ISSUE_READ") | self.fsm.ongoing("READ")) & hostif.d_stb & (hostif.d_read != pat), self.ok.eq(0)) ] # Address calculation self.addr = Signal(max=mem_size) reset_addr = Signal() self.sync += If( reset_addr, self.addr.eq(0), ).Elif(hostif.d_stb, self.addr.eq(self.addr + 1)) # Pattern selection if (flen(self.addr) >= 2 * width): addr_ext = self.addr else: addr_ext = Cat(self.addr, Replicate(0, 2 * width - flen(self.addr))) self.comb += [ Case( self.lat_test, { TEST_ALT0: pat.eq(Replicate(self.addr[0], width)), TEST_ALT1: pat.eq(Replicate(self.addr[0], width) ^ 0xAAAA), TEST_LFSR: pat.eq(0), # STUB TEST_ADDR: pat.eq(addr_ext[:width] ^ addr_ext[width:width * 2]), TEST_ZERO: pat.eq(0), TEST_ONES: pat.eq(0xFFFF) }), hostif.d_write.eq(pat) ] lastaddr = self.addr == (mem_size - 1) self.fsm.act( "IDLE", If(self.start, reset_addr.eq(1), NextState("WAIT_ISSUE_WR"))) self.fsm.act("WAIT_ISSUE_WR", hostif.i_wr.eq(1), hostif.i_stb.eq(1), hostif.i_addr.eq(0), If(hostif.i_ack, NextState("WRITE"))) self.fsm.act("WRITE", If(lastaddr & hostif.d_stb, NextState("WRITE-TERM"))) self.fsm.act( "WRITE-TERM", hostif.d_term.eq(1), If(hostif.d_stb, reset_addr.eq(1), NextState("WAIT_ISSUE_READ"))) self.fsm.act("WAIT_ISSUE_READ", hostif.i_wr.eq(0), hostif.i_stb.eq(1), hostif.i_addr.eq(0), If(hostif.i_ack, NextState("READ"))) self.fsm.act("READ", If(lastaddr & hostif.d_stb, NextState("READ-TERM"))) self.fsm.act("READ-TERM", hostif.d_term.eq(1), If(hostif.d_stb, NextState("IDLE")))
def __init__(self, pack_factor): hbits_dyn = _hbits - log2_int(pack_factor) timing_layout = [("hres", hbits_dyn), ("hsync_start", hbits_dyn), ("hsync_end", hbits_dyn), ("hscan", hbits_dyn), ("vres", _vbits), ("vsync_start", _vbits), ("vsync_end", _vbits), ("vscan", _vbits)] self.timing = Sink(timing_layout) self.pixels = Sink(pixel_layout(pack_factor)) self.phy = Source(phy_layout(pack_factor)) self.busy = Signal() ### hactive = Signal() vactive = Signal() active = Signal() hcounter = Signal(hbits_dyn) vcounter = Signal(_vbits) skip = bpc - bpc_phy self.comb += [ active.eq(hactive & vactive), If(active, [ getattr(getattr(self.phy.payload, p), c).eq( getattr(getattr(self.pixels.payload, p), c)[skip:]) for p in ["p" + str(i) for i in range(pack_factor)] for c in ["y", "cb_cr"] ], self.phy.de.eq(1)), self.pixels.ack.eq(self.phy.ack & active) ] load_timing = Signal() tr = Record(timing_layout) self.sync += If(load_timing, tr.eq(self.timing.payload)) generate_en = Signal() generate_frame_done = Signal() self.sync += [ generate_frame_done.eq(0), If( generate_en, hcounter.eq(hcounter + 1), If(hcounter == 0, hactive.eq(1)), If(hcounter == tr.hres, hactive.eq(0)), If(hcounter == tr.hsync_start, self.phy.hsync.eq(1)), If(hcounter == tr.hsync_end, self.phy.hsync.eq(0)), If( hcounter == tr.hscan, hcounter.eq(0), If(vcounter == tr.vscan, vcounter.eq(0), generate_frame_done.eq(1)).Else( vcounter.eq(vcounter + 1))), If(vcounter == 0, vactive.eq(1)), If(vcounter == tr.vres, vactive.eq(0)), If(vcounter == tr.vsync_start, self.phy.vsync.eq(1)), If(vcounter == tr.vsync_end, self.phy.vsync.eq(0))) ] self.submodules.fsm = FSM() self.fsm.act("GET_TIMING", self.timing.ack.eq(1), load_timing.eq(1), If(self.timing.stb, NextState("GENERATE"))) self.fsm.act( "GENERATE", self.busy.eq(1), If(~active | self.pixels.stb, self.phy.stb.eq(1), If(self.phy.ack, generate_en.eq(1))), If(generate_frame_done, NextState("GET_TIMING")))
def __init__(self, usb_core, clk_freq=12000000, magic_packet=0x43): self.wishbone = wishbone.Interface() # # # byte_counter = Signal(3, reset_less=True) byte_counter_reset = Signal() byte_counter_ce = Signal() self.sync += \ If(byte_counter_reset, byte_counter.eq(0) ).Elif(byte_counter_ce, byte_counter.eq(byte_counter + 1) ) # Unlike the UART or Ethernet bridges, we explicitly only # support two commands: reading and writing. This gets # integrated into the USB protocol, so it's not really a # state. 1 is "USB Device to Host", and is therefore a "read", # while 0 is "USB Host to Device", and is therefore a "write". cmd = Signal(1, reset_less=True) cmd_ce = Signal() # Instead of self.source and self.sink, we let the wrapping # module handle packing and unpacking the data. self.sink_data = Signal(8) # True when the "sink" value has data self.sink_valid = Signal() self.send_ack = Signal() # Indicates whether a "debug" packet is currently being processed self.n_debug_in_progress = Signal() address = Signal(32, reset_less=True) address_ce = Signal() data = Signal(32, reset_less=True) rx_data_ce = Signal() tx_data_ce = Signal() self.sync += [ If(cmd_ce, cmd.eq(usb_core.data_recv_payload[7:8])), If(address_ce, address.eq(Cat(address[8:32], usb_core.data_recv_payload))), If(rx_data_ce, data.eq(Cat(data[8:32], usb_core.data_recv_payload))).Elif( tx_data_ce, data.eq(self.wishbone.dat_r)) ] fsm = ResetInserter()(FSM(reset_state="IDLE")) self.submodules += fsm fsm.act( "IDLE", self.n_debug_in_progress.eq(1), If( usb_core.data_recv_put, If( usb_core.tok == PID.SETUP, If( usb_core.endp == 0, # If we get a SETUP packet with a "Vendor" type # going to this device, treat that as a DEBUG packet. cmd_ce.eq(1), byte_counter_reset.eq(1), If( usb_core.data_recv_payload[0:7] == magic_packet, NextState("RECEIVE_ADDRESS"), ).Else( # Wait for the end of the packet, to avoid # messing with normal USB operation NextState("WAIT_PKT_END"), ), )))) # The target address comes as the wValue and wIndex in the SETUP # packet. Once we get that data, we're ready to do the operation. fsm.act( "RECEIVE_ADDRESS", If( usb_core.data_recv_put, byte_counter_ce.eq(1), If( (byte_counter >= 1), If( (byte_counter <= 4), address_ce.eq(1), ), ), ), # We don't need to explicitly ACK the SETUP packet, because # they're always acknowledged implicitly. Wait until the # packet ends (i.e. until we've sent the ACK packet) before # moving to the next state. If( usb_core.end, byte_counter_reset.eq(1), If(cmd, NextState("READ_DATA")).Else(NextState("RECEIVE_DATA")), ), ) fsm.act( "RECEIVE_DATA", # Set the "ACK" bit to 1, so we acknowledge the packet # once it comes in, and so that we're in a position to # receive data. If( usb_core.endp == 0, self.send_ack.eq(1), If( usb_core.data_recv_put, rx_data_ce.eq(1), byte_counter_ce.eq(1), If(byte_counter == 3, NextState("WAIT_RECEIVE_DATA_END"), byte_counter_reset.eq(1)).Elif( usb_core.end, # NextState("WAIT_SEND_ACK_START"), NextState("WRITE_DATA"), byte_counter_reset.eq(1))))) fsm.act( "WAIT_RECEIVE_DATA_END", self.send_ack.eq(1), # Wait for the end of the USB packet, if # it hasn't come already. If( usb_core.end, # NextState("WAIT_SEND_ACK_START") NextState("WRITE_DATA"))) self.comb += [ # Trim off the last two bits of the address, because wishbone addresses # are word-based, and a word is 32-bits. Therefore, the last two bits # should always be zero. self.wishbone.adr.eq(address[2:]), self.wishbone.dat_w.eq(data), self.wishbone.sel.eq(2**len(self.wishbone.sel) - 1) ] fsm.act( "WRITE_DATA", byte_counter_reset.eq(1), self.wishbone.stb.eq(1), self.wishbone.we.eq(1), self.wishbone.cyc.eq(1), If( self.wishbone.ack | self.wishbone.err, NextState("WAIT_SEND_ACK_START"), )) fsm.act( "READ_DATA", byte_counter_reset.eq(1), self.wishbone.stb.eq(1), self.wishbone.we.eq(0), self.wishbone.cyc.eq(1), If(self.wishbone.ack | self.wishbone.err, tx_data_ce.eq(1), NextState("SEND_DATA_WAIT_START"))) fsm.act( "SEND_DATA_WAIT_START", byte_counter_reset.eq(1), If( usb_core.start, NextState("SEND_DATA"), ), ) self.comb += \ chooser(data, byte_counter, self.sink_data, n=4, reverse=False) fsm.act( "SEND_DATA", If( usb_core.endp == 0, # Keep sink_valid high during the packet, which indicates we have data # to send. This also causes an "ACK" to be transmitted. self.sink_valid.eq(1), If( usb_core.data_send_get, byte_counter_ce.eq(1), ), If(byte_counter == 4, NextState("WAIT_SEND_ACK_START")), If(usb_core.end, NextState("WAIT_SEND_ACK_START"))).Else( NextState("SEND_DATA_WAIT_START"), )) # To validate the transaction was successful, the host will now # send an "IN" request. Acknowledge that by setting # self.send_ack, without putting anything in self.sink_data. fsm.act( "WAIT_SEND_ACK_START", If( usb_core.endp == 0, self.send_ack.eq(1), If( usb_core.retry, byte_counter_reset.eq(1), NextState("SEND_DATA"), ).Elif( usb_core.start, NextState("WAIT_PKT_END_DBG"), ))) fsm.act("WAIT_PKT_END_DBG", self.send_ack.eq(1), If( usb_core.end, NextState("IDLE"), )) fsm.act("WAIT_PKT_END", self.n_debug_in_progress.eq(1), If( usb_core.end, NextState("IDLE"), ))
def __init__(self, cachesize, lasmim): self.wishbone = wishbone.Interface() ### data_width = flen(self.wishbone.dat_r) if lasmim.dw > data_width and (lasmim.dw % data_width) != 0: raise ValueError( "LASMI data width must be a multiple of {dw}".format( dw=data_width)) if lasmim.dw < data_width and (data_width % lasmim.dw) != 0: raise ValueError( "WISHBONE data width must be a multiple of {dw}".format( dw=lasmim.dw)) # Split address: # TAG | LINE NUMBER | LINE OFFSET offsetbits = log2_int(max(lasmim.dw // data_width, 1)) addressbits = lasmim.aw + offsetbits linebits = log2_int(cachesize) - offsetbits tagbits = addressbits - linebits wordbits = log2_int(max(data_width // lasmim.dw, 1)) adr_offset, adr_line, adr_tag = split(self.wishbone.adr, offsetbits, linebits, tagbits) word = Signal(wordbits) if wordbits else None # Data memory data_mem = Memory(lasmim.dw * 2**wordbits, 2**linebits) data_port = data_mem.get_port(write_capable=True, we_granularity=8) self.specials += data_mem, data_port write_from_lasmi = Signal() write_to_lasmi = Signal() if adr_offset is None: adr_offset_r = None else: adr_offset_r = Signal(offsetbits) self.sync += adr_offset_r.eq(adr_offset) self.comb += [ data_port.adr.eq(adr_line), If(write_from_lasmi, displacer(lasmim.dat_r, word, data_port.dat_w), displacer(Replicate(1, lasmim.dw // 8), word, data_port.we)).Else( data_port.dat_w.eq( Replicate(self.wishbone.dat_w, max(lasmim.dw // data_width, 1))), If( self.wishbone.cyc & self.wishbone.stb & self.wishbone.we & self.wishbone.ack, displacer(self.wishbone.sel, adr_offset, data_port.we, 2**offsetbits, reverse=True))), If(write_to_lasmi, chooser(data_port.dat_r, word, lasmim.dat_w), lasmim.dat_we.eq(2**(lasmim.dw // 8) - 1)), chooser(data_port.dat_r, adr_offset_r, self.wishbone.dat_r, reverse=True) ] # Tag memory tag_layout = [("tag", tagbits), ("dirty", 1)] tag_mem = Memory(layout_len(tag_layout), 2**linebits) tag_port = tag_mem.get_port(write_capable=True) self.specials += tag_mem, tag_port tag_do = Record(tag_layout) tag_di = Record(tag_layout) self.comb += [ tag_do.raw_bits().eq(tag_port.dat_r), tag_port.dat_w.eq(tag_di.raw_bits()) ] self.comb += [tag_port.adr.eq(adr_line), tag_di.tag.eq(adr_tag)] if word is not None: self.comb += lasmim.adr.eq(Cat(word, adr_line, tag_do.tag)) else: self.comb += lasmim.adr.eq(Cat(adr_line, tag_do.tag)) # Lasmim word computation, word_clr and word_inc will be simplified # at synthesis when wordbits=0 word_clr = Signal() word_inc = Signal() if word is not None: self.sync += \ If(word_clr, word.eq(0), ).Elif(word_inc, word.eq(word+1) ) def word_is_last(word): if word is not None: return word == 2**wordbits - 1 else: return 1 # Control FSM assert (lasmim.write_latency >= 1 and lasmim.read_latency >= 1) fsm = FSM(reset_state="IDLE") self.submodules += fsm fsm.delayed_enter("EVICT_DATAD", "EVICT_DATA", lasmim.write_latency - 1) fsm.delayed_enter("REFILL_DATAD", "REFILL_DATA", lasmim.read_latency - 1) fsm.act( "IDLE", If(self.wishbone.cyc & self.wishbone.stb, NextState("TEST_HIT"))) fsm.act( "TEST_HIT", word_clr.eq(1), If(tag_do.tag == adr_tag, self.wishbone.ack.eq(1), If(self.wishbone.we, tag_di.dirty.eq(1), tag_port.we.eq(1)), NextState("IDLE")).Else( If(tag_do.dirty, NextState("EVICT_REQUEST")).Else( NextState("REFILL_WRTAG")))) fsm.act("EVICT_REQUEST", lasmim.stb.eq(1), lasmim.we.eq(1), If(lasmim.req_ack, NextState("EVICT_WAIT_DATA_ACK"))) fsm.act("EVICT_WAIT_DATA_ACK", If(lasmim.dat_ack, NextState("EVICT_DATAD"))) fsm.act( "EVICT_DATA", write_to_lasmi.eq(1), word_inc.eq(1), If( word_is_last(word), NextState("REFILL_WRTAG"), ).Else(NextState("EVICT_REQUEST"))) fsm.act( "REFILL_WRTAG", # Write the tag first to set the LASMI address tag_port.we.eq(1), word_clr.eq(1), NextState("REFILL_REQUEST")) fsm.act("REFILL_REQUEST", lasmim.stb.eq(1), If(lasmim.req_ack, NextState("REFILL_WAIT_DATA_ACK"))) fsm.act("REFILL_WAIT_DATA_ACK", If(lasmim.dat_ack, NextState("REFILL_DATAD"))) fsm.act( "REFILL_DATA", write_from_lasmi.eq(1), word_inc.eq(1), If( word_is_last(word), NextState("TEST_HIT"), ).Else(NextState("REFILL_REQUEST")))
def __init__(self, pads): self.background = ModuleDoc( """MemLCD: Driver for the SHARP Memory LCD model LS032B7DD02 The LS032B7DD02 is a 336x536 pixel black and white memory LCD, with a 200ppi dot pitch. Memory LCDs can be thought of as 'fast E-ink displays that consume a tiny bit of standby power', as seen by these properties: * Extremely low standby power (30uW typ hold mode) * No special bias circuitry required to maintain image in hold mode * 120 degree viewing angle, 1:35 contrast ratio * All control logic fabricated on-glass using TFT devices that are auditable with a common 40x power desktop microscope and a bright backlight source This last property in particular makes the memory LCD extremely well suited for situations where absolute visibility into the construction of a secure enclave is desired. The memory organization of the LS032B7DD02 is simple: 536 lines of pixel data 336 wide. Each pixel is 1 bit (the display is black and white), and is fed into the module from left to right as pixels 1 through 336, inclusive. Lines are enumerated from top to bottom, from 1 to 536, inclusive. The LCD can only receive serial data. The protocol is a synchronous serial interface with an active high chip select. All data words are transmitted LSB first. A line transfer is initiated by sending a 6-bit mode selection, a 10-bit row address, and the subsequent 336 pixels, followed by 16 dummy bits which transfer the data from the LCD holding register to the display itself. .. wavedrom:: :caption: Single line data transfer to memory LCD { "signal": [ { "name": "SCLK", "wave": "0.P.......|......|...|..l." }, { "name": "SCS", "wave": "01........|......|...|.0.." }, { "name": "SI", "wave": "0===x..===|======|==x|....", "data": ["M0", "M1", "M2", "R0", "R1", " ", "R8", "R9", "D0", "D1", "D2", " ", "D334", "D335"] }, { "node": ".....................a.b..."}, ], "edge": ['a<->b 16 cycles'] } Alternatively, one can send successive lines without dropping SCS by substituting the 16 dummy bits at the end with a 6-bit don't care preamble (where the mode bits would have been), 10 bits of row address, and then the pixel data. .. wavedrom:: :caption: Multiple line data transfer to memory LCD { "signal": [ { "name": "SCLK", "wave": "0.P.......|......|...|....|....." }, { "name": "SCS", "wave": "01........|......|...|....|....." }, { "name": "SI", "wave": "0===x..===|======|==x|.===|=====", "data": ["M0", "M1", "M2", "R0", "R1", " ", "R8", "R9", "D0", "D1", "D2", " ", "D334", "D335", "R0", "R1", " ", "R8", "R9", "D0", "D1"] }, { "node": ".....................a.b..."}, ], "edge": ['a<->b 6 cycles'] } The very last line in the multiple line data transfer must terminate with 16 dummy cycles. Mode bits M0-M2 have the following meaning: M0: Set to 1 when transferring data lines. Set to 0 for hold mode, see below. M1: VCOM inversion flag. Ignore when hardware strap pin EXTMODE is high. Betrusted sets EXTMODE high, so the VCOM inversion is handled by low-power aux hardware. When EXTMODE is low, software must explicitly manage the VCOM inversion flag such the flag polarity changes once every second "as much as possible". M2: Normally set to 0. Set to 1 for all clear (see below) Data bit polarity: 1 = White 0 = Black For 'Hold mode' and 'All clear', a total of 16 cycles are sent, the first three being the mode bit and the last 13 being dummy cycles. .. wavedrom:: :caption: Hold and all clear timings { "signal": [ { "name": "SCLK", "wave": "0.P...|.l" }, { "name": "SCS", "wave": "01....|0." }, { "name": "SI", "wave": "0===x...", "data": ["M0", "M1", "M2", "R0", "R1", " ", "R8", "R9", "D0", "D1", "D2", " ", "D334", "D335", "R0", "R1", " ", "R8", "R9", "D0", "D1"] }, { "node": ".....a.b..."}, ], "edge": ['a<->b 13 cycles'] } All signals are 3.0V compatible, 5V tolerant (VIH is 2.7V-VDD). The display itself requires a single 5V power supply (4.8-5.5V typ 5.8V abs max). In hold mode, typical power is 30uW, max 330uW; with data updating at 1Hz, power is 250uW, max 750uW (SCLK=1MHz, EXTCOMMIN=1Hz). * The maximum clock frequency for SCLK is 2MHz (typ 1MHz). * EXTCOMMIN frequency is 1-10Hz, 1Hz typ * EXTCOMMIN minimum high duration is 1us * All rise/fall times must be less than 50ns * SCS setup time is 3us, hold time is 1us. Minimum low duration is 1us, minimum high is 188us for a data update, 12 us for a hold mode operation. * SI setup time is 120ns, 190ns hold time. * Operating temperature is -20 to 70C, storage temperature -30 to 80C. """) self.interface = ModuleDoc("""Wishbone interface for MemLCD MemLCD maintains a local framebuffer for the LCD. The CPU can read and write to the frame buffer to update pixel data, and then request a screen update to commit the frame buffer to the LCD. Only full lines can be updated on a memory LCD; partial updates are not possible. In order to optimize the update process, MemLCD maintains a "dirty bit" associated with each line. Only lines with modified pixels are written to the screen after an update request. A line is 336 bits wide. When padded to 32-bit words, this yields a line width of 44 bytes (0x2C, or 352 bits). In order to simplify math, the frame buffer rounds the line width up to the nearest power of two, or 64 bytes. The unused bits can be used as a "hint" to the MemLCD block as to which lines require updating. If the unused bits have any value other than 0, the MemLCD block will update those lines when an "UpdateDirty" command is triggered. It is up to the CPU to set and clear the dirty bits, they are not automatically cleared by the block upon update. Typically the clearing of the bits would be handled during the update-finished interrupt handling routine. If the dirty bits are not used, an "UpdateAll" command can be invoked, which will update every line of the LCD regardless of the contents of the dirty bits. The total depth of the memory is thus 44 bytes * 536 lines = 23,584 bytes or 5,896 words. Pixels are stored with the left-most pixel in the MSB of each 32-bit word, with the left-most pixels occupying the lowest address in the line. Lines are stored with the bottom line of the screen at the lowest address. These parameters are chosen so that a 1-bit BMP file can be copied into the frame buffer and it will render directly to the screen with no further transformations required. The CPU is responsible for not writing data to the LCD while it is updating. Concurrent writes to the LCD during updates can lead to unpredictable behavior. """) data_width = 32 width = 336 height = 536 bytes_per_line = 44 self.fb_depth = fb_depth = height * bytes_per_line // (data_width // 8) pixdata = Signal(32) pixadr_rd = Signal(max=fb_depth) # 1 is white, which is the "off" state fb_init = [0xffffffff] * int(fb_depth) for i in range(fb_depth // 11): fb_init[i * 11 + 10] = 0xffff mem = Memory( 32, fb_depth, init=fb_init ) # may need to round up to 8192 if a power of 2 is required by migen # read port for pixel data out self.specials.rdport = mem.get_port( write_capable=False, mode=READ_FIRST) # READ_FIRST allows BRAM to be used self.comb += self.rdport.adr.eq(pixadr_rd) self.comb += pixdata.eq(self.rdport.dat_r) # implementation note: vivado will complain about being unable to merge an output register, leading to # non-optimal timing, but a check of the timing path shows that at 100MHz there is about 4-5ns of setup margin, # so the merge is unnecessary in this case. Ergo, prefer comb over sync to reduce latency. # memory-mapped write port to wishbone bus self.bus = wishbone.Interface() self.submodules.wb_sram_if = wishbone.SRAM(mem, read_only=False) decoder_offset = log2_int(fb_depth, need_pow2=False) def slave_filter(a): return a[decoder_offset:32 - decoder_offset] == 0 # no aliasing in the block self.submodules.wb_con = wishbone.Decoder( self.bus, [(slave_filter, self.wb_sram_if.bus)], register=True) self.command = CSRStorage( 2, fields=[ CSRField( "UpdateDirty", description="Write a ``1`` to flush dirty lines to the LCD", pulse=True), CSRField( "UpdateAll", description="Update full screen regardless of tag state", pulse=True), ]) self.busy = CSRStatus( 1, name="Busy", description= """A ``1`` indicates that the block is currently updating the LCD""" ) self.prescaler = CSRStorage(8, reset=99, name="prescaler", description=""" Prescaler value. LCD clock is module (clock / (prescaler+1)). Reset value: 99, so for a default sysclk of 100MHz this yields an LCD SCLK of 1MHz""") self.submodules.ev = EventManager() self.ev.done = EventSourceProcess() self.ev.finalize() self.comb += self.ev.done.trigger.eq( self.busy.status) # Fire an interupt when busy drops self.sclk = sclk = getattr(pads, "sclk") self.scs = scs = getattr(pads, "scs") self.si = si = getattr(pads, "si") self.sendline = sendline = Signal() self.linedone = linedone = Signal() updateall = Signal() fetch_dirty = Signal() update_line = Signal( max=height ) # Keep track of both line and address to avoid instantiating a multiplier update_addr = Signal(max=height * bytes_per_line) fsm_up = FSM(reset_state="IDLE") self.submodules += fsm_up fsm_up.act( "IDLE", If( self.command.fields.UpdateDirty | self.command.fields.UpdateAll, NextValue(self.busy.status, 1), NextValue(fetch_dirty, 1), If(self.command.fields.UpdateAll, NextValue(updateall, 1)).Else(NextValue(updateall, 0)), NextState("START")).Else(NextValue(self.busy.status, 0))) fsm_up.act( "START", NextValue(update_line, height), NextValue( update_addr, (height - 1) * bytes_per_line ), # Represents the byte address of the beginning of the last line NextState("FETCHDIRTY")) fsm_up.act( "FETCHDIRTY", # Wait one cycle delay for the pixel data to be retrieved before evaluating it NextState("CHECKDIRTY")) fsm_up.act( "CHECKDIRTY", If(update_line == 0, NextState("IDLE")).Else( If( (pixdata[16:] != 0) | updateall, NextState("DIRTYLINE"), ).Else(NextValue(update_line, update_line - 1), NextValue(update_addr, update_addr - bytes_per_line), NextState("FETCHDIRTY")))) fsm_up.act("DIRTYLINE", NextValue(fetch_dirty, 0), sendline.eq(1), NextState("WAITDONE")) fsm_up.act( "WAITDONE", If(linedone, NextValue(fetch_dirty, 1), NextValue(update_line, update_line - 1), NextValue(update_addr, update_addr - bytes_per_line), NextState("FETCHDIRTY"))) modeshift = Signal(16) mode = Signal(6) pixshift = Signal(32) pixcount = Signal(max=width) bitreq = Signal() bitack = Signal() self.comb += mode.eq( 1 ) # Always in line write mode, not clearing, no vcom management necessary fsm_phy = FSM(reset_state="IDLE") self.submodules += fsm_phy # Update_addr units is in bytes. [2:] turns bytes to words # pixcount units are in pixels. [3:] turns pixels to bytes self.comb += [ If(fetch_dirty, pixadr_rd.eq( (update_addr + bytes_per_line - 4)[2:])).Else( pixadr_rd.eq((update_addr + pixcount[3:])[2:])) ] scs_cnt = Signal(max=200) fsm_phy.act( "IDLE", NextValue(si, 0), NextValue(linedone, 0), If( sendline, NextValue(scs, 1), NextValue(scs_cnt, 200), # 2 us setup NextValue(pixcount, 16), NextValue(modeshift, Cat(mode, update_line)), NextState("SCS_SETUP")).Else(NextValue(scs, 0))) fsm_phy.act( "SCS_SETUP", If(scs_cnt > 0, NextValue(scs_cnt, scs_cnt - 1)).Else(NextState("MODELINE"))) fsm_phy.act( "MODELINE", If(pixcount > 0, NextValue(modeshift, modeshift[1:]), NextValue(si, modeshift[0]), NextValue(pixcount, pixcount - 1), bitreq.eq(1), NextState("MODELINEWAIT")).Else(NextValue(pixcount, 1), NextValue(pixshift, pixdata), NextState("DATA"))) fsm_phy.act("MODELINEWAIT", If(bitack, NextState("MODELINE"))) fsm_phy.act( "DATA", If( pixcount < width + 17, If( pixcount[0:5] == 0, NextValue(pixshift, pixdata), ).Else(NextValue(pixshift, pixshift[1:]), ), NextValue(scs, 1), NextValue(si, pixshift[0]), NextValue(pixcount, pixcount + 1), bitreq.eq(1), NextState("DATAWAIT")).Else( NextValue(si, 0), NextValue(scs_cnt, 100), # 1 us hold NextState("SCS_HOLD"))) fsm_phy.act( "SCS_HOLD", If(scs_cnt > 0, NextValue(scs_cnt, scs_cnt - 1)).Else( NextValue(scs, 0), NextValue(scs_cnt, 100), # 1us minimum low time NextState("SCS_LOW"))) fsm_phy.act( "SCS_LOW", If(scs_cnt > 0, NextValue(scs_cnt, scs_cnt - 1)).Else(NextValue(linedone, 1), NextState("IDLE"))) fsm_phy.act("DATAWAIT", If(bitack, NextState("DATA"))) # This handles clock division fsm_bit = FSM(reset_state="IDLE") self.submodules += fsm_bit clkcnt = Signal(8) fsm_bit.act("IDLE", NextValue(sclk, 0), NextValue(clkcnt, self.prescaler.storage), If(bitreq, NextState("SCLK_LO"))) fsm_bit.act( "SCLK_LO", NextValue(clkcnt, clkcnt - 1), If(clkcnt < self.prescaler.storage[1:], NextValue(sclk, 1), NextState("SCLK_HI"))) fsm_bit.act( "SCLK_HI", NextValue(clkcnt, clkcnt - 1), If(clkcnt == 0, NextValue(sclk, 0), NextState("IDLE"), bitack.eq(1)))
def parse_state(st, to, *update): sm.act(st, self.sink.ack.eq(1), If(self.sink.stb, NextState(to), *update))
def __init__(self, ulpi, ulpi_reg): ulpi_data_out = Signal(8) ulpi_data_tristate = Signal() ulpi_data_next = Signal(8) ulpi_data_tristate_next = Signal() ulpi_stp_next = Signal() ulpi_state_rx = Signal() ulpi_state_rrd = Signal() self.data_out_source = Source(ULPI_DATA) RegWriteReqR = Signal() RegReadReqR = Signal() RegWriteReq = Signal() RegReadReq = Signal() RegReadAckSet = Signal() RegWriteAckSet = Signal() # register the reg read/write requests self.sync += RegReadReqR.eq(ulpi_reg.rreq) self.sync += RegWriteReqR.eq(ulpi_reg.wreq) # signal when read/write is requested but not done self.comb += RegReadReq.eq(RegReadReqR & ~ulpi_reg.rack) v = (RegReadReqR & ~ulpi_reg.rack) self.comb += RegWriteReq.eq(RegWriteReqR & ~ulpi_reg.wack) # ack logic: set ack=0 when req=0, set ack=1 when access done self.sync += If(~RegReadReqR, ulpi_reg.rack.eq(0) ).Elif(RegReadAckSet, ulpi_reg.rack.eq(1)) self.sync += If(~RegWriteReqR, ulpi_reg.wack.eq(0) ).Elif(RegWriteAckSet, ulpi_reg.wack.eq(1)) exp = If(~RegWriteReqR, ulpi_reg.wack.eq(0)).Elif(RegWriteAckSet, ulpi_reg.wack.eq(1)) # output data if required by state self.comb += ulpi.stp.eq(ulpi_stp_next) self.comb += ulpi_data_out.eq(ulpi_data_next) self.comb += ulpi_data_tristate.eq(ulpi_data_tristate_next) self.comb += ulpi.do.eq(ulpi_data_out) self.comb += ulpi.doe.eq(~ulpi_data_tristate) # capture RX data at the end of RX, but only if no turnaround was requested # We also support "stuffing" data, to indicate conditions such as: # - Simultaneous DIR + NXT assertion # (the spec doesn't require an RXCMD - DIR+NXT asserting may be the' # only SOP signal) # - End-of-packet # (Packets may end without an RXCMD, unless an error occurs) ulpi_rx_stuff = Signal() ulpi_rx_stuff_d = Signal(8) self.sync += self.data_out_source.stb.eq(ulpi_state_rx & ulpi.dir | ulpi_rx_stuff) self.sync += If(ulpi_rx_stuff, self.data_out_source.payload.d.eq(ulpi_rx_stuff_d), self.data_out_source.payload.rxcmd.eq(1) ).Else( If(~ulpi.nxt, self.data_out_source.payload.d.eq(ulpi.di & RXCMD_MASK), self.data_out_source.payload.rxcmd.eq(1) ).Else( self.data_out_source.payload.d.eq(ulpi.di), self.data_out_source.payload.rxcmd.eq(0) ) ) # capture register reads at the end of RRD self.sync += If(ulpi_state_rrd,ulpi_reg.rdata.eq(ulpi.di)) fsm = FSM() self.submodules += fsm fsm.act("IDLE", ulpi_data_next.eq(0x00), # NOOP ulpi_data_tristate_next.eq(0), ulpi_stp_next.eq(0), If(~ulpi.dir & ~ulpi.nxt & ~(RegWriteReq | RegReadReq), NextState("IDLE") ).Elif(ulpi.dir, # TA, and then either RXCMD or Data NextState("RX"), ulpi_data_tristate_next.eq(1), # If dir & nxt, we're starting a packet, so stuff a custom SOP If(ulpi.nxt, ulpi_rx_stuff.eq(1), ulpi_rx_stuff_d.eq(RXCMD_MAGIC_SOP) ) ).Elif(RegWriteReq, NextState("RW0"), ulpi_data_next.eq(0x80 | ulpi_reg.waddr), # REGW ulpi_data_tristate_next.eq(0), ulpi_stp_next.eq(0) ).Elif(RegReadReq, NextState("RR0"), ulpi_data_next.eq(0xC0 | ulpi_reg.raddr), # REGR ulpi_data_tristate_next.eq(0), ulpi_stp_next.eq(0) ).Else( NextState("ERROR") )) fsm.act("RX", If(ulpi.dir, # stay in RX NextState("RX"), ulpi_state_rx.eq(1), ulpi_data_tristate_next.eq(1) ).Else( # TA back to idle # Stuff an EOP on return to idle ulpi_rx_stuff.eq(1), ulpi_rx_stuff_d.eq(RXCMD_MAGIC_EOP), ulpi_data_tristate_next.eq(0), NextState("IDLE") )) fsm.act("RW0", If(ulpi.dir, NextState("RX"), ulpi_data_tristate_next.eq(1), ).Elif(~ulpi.dir, ulpi_data_next.eq(0x80 | ulpi_reg.waddr), # REGW ulpi_data_tristate_next.eq(0), ulpi_stp_next.eq(0), If(ulpi.nxt, NextState("RWD")).Else(NextState("RW0")), ).Else( NextState("ERROR") )) fsm.act("RWD", If(ulpi.dir, NextState("RX"), ulpi_data_tristate_next.eq(1) ).Elif(~ulpi.dir & ulpi.nxt, NextState("RWS"), ulpi_data_next.eq(ulpi_reg.wdata), ulpi_data_tristate_next.eq(0), ulpi_stp_next.eq(0) ).Else( NextState("ERROR") ), ) fsm.act("RWS", If(~ulpi.dir, NextState("IDLE"), ulpi_data_next.eq(0x00), # NOOP ulpi_data_tristate_next.eq(0), ulpi_stp_next.eq(1), RegWriteAckSet.eq(1) ).Elif(ulpi.dir, NextState("RX"), ulpi_data_tristate_next.eq(1), ), ) fsm.act("RR0", If(~ulpi.dir, ulpi_data_next.eq(0xC0 | ulpi_reg.raddr), # REGR NextState("RR1") ).Elif(ulpi.dir, NextState("RX"), RegWriteAckSet.eq(1) ).Else( NextState("ERROR") )) fsm.act("RR1", If(~ulpi.dir & ulpi.nxt, # PHY accepts REGR ulpi_data_tristate_next.eq(1), # TA NextState("RR2") ).Elif(~ulpi.dir & ~ulpi.nxt, # PHY delays REGR ulpi_data_next.eq(0xC0 | ulpi_reg.raddr), # REGR NextState("RR1") ).Elif(ulpi.dir, NextState("RX"), RegWriteAckSet.eq(1) ).Else( NextState("ERROR") )) fsm.act("RR2", ulpi_data_tristate_next.eq(1), If(~ulpi.nxt, # REGR continue NextState("RRD") ).Elif(ulpi.dir, # PHY indicates RX NextState("RX"), RegWriteAckSet.eq(1) ).Else( NextState("ERROR") )) fsm.act("RRD", If(ulpi.dir & ~ulpi.nxt, NextState("IDLE"), RegReadAckSet.eq(1), ulpi_state_rrd.eq(1), ).Elif(ulpi.dir & ulpi.nxt, NextState("RX"), RegWriteAckSet.eq(1) ).Else( NextState("ERROR") ), ulpi_data_tristate_next.eq(1), ) fsm.act("ERROR", NextState("IDLE"))
def __init__(self, geom_settings, timing_settings, controller_settings, address_align, bankn, req): self.refresh_req = Signal() self.refresh_gnt = Signal() self.cmd = CommandRequestRW(geom_settings.addressbits, geom_settings.bankbits) ### # Request FIFO layout = [("we", 1), ("adr", len(req.adr))] req_in = Record(layout) reqf = Record(layout) self.submodules.req_fifo = SyncFIFO(layout_len(layout), controller_settings.req_queue_size) self.comb += [ self.req_fifo.din.eq(req_in.raw_bits()), reqf.raw_bits().eq(self.req_fifo.dout) ] self.comb += [ req_in.we.eq(req.we), req_in.adr.eq(req.adr), self.req_fifo.we.eq(req.stb), req.req_ack.eq(self.req_fifo.writable), self.req_fifo.re.eq(req.dat_w_ack | req.dat_r_ack), req.lock.eq(self.req_fifo.readable) ] slicer = _AddressSlicer(geom_settings.colbits, address_align) # Row tracking has_openrow = Signal() openrow = Signal(geom_settings.rowbits) hit = Signal() self.comb += hit.eq(openrow == slicer.row(reqf.adr)) track_open = Signal() track_close = Signal() self.sync += [ If(track_open, has_openrow.eq(1), openrow.eq(slicer.row(reqf.adr))), If(track_close, has_openrow.eq(0)) ] # Address generation s_row_adr = Signal() self.comb += [ self.cmd.ba.eq(bankn), If(s_row_adr, self.cmd.a.eq(slicer.row(reqf.adr))).Else( self.cmd.a.eq(slicer.col(reqf.adr))) ] # Respect write-to-precharge specification precharge_ok = Signal() t_unsafe_precharge = 2 + timing_settings.tWR - 1 unsafe_precharge_count = Signal(max=t_unsafe_precharge + 1) self.comb += precharge_ok.eq(unsafe_precharge_count == 0) self.sync += [ If(self.cmd.stb & self.cmd.ack & self.cmd.is_write, unsafe_precharge_count.eq(t_unsafe_precharge)).Elif( ~precharge_ok, unsafe_precharge_count.eq(unsafe_precharge_count - 1)) ] # Control and command generation FSM fsm = FSM() self.submodules += fsm fsm.act( "REGULAR", If(self.refresh_req, NextState("REFRESH")).Elif( self.req_fifo.readable, If( has_openrow, If( hit, # NB: write-to-read specification is enforced by multiplexer self.cmd.stb.eq(1), req.dat_w_ack.eq(self.cmd.ack & reqf.we), req.dat_r_ack.eq(self.cmd.ack & ~reqf.we), self.cmd.is_read.eq(~reqf.we), self.cmd.is_write.eq(reqf.we), self.cmd.cas_n.eq(0), self.cmd.we_n.eq(~reqf.we)).Else( NextState("PRECHARGE"))).Else( NextState("ACTIVATE")))) fsm.act( "PRECHARGE", # Notes: # 1. we are presenting the column address, A10 is always low # 2. since we always go to the ACTIVATE state, we do not need # to assert track_close. If(precharge_ok, self.cmd.stb.eq(1), If(self.cmd.ack, NextState("TRP")), self.cmd.ras_n.eq(0), self.cmd.we_n.eq(0), self.cmd.is_cmd.eq(1))) fsm.act("ACTIVATE", s_row_adr.eq(1), track_open.eq(1), self.cmd.stb.eq(1), self.cmd.is_cmd.eq(1), If(self.cmd.ack, NextState("TRCD")), self.cmd.ras_n.eq(0)) fsm.act("REFRESH", self.refresh_gnt.eq(precharge_ok), track_close.eq(1), self.cmd.is_cmd.eq(1), If(~self.refresh_req, NextState("REGULAR"))) fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP - 1) fsm.delayed_enter("TRCD", "REGULAR", timing_settings.tRCD - 1)
def __init__(self, master, slave): dw_from = len(master.dat_r) dw_to = len(slave.dat_w) ratio = dw_from // dw_to # # # read = Signal() write = Signal() counter = Signal(max=ratio) counter_reset = Signal() counter_ce = Signal() self.sync += \ If(counter_reset, counter.eq(0) ).Elif(counter_ce, counter.eq(counter + 1) ) counter_done = Signal() self.comb += counter_done.eq(counter == ratio - 1) # Main FSM self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm.act( "IDLE", counter_reset.eq(1), If(master.stb & master.cyc, If(master.we, NextState("WRITE")).Else(NextState("READ")))) fsm.act( "WRITE", write.eq(1), slave.we.eq(1), slave.cyc.eq(1), If( master.stb & master.cyc, slave.stb.eq(1), If(slave.ack, counter_ce.eq(1), If(counter_done, master.ack.eq(1), NextState("IDLE")))).Elif(~master.cyc, NextState("IDLE"))) fsm.act( "READ", read.eq(1), slave.cyc.eq(1), If( master.stb & master.cyc, slave.stb.eq(1), If(slave.ack, counter_ce.eq(1), If(counter_done, master.ack.eq(1), NextState("IDLE")))).Elif(~master.cyc, NextState("IDLE"))) # Address self.comb += [ If( counter_done, slave.cti.eq(7) # indicate end of burst ).Else(slave.cti.eq(2)), slave.adr.eq(Cat(counter, master.adr)) ] # Datapath cases = {} for i in range(ratio): cases[i] = [ slave.sel.eq(master.sel[i * dw_to // 8:(i + 1) * dw_to // 8]), slave.dat_w.eq(master.dat_w[i * dw_to:(i + 1) * dw_to]) ] self.comb += Case(counter, cases) cached_data = Signal(dw_from) self.comb += master.dat_r.eq(Cat(cached_data[dw_to:], slave.dat_r)) self.sync += \ If(read & counter_ce, cached_data.eq(master.dat_r) )
def __init__(self, data_width=32, div_width=8): # Number of bits to transfer - 1 self.length = Signal(max=data_width) # Freescale CPHA self.clk_phase = Signal() # Combinatorial cs and clk signals to be registered # in Interface on ce self.clk_next = Signal() self.cs_next = Signal() self.ce = Signal() # No transfer is in progress self.idle = Signal() # When asserted and writiable, load register and start transfer self.load = Signal() # reg.pdi valid and all bits transferred # Asserted at the end of the last hold interval. # For one cycle (if the transfer completes the transaction) or # until load is asserted. self.readable = Signal() # Asserted before a transfer until the data has been loaded self.writable = Signal() # When asserted during load end the transaction with # this transfer. self.end = Signal() self.submodules.reg = reg = Register(data_width) self.submodules.cg = cg = ClockGen(div_width) ### # Bit counter n = Signal.like(self.length, reset_less=True) # Capture end for the in-flight transfer end = Signal(reset_less=True) self.comb += [self.ce.eq(cg.done & cg.count)] self.sync += [ If(reg.load, n.eq(self.length), end.eq(self.end)), If(reg.shift, n.eq(n - 1)) ] fsm = FSM("IDLE") self.submodules += fsm fsm.act( "IDLE", self.idle.eq(1), self.writable.eq(1), self.cs_next.eq(1), If( self.load, cg.count.eq(1), reg.load.eq(1), If( self.clk_phase, NextState("PRE"), ).Else( cg.extend.eq(1), NextState("SETUP"), ))) fsm.act( "PRE", # dummy half cycle after asserting CS in CPHA=1 self.cs_next.eq(1), cg.count.eq(1), cg.extend.eq(1), self.clk_next.eq(1), If(cg.done, NextState("SETUP"))) fsm.act("SETUP", self.cs_next.eq(1), cg.count.eq(1), self.clk_next.eq(~self.clk_phase), If(cg.done, reg.sample.eq(1), NextState("HOLD"))) fsm.act( "HOLD", self.cs_next.eq(1), cg.count.eq(1), cg.extend.eq(1), self.clk_next.eq(self.clk_phase), If( cg.done, If( n == 0, self.readable.eq(1), self.writable.eq(1), If( end, self.clk_next.eq(0), self.writable.eq(0), If(self.clk_phase, self.cs_next.eq(0), NextState("WAIT")).Else(NextState("POST"))).Elif( self.load, reg.load.eq(1), NextState("SETUP")).Else(cg.count.eq(0))).Else( reg.shift.eq(1), NextState("SETUP")))) fsm.act( "POST", # dummy half cycle before deasserting CS in CPHA=0 cg.count.eq(1), If(cg.done, NextState("WAIT"))) fsm.act( "WAIT", # dummy half cycle to meet CS deassertion minimum timing If(cg.done, NextState("IDLE")).Else(cg.count.eq(1)))
def __init__(self, cachesize, master, slave): self.master = master self.slave = slave # # # dw_from = len(master.dat_r) dw_to = len(slave.dat_r) if dw_to > dw_from and (dw_to % dw_from) != 0: raise ValueError( "Slave data width must be a multiple of {dw}".format( dw=dw_from)) if dw_to < dw_from and (dw_from % dw_to) != 0: raise ValueError( "Master data width must be a multiple of {dw}".format( dw=dw_to)) # Split address: # TAG | LINE NUMBER | LINE OFFSET offsetbits = log2_int(max(dw_to // dw_from, 1)) addressbits = len(slave.adr) + offsetbits linebits = log2_int(cachesize) - offsetbits tagbits = addressbits - linebits wordbits = log2_int(max(dw_from // dw_to, 1)) adr_offset, adr_line, adr_tag = split(master.adr, offsetbits, linebits, tagbits) word = Signal(wordbits) if wordbits else None # Data memory data_mem = Memory(dw_to * 2**wordbits, 2**linebits) data_port = data_mem.get_port(write_capable=True, we_granularity=8) self.specials += data_mem, data_port write_from_slave = Signal() if adr_offset is None: adr_offset_r = None else: adr_offset_r = Signal(offsetbits) self.sync += adr_offset_r.eq(adr_offset) self.comb += [ data_port.adr.eq(adr_line), If(write_from_slave, displacer(slave.dat_r, word, data_port.dat_w), displacer(Replicate(1, dw_to // 8), word, data_port.we)).Else( data_port.dat_w.eq( Replicate(master.dat_w, max(dw_to // dw_from, 1))), If( master.cyc & master.stb & master.we & master.ack, displacer(master.sel, adr_offset, data_port.we, 2**offsetbits, reverse=True))), chooser(data_port.dat_r, word, slave.dat_w), slave.sel.eq(2**(dw_to // 8) - 1), chooser(data_port.dat_r, adr_offset_r, master.dat_r, reverse=True) ] # Tag memory tag_layout = [("tag", tagbits), ("dirty", 1)] tag_mem = Memory(layout_len(tag_layout), 2**linebits) tag_port = tag_mem.get_port(write_capable=True) self.specials += tag_mem, tag_port tag_do = Record(tag_layout) tag_di = Record(tag_layout) self.comb += [ tag_do.raw_bits().eq(tag_port.dat_r), tag_port.dat_w.eq(tag_di.raw_bits()) ] self.comb += [tag_port.adr.eq(adr_line), tag_di.tag.eq(adr_tag)] if word is not None: self.comb += slave.adr.eq(Cat(word, adr_line, tag_do.tag)) else: self.comb += slave.adr.eq(Cat(adr_line, tag_do.tag)) # slave word computation, word_clr and word_inc will be simplified # at synthesis when wordbits=0 word_clr = Signal() word_inc = Signal() if word is not None: self.sync += \ If(word_clr, word.eq(0), ).Elif(word_inc, word.eq(word+1) ) def word_is_last(word): if word is not None: return word == 2**wordbits - 1 else: return 1 # Control FSM self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm.act("IDLE", If(master.cyc & master.stb, NextState("TEST_HIT"))) fsm.act( "TEST_HIT", word_clr.eq(1), If(tag_do.tag == adr_tag, master.ack.eq(1), If(master.we, tag_di.dirty.eq(1), tag_port.we.eq(1)), NextState("IDLE")).Else( If(tag_do.dirty, NextState("EVICT")).Else(NextState("REFILL_WRTAG")))) fsm.act( "EVICT", slave.stb.eq(1), slave.cyc.eq(1), slave.we.eq(1), If(slave.ack, word_inc.eq(1), If(word_is_last(word), NextState("REFILL_WRTAG")))) fsm.act( "REFILL_WRTAG", # Write the tag first to set the slave address tag_port.we.eq(1), word_clr.eq(1), NextState("REFILL")) fsm.act( "REFILL", slave.stb.eq(1), slave.cyc.eq(1), slave.we.eq(0), If( slave.ack, write_from_slave.eq(1), word_inc.eq(1), If( word_is_last(word), NextState("TEST_HIT"), ).Else(NextState("REFILL"))))
def __init__(self, sink_description, source_description, header): self.sink = sink = stream.Endpoint(sink_description) self.source = source = stream.Endpoint(source_description) self.header = Signal(header.length * 8) # # # # Parameters ------------------------------------------------------------------------------- data_width = len(self.sink.data) bytes_per_clk = data_width // 8 header_words = (header.length * 8) // data_width header_leftover = header.length % bytes_per_clk # Signals ---------------------------------------------------------------------------------- sr = Signal(header.length * 8, reset_less=True) sr_load = Signal() sr_shift = Signal() count = Signal(max=max(header_words, 2)) sink_d = stream.Endpoint(sink_description) # Header Encode/Load/Shift ----------------------------------------------------------------- self.comb += header.encode(sink, self.header) self.sync += If(sr_load, sr.eq(self.header)) if header_words != 1: self.sync += If(sr_shift, sr.eq(sr[data_width:])) # FSM -------------------------------------------------------------------------------------- self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm_from_idle = Signal() fsm.act( "IDLE", sink.ready.eq(1), NextValue(count, 1), If( sink.valid, sink.ready.eq(0), source.valid.eq(1), source.last.eq(0), source.data.eq(self.header[:data_width]), If( source.valid & source.ready, sr_load.eq(1), NextValue(fsm_from_idle, 1), If( header_words == 1, If(header_leftover != 0, NextState("UNALIGNED-DATA-COPY")).Else( NextState("ALIGNED-DATA-COPY"))).Else( NextState("HEADER-SEND"))))) fsm.act( "HEADER-SEND", source.valid.eq(1), source.last.eq(0), source.data.eq(sr[min(data_width, len(sr) - 1):]), If( source.valid & source.ready, sr_shift.eq(1), If( count == (header_words - 1), sr_shift.eq(0), If(header_leftover, NextState("UNALIGNED-DATA-COPY"), NextValue(count, count + 1)).Else( NextState("ALIGNED-DATA-COPY"))).Else( NextValue(count, count + 1), ))) fsm.act( "ALIGNED-DATA-COPY", source.valid.eq(sink.valid), source.last.eq(sink.last), source.data.eq(sink.data), If(source.valid & source.ready, sink.ready.eq(1), If(source.last, NextState("IDLE")))) header_offset_multiplier = 1 if header_words == 1 else 2 self.sync += If(source.ready, sink_d.eq(sink)) fsm.act( "UNALIGNED-DATA-COPY", source.valid.eq(sink.valid | sink_d.last), source.last.eq(sink_d.last), If( fsm_from_idle, source.data[:max(header_leftover * 8, 1)].eq( sr[min(header_offset_multiplier * data_width, len(sr) - 1):])).Else( source.data[:max(header_leftover * 8, 1)].eq( sink_d.data[min((bytes_per_clk - header_leftover) * 8, data_width - 1):])), source.data[header_leftover * 8:].eq(sink.data), If(source.valid & source.ready, sink.ready.eq(~source.last), NextValue(fsm_from_idle, 0), If(source.last, NextState("IDLE")))) # Error ------------------------------------------------------------------------------------ if hasattr(sink, "error") and hasattr(source, "error"): self.comb += source.error.eq(sink.error) # Last BE ---------------------------------------------------------------------------------- if hasattr(sink, "last_be") and hasattr(source, "last_be"): rotate_by = header.length % bytes_per_clk x = [ sink.last_be[(i + rotate_by) % bytes_per_clk] for i in range(bytes_per_clk) ] self.comb += source.last_be.eq(Cat(*x))
def __init__(self, wrport, depth, consume_watermark, ena, la_filters=[]): self.ulpi_sink = Endpoint(ULPI_DATA_TAG) self.out_addr = Endpoint(dmatpl(depth)) # Produce side self.submodules.produce_write = Acc_inc(max=depth) self.submodules.produce_header = Acc(max=depth) self.consume_point = Acc(max=depth) self.submodules.size = Acc_inc(16) self.submodules.flags = Acc_or(16) self.submodules.to_start = Acc(1) # Header format: # A0 F0 F1 SL SH 00 00 00 d0....dN # Flags format # # F0.0 - ERR - Line level error (ULPI.RXERR asserted during packet) # F0.1 - OVF - RX Path Overflow (shouldn't happen - debugging) # F0.2 - CLIP - Filter clipped (we do not set yet) # F0.3 - PERR - Protocol level err (but ULPI was fine, ???) # # Following not implemented yet # F0.6 - SHORT - short packet, no size, fixed 4 byte data # F0.7 - EXT - Extended header self.submodules.fsm = FSM() has_space = Signal() self.comb += has_space.eq( ((consume_watermark - self.produce_write.v - 1) & (depth - 1)) > 8) # Grab packet timestamp at SOP pkt_timestamp = Signal(len(self.ulpi_sink.payload.ts)) self.sync += If(self.ulpi_sink.payload.is_start & self.ulpi_sink.ack, pkt_timestamp.eq(self.ulpi_sink.payload.ts)) payload_is_rxcmd = Signal() self.comb += payload_is_rxcmd.eq(self.ulpi_sink.payload.is_start | self.ulpi_sink.payload.is_end | self.ulpi_sink.payload.is_err | self.ulpi_sink.payload.is_ovf) # Packet first/last bits clear_acc_flags = Signal() en_last = Signal() self.sync += en_last.eq(ena) self.submodules.packet_first = Acc(1) self.submodules.packet_last = Acc(1) # Stuff-packet bit # At start-of-capture or end-of-capture, we stuff a packet to # indicate the exact time of capture stuff_packet = Signal() self.comb += stuff_packet.eq(self.packet_first.v | self.packet_last.v) self.comb += If(ena & ~en_last, self.packet_first.set(1)).Elif( clear_acc_flags, self.packet_first.set(0)) self.comb += If(~ena & en_last, self.packet_last.set(1)).Elif(clear_acc_flags, self.packet_last.set(0)) flags_ini = Signal(16) self.comb += flags_ini.eq( Mux(self.packet_last.v, HF0_LAST, 0) | Mux(self.packet_first.v, HF0_FIRST, 0)) # Combine outputs of filters la_resets = [f.reset.eq(1) for f in la_filters] filter_done = 1 filter_reject = 0 for f in la_filters: filter_done = f.done & filter_done filter_reject = f.reject | filter_reject self.fsm.act( "IDLE", If(((self.ulpi_sink.stb | self.to_start.v) & ena | stuff_packet) & has_space, If(~self.to_start.v | (self.ulpi_sink.stb & stuff_packet), self.ulpi_sink.ack.eq(1)), self.produce_write.set(self.produce_header.v + 8), self.size.set(0), self.flags.set(flags_ini), self.to_start.set(0), la_resets, If(self.ulpi_sink.payload.is_start | self.to_start.v, NextState("DATA")).Elif( stuff_packet, NextState("WH0"), clear_acc_flags.eq(1), ) # If not enabled, we just dump RX'ed data ).Elif(~ena, self.ulpi_sink.ack.eq(1))) def write_hdr(statename, nextname, hdr_offs, val): self.fsm.act(statename, NextState(nextname), wrport.adr.eq(self.produce_header.v + hdr_offs), wrport.dat_w.eq(val), wrport.we.eq(1)) do_filter_write = Signal() # Feed data to lookaside filters for f in la_filters: self.comb += [ f.write.eq(do_filter_write), f.dat_w.eq(self.ulpi_sink.payload) ] packet_too_long = Signal() self.comb += packet_too_long.eq(self.size.v >= MAX_PACKET_SIZE) self.fsm.act( "DATA", If(packet_too_long, self.flags._or(HF0_TRUNC), NextState("WH0")).Elif( has_space & self.ulpi_sink.stb, self.ulpi_sink.ack.eq(1), If( payload_is_rxcmd, # Got another start-of-packet If( self.ulpi_sink.payload.is_start, self.flags._or(HF0_OVF), # If we got a SOP, we need to skip RXCMD det in IDLE self.to_start.set(1) # Mark error if we hit an error ).Elif( self.ulpi_sink.payload.is_err, self.flags._or(HF0_ERR), # Mark overflow if we got a stuffed overflow ).Elif(self.ulpi_sink.payload.is_ovf, self.flags._or(HF0_OVF)), # In any case (including END), we're done RXing NextState("waitdone")).Else( self.size.inc(), self.produce_write.inc(), wrport.adr.eq(self.produce_write.v), wrport.dat_w.eq(self.ulpi_sink.payload.d), wrport.we.eq(1), do_filter_write.eq(1)))) self.fsm.act( "waitdone", If( filter_done, If(filter_reject, NextState("IDLE")).Else(clear_acc_flags.eq(1), NextState("WH0")))) write_hdr("WH0", "WRF0", 0, 0xA0) # Write flags field write_hdr("WRF0", "WRF1", 1, self.flags.v[:8]) write_hdr("WRF1", "WRSL", 2, self.flags.v[8:16]) # Write size field write_hdr("WRSL", "WRSH", 3, self.size.v[:8]) write_hdr("WRSH", "WRTL", 4, self.size.v[8:16]) write_hdr("WRTL", "WRTM", 5, pkt_timestamp[:8]) write_hdr("WRTM", "WRTH", 6, pkt_timestamp[8:16]) write_hdr("WRTH", "SEND", 7, pkt_timestamp[16:24]) self.fsm.act( "SEND", self.out_addr.stb.eq(1), self.out_addr.payload.start.eq(self.produce_header.v), self.out_addr.payload.count.eq(self.size.v + 8), If(self.out_addr.ack, self.produce_header.set(self.produce_write.v), NextState("IDLE")))
def __init__(self, sink_description, source_description, header): self.sink = sink = stream.Endpoint(sink_description) self.source = source = stream.Endpoint(source_description) self.header = Signal(header.length * 8) # # # # Parameters ------------------------------------------------------------------------------- data_width = len(sink.data) bytes_per_clk = data_width // 8 header_words = (header.length * 8) // data_width header_leftover = header.length % bytes_per_clk # Signals ---------------------------------------------------------------------------------- sr = Signal(header.length * 8, reset_less=True) sr_shift = Signal() sr_shift_leftover = Signal() count = Signal(max=max(header_words, 2)) sink_d = stream.Endpoint(sink_description) # Header Shift/Decode ---------------------------------------------------------------------- if (header_words) == 1 and (header_leftover == 0): self.sync += If(sr_shift, sr.eq(sink.data)) else: self.sync += [ If(sr_shift, sr.eq(Cat(sr[bytes_per_clk * 8:], sink.data))), If(sr_shift_leftover, sr.eq(Cat(sr[header_leftover * 8:], sink.data))) ] self.comb += self.header.eq(sr) self.comb += header.decode(self.header, source) # FSM -------------------------------------------------------------------------------------- self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm_from_idle = Signal() fsm.act( "IDLE", sink.ready.eq(1), NextValue(count, 1), If( sink.valid, sr_shift.eq(1), NextValue(fsm_from_idle, 1), If( header_words == 1, If(header_leftover, NextState("UNALIGNED-DATA-COPY")).Else( NextState("ALIGNED-DATA-COPY")), ).Else(NextState("HEADER-RECEIVE")))) fsm.act( "HEADER-RECEIVE", sink.ready.eq(1), If( sink.valid, NextValue(count, count + 1), sr_shift.eq(1), If( count == (header_words - 1), If(header_leftover, NextValue(count, count + 1), NextState("UNALIGNED-DATA-COPY")).Else( NextState("ALIGNED-DATA-COPY"))))) self.sync += If(sink.valid & sink.ready, sink_d.eq(sink)) fsm.act( "UNALIGNED-DATA-COPY", source.valid.eq(sink.valid | sink_d.last), source.last.eq(sink.last | sink_d.last), sink.ready.eq(source.ready), source.data.eq(sink_d.data[header_leftover * 8:]), source.data[min((bytes_per_clk - header_leftover) * 8, data_width - 1):].eq(sink.data), If( fsm_from_idle, source.valid.eq(sink_d.last), sink.ready.eq(1), If( sink.valid, NextValue(fsm_from_idle, 0), sr_shift_leftover.eq(1), )), If(source.valid & source.ready, If(source.last, NextState("IDLE")))) fsm.act( "ALIGNED-DATA-COPY", source.valid.eq(sink.valid | sink_d.last), source.last.eq(sink.last | sink_d.last), sink.ready.eq(source.ready), source.data.eq(sink.data), If(source.valid & source.ready, If(source.last, NextState("IDLE")))) # Error ------------------------------------------------------------------------------------ if hasattr(sink, "error") and hasattr(source, "error"): self.comb += source.error.eq(sink.error) # Last BE ---------------------------------------------------------------------------------- if hasattr(sink, "last_be") and hasattr(source, "last_be"): x = [ sink.last_be[(i - (bytes_per_clk - header_leftover)) % bytes_per_clk] for i in range(bytes_per_clk) ] self.comb += source.last_be.eq(Cat(*x))
def __init__(self, data_width, clock_width, bits_width): ce = CEInserter() self.submodules.cg = ce(SPIClockGen(clock_width)) self.submodules.reg = ce(SPIRegister(data_width)) self.submodules.bits = ce(SPIBitCounter(bits_width)) self.div_write = Signal.like(self.cg.load) self.div_read = Signal.like(self.cg.load) self.clk_phase = Signal() self.start = Signal() self.cs = Signal() self.cs_next = Signal() self.oe = Signal() self.done = Signal() # # # fsm = CEInserter()(FSM("IDLE")) self.submodules += fsm fsm.act( "IDLE", If( self.start, If( self.clk_phase, NextState("WAIT"), ).Else(NextState("SETUP"), ))) fsm.act( "SETUP", self.reg.sample.eq(1), NextState("HOLD"), ) fsm.act( "HOLD", If( self.bits.done & ~self.start, If( self.clk_phase, NextState("IDLE"), ).Else(NextState("WAIT"), )).Else( self.reg.shift.eq(~self.start), NextState("SETUP"), )) fsm.act( "WAIT", If( self.bits.done, NextState("IDLE"), ).Else(NextState("SETUP"), )) write0 = Signal() read0 = Signal() self.sync += [ If( self.cg.edge & self.reg.shift, write0.eq(self.bits.write), read0.eq(self.bits.read), ), If( self.cg.edge & fsm.before_entering("IDLE"), write0.eq(0), read0.eq(0), ), ] self.comb += [ self.cg.ce.eq(self.start | self.cs | ~self.cg.edge), If( (read0 | self.bits.read) & ~self.bits.write, self.cg.load.eq(self.div_read), ).Else(self.cg.load.eq(self.div_write), ), self.cg.bias.eq(self.clk_phase), fsm.ce.eq(self.cg.edge), self.cs.eq(~fsm.ongoing("IDLE")), self.cs_next.eq( fsm.before_leaving("IDLE") | (self.cs & ~fsm.before_entering("IDLE"))), self.reg.ce.eq(self.cg.edge), self.bits.ce.eq(self.cg.edge & self.reg.sample), self.done.eq(self.cg.edge & self.bits.done & fsm.ongoing("HOLD")), self.oe.eq(write0 | self.bits.write), ]
def __init__(self, pads, default=_default_edid): self._hpd_notif = CSRStatus() self._hpd_en = CSRStorage() self.specials.mem = Memory(8, 128, init=default) ### # HPD if hasattr(pads, "hpd_notif"): self.specials += MultiReg(pads.hpd_notif, self._hpd_notif.status) else: self.comb += self._hpd_notif.status.eq(1) if hasattr(pads, "hpd_en"): self.comb += pads.hpd_en.eq(self._hpd_en.storage) # EDID scl_raw = Signal() sda_i = Signal() sda_raw = Signal() sda_drv = Signal() _sda_drv_reg = Signal() _sda_i_async = Signal() self.sync += _sda_drv_reg.eq(sda_drv) self.specials += [ MultiReg(pads.scl, scl_raw), Tristate(pads.sda, 0, _sda_drv_reg, _sda_i_async), MultiReg(_sda_i_async, sda_raw) ] scl_i = Signal() samp_count = Signal(6) samp_carry = Signal() self.sync += [ Cat(samp_count, samp_carry).eq(samp_count + 1), If(samp_carry, scl_i.eq(scl_raw), sda_i.eq(sda_raw)) ] scl_r = Signal() sda_r = Signal() scl_rising = Signal() sda_rising = Signal() sda_falling = Signal() self.sync += [scl_r.eq(scl_i), sda_r.eq(sda_i)] self.comb += [ scl_rising.eq(scl_i & ~scl_r), sda_rising.eq(sda_i & ~sda_r), sda_falling.eq(~sda_i & sda_r) ] start = Signal() self.comb += start.eq(scl_i & sda_falling) din = Signal(8) counter = Signal(max=9) self.sync += [ If(start, counter.eq(0)), If( scl_rising, If(counter == 8, counter.eq(0)).Else(counter.eq(counter + 1), din.eq(Cat(sda_i, din[:7])))) ] is_read = Signal() update_is_read = Signal() self.sync += If(update_is_read, is_read.eq(din[0])) offset_counter = Signal(max=128) oc_load = Signal() oc_inc = Signal() self.sync += [ If(oc_load, offset_counter.eq(din)).Elif( oc_inc, offset_counter.eq(offset_counter + 1)) ] rdport = self.mem.get_port() self.specials += rdport self.comb += rdport.adr.eq(offset_counter) data_bit = Signal() zero_drv = Signal() data_drv = Signal() self.comb += If(zero_drv, sda_drv.eq(1)).Elif(data_drv, sda_drv.eq(~data_bit)) data_drv_en = Signal() data_drv_stop = Signal() self.sync += If(data_drv_en, data_drv.eq(1)).Elif(data_drv_stop, data_drv.eq(0)) self.sync += If( data_drv_en, chooser(rdport.dat_r, counter, data_bit, 8, reverse=True)) fsm = FSM() self.submodules += fsm fsm.act("WAIT_START") fsm.act( "RCV_ADDRESS", If( counter == 8, If(din[1:] == 0x50, update_is_read.eq(1), NextState("ACK_ADDRESS0")).Else(NextState("WAIT_START")))) fsm.act("ACK_ADDRESS0", If(~scl_i, NextState("ACK_ADDRESS1"))) fsm.act("ACK_ADDRESS1", zero_drv.eq(1), If(scl_i, NextState("ACK_ADDRESS2"))) fsm.act( "ACK_ADDRESS2", zero_drv.eq(1), If(~scl_i, If(is_read, NextState("READ")).Else(NextState("RCV_OFFSET")))) fsm.act("RCV_OFFSET", If(counter == 8, oc_load.eq(1), NextState("ACK_OFFSET0"))) fsm.act("ACK_OFFSET0", If(~scl_i, NextState("ACK_OFFSET1"))) fsm.act("ACK_OFFSET1", zero_drv.eq(1), If(scl_i, NextState("ACK_OFFSET2"))) fsm.act("ACK_OFFSET2", zero_drv.eq(1), If(~scl_i, NextState("RCV_ADDRESS"))) fsm.act( "READ", If( ~scl_i, If(counter == 8, data_drv_stop.eq(1), NextState("ACK_READ")).Else(data_drv_en.eq(1)))) fsm.act( "ACK_READ", If(scl_rising, oc_inc.eq(1), If(sda_i, NextState("WAIT_START")).Else(NextState("READ")))) for state in fsm.actions.keys(): fsm.act(state, If(start, NextState("RCV_ADDRESS"))) fsm.act(state, If(~self._hpd_en.storage, NextState("WAIT_START")))
def __init__(self, sdram, clk_out, clk_sample, databits, rowbits, colbits, bankbits, burst, tRESET, tCL, tRP, tRFC, tRCD, tREFI, tWR): addrbits = rowbits + colbits + bankbits assert sdram.dq.nbits == databits colabits = colbits if colbits <= 10 else colbits + 1 max_col = Replicate(1, colbits) assert sdram.a.nbits >= colabits assert sdram.a.nbits >= rowbits assert sdram.ba.nbits == bankbits dqmbits = max(databits // 8, 1) assert sdram.dqm.nbits == dqmbits assert burst <= 1 << colbits self.hostif = Record(sdramHostIf(databits, addrbits)) # DQ handling, tristate, and sampling dq = TSTriple(databits) self.specials += dq.get_tristate(sdram.dq) dq_r = Signal(databits) self.clock_domains.cd_sample = ClockDomain(reset_less=True) self.comb += self.cd_sample.clk.eq(clk_sample) self.sync.sample += dq_r.eq(dq.i) # Signals used for driving SDRAM control signals # These registered and derived from the current FSM state. # However, the reset state actually determines the default value for # states where they are not explicitly assigned. For example, cmd is # INHIBIT at reset (because the FSM is in RESET state at reset and that # sets cmd to INHIBIT), but it's NOP for every other state where it # isn't assigned. cmd = Signal(4, reset=NOP) dqm = Signal() ba = Signal(bankbits) a = Signal(max(colabits, rowbits)) cke = Signal() sdram.cs_n.reset = 1 self.sync += [ sdram.dqm.eq(Replicate(dqm, dqmbits)), sdram.cs_n.eq(cmd[3]), sdram.ras_n.eq(cmd[2]), sdram.cas_n.eq(cmd[1]), sdram.we_n.eq(cmd[0]), sdram.ba.eq(ba), sdram.a.eq(a), sdram.cke.eq(cke), ] self.comb += [ sdram.clk.eq(clk_out), ] # Counter to time reset cycle of the SDRAM # We enable CKE on the first cycle after system reset, then wait tRESET reset_ctr = Signal(max=tRESET + 1) self.sync += [cke.eq(1), reset_ctr.eq(reset_ctr + 1)] # Counter to time refresh intervals # Note that this can go higher than tREFI, since we might be in the # middle of a burst, but long-term refresh cycles will be issued often # enough to meet refresh timing. refresh_interval = tREFI - 2 # A bit of leeway for safety refresh_ctr = Signal(max=(refresh_interval + 2 * burst + 128)) self.sync += If( cmd == AUTO_REFRESH, If(refresh_ctr > refresh_interval, refresh_ctr.eq(refresh_ctr - refresh_interval)).Else( refresh_ctr.eq(0))).Else(refresh_ctr.eq(refresh_ctr + 1)) tMRD = 3 # JEDEC spec, Micron only needs 2 # Mode: Full page burst mode, burst write mode = 0b0000000111 | (tCL << 4) # last_col indicates that the read/write is about to wrap, and so should end last_col = Signal() i_col_cnt = Signal(colbits) self.comb += last_col.eq(i_col_cnt == max_col) def delay_clocks(v, d): for i in range(d): n = Signal() self.sync += n.eq(v) v = n return v # Read cycle state signals read_cycle = Signal() # Reads come back tCL + 1 clocks later. The extra two cycles # are due to the registration of FIFO outputs and inputs dq_r_sample = Signal(databits) self.sync += dq_r_sample.eq(dq_r) returning_read = delay_clocks(read_cycle, tCL + 2) can_continue_read = Signal() kill_read = Signal() self.comb += [ can_continue_read.eq(~self.hostif.d_term & ~last_col & ~kill_read), self.hostif.d_read.eq(dq_r_sample), ] self.sync += [ # If the output FIFO becomes full, kill the current read If(returning_read & self.hostif.d_term, kill_read.eq(1)).Elif(~returning_read, kill_read.eq(0)), ] # Write state signals write_cycle = Signal() can_continue_write = Signal() self.sync += [ dq.o.eq(self.hostif.d_write), dq.oe.eq(write_cycle), ] self.comb += [ can_continue_write.eq(~self.hostif.d_term & ~last_col), dqm.eq(self.hostif.d_term & write_cycle), ] # Shared signals cmd_needs_reissue = Signal() cmd_reissue = Signal() self.sync += [ If(write_cycle | read_cycle, i_col_cnt.eq(i_col_cnt + 1)), If( ~self.hostif.d_term & last_col & write_cycle | read_cycle & last_col & ~kill_read & ~(returning_read & self.hostif.d_term), cmd_needs_reissue.eq(1)).Elif(cmd_reissue, cmd_needs_reissue.eq(0)) ] # Hostif streaming interface signal generation self.comb += [ self.hostif.d_stb.eq(write_cycle | returning_read & ~kill_read), ] # Address generation def split(addr): col = addr[:colbits] if colbits > 10: col = Cat(col[:10], 0, col[10:]) return col, addr[colbits:colbits + rowbits], addr[colbits + rowbits:] # Issued cmd ptr latch_cmd = Signal() iwr = Signal(1) iptr = Signal(addrbits) i_col, i_row, i_bank = split(iptr) self.sync += If(latch_cmd, iptr.eq(self.hostif.i_addr), iwr.eq(self.hostif.i_wr), i_col_cnt.eq(split(self.hostif.i_addr)[0])).Elif( cmd_reissue, iptr[:colbits].eq(0), iptr[colbits:].eq(iptr[colbits:] + 1), i_col_cnt.eq(0)) # Finite state machine driving the controller fsm = self.submodules.fsm = FSM(reset_state="RESET") # Initialization sequence fsm.act("RESET", cmd.eq(INHIBIT), If(reset_ctr == tRESET, NextState("INIT_IDLE"))) fsm.delayed_enter("INIT_IDLE", "INIT_PRECHARGE", 5) fsm.act("INIT_PRECHARGE", cmd.eq(PRECHARGE), a[10].eq(1)) fsm.delayed_enter("INIT_PRECHARGE", "INIT_REFRESH1", tRP) fsm.act("INIT_REFRESH1", cmd.eq(AUTO_REFRESH)) fsm.delayed_enter("INIT_REFRESH1", "INIT_REFRESH2", tRFC) fsm.act("INIT_REFRESH2", cmd.eq(AUTO_REFRESH)) fsm.delayed_enter("INIT_REFRESH2", "INIT_MODE", tRFC) fsm.act("INIT_MODE", cmd.eq(LOAD_MODE), a.eq(mode)) fsm.delayed_enter("INIT_MODE", "IDLE", tMRD) # Main loop fsm.act( "IDLE", If(refresh_ctr >= refresh_interval, NextState("REFRESH")).Elif( cmd_needs_reissue, cmd_reissue.eq(1), If(iwr, NextState("WRITE_ACTIVE")).Else( NextState("READ_ACTIVE"))).Elif( self.hostif.i_wr & self.hostif.i_stb, self.hostif.i_ack.eq(1), latch_cmd.eq(1), NextState("WRITE_ACTIVE")).Elif( ~self.hostif.i_wr & self.hostif.i_stb, self.hostif.i_ack.eq(1), latch_cmd.eq(1), NextState("READ_ACTIVE"))) # REFRESH fsm.act("REFRESH", cmd.eq(AUTO_REFRESH)) fsm.delayed_enter("REFRESH", "IDLE", tRFC) # WRITE fsm.act("WRITE_ACTIVE", cmd.eq(ACTIVE), ba.eq(i_bank), a.eq(i_row)) fsm.delayed_enter("WRITE_ACTIVE", "WRITE", tRCD) fsm.act( "WRITE", cmd.eq(WRITE), ba.eq(i_bank), a.eq(i_col), write_cycle.eq(1), If(can_continue_write, NextState("WRITING")).Else( If(dqm, NextState("PRECHARGE")).Else(NextState("PRECHARGE_TWR")))) fsm.act( "WRITING", write_cycle.eq(1), If( ~can_continue_write, If(dqm, NextState("PRECHARGE")).Else(NextState("PRECHARGE_TWR")))) # READ fsm.act("READ_ACTIVE", cmd.eq(ACTIVE), ba.eq(i_bank), a.eq(i_row)) fsm.delayed_enter("READ_ACTIVE", "READ", tRCD) fsm.act( "READ", cmd.eq(READ), ba.eq(i_bank), a.eq(i_col), read_cycle.eq(1), If(can_continue_read, NextState("READING")).Else(NextState("PRECHARGE"))) fsm.act("READING", read_cycle.eq(1), If(~can_continue_read, NextState("PRECHARGE"))) if (tWR - 1) > 0: fsm.act("PRECHARGE_TWR", cmd.eq(BURST_TERM)) fsm.delayed_enter("PRECHARGE_TWR", "PRECHARGE", tWR - 1), fsm.act("PRECHARGE", cmd.eq(PRECHARGE), a[10].eq(1)), fsm.delayed_enter("PRECHARGE", "IDLE", tRP)
def __init__(self, phy, clk_freq): self.wishbone = wishbone.Interface() # # # byte_counter = Signal(3) byte_counter_reset = Signal() byte_counter_ce = Signal() self.sync += \ If(byte_counter_reset, byte_counter.eq(0) ).Elif(byte_counter_ce, byte_counter.eq(byte_counter + 1) ) word_counter = Signal(3) word_counter_reset = Signal() word_counter_ce = Signal() self.sync += \ If(word_counter_reset, word_counter.eq(0) ).Elif(word_counter_ce, word_counter.eq(word_counter + 1) ) cmd = Signal(8) cmd_ce = Signal() length = Signal(8) length_ce = Signal() address = Signal(32) address_ce = Signal() data = Signal(32) rx_data_ce = Signal() tx_data_ce = Signal() self.sync += [ If(cmd_ce, cmd.eq(phy.source.data)), If(length_ce, length.eq(phy.source.data)), If(address_ce, address.eq(Cat(phy.source.data, address[0:24]))), If(rx_data_ce, data.eq(Cat(phy.source.data, data[0:24]))).Elif(tx_data_ce, data.eq(self.wishbone.dat_r)) ] fsm = ResetInserter()(FSM(reset_state="IDLE")) timer = WaitTimer(clk_freq // 10) self.submodules += fsm, timer self.comb += [fsm.reset.eq(timer.done), phy.source.ack.eq(1)] fsm.act( "IDLE", If( phy.source.stb, cmd_ce.eq(1), If((phy.source.data == self.cmds["write"]) | (phy.source.data == self.cmds["read"]), NextState("RECEIVE_LENGTH")), byte_counter_reset.eq(1), word_counter_reset.eq(1))) fsm.act( "RECEIVE_LENGTH", If(phy.source.stb, length_ce.eq(1), NextState("RECEIVE_ADDRESS"))) fsm.act( "RECEIVE_ADDRESS", If( phy.source.stb, address_ce.eq(1), byte_counter_ce.eq(1), If( byte_counter == 3, If(cmd == self.cmds["write"], NextState("RECEIVE_DATA")).Elif( cmd == self.cmds["read"], NextState("READ_DATA")), byte_counter_reset.eq(1), ))) fsm.act( "RECEIVE_DATA", If( phy.source.stb, rx_data_ce.eq(1), byte_counter_ce.eq(1), If(byte_counter == 3, NextState("WRITE_DATA"), byte_counter_reset.eq(1)))) self.comb += [ self.wishbone.adr.eq(address + word_counter), self.wishbone.dat_w.eq(data), self.wishbone.sel.eq(2**len(self.wishbone.sel) - 1) ] fsm.act( "WRITE_DATA", self.wishbone.stb.eq(1), self.wishbone.we.eq(1), self.wishbone.cyc.eq(1), If( self.wishbone.ack, word_counter_ce.eq(1), If(word_counter == (length - 1), NextState("IDLE")).Else(NextState("RECEIVE_DATA")))) fsm.act( "READ_DATA", self.wishbone.stb.eq(1), self.wishbone.we.eq(0), self.wishbone.cyc.eq(1), If(self.wishbone.ack, tx_data_ce.eq(1), NextState("SEND_DATA"))) self.comb += \ chooser(data, byte_counter, phy.sink.data, n=4, reverse=True) fsm.act( "SEND_DATA", phy.sink.stb.eq(1), If( phy.sink.ack, byte_counter_ce.eq(1), If( byte_counter == 3, word_counter_ce.eq(1), If(word_counter == (length - 1), NextState("IDLE")).Else(NextState("READ_DATA"), byte_counter_reset.eq(1))))) self.comb += timer.wait.eq(~fsm.ongoing("IDLE")) self.comb += phy.sink.eop.eq((byte_counter == 3) & (word_counter == length - 1)) if hasattr(phy.sink, "length"): self.comb += phy.sink.length.eq(4 * length)