diff --git a/src/gateware/cxp_pipeline.py b/src/gateware/cxp_pipeline.py index fcb3a6b..b0f7adc 100644 --- a/src/gateware/cxp_pipeline.py +++ b/src/gateware/cxp_pipeline.py @@ -4,12 +4,23 @@ from misoc.interconnect.csr import * from misoc.interconnect import stream from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine, LiteEthMACCRCChecker +from functools import reduce +from itertools import combinations +from operator import or_, and_ + char_width = 8 char_layout = [("data", char_width), ("k", char_width//8)] word_dw = 32 word_layout = [("data", word_dw), ("k", word_dw//8)] +word_layout_dchar = [ + ("data", word_dw), + ("k", word_dw//8), + ("dchar", char_width), + ("dchar_k", char_width//8) +] + buffer_count = 4 buffer_depth = 512 @@ -261,7 +272,7 @@ class TX_Bootstrap(Module, AutoCSR): class RX_Debug_Buffer(Module,AutoCSR): def __init__(self): - self.submodules.buf_out = buf_out = stream.SyncFIFO(word_layout, 128) + self.submodules.buf_out = buf_out = stream.SyncFIFO(word_layout_dchar, 128) self.sink = buf_out.sink self.inc = CSR() @@ -277,21 +288,64 @@ class RX_Debug_Buffer(Module,AutoCSR): self.dout_valid.status.eq(buf_out.source.stb), ] -class Duplicate_Majority_Voter(Module): - def __init__(self, char_4x, k_4x): - assert char_4x.nbits == 32 - assert k_4x.nbits == 4 - - # Section 9.2.2.1 (CXP-001-2021) - # decoder should immune to single bit errors when handling duplicated characters - self.char = Signal(char_width) - self.k = Signal() +class Duplicated_Char_Decoder(Module): + def __init__(self): + self.sink = stream.Endpoint(word_layout) + self.buffer = stream.Endpoint(word_layout) + self.source = stream.Endpoint(word_layout_dchar) - a, b, c, d = [char_4x[i*8:(i+1)*8] for i in range(4)] - a_k, b_k, c_k, d_k = [k_4x[i:(i+1)] for i in range(4)] - self.comb += [ - self.char.eq(a&b&c | a&b&d | a&c&d | b&c&d), - self.k.eq(a_k&b_k&c_k | a_k&b_k&d_k | a_k&c_k&d_k | b_k&c_k&d_k), + # # # + + + # For duplicated characters, an error correction method (e.g. majority voting) is required to meet the CXP spec: + # RX decoder should immune to single bit errors when handling duplicated characters - Section 9.2.2.1 (CXP-001-2021) + # + # + # 32 + # +---> buffer -----/-----+ + # 32 | | 32+8(dchar) + # sink ---/---+ ---> source -----/-----> downstream + # | 8(dchar) | decoders + # +---> majority -----/-----+ + # voting + # + # + # Due to the tight setup/hold time requiremnt for 12.5Gbps CXP, the voting logic cannot be implemented as combinational logic + # Hence, a pipeline approach is needed to avoid any s/h violation, where the majority voting result are pre-calculate and injected into the bus immediate after the PHY. + # And any downstream modules can access the voting result without implementing the voting logic inside the decoder + + self.sync += [ + self.sink.ack.eq(self.buffer.ack), + self.buffer.stb.eq(self.sink.stb), + If(self.sink.stb, + self.buffer.data.eq(self.sink.data), + self.buffer.k.eq(self.sink.k), + ), + ] + + # cycle 1 - calculate ABC, ABD, ACD & BCD + char = [[self.sink.data[i*8:(i+1)*8], self.sink.k[i]] for i in range(4)] + voters = [Record([("data", 8), ("k", 1)]) for _ in range(4)] + + for i, comb in enumerate(combinations(char, 3)): + self.sync += [ + If(self.sink.stb, + voters[i].data.eq(reduce(and_, [code[0] for code in comb])), + voters[i].k.eq(reduce(and_, [code[1] for code in comb])), + ) + ] + + + # cycle 2 - inject the voting result + self.sync += [ + self.buffer.ack.eq(self.source.ack), + self.source.stb.eq(self.buffer.stb), + If(self.buffer.stb, + self.source.data.eq(self.buffer.data), + self.source.k.eq(self.buffer.k), + self.source.dchar.eq(Replicate(reduce(or_, [v.data for v in voters]), 4)), + self.source.dchar_k.eq(Replicate(reduce(or_, [v.k for v in voters]), 4)), + ), ] @FullMemoryWE() @@ -314,15 +368,14 @@ class RX_Bootstrap(Module): "heartbeat": 0x09, } - self.sink = stream.Endpoint(word_layout) - self.source = stream.Endpoint(word_layout) + self.sink = stream.Endpoint(word_layout_dchar) + self.source = stream.Endpoint(word_layout_dchar) self.submodules.fsm = fsm = FSM(reset_state="IDLE") - self.submodules.voter = voter = Duplicate_Majority_Voter(self.sink.data, self.sink.k) fsm.act("IDLE", self.sink.ack.eq(1), - If((self.sink.stb & (voter.char == KCode["pak_start"]) & (voter.k == 1)), + If((self.sink.stb & (self.sink.dchar == KCode["pak_start"]) & (self.sink.dchar_k == 1)), NextState("DECODE"), ) ) @@ -333,9 +386,9 @@ class RX_Bootstrap(Module): fsm.act("DECODE", self.sink.ack.eq(1), If(self.sink.stb, - NextValue(self.packet_type, voter.char), + NextValue(self.packet_type, self.sink.dchar), - Case(voter.char, { + Case(self.sink.dchar, { type["data_stream"]: NextState("STREAMING"), type["test_packet"]: [ NextValue(cnt, cnt.reset), @@ -363,7 +416,7 @@ class RX_Bootstrap(Module): ) # For stream data packet fsm.act("STREAMING", - If((self.sink.stb & (voter.char == KCode["pak_end"]) & (voter.k == 1)), + If((self.sink.stb & (self.sink.dchar == KCode["pak_end"]) & (self.sink.dchar_k == 1)), # discard K29,7 self.sink.ack.eq(1), NextState("IDLE") @@ -378,7 +431,7 @@ class RX_Bootstrap(Module): fsm.act("VERIFY_TEST_PATTERN", self.sink.ack.eq(1), If(self.sink.stb, - If(((voter.char == KCode["pak_end"]) & (voter.k == 1)), + If(((self.sink.dchar == KCode["pak_end"]) & (self.sink.dchar_k == 1)), NextState("IDLE"), ).Else( If(((self.sink.data != Cat(cnt, cnt+1, cnt+2, cnt+3))), @@ -405,7 +458,6 @@ class RX_Bootstrap(Module): self.comb += [ mem_port.adr[:addr_nbits].eq(addr), mem_port.adr[addr_nbits:].eq(write_ptr), - mem_port.dat_w.eq(self.sink.data), ] # For control ack, event packet @@ -413,10 +465,11 @@ class RX_Bootstrap(Module): mem_port.we.eq(0), self.sink.ack.eq(1), If(self.sink.stb, - If(((voter.char == KCode["pak_end"]) & (voter.k == 1)), + If(((self.sink.dchar == KCode["pak_end"]) & (self.sink.dchar_k == 1)), NextState("MOVE_BUFFER_PTR"), ).Else( mem_port.we.eq(1), + mem_port.dat_w.eq(self.sink.data), NextValue(addr, addr + 1), If(addr == buffer_depth - 1, # discard the packet @@ -439,10 +492,10 @@ class RX_Bootstrap(Module): NextState("IDLE"), ) -class CXP_Trig_Ack_Checker(Module, AutoCSR): +class Trigger_Ack_Checker(Module, AutoCSR): def __init__(self): - self.sink = stream.Endpoint(word_layout) - self.source = stream.Endpoint(word_layout) + self.sink = stream.Endpoint(word_layout_dchar) + self.source = stream.Endpoint(word_layout_dchar) self.ack = Signal() @@ -450,10 +503,8 @@ class CXP_Trig_Ack_Checker(Module, AutoCSR): self.submodules.fsm = fsm = FSM(reset_state="COPY") - self.submodules.voter = voter = Duplicate_Majority_Voter(self.sink.data, self.sink.k) - fsm.act("COPY", - If((self.sink.stb & (voter.char == KCode["io_ack"]) & (voter.k == 1)), + If((self.sink.stb & (self.sink.dchar == KCode["io_ack"]) & (self.sink.dchar_k == 1)), # discard K28,6 self.sink.ack.eq(1), NextState("CHECK_ACK") @@ -467,7 +518,7 @@ class CXP_Trig_Ack_Checker(Module, AutoCSR): NextState("COPY"), # discard the word after K28,6 self.sink.ack.eq(1), - If((voter.char == 0x01) & (voter.k == 0), + If((self.sink.dchar == 0x01) & (self.sink.dchar_k == 0), self.ack.eq(1), ) )