def __init__(self, *, depth, addr_width, data_width, granularity): self.w = Record([("en", 1), ("rdy", 1), ("op", [ ("addr", addr_width), ("mask", data_width // granularity), ("data", data_width), ])]) self.r = Record.like(self.w) self._fifo = SyncFIFOBuffered(width=len(self.w.op), depth=depth)
def _make_output_queue(self, m): m.submodules['FIFO'] = fifo = SyncFIFOBuffered( depth=config.OUTPUT_QUEUE_DEPTH, width=32) m.submodules['oq_get'] = oq_get = NextWordGetter() m.d.comb += [ oq_get.data.eq(fifo.r_data), oq_get.ready.eq(fifo.r_rdy), fifo.r_en.eq(oq_get.next), ] self.register_xetter(34, oq_get) oq_has_space = fifo.w_level < (config.OUTPUT_QUEUE_DEPTH - 8) return fifo.w_data, fifo.w_en, oq_has_space
def elab(self, m: Module): m.submodules.wrapped = fifo = SyncFIFOBuffered(depth=self.depth, width=len( self.input.payload)) m.d.comb += [ fifo.w_en.eq(self.input.valid), fifo.w_data.eq(self.input.payload), self.input.ready.eq(fifo.w_rdy), self.output.valid.eq(fifo.r_rdy), self.output.payload.eq(fifo.r_data), fifo.r_en.eq(self.output.ready), self.r_level.eq(fifo.r_level), ]
def elaborate(self, platform: Platform) -> Module: m = Module() ratio = self.ratio assert ratio == 4 # Maybe these should be moved into PCIeDLLTLP class since it also involves RX a bit m.submodules.buffer = buffer = TLPBuffer(ratio = ratio, max_tlps = 2 ** len(self.replay_num)) m.submodules.unacknowledged_tlp_fifo = unacknowledged_tlp_fifo = SyncFIFOBuffered(width = 12, depth = buffer.max_tlps) m.d.comb += self.dll.status.retry_buffer_occupation.eq(buffer.slots_occupied) m.d.comb += self.dll.status.tx_seq_num.eq(self.next_transmit_seq) source_from_buffer = Signal() sink_ready = Signal() m.d.comb += buffer.tlp_source.ready.eq(sink_ready & source_from_buffer) m.d.comb += self.tlp_sink.ready.eq(sink_ready & ~source_from_buffer & ~buffer.slots_full) sink_valid = Mux(source_from_buffer, buffer.tlp_source.all_valid, self.tlp_sink.all_valid) sink_symbol = [Mux(source_from_buffer, buffer.tlp_source.symbol[i], self.tlp_sink.symbol[i]) for i in range(ratio)] self.tlp_sink.connect(buffer.tlp_sink, m.d.comb) with m.If(self.dll.up): m.d.comb += buffer.store_tlp.eq(1) # TODO: Is this a good idea? m.d.rx += buffer.store_tlp_id.eq(self.next_transmit_seq) m.d.rx += buffer.send_tlp_id.eq(unacknowledged_tlp_fifo.r_data) with m.Else(): m.d.rx += self.next_transmit_seq.eq(self.next_transmit_seq.reset) m.d.rx += self.ackd_seq.eq(self.ackd_seq.reset) m.d.rx += self.replay_num.eq(self.replay_num.reset) m.d.rx += self.accepts_tlps.eq((self.next_transmit_seq - self.ackd_seq) >= 2048) # mod 4096 is already applied since the signal is 12 bits long with m.If(self.replay_timer_running): m.d.rx += self.replay_timer.eq(self.replay_timer + 1) with m.If(unacknowledged_tlp_fifo.r_level == 0): m.d.rx += self.replay_timer_running.eq(0) m.d.rx += self.replay_timer.eq(0) with m.FSM(name="Replay_FSM", domain = "rx"): with m.State("Idle"): m.d.rx += source_from_buffer.eq(0) with m.If(self.replay_timer >= self.replay_timeout): m.next = "Replay" with m.If(self.dll.received_ack_nak & ~self.dll.received_ack): m.next = "Replay" with m.State("Replay"): m.d.rx += source_from_buffer.eq(1) m.d.rx += self.replay_timer.eq(0) # TODO: This should be after the TLP is sent with m.If(buffer.slots_empty): m.d.rx += source_from_buffer.eq(0) m.next = "Idle" m.d.rx += self.ackd_seq.eq(self.dll.received_ack_nak_id) # If ACK received: # Delete entry from retry buffer and advance unacknowledged_tlp_fifo # TODO: This doesn't check whether the ID stored in the FIFO was the one which was acknowledged. Maybe it can be replaced with a counter or that can be checked. with m.If(self.dll.received_ack_nak & self.dll.received_ack): m.d.comb += buffer.delete_tlp.eq(1) m.d.comb += buffer.delete_tlp_id.eq(self.dll.received_ack_nak_id) m.d.comb += unacknowledged_tlp_fifo.r_en.eq(1) m.d.comb += buffer.in_buffer_id.eq(self.dll.received_ack_nak_id) with m.If(buffer.in_buffer): m.d.rx += self.replay_timer.eq(0) reset_crc = Signal(reset = 1) # TODO Warning: Endianness crc_input = Signal(32) m.submodules.lcrc = lcrc = LCRC(crc_input, reset_crc) last_valid = Signal() m.d.rx += last_valid.eq(sink_valid) last_last_valid = Signal() # TODO: Maybe there is a better way m.d.rx += last_last_valid.eq(last_valid) last_last_last_valid = Signal() # TODO: Maybe there is a better way 😿 like a longer signal which gets shifted for example m.d.rx += last_last_last_valid.eq(last_last_valid) tlp_bytes = Signal(8 * self.ratio) tlp_bytes_before = Signal(8 * self.ratio) with m.If(sink_valid): m.d.comb += Cat(tlp_bytes[0 : 8 * ratio]).eq(Cat(sink_symbol)) # TODO: Endianness correct? m.d.rx += Cat(tlp_bytes_before[0 : 8 * ratio]).eq(Cat(sink_symbol)) # TODO: Endianness correct? with m.Else(): with m.If(self.nullify): m.d.comb += Cat(tlp_bytes[0 : 8 * ratio]).eq(~lcrc.output) # ~~x = x m.d.rx += Cat(tlp_bytes_before[0 : 8 * ratio]).eq(~lcrc.output) m.d.comb += buffer.delete_tlp.eq(1) m.d.comb += buffer.delete_tlp_id.eq(self.next_transmit_seq) with m.Else(): m.d.comb += Cat(tlp_bytes[0 : 8 * ratio]).eq(lcrc.output) # TODO: Endianness correct? m.d.rx += Cat(tlp_bytes_before[0 : 8 * ratio]).eq(lcrc.output) # TODO: Endianness correct? #m.d.rx += Cat(tlp_bytes[8 * ratio : 2 * 8 * ratio]).eq(Cat(tlp_bytes[0 : 8 * ratio])) even_more_delay = [Signal(9) for i in range(4)] m.d.rx += unacknowledged_tlp_fifo.w_en.eq(0) m.d.comb += sink_ready.eq(0) # TODO: maybe move to rx? delayed_symbol = Signal(32) m.d.rx += delayed_symbol.eq(Cat(sink_symbol)) m.d.comb += crc_input.eq(delayed_symbol) for i in range(4): m.d.rx += self.dllp_source.valid[i].eq(0) with m.If(self.dllp_source.ready & unacknowledged_tlp_fifo.w_rdy): m.d.comb += sink_ready.eq(1) # TODO: maybe move to rx? with m.FSM(name = "TLP_transmit_FSM", domain = "rx"): with m.State("Idle"): with m.If(~last_valid & sink_valid): m.d.comb += reset_crc.eq(0) m.d.comb += crc_input.eq(Cat(self.next_transmit_seq[8 : 12], Const(0, shape = 4), self.next_transmit_seq[0 : 8])) m.d.rx += even_more_delay[0].eq(Ctrl.STP) m.d.rx += even_more_delay[1].eq(self.next_transmit_seq[8 : 12]) m.d.rx += even_more_delay[2].eq(self.next_transmit_seq[0 : 8]) m.d.rx += even_more_delay[3].eq(tlp_bytes[8 * 0 : 8 * 1]) m.next = "Transmit" with m.State("Transmit"): with m.If(last_valid & sink_valid): m.d.comb += reset_crc.eq(0) m.d.rx += even_more_delay[0].eq(tlp_bytes_before[8 * 1 : 8 * 2]) m.d.rx += even_more_delay[1].eq(tlp_bytes_before[8 * 2 : 8 * 3]) m.d.rx += even_more_delay[2].eq(tlp_bytes_before[8 * 3 : 8 * 4]) m.d.rx += even_more_delay[3].eq(tlp_bytes[8 * 0 : 8 * 1]) for i in range(4): m.d.rx += self.dllp_source.symbol[i].eq(even_more_delay[i]) for i in range(4): m.d.rx += self.dllp_source.valid[i].eq(1) with m.Elif(~sink_valid): m.d.comb += reset_crc.eq(0) m.d.comb += sink_ready.eq(0) # TODO: maybe move to rx? m.d.rx += even_more_delay[0].eq(tlp_bytes_before[8 * 1 : 8 * 2]) m.d.rx += even_more_delay[1].eq(tlp_bytes_before[8 * 2 : 8 * 3]) m.d.rx += even_more_delay[2].eq(tlp_bytes_before[8 * 3 : 8 * 4]) for i in range(4): m.d.rx += self.dllp_source.symbol[i].eq(even_more_delay[i]) for i in range(4): m.d.rx += self.dllp_source.valid[i].eq(1) m.next = "Post-1" with m.State("Post-1"): m.d.rx += self.dllp_source.symbol[3].eq(tlp_bytes[8 * 0 : 8 * 1]) for i in range(3): m.d.rx += self.dllp_source.symbol[i].eq(even_more_delay[i]) for i in range(4): m.d.rx += self.dllp_source.valid[i].eq(1) m.next = "Post-2" with m.State("Post-2"): m.d.comb += sink_ready.eq(0) # TODO: maybe move to rx? m.d.rx += self.dllp_source.symbol[0].eq(tlp_bytes_before[8 * 1 : 8 * 2]) m.d.rx += self.dllp_source.symbol[1].eq(tlp_bytes_before[8 * 2 : 8 * 3]) m.d.rx += self.dllp_source.symbol[2].eq(tlp_bytes_before[8 * 3 : 8 * 4]) with m.If(self.nullify): m.d.rx += self.dllp_source.symbol[3].eq(Ctrl.EDB) m.d.rx += self.nullify.eq(0) with m.Else(): m.d.rx += self.dllp_source.symbol[3].eq(Ctrl.END) m.d.rx += unacknowledged_tlp_fifo.w_data.eq(self.next_transmit_seq) m.d.rx += unacknowledged_tlp_fifo.w_en.eq(1) m.d.rx += self.next_transmit_seq.eq(self.next_transmit_seq + 1) m.d.rx += self.replay_timer_running.eq(1) # TODO: Maybe this should be in the Else block above for i in range(4): m.d.rx += self.dllp_source.valid[i].eq(1) m.next = "Idle" # TODO: This could be a FSM #with m.If(self.dllp_source.ready & unacknowledged_tlp_fifo.w_rdy): # m.d.comb += sink_ready.eq(1) # TODO: maybe move to rx? # # with m.If(~last_valid & sink_valid): # m.d.comb += reset_crc.eq(0) # m.d.comb += crc_input.eq(Cat(self.next_transmit_seq[8 : 12], Const(0, shape = 4), self.next_transmit_seq[0 : 8])) # m.d.rx += even_more_delay[0].eq(Ctrl.STP) # m.d.rx += even_more_delay[1].eq(self.next_transmit_seq[8 : 12]) # m.d.rx += even_more_delay[2].eq(self.next_transmit_seq[0 : 8]) # m.d.rx += even_more_delay[3].eq(tlp_bytes[8 * 0 : 8 * 1]) # #for i in range(4): # # m.d.rx += self.dllp_source.symbol[i].eq(even_more_delay[i]) # # with m.Elif(last_valid & sink_valid): # m.d.comb += reset_crc.eq(0) # m.d.rx += even_more_delay[0].eq(tlp_bytes_before[8 * 1 : 8 * 2]) # m.d.rx += even_more_delay[1].eq(tlp_bytes_before[8 * 2 : 8 * 3]) # m.d.rx += even_more_delay[2].eq(tlp_bytes_before[8 * 3 : 8 * 4]) # m.d.rx += even_more_delay[3].eq(tlp_bytes[8 * 0 : 8 * 1]) # for i in range(4): # m.d.rx += self.dllp_source.symbol[i].eq(even_more_delay[i]) # for i in range(4): # m.d.rx += self.dllp_source.valid[i].eq(1) # # with m.Elif(last_valid & ~sink_valid): # Maybe this can be done better and replaced by the two if blocks above # m.d.comb += reset_crc.eq(0) # m.d.comb += sink_ready.eq(0) # TODO: maybe move to rx? # m.d.rx += even_more_delay[0].eq(tlp_bytes_before[8 * 1 : 8 * 2]) # m.d.rx += even_more_delay[1].eq(tlp_bytes_before[8 * 2 : 8 * 3]) # m.d.rx += even_more_delay[2].eq(tlp_bytes_before[8 * 3 : 8 * 4]) # for i in range(4): # m.d.rx += self.dllp_source.symbol[i].eq(even_more_delay[i]) # for i in range(4): # m.d.rx += self.dllp_source.valid[i].eq(1) # # with m.Elif(last_last_valid & ~sink_valid): # m.d.rx += self.dllp_source.symbol[3].eq(tlp_bytes[8 * 0 : 8 * 1]) # # for i in range(3): # m.d.rx += self.dllp_source.symbol[i].eq(even_more_delay[i]) # # for i in range(4): # m.d.rx += self.dllp_source.valid[i].eq(1) # # with m.Elif(last_last_last_valid & ~sink_valid): # m.d.comb += sink_ready.eq(0) # TODO: maybe move to rx? # m.d.rx += self.dllp_source.symbol[0].eq(tlp_bytes_before[8 * 1 : 8 * 2]) # m.d.rx += self.dllp_source.symbol[1].eq(tlp_bytes_before[8 * 2 : 8 * 3]) # m.d.rx += self.dllp_source.symbol[2].eq(tlp_bytes_before[8 * 3 : 8 * 4]) # # with m.If(self.nullify): # m.d.rx += self.dllp_source.symbol[3].eq(Ctrl.EDB) # m.d.rx += self.nullify.eq(0) # # with m.Else(): # m.d.rx += self.dllp_source.symbol[3].eq(Ctrl.END) # m.d.rx += unacknowledged_tlp_fifo.w_data.eq(self.next_transmit_seq) # m.d.rx += unacknowledged_tlp_fifo.w_en.eq(1) # m.d.rx += self.next_transmit_seq.eq(self.next_transmit_seq + 1) # # m.d.rx += self.replay_timer_running.eq(1) # TODO: Maybe this should be in the Else block above # # for i in range(4): # m.d.rx += self.dllp_source.valid[i].eq(1) return m
def elaborate(self, platform: Platform) -> Module: m = Module() ratio = self.ratio assert ratio == 4 # TODO: Send NAK if buffer is full m.submodules.buffer = buffer = self.buffer m.submodules.received_tlp_fifo = received_tlp_fifo = SyncFIFOBuffered(width = 12, depth = buffer.max_tlps) m.d.comb += self.dll.status.receive_buffer_occupation.eq(buffer.slots_occupied) m.d.comb += self.dll.status.rx_seq_num.eq(self.actual_receive_seq) with m.If(~self.dll.up): m.d.rx += self.ack_nak_latency_timer.eq(self.ack_nak_latency_timer.reset) #m.d.rx += self.accepts_tlps.eq((self.next_transmit_seq - self.ackd_seq) >= 2048) # mod 4096 is already applied since the signal is 12 bits long #with m.If(self.timer_running): # m.d.rx += self.replay_timer.eq(self.replay_timer + 1) source_3_delayed = Signal(8) m.d.rx += source_3_delayed.eq(self.dllp_sink.symbol[3][0:8]) # Aligned as received by TLP layer from other end of the link source_symbols = [source_3_delayed, self.dllp_sink.symbol[0][0:8], self.dllp_sink.symbol[1][0:8], self.dllp_sink.symbol[2][0:8]] last_symbols = Signal(32) m.d.rx += last_symbols.eq(Cat(source_symbols)) reset_crc = Signal(reset = 1) # TODO Warning: Endianness crc_input = Signal(32) m.submodules.lcrc = lcrc = LCRC(crc_input, reset_crc) m.d.rx += crc_input.eq(Cat(source_symbols)) for i in range(4): m.d.rx += buffer.tlp_sink.symbol[i].eq(Cat(source_symbols[i])) end_good = Signal() def ack(): m.d.comb += self.dll.schedule_ack_nak.eq(1) m.d.rx += [ self.dll.scheduled_ack.eq(1), self.dll.scheduled_ack_nak_id.eq(self.actual_receive_seq), self.ack_nak_latency_timer.eq(self.ack_nak_latency_timer.reset), ] def nak(): with m.If(~self.nak_scheduled): m.d.comb += self.dll.schedule_ack_nak.eq(1) m.d.rx += [ self.dll.scheduled_ack.eq(0), self.dll.scheduled_ack_nak_id.eq(self.next_receive_seq - 1), self.nak_scheduled.eq(1), self.ack_nak_latency_timer.eq(self.ack_nak_latency_timer.reset), ] with m.If(self.ack_nak_latency_timer < self.ack_nak_latency_limit): m.d.rx += self.ack_nak_latency_timer.eq(self.ack_nak_latency_timer + 1) with m.FSM(name = "TLP_rx_FSM", domain = "rx"): with m.State("Idle"): with m.If(self.dllp_sink.symbol[0] == Ctrl.STP): tlp_id = Cat(source_symbols[3], source_symbols[2][0:4]) m.d.comb += buffer.store_tlp.eq(1) m.d.rx += buffer.store_tlp_id.eq(tlp_id) m.d.rx += reset_crc.eq(0) m.d.rx += crc_input.eq(Cat(source_symbols[2], source_symbols[3])) m.d.rx += self.actual_receive_seq.eq(tlp_id) m.next = "Receive" #with m.If(self.dll.up & (self.ack_nak_latency_timer == self.ack_nak_latency_limit) & ~ self.nak_scheduled) with m.State("Receive"): with m.If((self.dllp_sink.symbol[3] == Ctrl.END) | (self.dllp_sink.symbol[3] == Ctrl.EDB)): for i in range(4): m.d.rx += buffer.tlp_sink.valid[i].eq(0) m.d.rx += end_good.eq(self.dllp_sink.symbol[3] == Ctrl.END) m.next = "LCRC" with m.Else(): for i in range(4): m.d.rx += buffer.tlp_sink.valid[i].eq(1) with m.State("LCRC"): m.d.rx += reset_crc.eq(1) with m.If((lcrc.output == last_symbols) & end_good): with m.If(self.actual_receive_seq == self.next_receive_seq): m.d.comb += received_tlp_fifo.w_en.eq(1) m.d.comb += received_tlp_fifo.w_data.eq(self.actual_receive_seq) m.d.rx += self.next_receive_seq.eq(self.next_receive_seq + 1) m.d.rx += self.nak_scheduled.eq(0) with m.If(~buffer.slots_full): ack() # This should be fine, really, see PCIe Base 1.1 Page 157 Point 2 with m.Else(): m.next = "Wait" with m.Elif((self.next_receive_seq - self.actual_receive_seq) <= 2048): # Duplicate received ack() with m.Else(): nak() with m.Else(): with m.If((~lcrc.output == last_symbols) & ~end_good): m.d.comb += buffer.delete_tlp.eq(1) m.d.comb += buffer.delete_tlp_id.eq(self.actual_receive_seq) with m.Else(): # TODO: Delete TLP here too? nak() m.next = "Idle" with m.State("Wait"): # It goes in this state if there is no free space, after space has been freed it is acknowledged such that the next TLP can be received. with m.If(~buffer.slots_full): ack() m.next = "Idle" return m
def elaborate(self, platform): m = Module() m.submodules.bus = bus = self.bus in_fifo = self.in_fifo out_fifo = self.out_fifo buf_fifo = m.submodules.buf_fifo = SyncFIFOBuffered( width=8, depth=_COMMAND_BUFFER_SIZE) m.d.comb += bus.ce.eq(1) with m.FSM(): # Page writes in parallel EEPROMs do not tolerate delays, so the entire page needs # to be buffered before programming starts. After receiving the QUEUE command, all # subsequent commands except for RUN are placed into the buffer. The RUN command # restarts command processing. Until the buffer is empty, only buffered commands are # processed. cmd_fifo = Array([out_fifo, buf_fifo])[buf_fifo.r_rdy] a_bytes = (bus.a_bits + 7) // 8 dq_bytes = (bus.dq_bits + 7) // 8 a_index = Signal(range(a_bytes + 1)) dq_index = Signal(range(dq_bytes + 1)) a_latch = Signal(bus.a_bits) dq_latch = Signal(bus.dq_bits) read_cycle_cyc = (math.ceil( self._read_cycle_delay * platform.default_clk_frequency) + 2 ) # FFSynchronizer latency write_cycle_cyc = math.ceil(self._write_cycle_delay * platform.default_clk_frequency) write_hold_cyc = math.ceil(self._write_hold_delay * platform.default_clk_frequency) timer = Signal( range( max(read_cycle_cyc, write_cycle_cyc, write_hold_cyc) + 1)) with m.State("COMMAND"): with m.If(cmd_fifo.r_rdy): m.d.comb += cmd_fifo.r_en.eq(1) with m.Switch(cmd_fifo.r_data): with m.Case(_Command.QUEUE): m.next = "QUEUE-RECV" with m.Case(_Command.SEEK): m.d.sync += a_index.eq(0) m.next = "SEEK-RECV" with m.Case(_Command.INCR): m.d.sync += bus.a.eq(bus.a + 1) m.next = "SEEK-WAIT" with m.Case(_Command.READ): m.d.sync += dq_index.eq(0) m.next = "READ-PULSE" with m.Case(_Command.WRITE): m.d.sync += dq_index.eq(0) m.next = "WRITE-RECV" with m.Case(_Command.POLL): m.next = "POLL-PULSE" with m.Else(): m.d.comb += in_fifo.flush.eq(1) with m.State("QUEUE-RECV"): with m.If(out_fifo.r_rdy): escaped = Signal() with m.If(~escaped & (out_fifo.r_data == _Command.QUEUE)): m.d.comb += out_fifo.r_en.eq(1) m.d.sync += escaped.eq(1) with m.Elif(escaped & (out_fifo.r_data == _Command.RUN)): m.d.comb += out_fifo.r_en.eq(1) m.next = "COMMAND" with m.Else(): m.d.sync += escaped.eq(0) m.d.comb += out_fifo.r_en.eq(buf_fifo.w_rdy) m.d.comb += buf_fifo.w_data.eq(out_fifo.r_data) m.d.comb += buf_fifo.w_en.eq(1) with m.State("SEEK-RECV"): with m.If(a_index == a_bytes): m.d.sync += bus.a.eq(a_latch) m.next = "SEEK-WAIT" with m.Elif(cmd_fifo.r_rdy): m.d.comb += cmd_fifo.r_en.eq(1) m.d.sync += a_latch.word_select(a_index, 8).eq(cmd_fifo.r_data) m.d.sync += a_index.eq(a_index + 1) with m.State("SEEK-WAIT"): with m.If(bus.rdy): m.next = "COMMAND" with m.State("READ-PULSE"): m.d.sync += bus.oe.eq(1) m.d.sync += timer.eq(read_cycle_cyc) m.next = "READ-CYCLE" with m.State("READ-CYCLE"): with m.If(timer == 0): # Normally, this would be the place to deassert OE. However, this would reduce # metastability (during burst reads) in the output buffers of a memory that is # reading bits close to the buffer threshold. Wait, isn't metastability bad? # Normally yes, but this is a special case! Metastability causes unstable # bits, and unstable bits reduce the chance that corrupt data will slip # through undetected. m.d.sync += dq_latch.eq(bus.q) m.next = "READ-SEND" with m.Else(): m.d.sync += timer.eq(timer - 1) with m.State("READ-SEND"): with m.If(dq_index == dq_bytes): m.next = "COMMAND" with m.Elif(in_fifo.w_rdy): m.d.comb += in_fifo.w_en.eq(1) m.d.comb += in_fifo.w_data.eq( dq_latch.word_select(dq_index, 8)) m.d.sync += dq_index.eq(dq_index + 1) with m.State("WRITE-RECV"): with m.If(dq_index == dq_bytes): m.d.sync += bus.d.eq(dq_latch) m.d.sync += bus.oe.eq(0) # see comment in READ-CYCLE m.d.sync += bus.we.eq(1) m.d.sync += timer.eq(write_cycle_cyc) m.next = "WRITE-CYCLE" with m.Elif(cmd_fifo.r_rdy): m.d.comb += cmd_fifo.r_en.eq(1) m.d.sync += dq_latch.word_select(dq_index, 8).eq(cmd_fifo.r_data) m.d.sync += dq_index.eq(dq_index + 1) with m.State("WRITE-CYCLE"): with m.If(timer == 0): m.d.sync += bus.we.eq(0) m.d.sync += timer.eq(write_hold_cyc) m.next = "WRITE-HOLD" with m.Else(): m.d.sync += timer.eq(timer - 1) with m.State("WRITE-HOLD"): with m.If(timer == 0): m.next = "COMMAND" with m.Else(): m.d.sync += timer.eq(timer - 1) with m.State("POLL-PULSE"): m.d.sync += bus.oe.eq(1) m.d.sync += timer.eq(read_cycle_cyc) m.next = "POLL-CYCLE" with m.State("POLL-CYCLE"): with m.If(timer == 0): # There are many different ways EEPROMs can signal readiness, but if they do it # on data lines, they are common in that they all present something else other # than the last written byte on DQ lines. with m.If(bus.q == dq_latch): with m.If(in_fifo.w_rdy): m.d.comb += in_fifo.w_en.eq(1) m.d.sync += bus.oe.eq(0) m.next = "COMMAND" with m.Else(): m.d.sync += timer.eq(timer - 1) return m
def elaborate(self, platform: Platform) -> Module: m = Module() m.submodules.serdes = serdes = self.__serdes m.submodules += self.lane #m.d.comb += serdes.lane.speed.eq(self.lane.speed) m.d.comb += serdes.lane.reset.eq(self.lane.reset) data_width = len(serdes.lane.rx_symbol) m.domains.rxf = ClockDomain() m.domains.txf = ClockDomain() m.d.comb += [ #ClockSignal("sync").eq(serdes.refclk), ClockSignal("rxf").eq(serdes.rx_clk), ClockSignal("txf").eq(serdes.tx_clk), ] platform.add_clock_constraint( self.rx_clk, 125e6 if self.speed_5GTps else 625e5 ) # For NextPNR, set the maximum clock frequency such that errors are given platform.add_clock_constraint(self.tx_clk, 125e6 if self.speed_5GTps else 625e5) m.submodules.lane = lane = PCIeSERDESInterface( 4 ) # TODO: Uhh is this supposed to be here? // I think it might be the fast lane # IF SOMETHING IS BROKE: Check if the TX actually transmits good data and not order-swapped data # TODO: Maybe use hardware divider? Though this seems to be fine m.d.rxf += self.rx_clk.eq(~self.rx_clk) with m.If(~self.rx_clk): m.d.rxf += lane.rx_symbol[data_width:data_width * 2].eq( serdes.lane.rx_symbol) m.d.rxf += lane.rx_valid[serdes.gearing:serdes.gearing * 2].eq( serdes.lane.rx_valid) with m.Else(): m.d.rxf += lane.rx_symbol[0:data_width].eq(serdes.lane.rx_symbol) m.d.rxf += lane.rx_valid[0:serdes.gearing].eq(serdes.lane.rx_valid) # To ensure that it outputs consistent data # m.d.rxf += self.lane.rx_symbol.eq(lane.rx_symbol) # m.d.rxf += self.lane.rx_valid.eq(lane.rx_valid) m.d.txf += self.tx_clk.eq(~self.tx_clk) m.d.txf += serdes.lane.tx_symbol.eq( Mux(self.tx_clk, lane.tx_symbol[data_width:data_width * 2], lane.tx_symbol[0:data_width])) m.d.txf += serdes.lane.tx_disp.eq( Mux(self.tx_clk, lane.tx_disp[serdes.gearing:serdes.gearing * 2], lane.tx_disp[0:serdes.gearing])) m.d.txf += serdes.lane.tx_set_disp.eq( Mux(self.tx_clk, lane.tx_set_disp[serdes.gearing:serdes.gearing * 2], lane.tx_set_disp[0:serdes.gearing])) m.d.txf += serdes.lane.tx_e_idle.eq( Mux(self.tx_clk, lane.tx_e_idle[serdes.gearing:serdes.gearing * 2], lane.tx_e_idle[0:serdes.gearing])) # CDC # TODO: Keep the SyncFIFO? Its faster but is it reliable? #rx_fifo = m.submodules.rx_fifo = AsyncFIFOBuffered(width=(data_width + serdes.gearing) * 2, depth=4, r_domain="rx", w_domain="rxf") rx_fifo = m.submodules.rx_fifo = DomainRenamer("rxf")(SyncFIFOBuffered( width=(data_width + serdes.gearing) * 2, depth=4)) m.d.rxf += rx_fifo.w_data.eq(Cat(lane.rx_symbol, lane.rx_valid)) m.d.comb += Cat(self.lane.rx_symbol, self.lane.rx_valid).eq(rx_fifo.r_data) m.d.comb += rx_fifo.r_en.eq(1) m.d.rxf += rx_fifo.w_en.eq(self.rx_clk) #tx_fifo = m.submodules.tx_fifo = AsyncFIFOBuffered(width=(data_width + serdes.gearing * 3) * 2, depth=4, r_domain="txf", w_domain="tx") tx_fifo = m.submodules.tx_fifo = DomainRenamer("txf")(SyncFIFOBuffered( width=(data_width + serdes.gearing * 3) * 2, depth=4)) m.d.comb += tx_fifo.w_data.eq( Cat(self.lane.tx_symbol, self.lane.tx_set_disp, self.lane.tx_disp, self.lane.tx_e_idle)) m.d.txf += Cat(lane.tx_symbol, lane.tx_set_disp, lane.tx_disp, lane.tx_e_idle).eq(tx_fifo.r_data) m.d.txf += tx_fifo.r_en.eq(self.tx_clk) m.d.comb += tx_fifo.w_en.eq(1) #m.d.txf += Cat(lane.tx_symbol, lane.tx_set_disp, lane.tx_disp, lane.tx_e_idle).eq(Cat(self.lane.tx_symbol, self.lane.tx_set_disp, self.lane.tx_disp, self.lane.tx_e_idle)) return m
def elaborate(self, platform): m = Module() m.submodules.bridge = self._bridge # Shortcuts to our components. interface = self.interface token = self.interface.tokenizer rx = self.interface.rx handshakes_out = self.interface.handshakes_out # Logic condition for getting a new setup packet. new_setup = token.new_token & token.is_setup reset_requested = self.reset.w_stb & self.reset.w_data clear_fifo = new_setup | reset_requested # # Core FIFO. # m.submodules.fifo = fifo = ResetInserter(clear_fifo)(SyncFIFOBuffered( width=8, depth=8)) m.d.comb += [ # We'll write to the active FIFO whenever the last received token is a SETUP # token, and we have incoming data; and we'll always write the data received fifo.w_en.eq(token.is_setup & rx.valid & rx.next), fifo.w_data.eq(rx.payload), # We'll advance the FIFO whenever our CPU reads from the data CSR; # and we'll always read our data from the FIFO. fifo.r_en.eq(self.data.r_stb), self.data.r_data.eq(fifo.r_data), # Pass the FIFO status on to our CPU. self.have.r_data.eq(fifo.r_rdy), # Always acknowledge SETUP packets as they arrive. handshakes_out.ack.eq(token.is_setup & interface.rx_ready_for_response), # Trigger a SETUP interrupt as we ACK the setup packet, since that's also the point # where we know we're done receiving data. self.setup_received.stb.eq(handshakes_out.ack) ] # # Control registers # # Our address register always reads the current address of the device; # but will generate a m.d.comb += self._address.r_data.eq(interface.active_address) with m.If(self._address.w_stb): m.d.comb += [ interface.address_changed.eq(1), interface.new_address.eq(self._address.w_data), ] # # Status and interrupts. # with m.If(token.new_token): m.d.usb += self.epno.r_data.eq(token.endpoint) # TODO: generate interrupts return DomainRenamer({"sync": "usb"})(m)
def elaborate(self, platform): m = Module() m.submodules.bridge = self._bridge # Shortcuts to our components. interface = self.interface token = self.interface.tokenizer rx = self.interface.rx handshakes_out = self.interface.handshakes_out # # Control registers # # Active endpoint number. with m.If(self.epno.w_stb): m.d.usb += self.epno.r_data.eq(self.epno.w_data) # Keep track of which endpoints are primed. endpoint_primed = Array(Signal() for _ in range(16)) # Keep track of which endpoints are stalled. endpoint_stalled = Array(Signal() for _ in range(16)) # Keep track of the PIDs for each endpoint, which we'll toggle automatically. endpoint_data_pid = Array(Signal() for _ in range(16)) # Keep track of whether we're enabled. with m.If(self.enable.w_stb): m.d.usb += self.enable.r_data.eq(self.enable.w_data) # If Prime is written to, mark the relevant endpoint as primed. with m.If(self.prime.w_stb): m.d.usb += endpoint_primed[self.epno.r_data].eq(self.prime.w_data) # If we've just ACK'd a receive, clear our enable and un-prime the given endpoint. with m.If(interface.handshakes_out.ack & token.is_out): m.d.usb += [ self.enable.r_data.eq(0), endpoint_primed[token.endpoint].eq(0), ] # Set the value of our endpoint `stall` based on our `stall` register... with m.If(self.stall.w_stb): m.d.usb += endpoint_stalled[self.epno.r_data].eq(self.stall.w_data) # Allow our controller to override our DATA pid, selectively. with m.If(self.pid.w_stb): m.d.usb += endpoint_data_pid[self.epno.r_data].eq(self.pid.w_data) # Clear our endpoint `stall` when we get a SETUP packet, and reset the endpoint's # data PID to DATA1, as per [USB2.0: 8.5.3], the first packet of the DATA or STATUS # phase always carries a DATA1 PID. with m.If(token.is_setup & token.new_token): m.d.usb += [ endpoint_stalled[token.endpoint].eq(0), endpoint_data_pid[token.endpoint].eq(1) ] # # Core FIFO. # m.submodules.fifo = fifo = ResetInserter(self.reset.w_stb)( SyncFIFOBuffered(width=8, depth=self._max_packet_size)) # Shortcut for when we should allow a receive. We'll read when: # - Our `epno` register matches the target register; and # - We've primed the relevant endpoint. # - Our most recent token is an OUT. # - We're not stalled. stalled = token.is_out & endpoint_stalled[token.endpoint] endpoint_primed = endpoint_primed[token.endpoint] ready_to_receive = endpoint_primed & self.enable.r_data & ~stalled allow_receive = token.is_out & ready_to_receive nak_receives = token.is_out & ~ready_to_receive & ~stalled # Shortcut for when we have a "redundant"/incorrect PID. In these cases, we'll assume # the host missed our ACK, and per the USB spec, implicitly ACK the packet. is_redundant_pid = (interface.rx_pid_toggle != endpoint_data_pid[token.endpoint]) is_redundant_packet = endpoint_primed & token.is_out & is_redundant_pid # Shortcut conditions under which we'll ACK and NAK a receive. ack_redundant_packet = (is_redundant_packet & interface.rx_ready_for_response) ack_receive = allow_receive & interface.rx_ready_for_response nak_receive = nak_receives & interface.rx_ready_for_response & ~ack_redundant_packet # Conditions under which we'll ACK or NAK a ping. ack_ping = ready_to_receive & token.is_ping & token.ready_for_response nak_ping = ~ready_to_receive & token.is_ping & token.ready_for_response m.d.comb += [ # We'll write to the endpoint iff we've valid data, and we're allowed receive. fifo.w_en.eq(allow_receive & rx.valid & rx.next & ~is_redundant_packet), fifo.w_data.eq(rx.payload), # We'll advance the FIFO whenever our CPU reads from the data CSR; # and we'll always read our data from the FIFO. fifo.r_en.eq(self.data.r_stb), self.data.r_data.eq(fifo.r_data), # Pass the FIFO status on to our CPU. self.have.r_data.eq(fifo.r_rdy), # If we've just finished an allowed receive, ACK. handshakes_out.ack.eq(ack_receive | ack_ping | ack_redundant_packet), # Trigger our DONE interrupt once we ACK a received/allowed packet. self._done_irq.stb.eq(ack_receive), # If we were stalled, stall. handshakes_out.stall.eq(stalled & interface.rx_ready_for_response), # If we're not ACK'ing or STALL'ing, NAK all packets. handshakes_out.nak.eq(nak_receive | nak_ping), # Always indicate the current DATA PID in the PID register. self.pid.r_data.eq(endpoint_data_pid[self.epno.r_data]) ] # Whenever we capture data, update our associated endpoint number # to match the endpoint on which we received the relevant data. with m.If(fifo.w_en): m.d.usb += self.data_ep.r_data.eq(token.endpoint) # Whenever we ACK a non-redundant receive, toggle our DATA PID. # (unless the user happens to be overriding it by writing to the PID register). with m.If(ack_receive & ~is_redundant_packet & ~self.pid.w_stb): m.d.usb += endpoint_data_pid[token.endpoint].eq( ~endpoint_data_pid[token.endpoint]) # # Interrupt/status # return DomainRenamer({"sync": "usb"})(m)
def elaborate(self, platform): m = Module() m.submodules.bridge = self._bridge # Shortcuts to our components. token = self.interface.tokenizer tx = self.interface.tx handshakes_out = self.interface.handshakes_out # # Core FIFO. # # Create our FIFO; and set it to be cleared whenever the user requests. m.submodules.fifo = fifo = ResetInserter(self.reset.w_stb)( SyncFIFOBuffered(width=8, depth=self._max_packet_size)) m.d.comb += [ # Whenever the user DATA register is written to, add the relevant data to our FIFO. fifo.w_en.eq(self.data.w_stb), fifo.w_data.eq(self.data.w_data), ] # Keep track of the amount of data in our FIFO. bytes_in_fifo = Signal(range(0, self._max_packet_size + 1)) # If we're clearing the whole FIFO, reset our data count. with m.If(self.reset.w_stb): m.d.usb += bytes_in_fifo.eq(0) # Keep track of our FIFO's data count as data is added or removed. increment = fifo.w_en & fifo.w_rdy decrement = fifo.r_en & fifo.r_rdy with m.Elif(increment & ~decrement): m.d.usb += bytes_in_fifo.eq(bytes_in_fifo + 1) with m.Elif(decrement & ~increment): m.d.usb += bytes_in_fifo.eq(bytes_in_fifo - 1) # # Register updates. # # Active endpoint number. with m.If(self.epno.w_stb): m.d.usb += self.epno.r_data.eq(self.epno.w_data) # Keep track of which endpoints are stalled. endpoint_stalled = Array(Signal() for _ in range(16)) # Keep track of the current DATA pid for each endpoint. endpoint_data_pid = Array(Signal() for _ in range(16)) # Clear our system state on reset. with m.If(self.reset.w_stb): for i in range(16): m.d.usb += [ endpoint_stalled[i].eq(0), endpoint_data_pid[i].eq(0), ] # Set the value of our endpoint `stall` based on our `stall` register... with m.If(self.stall.w_stb): m.d.usb += endpoint_stalled[self.epno.r_data].eq(self.stall.w_data) # Clear our endpoint `stall` when we get a SETUP packet, and reset the endpoint's # data PID to DATA1, as per [USB2.0: 8.5.3], the first packet of the DATA or STATUS # phase always carries a DATA1 PID. with m.If(token.is_setup & token.new_token): m.d.usb += [ endpoint_stalled[token.endpoint].eq(0), endpoint_data_pid[token.endpoint].eq(1) ] # # Status registers. # m.d.comb += [ self.have.r_data.eq(fifo.r_rdy), self.pid.r_data.eq(endpoint_data_pid[self.epno.r_data]) ] # # Data toggle control. # endpoint_matches = (token.endpoint == self.epno.r_data) packet_complete = self.interface.handshakes_in.ack & token.is_in & endpoint_matches # Always drive the DATA pid we're transmitting with our current data pid. m.d.comb += self.interface.tx_pid_toggle.eq( endpoint_data_pid[token.endpoint]) # If our controller is overriding the data PID, accept the override. with m.If(self.pid.w_stb): m.d.usb += endpoint_data_pid[self.epno.r_data].eq(self.pid.w_data) # Otherwise, toggle our expected DATA PID once we receive a complete packet. with m.Elif(packet_complete): m.d.usb += endpoint_data_pid[token.endpoint].eq( ~endpoint_data_pid[token.endpoint]) # # Control logic. # # Logic shorthand. new_in_token = (token.is_in & token.ready_for_response) stalled = endpoint_stalled[token.endpoint] with m.FSM(domain='usb') as f: # Drive our IDLE line based on our FSM state. m.d.comb += self.idle.r_data.eq(f.ongoing('IDLE')) # IDLE -- our CPU hasn't yet requested that we send data. # We'll wait for it to do so, and NAK any packets that arrive. with m.State("IDLE"): # If we get an IN token... with m.If(new_in_token): # STALL it, if the endpoint is STALL'd... with m.If(stalled): m.d.comb += handshakes_out.stall.eq(1) # Otherwise, NAK. with m.Else(): m.d.comb += handshakes_out.nak.eq(1) # If the user request that we send data, "prime" the endpoint. # This means we have data to send, but are just waiting for an IN token. with m.If(self.epno.w_stb & ~stalled): m.next = "PRIMED" # Always return to IDLE on reset. with m.If(self.reset.w_stb): m.next = "IDLE" # PRIMED -- our CPU has provided data, but we haven't been sent an IN token, yet. # Await that IN token. with m.State("PRIMED"): with m.If(new_in_token): # If the target endpoint is STALL'd, reply with STALL no matter what. with m.If(stalled): m.d.comb += handshakes_out.stall.eq(1) # If we have a new IN token to our endpoint, move to responding to it. with m.Elif(endpoint_matches): # If there's no data in our endpoint, send a ZLP. with m.If(~fifo.r_rdy): m.next = "SEND_ZLP" # Otherwise, send our data, starting with our first byte. with m.Else(): m.d.usb += tx.first.eq(1) m.next = "SEND_DATA" # Otherwise, we don't have a response; NAK the packet. with m.Else(): m.d.comb += handshakes_out.nak.eq(1) # Always return to IDLE on reset. with m.If(self.reset.w_stb): m.next = "IDLE" # SEND_ZLP -- we're now now ready to respond to an IN token with a ZLP. # Send our response. with m.State("SEND_ZLP"): m.d.comb += [tx.valid.eq(1), tx.last.eq(1)] m.d.comb += self._done_irq.stb.eq(1) m.next = 'IDLE' # SEND_DATA -- we're now ready to respond to an IN token to our endpoint. # Send our response. with m.State("SEND_DATA"): last_byte = (bytes_in_fifo == 1) m.d.comb += [ tx.valid.eq(1), tx.last.eq(last_byte), # Drive our transmit data directly from our FIFO... tx.payload.eq(fifo.r_data), # ... and advance our FIFO each time a data byte is transmitted. fifo.r_en.eq(tx.ready) ] # After we've sent a byte, drop our first flag. with m.If(tx.ready): m.d.usb += tx.first.eq(0) # Once we transmit our last packet, we're done transmitting. Move back to IDLE. with m.If(last_byte & tx.ready): # Trigger our DONE interrupt. m.d.comb += self._done_irq.stb.eq(1) m.next = 'IDLE' # Always return to IDLE on reset. with m.If(self.reset.w_stb): m.next = "IDLE" return DomainRenamer({"sync": "usb"})(m)