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.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),
)
)