1
0
Fork 0

Compare commits

..

3 Commits

Author SHA1 Message Date
morgan b4a75fd8c9 sim: prototyping frame decoder
sim: add double buffer
sim: add eop marker for crc checker in double buffer
sim: add KCode, pak type & CRC generator
sim: add Stream Dispatcher
2024-11-07 12:06:28 +08:00
morgan fbb2089c34 pipeline GW: remove cxp
pipeline GW: fix data loss when downstream not ack
2024-11-07 12:04:54 +08:00
morgan 3fd7d3d905 downconn GW: move rx reset out of comma checker
rx reset: only active for singe/master rx
comma checker: rename to aligner & active on all rx config
2024-11-04 11:40:39 +08:00
4 changed files with 622 additions and 85 deletions

393
sim_pipeline.py Normal file
View File

@ -0,0 +1,393 @@
from migen import *
from misoc.interconnect.csr import *
from misoc.interconnect import stream
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
from src.gateware.cxp_pipeline import *
class EOP_Marker(Module):
def __init__(self):
self.sink = stream.Endpoint(word_layout_dchar)
self.source = stream.Endpoint(word_layout_dchar)
# # #
last_stb = Signal()
self.sync += [
If((~self.source.stb | self.source.ack),
self.source.stb.eq(self.sink.stb),
self.source.payload.eq(self.sink.payload),
),
last_stb.eq(self.sink.stb),
]
self.comb += [
self.sink.ack.eq(~self.source.stb | self.source.ack),
self.source.eop.eq(~self.sink.stb & last_stb),
]
class Stream_MetaData_Extractor(Module):
def __init__(self):
self.sink = stream.Endpoint(word_layout_dchar)
self.source = stream.Endpoint(word_layout_dchar)
# # #
# GOAL:
# 0) accept four sinks??
# 1) decode SPH, SPT
# 2) decode Image header, line break
# 3) verify the crc before phrasing image data downstream
# HOW??
# combine streams?
# phrase linedata to sys CD
# check stream data tag
# only need to support mono16 for now
self.stream_id = Signal(char_width)
self.pak_tag = Signal(char_width)
self.stream_pak_size = Signal(char_width * 2)
self.submodules.fsm = fsm = FSM(reset_state="WAIT_HEADER")
fsm.act(
"WAIT_HEADER",
NextValue(self.stream_id, self.stream_id.reset),
NextValue(self.pak_tag, self.pak_tag.reset),
NextValue(self.stream_pak_size, self.stream_pak_size.reset),
self.sink.ack.eq(1),
If(
self.sink.stb,
NextValue(self.stream_id, self.sink.dchar),
NextState("GET_PAK_TAG"),
),
)
fsm.act(
"GET_PAK_TAG",
If(
self.sink.stb,
self.sink.ack.eq(1),
NextValue(self.pak_tag, self.sink.dchar),
NextState("GET_PAK_SIZE_0"),
),
)
fsm.act(
"GET_PAK_SIZE_0",
self.sink.ack.eq(1),
If(
self.sink.stb,
NextValue(self.stream_pak_size[8:], self.sink.dchar),
NextState("GET_PAK_SIZE_1"),
),
)
fsm.act(
"GET_PAK_SIZE_1",
self.sink.ack.eq(1),
If(
self.sink.stb,
NextValue(self.stream_pak_size[:8], self.sink.dchar),
NextState("STORE_BUFFER"),
),
)
fsm.act(
"STORE_BUFFER",
self.sink.connect(self.source),
# both serve the same function but using the pak size I can remove eop injecter and save 1 cycle
If(self.sink.stb,
NextValue(self.stream_pak_size, self.stream_pak_size - 1),
If(self.stream_pak_size == 1,
NextState("WAIT_HEADER"),
)
),
# If((self.sink.stb & self.sink.eop),
# NextState("WAIT_HEADER"),
# )
)
class Frame_Decoder(Module):
def __init__(self):
self.sink = stream.Endpoint(word_layout_dchar)
self.source = stream.Endpoint(word_layout_dchar)
# # #
# TODO: decode Image header, line break
class Pixel_Decoder(Module):
def __init__(self, pixel_format="mono16"):
assert pixel_format == "mono16"
self.sink = stream.Endpoint(word_layout_dchar)
self.source = stream.Endpoint(word_layout_dchar)
# # #
# TODO: support mono16 for now?
class Streams_Dispatcher(Module):
def __init__(self, downconns):
n_downconn = len(downconns)
self.submodules.mux = mux = stream.Multiplexer(word_layout_dchar, n_downconn)
for i, c in enumerate(downconns):
# if i == 0:
self.comb += [
# no backpressure
c.source.ack.eq(1),
c.source.connect(getattr(mux, "sink"+str(i)))
]
self.source = stream.Endpoint(word_layout_dchar)
self.submodules.fsm = fsm = FSM(reset_state="WAIT_HEADER")
# TODO: add different downstream
# stream_id = Signal()
# case = dict((i, mux.source.connect(b.sink)) for i, b in enumerate(buffers))
fsm.act(
"WAIT_HEADER",
mux.source.connect(self.source),
If(mux.source.eop,
NextState("SWITCH_CONN"),
),
)
read_mask = Signal(max=n_downconn)
self.comb += mux.sel.eq(read_mask)
fsm.act(
"SWITCH_CONN",
If(read_mask == n_downconn - 1,
NextValue(read_mask, read_mask.reset),
).Else(
NextValue(read_mask, read_mask + 1),
),
NextState("WAIT_HEADER"),
)
def reverse_bytes(s):
assert len(s) % 8 == 0
char = [s[i*8:(i+1)*8] for i in range(len(s)//8)]
return Cat(char[::-1])
@ResetInserter()
@CEInserter()
class CXPCRC32(Module):
# Section 9.2.2.2 (CXP-001-2021)
width = 32
polynom = 0x04C11DB7
seed = 2**width - 1
check = 0x00000000
def __init__(self, data_width):
self.data = Signal(data_width)
self.value = Signal(self.width)
self.error = Signal()
# # #
self.submodules.engine = LiteEthMACCRCEngine(
data_width, self.width, self.polynom
)
reg = Signal(self.width, reset=self.seed)
self.sync += reg.eq(self.engine.next)
self.comb += [
# the CRC Engine use Big Endian, need to reverse the bytes
self.engine.data.eq(reverse_bytes(self.data)),
self.engine.last.eq(reg),
self.value.eq(reverse_bytes(reg[::-1])),
self.error.eq(reg != self.check),
]
class CXPCRC32Inserter(Module):
def __init__(self):
self.sink = stream.Endpoint(word_layout)
self.source = stream.Endpoint(word_layout)
# # #
self.submodules.crc = crc = CXPCRC32(word_dw)
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
crc.reset.eq(1),
self.sink.ack.eq(1),
If(self.sink.stb,
self.sink.ack.eq(0),
NextState("COPY"),
)
)
fsm.act("COPY",
crc.ce.eq(self.sink.stb & self.source.ack),
crc.data.eq(self.sink.data),
self.sink.connect(self.source),
self.source.eop.eq(0),
If(self.sink.stb & self.sink.eop & self.source.ack,
NextState("INSERT"),
)
)
fsm.act("INSERT",
self.source.stb.eq(1),
self.source.eop.eq(1),
self.source.data.eq(crc.value),
If(self.source.ack, NextState("IDLE"))
)
class StreamPacket_Wrapper(Module):
def __init__(self):
self.sink = stream.Endpoint(word_layout)
self.source = stream.Endpoint(word_layout)
# # #
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
self.sink.ack.eq(1),
If(self.sink.stb,
self.sink.ack.eq(0),
NextState("INSERT_HEADER_0"),
)
)
fsm.act("INSERT_HEADER_0",
self.sink.ack.eq(0),
self.source.stb.eq(1),
self.source.data.eq(Replicate(KCode["pak_start"], 4)),
self.source.k.eq(Replicate(1, 4)),
If(self.source.ack, NextState("INSERT_HEADER_1")),
)
fsm.act("INSERT_HEADER_1",
self.sink.ack.eq(0),
self.source.stb.eq(1),
self.source.data.eq(Replicate(C(0x01, char_width), 4)),
self.source.k.eq(Replicate(0, 4)),
If(self.source.ack, NextState("COPY")),
)
fsm.act("COPY",
self.sink.connect(self.source),
self.source.eop.eq(0),
If(self.sink.stb & self.sink.eop & self.source.ack,
NextState("INSERT_FOOTER"),
),
)
fsm.act("INSERT_FOOTER",
self.sink.ack.eq(0),
self.source.stb.eq(1),
self.source.data.eq(Replicate(KCode["pak_end"], 4)),
self.source.k.eq(Replicate(1, 4)),
# Simulate RX don't have eop tagged
# self.source.eop.eq(1),
If(self.source.ack, NextState("IDLE")),
)
# With KCode & 0x01*4
class StreamData_Generator(Module):
def __init__(self):
# should be big enough for all test
self.submodules.buffer = buffer = stream.SyncFIFO(word_layout, 32)
self.submodules.crc_inserter = crc_inserter = CXPCRC32Inserter()
self.submodules.wrapper = wrapper = StreamPacket_Wrapper()
# # #
pipeline = [buffer, crc_inserter, wrapper]
for s, d in zip(pipeline, pipeline[1:]):
self.comb += s.source.connect(d.sink)
self.sink = pipeline[0].sink
self.source = pipeline[-1].source
# For verifying crc in stream data packet
class Double_Stream_Buffer(Module):
# default size is 2 kBtyes - Section 9.5.2 (CXP-001-2021)
def __init__(self, size=16000):
# detect and tag end of packet for crc
# self.submodules.eop_marker = eop_marker = EOP_Marker()
# self.sink = eop_marker.sink
self.sink = stream.Endpoint(word_layout_dchar)
self.submodules.crc = crc = CXPCRC32(word_dw)
self.comb += crc.data.eq(self.sink.data)
self.submodules.fsm = fsm = FSM(reset_state="RESET")
write_mask = Signal()
self.submodules.line_buffer0 = line_buffer0 = ResetInserter()(stream.SyncFIFO(word_layout_dchar, 2**bits_for(size//word_dw)))
self.submodules.line_buffer1 = line_buffer1 = ResetInserter()(stream.SyncFIFO(word_layout_dchar, 2**bits_for(size//word_dw)))
fsm.act("RESET",
Case(write_mask,
{
0: line_buffer0.reset.eq(1),
1: line_buffer1.reset.eq(1),
}
),
crc.reset.eq(1),
NextState("CHECKING"),
)
fsm.act("CHECKING",
self.sink.ack.eq(1),
If(self.sink.stb,
crc.ce.eq(1),
If(self.sink.eop,
# discard the crc at the end
NextState("SWITCH_BUFFER")
).Else(
If(write_mask == 0,
self.sink.connect(line_buffer0.sink),
).Else(
self.sink.connect(line_buffer1.sink),
),
)
)
)
# only valid data will be passed to downstream
fsm.act("SWITCH_BUFFER",
If(~crc.error,
NextValue(write_mask, ~write_mask),
),
NextState("RESET"),
)
self.submodules.mux = mux = stream.Multiplexer(word_layout_dchar, 2)
self.comb += [
line_buffer0.source.connect(mux.sink0),
line_buffer1.source.connect(mux.sink1),
mux.sel.eq(~write_mask),
]
self.source = mux.source
# DEBUG:
self.write = Signal()
self.error = Signal()
self.comb += [
self.write.eq(write_mask),
self.error.eq(crc.error),
]
# # to add eop in the same cycle
# # the tricks relies on the fact source lags sink one cycle
# # but fsm .connect by default use combinational logic which the same cycle rising/falling edge check immpossible
# fsm.act("CHECKING",
# NextValue(self.source.payload.raw_bits(), 0),
# NextValue(self.source.stb, 0),
# If(self.sink.stb,
# crc.ce.eq(1),
# If(self.sink.eop,
# NextState("RESET")
# ).Else(
# NextValue(self.source.payload.raw_bits(), self.sink.payload.raw_bits()),
# NextValue(self.source.stb, 1),
# )
# )
# )
# last_eop = Signal()
# self.comb += self.source.eop.eq(~last_eop & self.sink.eop)

159
sim_stream.py Normal file
View File

@ -0,0 +1,159 @@
from migen import *
from misoc.interconnect import stream
from sim_pipeline import *
from src.gateware.cxp_pipeline import *
class CXP_Links(Module):
def __init__(self):
# TODO: select the correct buffer to read from
# NOTE: although there are double buffer in each connect, the reading must be faster than writing to avoid data loss
self.downconns = []
for i in range(2):
downconn = Pipeline()
setattr(self.submodules, "cxp_conn"+str(i), downconn)
self.downconns.append(downconn)
self.submodules.dispatcher = dispatcher = Streams_Dispatcher(self.downconns)
# TODO: add extractor
# self.submodules.double_buffer = double_buffer = Double_Stream_Buffer()
pipeline = [dispatcher]
for s, d in zip(pipeline, pipeline[1:]):
self.comb += s.source.connect(d.sink)
self.source = pipeline[-1].source
# no backpressure
self.sync += self.source.ack.eq(1)
class Pipeline(Module):
def __init__(self):
self.submodules.generator = generator = StreamData_Generator()
self.submodules.dchar_decoder = dchar_decoder = Duplicated_Char_Decoder()
self.submodules.data_decoder = data_decoder = RX_Bootstrap()
self.submodules.eop_marker = eop_marker = EOP_Marker()
# # #
pipeline = [generator, dchar_decoder, data_decoder, eop_marker]
for s, d in zip(pipeline, pipeline[1:]):
self.comb += s.source.connect(d.sink)
self.sink = pipeline[0].sink
self.source = pipeline[-1].source
# self.comb += self.source.ack.eq(1)
dut = CXP_Links()
def check_case(packet=[]):
print("=================TEST========================")
downconns = dut.downconns
ch = 0
for i, p in enumerate(packet):
for x in range(len(downconns)):
if x == ch:
yield downconns[x].sink.data.eq(p["data"])
yield downconns[x].sink.k.eq(p["k"])
yield downconns[x].sink.stb.eq(1)
else:
yield downconns[x].sink.data.eq(0)
yield downconns[x].sink.k.eq(0)
yield downconns[x].sink.stb.eq(0)
yield downconns[x].sink.eop.eq(0)
if "eop" in p:
yield downconns[ch].sink.eop.eq(1)
# compensate for delay
# yield
# yield downconns[ch].sink.data.eq(0)
# yield downconns[ch].sink.k.eq(0)
# yield downconns[ch].sink.stb.eq(0)
# yield downconns[ch].sink.eop.eq(0)
# yield
# yield
# yield
ch = (ch + 1) % len(downconns)
else:
yield downconns[ch].sink.eop.eq(0)
# check cycle result
yield
source = dut.dispatcher.mux.source
print(
f"\nCYCLE#{i} : source char = {yield source.data:#X} k = {yield source.k:#X} stb = {yield source.stb} ack = {yield source.ack} eop = {yield source.eop}"
# f" source dchar = {yield source.dchar:#X} dchar_k = {yield source.dchar_k:#X}"
f"\nCYCLE#{i} : read mask = {yield dut.dispatcher.mux.sel}"
# f"\nCYCLE#{i} : stream id = {yield decoder.stream_id:#X} pak_tag = {yield decoder.pak_tag:#X}"
# f" stream_pak_size = {yield decoder.stream_pak_size:#X}"
)
# crc = downconns[1].generator.crc_inserter.crc
# crc = dut.double_buffer.crc
# print(
# f"CYCLE#{i} : crc error = {yield crc.error:#X} crc value = {yield crc.value:#X}"
# f" crc data = {yield crc.data:#X} engine next = {yield crc.engine.next:#X} ce = {yield crc.ce}"
# )
# extra clk cycles
cyc = i + 1
for i in range(cyc, cyc + 30):
for x in range(len(downconns)):
# yield won't reset every cycle
yield downconns[x].sink.data.eq(0)
yield downconns[x].sink.k.eq(0)
yield downconns[x].sink.stb.eq(0)
yield downconns[x].sink.eop.eq(0)
yield
print(
f"\nCYCLE#{i} : source char = {yield source.data:#X} k = {yield source.k:#X} stb = {yield source.stb} ack = {yield source.ack} eop = {yield source.eop}"
# f" source dchar = {yield source.dchar:#X} dchar_k = {yield source.dchar_k:#X}"
f"\nCYCLE#{i} : read mask = {yield dut.dispatcher.mux.sel}"
# f"\nCYCLE#{i} : stream id = {yield decoder.stream_id:#X} pak_tag = {yield decoder.pak_tag:#X}"
# f" stream_pak_size = {yield decoder.stream_pak_size:#X}"
)
assert True
def testbench():
stream_id = 0x69
streams = [
[
{"data": 0x11111111, "k": Replicate(0, 4)},
{"data": 0xB105F00D, "k": Replicate(0, 4)},
],
[
{"data": 0x22222222, "k": Replicate(0, 4)},
{"data": 0xC00010FF, "k": Replicate(0, 4)},
],
[
{"data": 0x33333333, "k": Replicate(0, 4)},
{"data": 0xC0A79AE5, "k": Replicate(0, 4)},
],
]
packet = []
for i, s in enumerate(streams):
s[-1]["eop"] = 0
packet += [
{"data": Replicate(C(stream_id, char_width), 4), "k": Replicate(0, 4)},
{"data": Replicate(C(i, char_width), 4), "k": Replicate(0, 4)},
{
"data": Replicate(C(len(s) >> 8 & 0xFF, char_width), 4),
"k": Replicate(0, 4),
},
{"data": Replicate(C(len(s) & 0xFF, char_width), 4), "k": Replicate(0, 4)},
*s,
]
yield from check_case(packet)
run_simulation(dut, testbench(), vcd_name="sim-cxp.vcd")

View File

@ -58,7 +58,7 @@ class Receiver(Module):
data_valid = Signal() data_valid = Signal()
self.sync.cxp_gtx_rx += [ self.sync.cxp_gtx_rx += [
data_valid.eq(gtx.comma_checker.rxfsm.ongoing("READY")), data_valid.eq(gtx.comma_aligner.rxfsm.ongoing("READY")),
self.source.stb.eq(0), self.source.stb.eq(0),
If(data_valid & self.source.ack & ~((gtx.decoders[0].d == 0xBC) & (gtx.decoders[0].k == 1)), If(data_valid & self.source.ack & ~((gtx.decoders[0].d == 0xBC) & (gtx.decoders[0].k == 1)),
@ -218,9 +218,33 @@ class QPLL(Module, AutoCSR):
), ),
] ]
class RX_Resetter(Module):
def __init__(self, reset_period=10_000_000):
self.rx_ready = Signal()
self.rx_reset = Signal()
# # #
# periodically reset rx until rx is connected and receiving valid data
# as after connecting RXP/RXN, the whole RX need to be reset
reset_counter = Signal(reset=reset_period-1, max=reset_period)
self.sync += [
self.rx_reset.eq(0),
If(~self.rx_ready,
If(reset_counter == 0,
reset_counter.eq(reset_counter.reset),
self.rx_reset.eq(1),
).Else(
reset_counter.eq(reset_counter - 1),
)
)
]
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped # Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
# compared to the usual 8b10b binary representation. # compared to the usual 8b10b binary representation.
class Comma_Checker(Module): class Comma_Aligner(Module):
def __init__(self, comma, reset_period=10_000_000): def __init__(self, comma, reset_period=10_000_000):
self.data = Signal(20) self.data = Signal(20)
self.comma_aligned = Signal() self.comma_aligned = Signal()
@ -229,28 +253,9 @@ class Comma_Checker(Module):
self.aligner_en = Signal() self.aligner_en = Signal()
self.ready_sys = Signal() self.ready_sys = Signal()
self.restart_sys = Signal()
# # # # # #
# periodically reset rx until rx is connected and receiving valid data
# as after connecting RXP/RXN, the whole RX need to be reset
reset_counter = Signal(reset=reset_period-1, max=reset_period)
self.sync += [
self.restart_sys.eq(0),
If(~self.ready_sys,
If(reset_counter == 0,
reset_counter.eq(reset_counter.reset),
self.restart_sys.eq(1),
).Else(
reset_counter.eq(reset_counter - 1),
)
)
]
# Data and comma checker # Data and comma checker
# From UG476 (v1.12.1) p.228 # From UG476 (v1.12.1) p.228
# The built-in RXBYTEISALIGNED can be falsely asserted at linerate higher than 5Gbps # The built-in RXBYTEISALIGNED can be falsely asserted at linerate higher than 5Gbps
@ -694,14 +699,29 @@ class GTX(Module):
Instance("BUFG", i_I=txpll_clkout, o_O=self.cd_cxp_gtx_tx.clk), Instance("BUFG", i_I=txpll_clkout, o_O=self.cd_cxp_gtx_tx.clk),
AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~self.txpll_locked & ~tx_init.done) AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~self.txpll_locked & ~tx_init.done)
] ]
self.comb += tx_init.restart.eq(self.tx_restart)
# RX clocking # RX clocking
# the CDR matches the required frequency for RXUSRCLK, no need for PLL # the CDR matches the required frequency for RXUSRCLK, no need for PLL
self.clock_domains.cd_cxp_gtx_rx = ClockDomain()
self.specials += [ # Slave Rx will use cxp_gtx_rx instead
Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_cxp_gtx_rx.clk), if rx_mode == "single" or rx_mode == "master":
AsyncResetSynchronizer(self.cd_cxp_gtx_rx, ~rx_init.done) self.clock_domains.cd_cxp_gtx_rx = ClockDomain()
] self.specials += [
Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_cxp_gtx_rx.clk),
AsyncResetSynchronizer(self.cd_cxp_gtx_rx, ~rx_init.done)
]
self.submodules.rx_resetter = rx_resetter = RX_Resetter()
self.comb += [
rx_resetter.rx_ready.eq(self.rx_ready),
rx_init.restart.eq(self.rx_restart | rx_resetter.rx_reset),
]
else:
self.comb += rx_init.restart.eq(self.rx_restart),
# 8b10b Encoder/Decoder
self.comb += [ self.comb += [
txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1], self.encoder.output[2], self.encoder.output[3])), txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1], self.encoder.output[2], self.encoder.output[3])),
@ -711,16 +731,12 @@ class GTX(Module):
self.decoders[3].input.eq(rxdata[30:]), self.decoders[3].input.eq(rxdata[30:]),
] ]
self.submodules.comma_aligner = comma_aligner = Comma_Aligner(0b0101111100)
self.submodules.comma_checker = comma_checker = Comma_Checker(0b0101111100)
self.comb += [ self.comb += [
comma_checker.data.eq(rxdata), comma_aligner.data.eq(rxdata),
comma_checker.comma_aligned.eq(comma_aligned), comma_aligner.comma_aligned.eq(comma_aligned),
comma_checker.comma_realigned.eq(comma_realigned), comma_aligner.comma_realigned.eq(comma_realigned),
comma_checker.comma_det.eq(comma_det), comma_aligner.comma_det.eq(comma_det),
comma_aligner_en.eq(comma_checker.aligner_en), comma_aligner_en.eq(comma_aligner.aligner_en),
self.rx_ready.eq(comma_checker.ready_sys), self.rx_ready.eq(comma_aligner.ready_sys),
rx_init.restart.eq(self.rx_restart | comma_checker.restart_sys),
tx_init.restart.eq(self.tx_restart),
] ]

