Merge pull request #74 from david-sawatzke/32bitmacpath

Add toggleable 32 bit support to mac pipeline
This commit is contained in:
enjoy-digital 2021-09-27 17:22:51 +02:00 committed by GitHub
commit 7847f72c6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 180 additions and 69 deletions

View file

@ -20,9 +20,10 @@ class LiteEthMAC(Module, AutoCSR):
ntxslots = 2,
hw_mac = None,
timestamp = None,
full_memory_we = False):
full_memory_we = False,
sys_data_path = True):
assert interface in ["crossbar", "wishbone", "hybrid"]
self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_preamble_crc)
self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_preamble_crc, sys_data_path)
self.csrs = []
if interface == "crossbar":
self.submodules.crossbar = LiteEthMACCrossbar(dw)

View file

@ -7,7 +7,7 @@
# SPDX-License-Identifier: BSD-2-Clause
from liteeth.common import *
from liteeth.mac import gap, preamble, crc, padding, last_be
from liteeth.mac import gap, preamble, crc, padding, last_be, endian_converter
from liteeth.phy.model import LiteEthPHYModel
from migen.genlib.cdc import PulseSynchronizer
@ -17,16 +17,32 @@ from litex.soc.interconnect.stream import BufferizeEndpoints, DIR_SOURCE, DIR_SI
# MAC Core -----------------------------------------------------------------------------------------
class LiteEthMACCore(Module, AutoCSR):
def __init__(self, phy, dw, endianness="big", with_preamble_crc=True, with_padding=True):
if dw < phy.dw:
raise ValueError("Core data width({}) must be larger than PHY data width({})".format(dw, phy.dw))
def __init__(self, phy, dw,
endianness = "big",
with_preamble_crc = True,
sys_data_path = True,
with_padding = True):
core_dw = dw
if core_dw < phy.dw:
raise ValueError("Core data width({}) must be larger than PHY data width({})".format(core_dw, phy.dw))
rx_pipeline = [phy]
tx_pipeline = [phy]
if sys_data_path:
# The pipeline for dw>8 only works for little endian
self.data_path_converter(tx_pipeline, rx_pipeline, core_dw, phy.dw, "little")
cd_tx = cd_rx = "sys"
dw = core_dw
else:
cd_tx = "eth_tx"
cd_rx = "eth_rx"
dw = phy.dw
# Interpacket gap
tx_gap_inserter = gap.LiteEthMACGap(phy.dw)
self.submodules += ClockDomainsRenamer("eth_tx")(tx_gap_inserter)
tx_gap_inserter = gap.LiteEthMACGap(dw)
self.submodules += ClockDomainsRenamer(cd_tx)(tx_gap_inserter)
tx_pipeline += [tx_gap_inserter]
# Preamble / CRC
@ -36,65 +52,85 @@ class LiteEthMACCore(Module, AutoCSR):
self._preamble_crc = CSRStatus(reset=1)
elif with_preamble_crc:
self._preamble_crc = CSRStatus(reset=1)
self.preamble_errors = CSRStatus(32)
self.crc_errors = CSRStatus(32)
# Preamble insert/check
preamble_inserter = preamble.LiteEthMACPreambleInserter(phy.dw)
preamble_checker = preamble.LiteEthMACPreambleChecker(phy.dw)
self.submodules += ClockDomainsRenamer("eth_tx")(preamble_inserter)
self.submodules += ClockDomainsRenamer("eth_rx")(preamble_checker)
preamble_inserter = preamble.LiteEthMACPreambleInserter(dw)
preamble_checker = preamble.LiteEthMACPreambleChecker(dw)
self.submodules += ClockDomainsRenamer(cd_tx)(preamble_inserter)
self.submodules += ClockDomainsRenamer(cd_rx)(preamble_checker)
tx_pipeline += [preamble_inserter]
rx_pipeline += [preamble_checker]
self.submodules.ps_preamble_error = PulseSynchronizer(cd_rx, "sys")
# Preamble error counter
self.preamble_errors = CSRStatus(32)
self.comb += self.ps_preamble_error.i.eq(preamble_checker.error),
self.sync += If(self.ps_preamble_error.o,
self.preamble_errors.status.eq(self.preamble_errors.status + 1)),
# CRC insert/check
crc32_inserter = BufferizeEndpoints({"sink": DIR_SINK})(crc.LiteEthMACCRC32Inserter(eth_phy_description(phy.dw)))
crc32_checker = BufferizeEndpoints({"sink": DIR_SINK})(crc.LiteEthMACCRC32Checker(eth_phy_description(phy.dw)))
self.submodules += ClockDomainsRenamer("eth_tx")(crc32_inserter)
self.submodules += ClockDomainsRenamer("eth_rx")(crc32_checker)
crc32_inserter = BufferizeEndpoints({"sink": DIR_SINK})(crc.LiteEthMACCRC32Inserter(eth_phy_description(dw)))
crc32_checker = BufferizeEndpoints({"sink": DIR_SINK})(crc.LiteEthMACCRC32Checker(eth_phy_description(dw)))
self.submodules += ClockDomainsRenamer(cd_tx)(crc32_inserter)
self.submodules += ClockDomainsRenamer(cd_rx)(crc32_checker)
tx_pipeline += [preamble_inserter, crc32_inserter]
rx_pipeline += [preamble_checker, crc32_checker]
tx_pipeline += [crc32_inserter]
rx_pipeline += [crc32_checker]
# Error counters
self.submodules.ps_preamble_error = PulseSynchronizer("eth_rx", "sys")
self.submodules.ps_crc_error = PulseSynchronizer("eth_rx", "sys")
self.comb += [
self.ps_preamble_error.i.eq(preamble_checker.error),
self.ps_crc_error.i.eq(crc32_checker.error),
]
self.sync += [
If(self.ps_preamble_error.o,
self.preamble_errors.status.eq(self.preamble_errors.status + 1)),
If(self.ps_crc_error.o,
self.crc_errors.status.eq(self.crc_errors.status + 1)),
]
# CRC error counter
self.crc_errors = CSRStatus(32)
self.submodules.ps_crc_error = PulseSynchronizer(cd_rx, "sys")
self.comb += self.ps_crc_error.i.eq(crc32_checker.error),
self.sync += If(self.ps_crc_error.o,
self.crc_errors.status.eq(self.crc_errors.status + 1)),
# Padding
if with_padding:
padding_inserter = padding.LiteEthMACPaddingInserter(phy.dw, 60)
padding_checker = padding.LiteEthMACPaddingChecker(phy.dw, 60)
self.submodules += ClockDomainsRenamer("eth_tx")(padding_inserter)
self.submodules += ClockDomainsRenamer("eth_rx")(padding_checker)
padding_inserter = padding.LiteEthMACPaddingInserter(dw, 60)
padding_checker = padding.LiteEthMACPaddingChecker(dw, 60)
self.submodules += ClockDomainsRenamer(cd_tx)(padding_inserter)
self.submodules += ClockDomainsRenamer(cd_rx)(padding_checker)
tx_pipeline += [padding_inserter]
rx_pipeline += [padding_checker]
if sys_data_path:
# Since the pipeline only works for little endian when dw > 8,
# convert to big endian if necessary
if endianness == "big" and dw != 8:
tx_converter = endian_converter.LiteEthMACEndianConverter(dw)
rx_converter = endian_converter.LiteEthMACEndianConverter(dw)
self.submodules += tx_converter, rx_converter
tx_pipeline += [tx_converter]
rx_pipeline += [rx_converter]
else:
self.data_path_converter(tx_pipeline, rx_pipeline, core_dw, phy.dw, endianness)
# Graph
self.submodules.tx_pipeline = stream.Pipeline(*reversed(tx_pipeline))
self.submodules.rx_pipeline = stream.Pipeline(*rx_pipeline)
self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source
def data_path_converter(self, tx_pipeline, rx_pipeline, dw, phy_dw, endianness):
# Delimiters
if dw != 8:
tx_last_be = last_be.LiteEthMACTXLastBE(phy.dw)
rx_last_be = last_be.LiteEthMACRXLastBE(phy.dw)
tx_last_be = last_be.LiteEthMACTXLastBE(phy_dw)
rx_last_be = last_be.LiteEthMACRXLastBE(phy_dw)
self.submodules += ClockDomainsRenamer("eth_tx")(tx_last_be)
self.submodules += ClockDomainsRenamer("eth_rx")(rx_last_be)
tx_pipeline += [tx_last_be]
rx_pipeline += [rx_last_be]
# Converters
if dw != phy.dw:
if dw != phy_dw:
reverse = endianness == "big"
tx_converter = stream.StrideConverter(
description_from = eth_phy_description(dw),
description_to = eth_phy_description(phy.dw),
description_to = eth_phy_description(phy_dw),
reverse = reverse)
rx_converter = stream.StrideConverter(
description_from = eth_phy_description(phy.dw),
description_from = eth_phy_description(phy_dw),
description_to = eth_phy_description(dw),
reverse = reverse)
self.submodules += ClockDomainsRenamer("eth_tx")(tx_converter)
@ -108,9 +144,3 @@ class LiteEthMACCore(Module, AutoCSR):
self.submodules += tx_cdc, rx_cdc
tx_pipeline += [tx_cdc]
rx_pipeline += [rx_cdc]
# Graph
self.submodules.tx_pipeline = stream.Pipeline(*reversed(tx_pipeline))
self.submodules.rx_pipeline = stream.Pipeline(*rx_pipeline)
self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source

