1
0
Fork 0

pipeline GW: fix packet loss & rx timing

pipeline GW: add duplicate char decoder/injector
pipeline GW: remove majority voting to fix timing
This commit is contained in:
morgan 2024-10-24 12:47:47 +08:00
parent 63112919a3
commit b370ec00ea
1 changed files with 83 additions and 32 deletions

View File

@ -4,12 +4,23 @@ from misoc.interconnect.csr import *
from misoc.interconnect import stream from misoc.interconnect import stream
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine, LiteEthMACCRCChecker 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_width = 8
char_layout = [("data", char_width), ("k", char_width//8)] char_layout = [("data", char_width), ("k", char_width//8)]
word_dw = 32 word_dw = 32
word_layout = [("data", word_dw), ("k", word_dw//8)] 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_count = 4
buffer_depth = 512 buffer_depth = 512
@ -261,7 +272,7 @@ class TX_Bootstrap(Module, AutoCSR):
class RX_Debug_Buffer(Module,AutoCSR): class RX_Debug_Buffer(Module,AutoCSR):
def __init__(self): 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.sink = buf_out.sink
self.inc = CSR() self.inc = CSR()
@ -277,21 +288,64 @@ class RX_Debug_Buffer(Module,AutoCSR):
self.dout_valid.status.eq(buf_out.source.stb), self.dout_valid.status.eq(buf_out.source.stb),
] ]
class Duplicate_Majority_Voter(Module): class Duplicated_Char_Decoder(Module):
def __init__(self, char_4x, k_4x): def __init__(self):
assert char_4x.nbits == 32 self.sink = stream.Endpoint(word_layout)
assert k_4x.nbits == 4 self.buffer = stream.Endpoint(word_layout)
self.source = stream.Endpoint(word_layout_dchar)
# 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()
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)] # For duplicated characters, an error correction method (e.g. majority voting) is required to meet the CXP spec:
self.comb += [ # RX decoder should immune to single bit errors when handling duplicated characters - Section 9.2.2.1 (CXP-001-2021)
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), #
# 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() @FullMemoryWE()
@ -314,15 +368,14 @@ class RX_Bootstrap(Module):
"heartbeat": 0x09, "heartbeat": 0x09,
} }
self.sink = stream.Endpoint(word_layout) self.sink = stream.Endpoint(word_layout_dchar)
self.source = stream.Endpoint(word_layout) self.source = stream.Endpoint(word_layout_dchar)
self.submodules.fsm = fsm = FSM(reset_state="IDLE") self.submodules.fsm = fsm = FSM(reset_state="IDLE")
self.submodules.voter = voter = Duplicate_Majority_Voter(self.sink.data, self.sink.k)
fsm.act("IDLE", fsm.act("IDLE",
self.sink.ack.eq(1), 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"), NextState("DECODE"),
) )
) )
@ -333,9 +386,9 @@ class RX_Bootstrap(Module):
fsm.act("DECODE", fsm.act("DECODE",
self.sink.ack.eq(1), self.sink.ack.eq(1),
If(self.sink.stb, 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["data_stream"]: NextState("STREAMING"),
type["test_packet"]: [ type["test_packet"]: [
NextValue(cnt, cnt.reset), NextValue(cnt, cnt.reset),
@ -363,7 +416,7 @@ class RX_Bootstrap(Module):
) )
# For stream data packet # For stream data packet
fsm.act("STREAMING", 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 # discard K29,7
self.sink.ack.eq(1), self.sink.ack.eq(1),
NextState("IDLE") NextState("IDLE")
@ -378,7 +431,7 @@ class RX_Bootstrap(Module):
fsm.act("VERIFY_TEST_PATTERN", fsm.act("VERIFY_TEST_PATTERN",
self.sink.ack.eq(1), self.sink.ack.eq(1),
If(self.sink.stb, 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"), NextState("IDLE"),
).Else( ).Else(
If(((self.sink.data != Cat(cnt, cnt+1, cnt+2, cnt+3))), If(((self.sink.data != Cat(cnt, cnt+1, cnt+2, cnt+3))),
@ -405,7 +458,6 @@ class RX_Bootstrap(Module):
self.comb += [ self.comb += [
mem_port.adr[:addr_nbits].eq(addr), mem_port.adr[:addr_nbits].eq(addr),
mem_port.adr[addr_nbits:].eq(write_ptr), mem_port.adr[addr_nbits:].eq(write_ptr),
mem_port.dat_w.eq(self.sink.data),
] ]
# For control ack, event packet # For control ack, event packet
@ -413,10 +465,11 @@ class RX_Bootstrap(Module):
mem_port.we.eq(0), mem_port.we.eq(0),
self.sink.ack.eq(1), self.sink.ack.eq(1),
If(self.sink.stb, 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"), NextState("MOVE_BUFFER_PTR"),
).Else( ).Else(
mem_port.we.eq(1), mem_port.we.eq(1),
mem_port.dat_w.eq(self.sink.data),
NextValue(addr, addr + 1), NextValue(addr, addr + 1),
If(addr == buffer_depth - 1, If(addr == buffer_depth - 1,
# discard the packet # discard the packet
@ -439,10 +492,10 @@ class RX_Bootstrap(Module):
NextState("IDLE"), NextState("IDLE"),
) )
class CXP_Trig_Ack_Checker(Module, AutoCSR): class Trigger_Ack_Checker(Module, AutoCSR):
def __init__(self): def __init__(self):
self.sink = stream.Endpoint(word_layout) self.sink = stream.Endpoint(word_layout_dchar)
self.source = stream.Endpoint(word_layout) self.source = stream.Endpoint(word_layout_dchar)
self.ack = Signal() self.ack = Signal()
@ -450,10 +503,8 @@ class CXP_Trig_Ack_Checker(Module, AutoCSR):
self.submodules.fsm = fsm = FSM(reset_state="COPY") self.submodules.fsm = fsm = FSM(reset_state="COPY")
self.submodules.voter = voter = Duplicate_Majority_Voter(self.sink.data, self.sink.k)
fsm.act("COPY", 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 # discard K28,6
self.sink.ack.eq(1), self.sink.ack.eq(1),
NextState("CHECK_ACK") NextState("CHECK_ACK")
@ -467,7 +518,7 @@ class CXP_Trig_Ack_Checker(Module, AutoCSR):
NextState("COPY"), NextState("COPY"),
# discard the word after K28,6 # discard the word after K28,6
self.sink.ack.eq(1), 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), self.ack.eq(1),
) )
) )