View File

@ -2,7 +2,6 @@ from migen import *
from migen.genlib.cdc import MultiReg from migen.genlib.cdc import MultiReg
from misoc.interconnect.csr import * 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 functools import reduce from functools import reduce
from itertools import combinations from itertools import combinations
@ -291,7 +290,6 @@ class RX_Debug_Buffer(Module,AutoCSR):
class Duplicated_Char_Decoder(Module): class Duplicated_Char_Decoder(Module):
def __init__(self): def __init__(self):
self.sink = stream.Endpoint(word_layout) self.sink = stream.Endpoint(word_layout)
self.buffer = stream.Endpoint(word_layout)
self.source = stream.Endpoint(word_layout_dchar) self.source = stream.Endpoint(word_layout_dchar)
# # # # # #
@ -314,22 +312,23 @@ class Duplicated_Char_Decoder(Module):
# 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. # 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 # And any downstream modules can access the voting result without implementing the voting logic inside the decoder
# cycle 1 - buffer data & calculate intermediate result
buffer = stream.Endpoint(word_layout)
self.sync += [ self.sync += [
self.sink.ack.eq(self.buffer.ack), If((~buffer.stb | buffer.ack),
self.buffer.stb.eq(self.sink.stb), buffer.stb.eq(self.sink.stb),
If(self.sink.stb, buffer.payload.eq(self.sink.payload),
self.buffer.data.eq(self.sink.data), )
self.buffer.k.eq(self.sink.k),
),
] ]
self.comb += self.sink.ack.eq(~buffer.stb | buffer.ack)
# cycle 1 - calculate ABC, ABD, ACD & BCD
# calculate ABC, ABD, ACD, BCD
char = [[self.sink.data[i*8:(i+1)*8], self.sink.k[i]] for i in range(4)] 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)] voters = [Record([("data", 8), ("k", 1)]) for _ in range(4)]
for i, comb in enumerate(combinations(char, 3)): for i, comb in enumerate(combinations(char, 3)):
self.sync += [ self.sync += [
If(self.sink.stb, If((~buffer.stb | buffer.ack),
voters[i].data.eq(reduce(and_, [code[0] for code in comb])), voters[i].data.eq(reduce(and_, [code[0] for code in comb])),
voters[i].k.eq(reduce(and_, [code[1] for code in comb])), voters[i].k.eq(reduce(and_, [code[1] for code in comb])),
) )
@ -338,15 +337,16 @@ class Duplicated_Char_Decoder(Module):
# cycle 2 - inject the voting result # cycle 2 - inject the voting result
self.sync += [ self.sync += [
self.buffer.ack.eq(self.source.ack), If((~self.source.stb | self.source.ack),
self.source.stb.eq(self.buffer.stb), self.source.stb.eq(buffer.stb),
If(self.buffer.stb, self.source.data.eq(buffer.data),
self.source.data.eq(self.buffer.data), self.source.k.eq(buffer.k),
self.source.k.eq(self.buffer.k),
self.source.dchar.eq(Replicate(reduce(or_, [v.data for v in voters]), 4)), 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)), self.source.dchar_k.eq(Replicate(reduce(or_, [v.k for v in voters]), 4)),
), )
] ]
self.comb += buffer.ack.eq(~self.source.stb | self.source.ack)
@FullMemoryWE() @FullMemoryWE()
class RX_Bootstrap(Module): class RX_Bootstrap(Module):
@ -523,34 +523,3 @@ class Trigger_Ack_Checker(Module, AutoCSR):
) )
) )
) )
@ResetInserter()
@CEInserter()
class CXPCRC32(Module):
# Section 9.2.2.2 (CXP-001-2021)
width = 32
polynom = 0x04C11DB7
seed = 2**width-1
check = 0x00000000
def __init__(self, data_width):
self.data = Signal(data_width)
self.value = Signal(self.width)
self.error = Signal()
# # #
self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom)
reg = Signal(self.width, reset=self.seed)
self.sync += reg.eq(self.engine.next)
self.comb += [
self.engine.data.eq(self.data),
self.engine.last.eq(reg),
self.value.eq(reg[::-1]),
self.error.eq(self.engine.next != self.check)
]
# For verifying crc in stream data packet
class CXPCRC32Checker(LiteEthMACCRCChecker):
def __init__(self, layout):
LiteEthMACCRCChecker.__init__(self, CXPCRC32, layout)