View file

@ -102,8 +102,10 @@ class LiteEthMACCRC32(Module):
Attributes
----------
d : in
data : in
Data input.
last_be : in
Valid byte in data input (optional).
value : out
CRC value (used for generator).
error : out
@ -114,22 +116,36 @@ class LiteEthMACCRC32(Module):
init = 2**width-1
check = 0xC704DD7B
def __init__(self, data_width):
dw = data_width//8
self.data = Signal(data_width)
self.last_be = Signal(dw)
self.value = Signal(self.width)
self.error = Signal()
# Add a separate last_be signal, to maintain backwards compatability
last_be = Signal(data_width//8)
# # #
self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom)
reg = Signal(self.width, reset=self.init)
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)
If(self.last_be != 0,
last_be.eq(self.last_be)
).Else(
last_be.eq(2**(dw-1)))
]
# Since the data can end at any byte end, indicated by `last_be`
# maintain separate engines for each 8 byte increment in the data word
engines = [LiteEthMACCRCEngine((e+1)*8, self.width, self.polynom) for e in range(dw)]
self.submodules += engines
reg = Signal(self.width, reset=self.init)
self.sync += reg.eq(engines[-1].next)
self.comb += [engines[e].data.eq(self.data[:(e+1)*8]) for e in range(dw)],
self.comb += [engines[e].last.eq(reg) for e in range(dw)]
self.comb += [If(last_be[e],
self.value.eq(reverse_bits(~engines[e].next)),
self.error.eq(engines[e].next != self.check))
for e in range(dw)]
# MAC CRC Inserter ---------------------------------------------------------------------------------
@ -146,9 +162,9 @@ class LiteEthMACCRCInserter(Module):
Attributes
----------
sink : in
Packets octets without CRC.
Packet data without CRC.
source : out
Packets octets with CRC.
Packet data with CRC.
"""
def __init__(self, crc_class, description):
self.sink = sink = stream.Endpoint(description)
@ -157,10 +173,15 @@ class LiteEthMACCRCInserter(Module):
# # #
dw = len(sink.data)
assert dw in [8, 32]
crc = crc_class(dw)
fsm = FSM(reset_state="IDLE")
self.submodules += crc, fsm
# crc packet checksum
crc_packet = Signal(crc.width)
last_be = Signal().like(sink.last_be)
fsm.act("IDLE",
crc.reset.eq(1),
sink.ready.eq(1),
@ -172,9 +193,23 @@ class LiteEthMACCRCInserter(Module):
fsm.act("COPY",
crc.ce.eq(sink.valid & source.ready),
crc.data.eq(sink.data),
crc.last_be.eq(sink.last_be),
sink.connect(source),
source.last.eq(0),
source.last_be.eq(0),
If(sink.last,
# Fill the empty space of the last data word with the
# beginning of the crc value
[If(sink.last_be[e],
source.data.eq(Cat(sink.data[:(e+1)*8],
crc.value)[:dw])) for e in range(dw//8)],
).Else(
crc.ce.eq(sink.valid & source.ready),
),
If(sink.valid & sink.last & source.ready,
NextValue(crc_packet, crc.value),
NextValue(last_be, sink.last_be),
NextState("CRC"),
)
)
@ -184,7 +219,7 @@ class LiteEthMACCRCInserter(Module):
cnt_done = Signal()
fsm.act("CRC",
source.valid.eq(1),
chooser(crc.value, cnt, source.data, reverse=True),
chooser(crc_packet, cnt, source.data, reverse=True),
If(cnt_done,
source.last.eq(1),
If(source.ready, NextState("IDLE"))
@ -202,6 +237,9 @@ class LiteEthMACCRCInserter(Module):
source.valid.eq(1),
source.last.eq(1),
source.data.eq(crc.value),
source.last_be.eq(last_be),
[If(last_be[e],
source.data.eq(crc_packet[-(e+1)*8:])) for e in range(dw//8)],
If(source.ready, NextState("IDLE"))
)
@ -225,9 +263,9 @@ class LiteEthMACCRCChecker(Module):
Attributes
----------
sink : in
Packet octets with CRC.
Packet data with CRC.
source : out
Packet octets without CRC and "error" set to 0
Packet data without CRC and "error" set to 0
on last when CRC OK / set to 1 when CRC KO.
error : out
Pulses every time a CRC error is detected.
@ -241,6 +279,7 @@ class LiteEthMACCRCChecker(Module):
# # #
dw = len(sink.data)
assert dw in [8, 32]
crc = crc_class(dw)
self.submodules += crc
ratio = crc.width//dw
@ -268,8 +307,13 @@ class LiteEthMACCRCChecker(Module):
source.last.eq(sink.last),
fifo.source.ready.eq(fifo_out),
source.payload.eq(fifo.source.payload),
source.last_be.eq(sink.last_be),
source.error.eq(sink.error | crc.error),
# `source.error` has a width > 1 for dw > 8, but since the crc error
# applies to the whole ethernet packet, all the bytes are marked as
# containing an error. This way later reducing the data width
# doesn't run into issues with missing the error
source.error.eq(sink.error | Replicate(crc.error, dw//8)),
self.error.eq(source.valid & source.last & crc.error),
]
@ -278,7 +322,10 @@ class LiteEthMACCRCChecker(Module):
fifo.reset.eq(1),
NextState("IDLE"),
)
self.comb += crc.data.eq(sink.data)
self.comb += [
crc.data.eq(sink.data),
crc.last_be.eq(sink.last_be),
]
fsm.act("IDLE",
If(sink.valid & sink.ready,
crc.ce.eq(1),

View file

@ -0,0 +1,18 @@
#
# This file is part of LiteEth.
#
# Copyright (c) 2021 David Sawatzke <d-git@sawatzke.dev>
# SPDX-License-Identifier: BSD-2-Clause
from liteeth.common import *
class LiteEthMACEndianConverter(Module):
def __init__(self, dw):
self.sink = sink = stream.Endpoint(eth_phy_description(dw))
self.source = source = stream.Endpoint(eth_phy_description(dw))
self.comb += [
sink.connect(source),
source.data.eq(reverse_bytes(sink.data)),
source.last_be.eq(reverse_bits(sink.last_be)),
source.error.eq(reverse_bits(sink.error)),
]

View file

@ -14,12 +14,14 @@ from liteeth.common import *
class LiteEthMACPaddingInserter(Module):
def __init__(self, dw, padding):
assert dw in [8, 16, 32, 64]
self.sink = sink = stream.Endpoint(eth_phy_description(dw))
self.source = source = stream.Endpoint(eth_phy_description(dw))
# # #
padding_limit = math.ceil(padding/(dw/8))-1
last_be = 2**((padding-1)%(dw//8))
counter = Signal(16)
counter_done = Signal()
@ -33,8 +35,14 @@ class LiteEthMACPaddingInserter(Module):
If(sink.last,
If(~counter_done,
source.last.eq(0),
source.last_be.eq(0),
NextState("PADDING")
).Else(
).Elif((counter == padding_limit) & (last_be > sink.last_be),
# If the right amount of data words are transmitted, but
# too few bytes, transmit more bytes of the word. The
# formerly "unused" bytes get transmitted as well
source.last_be.eq(last_be)
). Else(
NextValue(counter, 0),
)
)
@ -42,7 +50,9 @@ class LiteEthMACPaddingInserter(Module):
)
fsm.act("PADDING",
source.valid.eq(1),
source.last.eq(counter_done),
If(counter_done,
source.last_be.eq(last_be),
source.last.eq(1)),
source.data.eq(0),
If(source.valid & source.ready,
NextValue(counter, counter + 1),

View file

@ -25,13 +25,16 @@ class LiteEthMACPreambleInserter(Module):
Preamble, SFD, and packet octets.
"""
def __init__(self, dw):
assert dw in [8, 16, 32, 64]
self.sink = stream.Endpoint(eth_phy_description(dw))
self.source = stream.Endpoint(eth_phy_description(dw))
# # #
preamble = Signal(64, reset=eth_preamble)
count = Signal(max=(64//dw)-1, reset_less=True)
# For 64 bits, `count` doesn't need to change. But migen won't create a
# signal with a width of 0 bits, so add an unused bit for 64 bit path
count = Signal(max=(64//dw) if dw != 64 else 2, reset_less=True)
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
self.sink.ready.eq(1),
@ -81,7 +84,7 @@ class LiteEthMACPreambleChecker(Module):
Pulses every time a preamble error is detected.
"""
def __init__(self, dw):
assert dw == 8
assert dw in [8, 16, 32, 64]
self.sink = sink = stream.Endpoint(eth_phy_description(dw))
self.source = source = stream.Endpoint(eth_phy_description(dw))
@ -89,10 +92,12 @@ class LiteEthMACPreambleChecker(Module):
# # #
preamble = Signal(64, reset=eth_preamble)
self.submodules.fsm = fsm = FSM(reset_state="PREAMBLE")
fsm.act("PREAMBLE",
sink.ready.eq(1),
If(sink.valid & ~sink.last & (sink.data == (eth_preamble >> 56)),
# Match to end of preamble
If(sink.valid & ~sink.last & (sink.data == preamble[-dw:]),
NextState("COPY")
),
If(sink.valid & sink.last, self.error.eq(1))