forked from M-Labs/artiq-zynq
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:
parent
63112919a3
commit
b370ec00ea
|
@ -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),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue