soc/interconnect/packet: add > 8-bit support to Packetizer/Depacketizer
With high speed link (10gbps XGMII ethernet for example), stream data_width is generally > 8-bit which make header/data un-aligned on bytes boundaries. The change allows the Packetizer/Depacketizer to work on stream with a data_width > 8-bit.
This commit is contained in:
parent
5f151152ca
commit
5c19b133ac
|
@ -155,21 +155,24 @@ class Header:
|
||||||
|
|
||||||
class Packetizer(Module):
|
class Packetizer(Module):
|
||||||
def __init__(self, sink_description, source_description, header):
|
def __init__(self, sink_description, source_description, header):
|
||||||
self.sink = sink = stream.Endpoint(sink_description)
|
self.sink = sink = stream.Endpoint(sink_description)
|
||||||
self.source = source = stream.Endpoint(source_description)
|
self.source = source = stream.Endpoint(source_description)
|
||||||
self.header = Signal(header.length*8)
|
self.header = Signal(header.length*8)
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
dw = len(self.sink.data)
|
dw = len(self.sink.data)
|
||||||
|
cw = dw // 8
|
||||||
header_reg = Signal(header.length*8, reset_less=True)
|
header_reg = Signal(header.length*8, reset_less=True)
|
||||||
header_words = (header.length*8)//dw
|
header_words = (header.length*8)//dw
|
||||||
load = Signal()
|
header_residue = header.length % cw
|
||||||
shift = Signal()
|
if header_residue:
|
||||||
counter = Signal(max=max(header_words, 2))
|
header_leftover = Signal(header_residue*8)
|
||||||
|
load = Signal()
|
||||||
|
shift = Signal()
|
||||||
|
counter = Signal(max=max(header_words, 2))
|
||||||
counter_reset = Signal()
|
counter_reset = Signal()
|
||||||
counter_ce = Signal()
|
counter_ce = Signal()
|
||||||
self.sync += \
|
self.sync += \
|
||||||
If(counter_reset,
|
If(counter_reset,
|
||||||
counter.eq(0)
|
counter.eq(0)
|
||||||
|
@ -195,52 +198,102 @@ class Packetizer(Module):
|
||||||
|
|
||||||
fsm = FSM(reset_state="IDLE")
|
fsm = FSM(reset_state="IDLE")
|
||||||
self.submodules += fsm
|
self.submodules += fsm
|
||||||
|
self.transitioning = transitioning = Signal() # TODO: Perhaps fsm already has a transitioning signal
|
||||||
|
|
||||||
if header_words == 1:
|
# TODO: What is the recommended way to delay a record, a FIFO?
|
||||||
|
last_buf, valid_buf = Signal(), Signal()
|
||||||
|
self.data_buf = data_buf = Signal(len(sink.data))
|
||||||
|
if header_words == 1 and header_residue == 0:
|
||||||
idle_next_state = "COPY"
|
idle_next_state = "COPY"
|
||||||
|
elif header_words == 1 and header_residue:
|
||||||
|
idle_next_state = "STAGGERCOPY"
|
||||||
else:
|
else:
|
||||||
idle_next_state = "SEND-HEADER"
|
idle_next_state = "SEND_HEADER"
|
||||||
|
|
||||||
fsm.act("IDLE",
|
fsm.act("IDLE",
|
||||||
sink.ready.eq(1),
|
sink.ready.eq(1),
|
||||||
counter_reset.eq(1),
|
counter_reset.eq(1),
|
||||||
If(sink.valid,
|
If(sink.valid,
|
||||||
sink.ready.eq(0),
|
sink.ready.eq(0),
|
||||||
source.valid.eq(1),
|
source.valid.eq(1),
|
||||||
source.last.eq(0),
|
source.last.eq(0),
|
||||||
source.data.eq(self.header[:dw]),
|
source.data.eq(self.header[:dw]),
|
||||||
If(source.valid & source.ready,
|
If(source.valid & source.ready,
|
||||||
load.eq(1),
|
load.eq(1),
|
||||||
NextState(idle_next_state)
|
NextValue(transitioning, 1),
|
||||||
)
|
NextState(idle_next_state)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
# if header_residue and header_words >= 2:
|
||||||
|
# self.sync += [header_leftover.eq(header_reg[2*dw:(2*dw+header_residue*8)]),
|
||||||
|
# ]
|
||||||
|
|
||||||
|
self.sync += [last_buf.eq(sink.last),
|
||||||
|
data_buf.eq(sink.data),
|
||||||
|
valid_buf.eq(sink.valid),
|
||||||
|
]
|
||||||
if header_words != 1:
|
if header_words != 1:
|
||||||
fsm.act("SEND-HEADER",
|
fsm.act("SEND_HEADER",
|
||||||
source.valid.eq(1),
|
source.valid.eq(1),
|
||||||
source.last.eq(0),
|
source.last.eq(0),
|
||||||
source.data.eq(header_reg[dw:2*dw]),
|
source.data.eq(header_reg[dw:2*dw]),
|
||||||
If(source.valid & source.ready,
|
If(source.valid & source.ready,
|
||||||
shift.eq(1),
|
shift.eq(1),
|
||||||
counter_ce.eq(1),
|
counter_ce.eq(1),
|
||||||
If(counter == header_words-2,
|
If(counter == header_words-2,
|
||||||
NextState("COPY")
|
shift.eq(0),
|
||||||
)
|
counter_ce.eq(1 if header_residue else 0),
|
||||||
|
NextValue(transitioning, 1),
|
||||||
|
NextState("STAGGERCOPY" if header_residue else "COPY")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if hasattr(sink, "error"):
|
if hasattr(sink, "error"):
|
||||||
self.comb += source.error.eq(sink.error)
|
self.comb += source.error.eq(sink.error)
|
||||||
fsm.act("COPY",
|
if hasattr(sink, "last_be"):
|
||||||
source.valid.eq(sink.valid),
|
# header_lengh + last_be
|
||||||
source.last.eq(sink.last),
|
cw = dw//8
|
||||||
source.data.eq(sink.data),
|
rotate_by = header.length % cw
|
||||||
If(source.valid & source.ready,
|
x = [sink.last_be[(i + rotate_by) % cw] for i in range(cw)]
|
||||||
sink.ready.eq(1),
|
self.comb += source.last_be.eq(Cat(*x))
|
||||||
If(source.last,
|
if header_residue:
|
||||||
NextState("IDLE")
|
header_offset_multiplier = hom = 1 if header_words == 1 else 2
|
||||||
)
|
fsm.act("STAGGERCOPY",
|
||||||
|
source.valid.eq(valid_buf),
|
||||||
|
source.last.eq(last_buf),
|
||||||
|
If(transitioning,
|
||||||
|
source.data.eq(Cat(header_reg[hom*dw:hom*dw+header_residue*8],
|
||||||
|
sink.data[:(cw-header_residue)*8]))
|
||||||
|
).Else(
|
||||||
|
source.data.eq(Cat(data_buf[(cw-header_residue)*8:],
|
||||||
|
sink.data[:(cw-header_residue)*8]))
|
||||||
|
),
|
||||||
|
If(source.valid & source.ready,
|
||||||
|
sink.ready.eq(1),
|
||||||
|
If(sink.valid,
|
||||||
|
NextValue(transitioning, 0)
|
||||||
|
),
|
||||||
|
If(source.last,
|
||||||
|
NextState("IDLE")
|
||||||
|
)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
|
fsm.act("COPY",
|
||||||
|
source.valid.eq(sink.valid),
|
||||||
|
source.last.eq(sink.last),
|
||||||
|
source.data.eq(sink.data),
|
||||||
|
If(source.valid & source.ready,
|
||||||
|
NextValue(transitioning, 0),
|
||||||
|
sink.ready.eq(1),
|
||||||
|
If(source.last,
|
||||||
|
NextState("IDLE")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Depacketizer -------------------------------------------------------------------------------------
|
# Depacketizer -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -254,13 +307,16 @@ class Depacketizer(Module):
|
||||||
|
|
||||||
dw = len(sink.data)
|
dw = len(sink.data)
|
||||||
|
|
||||||
|
cw = dw // 8
|
||||||
|
|
||||||
header_reg = Signal(header.length*8, reset_less=True)
|
header_reg = Signal(header.length*8, reset_less=True)
|
||||||
header_words = (header.length*8)//dw
|
header_words = (header.length*8)//dw
|
||||||
|
header_residue = header.length % cw
|
||||||
|
|
||||||
shift = Signal()
|
shift = Signal()
|
||||||
counter = Signal(max=max(header_words, 2))
|
counter = Signal(max=max(header_words, 2))
|
||||||
counter_reset = Signal()
|
counter_reset = Signal()
|
||||||
counter_ce = Signal()
|
counter_ce = Signal()
|
||||||
self.sync += \
|
self.sync += \
|
||||||
If(counter_reset,
|
If(counter_reset,
|
||||||
counter.eq(0)
|
counter.eq(0)
|
||||||
|
@ -268,23 +324,30 @@ class Depacketizer(Module):
|
||||||
counter.eq(counter + 1)
|
counter.eq(counter + 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
if header_words == 1:
|
if header_words == 1 and header_residue == 0:
|
||||||
self.sync += \
|
self.sync += \
|
||||||
If(shift,
|
If(shift,
|
||||||
header_reg.eq(sink.data)
|
header_reg.eq(sink.data)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.sync += \
|
self.sync += \
|
||||||
If(shift,
|
If(shift,
|
||||||
header_reg.eq(Cat(header_reg[dw:], sink.data))
|
header_reg.eq(Cat(header_reg[dw:], sink.data))
|
||||||
)
|
)
|
||||||
self.comb += self.header.eq(header_reg)
|
self.comb += self.header.eq(header_reg)
|
||||||
|
|
||||||
fsm = FSM(reset_state="IDLE")
|
fsm = FSM(reset_state="IDLE")
|
||||||
self.submodules += fsm
|
self.submodules += fsm
|
||||||
|
# TODO: Perhaps fsm already has a transitioning signal
|
||||||
|
self.transitioning = transitioning = Signal()
|
||||||
|
|
||||||
if header_words == 1:
|
last_buf, valid_buf = Signal(), Signal()
|
||||||
|
self.data_buf = data_buf = Signal(len(sink.data))
|
||||||
|
|
||||||
|
if header_words == 1 and header_residue == 0:
|
||||||
idle_next_state = "COPY"
|
idle_next_state = "COPY"
|
||||||
|
elif header_words == 1 and header_residue:
|
||||||
|
idle_next_state = "STAGGERCOPY"
|
||||||
else:
|
else:
|
||||||
idle_next_state = "RECEIVE_HEADER"
|
idle_next_state = "RECEIVE_HEADER"
|
||||||
|
|
||||||
|
@ -292,10 +355,16 @@ class Depacketizer(Module):
|
||||||
sink.ready.eq(1),
|
sink.ready.eq(1),
|
||||||
counter_reset.eq(1),
|
counter_reset.eq(1),
|
||||||
If(sink.valid,
|
If(sink.valid,
|
||||||
shift.eq(1),
|
shift.eq(1),
|
||||||
NextState(idle_next_state)
|
NextValue(transitioning, 1),
|
||||||
|
NextState(idle_next_state)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.sync += [If(sink.ready, data_buf.eq(sink.data)),
|
||||||
|
valid_buf.eq(sink.valid),
|
||||||
|
]
|
||||||
|
|
||||||
if header_words != 1:
|
if header_words != 1:
|
||||||
fsm.act("RECEIVE_HEADER",
|
fsm.act("RECEIVE_HEADER",
|
||||||
sink.ready.eq(1),
|
sink.ready.eq(1),
|
||||||
|
@ -303,27 +372,57 @@ class Depacketizer(Module):
|
||||||
counter_ce.eq(1),
|
counter_ce.eq(1),
|
||||||
shift.eq(1),
|
shift.eq(1),
|
||||||
If(counter == header_words-2,
|
If(counter == header_words-2,
|
||||||
NextState("COPY")
|
counter_ce.eq(1 if header_residue else 0),
|
||||||
|
NextValue(transitioning, 1),
|
||||||
|
NextState("STAGGERCOPY" if header_residue else "COPY")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
no_payload = Signal()
|
no_payload = Signal()
|
||||||
self.sync += \
|
self.sync += \
|
||||||
If(fsm.before_entering("COPY"),
|
If(fsm.before_entering("COPY") | fsm.before_entering("STAGGERCOPY"),
|
||||||
no_payload.eq(sink.last)
|
no_payload.eq(sink.last)
|
||||||
)
|
)
|
||||||
|
|
||||||
if hasattr(sink, "error"):
|
if hasattr(sink, "error"):
|
||||||
self.comb += source.error.eq(sink.error)
|
self.comb += source.error.eq(sink.error)
|
||||||
|
if hasattr(sink, "last_be"):
|
||||||
|
# header_lengh + last_be
|
||||||
|
cw = dw//8
|
||||||
|
x = [sink.last_be[(i - (cw - header_residue)) % cw] for i in range(cw)]
|
||||||
|
self.comb += source.last_be.eq(Cat(*x))
|
||||||
self.comb += [
|
self.comb += [
|
||||||
source.last.eq(sink.last | no_payload),
|
|
||||||
source.data.eq(sink.data),
|
|
||||||
header.decode(self.header, source)
|
header.decode(self.header, source)
|
||||||
]
|
]
|
||||||
fsm.act("COPY",
|
if header_residue:
|
||||||
sink.ready.eq(source.ready),
|
fsm.act("STAGGERCOPY",
|
||||||
source.valid.eq(sink.valid | no_payload),
|
source.last.eq(sink.last | no_payload),
|
||||||
If(source.valid & source.ready & source.last,
|
sink.ready.eq(source.ready),
|
||||||
NextState("IDLE")
|
source.valid.eq(sink.valid & ~transitioning | no_payload),
|
||||||
|
If(sink.valid & source.ready,
|
||||||
|
If(transitioning,
|
||||||
|
NextValue(header_reg, Cat(header_reg[header_residue*8:],
|
||||||
|
sink.data[:header_residue*8]))
|
||||||
|
).Else(
|
||||||
|
source.data.eq(Cat(data_buf[header_residue*8:],
|
||||||
|
sink.data[:header_residue*8])),
|
||||||
|
),
|
||||||
|
NextValue(transitioning, 0)
|
||||||
|
),
|
||||||
|
If(source.valid & source.ready & source.last,
|
||||||
|
NextState("IDLE")
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
|
fsm.act("COPY",
|
||||||
|
source.last.eq(sink.last | no_payload),
|
||||||
|
source.data.eq(sink.data),
|
||||||
|
sink.ready.eq(source.ready),
|
||||||
|
source.valid.eq(sink.valid | no_payload),
|
||||||
|
If(source.valid & source.ready,
|
||||||
|
NextValue(transitioning, 0),
|
||||||
|
If(source.last,
|
||||||
|
NextState("IDLE")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue