commit
c1dc02093d
|
@ -1,98 +1,78 @@
|
|||
#
|
||||
# This file is part of LiteEth.
|
||||
#
|
||||
# Copyright (c) 2015-2021 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||
# Copyright (c) 2015-2024 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||
# Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
|
||||
# Copyright (c) 2021 David Sawatzke <d-git@sawatzke.dev>
|
||||
# Copyright (c) 2017 whitequark <whitequark@whitequark.org>
|
||||
# Copyright (c) 2018 Felix Held <felix-github@felixheld.de>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
from functools import reduce
|
||||
from operator import xor
|
||||
from collections import OrderedDict
|
||||
from math import ceil
|
||||
|
||||
from litex.gen import *
|
||||
|
||||
from liteeth.common import *
|
||||
|
||||
from litex.gen.genlib.misc import chooser, WaitTimer
|
||||
|
||||
# MAC CRC Engine -----------------------------------------------------------------------------------
|
||||
|
||||
class LiteEthMACCRCEngine(Module):
|
||||
"""Cyclic Redundancy Check Engine
|
||||
class LiteEthMACCRCEngine(LiteXModule):
|
||||
"""
|
||||
Cyclic Redundancy Check (CRC) Engine using an asynchronous LFSR.
|
||||
|
||||
Compute next CRC value from last CRC value and data input using
|
||||
an optimized asynchronous LFSR.
|
||||
This module calculates the next CRC value based on the previous CRC value and the current data input.
|
||||
The CRC calculation is optimized for speed and resource efficiency.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_width : int
|
||||
Width of the data bus.
|
||||
width : int
|
||||
Width of the CRC.
|
||||
The bit width of the data bus and CRC value.
|
||||
polynom : int
|
||||
Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC)
|
||||
|
||||
Attributes
|
||||
----------
|
||||
data : in
|
||||
Data input.
|
||||
last : in
|
||||
last CRC value.
|
||||
next :
|
||||
next CRC value.
|
||||
The polynomial used for the CRC calculation, specified as an integer (e.g., 0x04C11DB7 for IEEE 802.3).
|
||||
"""
|
||||
def __init__(self, data_width, width, polynom):
|
||||
self.data = Signal(data_width)
|
||||
self.last = Signal(width)
|
||||
self.next = Signal(width)
|
||||
self.data = Signal(data_width) # Data (Input).
|
||||
self.crc_prev = Signal(width) # CRC Previous (Input).
|
||||
self.crc_next = Signal(width) # CRC Next (Output).
|
||||
|
||||
# # #
|
||||
|
||||
def _optimize_eq(l):
|
||||
"""
|
||||
remove an even numbers of XORs with the same bit
|
||||
replace an odd number of XORs with a single XOR
|
||||
"""
|
||||
d = OrderedDict()
|
||||
for e in l:
|
||||
if e in d:
|
||||
d[e] += 1
|
||||
else:
|
||||
d[e] = 1
|
||||
r = []
|
||||
for key, value in d.items():
|
||||
if value%2 != 0:
|
||||
r.append(key)
|
||||
return r
|
||||
# Determine bits affected by the polynom.
|
||||
polynom_taps = [bit for bit in range(width) if (1 << bit) & polynom]
|
||||
|
||||
# compute and optimize the parallel implementation of the CRC's LFSR
|
||||
taps = [x for x in range(width) if (1 << x) & polynom]
|
||||
curval = [[("state", i)] for i in range(width)]
|
||||
for i in range(data_width):
|
||||
feedback = curval.pop() + [("din", i)]
|
||||
for j in range(width-1):
|
||||
if j+1 in taps:
|
||||
curval[j] += feedback
|
||||
curval[j] = _optimize_eq(curval[j])
|
||||
curval.insert(0, feedback)
|
||||
# Prepare the list for CRC calculation through LFSR.
|
||||
crc_bits = [[("state", i)] for i in range(width)]
|
||||
for n in range(data_width):
|
||||
feedback = crc_bits.pop(-1) + [("din", n)]
|
||||
for pos in range(width - 1):
|
||||
if (pos + 1) in polynom_taps:
|
||||
crc_bits[pos] += feedback
|
||||
crc_bits[pos] = self.optimize_xors(crc_bits[pos])
|
||||
crc_bits.insert(0, feedback)
|
||||
|
||||
# implement logic
|
||||
# Calculate the next CRC value based on XOR operations.
|
||||
for i in range(width):
|
||||
xors = []
|
||||
for t, n in curval[i]:
|
||||
for t, n in crc_bits[i]:
|
||||
if t == "state":
|
||||
xors += [self.last[n]]
|
||||
xors += [self.crc_prev[n]]
|
||||
elif t == "din":
|
||||
xors += [self.data[n]]
|
||||
self.comb += self.next[i].eq(reduce(xor, xors))
|
||||
self.comb += self.crc_next[i].eq(Reduce("XOR", xors))
|
||||
|
||||
@staticmethod
|
||||
def optimize_xors(bits):
|
||||
"""Return items with odd occurrences for XOR optimization."""
|
||||
from collections import Counter
|
||||
return [bit for bit, count in Counter(bits).items() if count % 2 == 1]
|
||||
|
||||
# MAC CRC32 ----------------------------------------------------------------------------------------
|
||||
|
||||
@ResetInserter()
|
||||
@CEInserter()
|
||||
class LiteEthMACCRC32(Module):
|
||||
class LiteEthMACCRC32(LiteXModule):
|
||||
"""IEEE 802.3 CRC
|
||||
|
||||
Implement an IEEE 802.3 CRC generator/checker.
|
||||
|
@ -106,52 +86,50 @@ class LiteEthMACCRC32(Module):
|
|||
----------
|
||||
data : in
|
||||
Data input.
|
||||
last_be : in
|
||||
Valid byte in data input (optional).
|
||||
be : in
|
||||
Data byte enable (optional, defaults to full word).
|
||||
value : out
|
||||
CRC value (used for generator).
|
||||
error : out
|
||||
CRC error (used for checker).
|
||||
"""
|
||||
width = 32
|
||||
polynom = 0x04C11DB7
|
||||
polynom = 0x04c11db7
|
||||
init = 2**width-1
|
||||
check = 0xC704DD7B
|
||||
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.data = Signal(data_width)
|
||||
self.be = Signal(data_width//8, reset=2**data_width//8 - 1)
|
||||
self.value = Signal(self.width)
|
||||
self.error = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
self.comb += [
|
||||
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)]
|
||||
# Create a CRC Engine for each byte segment.
|
||||
# Ex for a 32-bit Data-Path, we create 4 engines: 8, 16, 24 and 32-bit engines.
|
||||
engines = []
|
||||
for n in range(data_width//8):
|
||||
engines.append(LiteEthMACCRCEngine((n + 1)*8, self.width, self.polynom))
|
||||
self.submodules += engines
|
||||
|
||||
# Register Full-Word CRC Engine (last one).
|
||||
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)]
|
||||
self.sync += reg.eq(engines[-1].crc_next)
|
||||
|
||||
# MAC CRC Inserter ---------------------------------------------------------------------------------
|
||||
# Select CRC Engine/Result.
|
||||
for n in range(data_width//8):
|
||||
self.comb += [
|
||||
engines[n].data.eq(self.data[:(n + 1)*8]),
|
||||
engines[n].crc_prev.eq(reg),
|
||||
If(self.be[n],
|
||||
self.value.eq(reverse_bits(~engines[n].crc_next)),
|
||||
self.error.eq(engines[n].crc_next != self.check),
|
||||
)
|
||||
]
|
||||
|
||||
class LiteEthMACCRCInserter(Module):
|
||||
# MAC CRC32 Inserter -------------------------------------------------------------------------------
|
||||
|
||||
class LiteEthMACCRC32Inserter(LiteXModule):
|
||||
"""CRC Inserter
|
||||
|
||||
Append a CRC at the end of each packet.
|
||||
|
@ -168,22 +146,26 @@ class LiteEthMACCRCInserter(Module):
|
|||
source : out
|
||||
Packet data with CRC.
|
||||
"""
|
||||
def __init__(self, crc_class, description):
|
||||
self.sink = sink = stream.Endpoint(description)
|
||||
def __init__(self, description):
|
||||
self.sink = sink = stream.Endpoint(description)
|
||||
self.source = source = stream.Endpoint(description)
|
||||
|
||||
# # #
|
||||
|
||||
dw = len(sink.data)
|
||||
assert dw in [8, 32, 64]
|
||||
crc = crc_class(dw)
|
||||
fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += crc, fsm
|
||||
# Parameters.
|
||||
data_width = len(sink.data)
|
||||
ratio = 32//data_width
|
||||
assert data_width in [8, 32, 64]
|
||||
|
||||
# crc packet checksum
|
||||
crc_packet = Signal(crc.width)
|
||||
last_be = Signal().like(sink.last_be)
|
||||
# Signals.
|
||||
crc_packet = Signal(32)
|
||||
last_be = Signal(data_width//8)
|
||||
|
||||
# CRC32 Generator.
|
||||
self.crc = crc = LiteEthMACCRC32(data_width)
|
||||
|
||||
# FSM.
|
||||
self.fsm = fsm = FSM(reset_state="IDLE")
|
||||
fsm.act("IDLE",
|
||||
crc.reset.eq(1),
|
||||
sink.ready.eq(1),
|
||||
|
@ -192,35 +174,36 @@ class LiteEthMACCRCInserter(Module):
|
|||
NextState("COPY"),
|
||||
)
|
||||
)
|
||||
self.comb += [
|
||||
crc.data.eq(sink.data),
|
||||
crc.be.eq(sink.last_be),
|
||||
]
|
||||
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
|
||||
# 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)],
|
||||
# If the whole crc value fits in the last sink paket, signal the
|
||||
# end. This also means the next state is idle
|
||||
If((dw == 64) & (sink.last_be <= 0xF),
|
||||
crc.value)[:data_width])) for e in range(data_width//8)],
|
||||
# If the whole crc value fits in the last sink packet, signal the end. This also
|
||||
# means the next state is idle
|
||||
If((data_width == 64) & (sink.last_be <= 0xf),
|
||||
source.last.eq(1),
|
||||
source.last_be.eq(sink.last_be << (dw//8 - 4))
|
||||
source.last_be.eq(sink.last_be << (data_width//8 - 4))
|
||||
),
|
||||
).Else(
|
||||
crc.ce.eq(sink.valid & source.ready),
|
||||
),
|
||||
|
||||
If(sink.valid & sink.last & source.ready,
|
||||
If((dw == 64) & (sink.last_be <= 0xF),
|
||||
If((data_width == 64) & (sink.last_be <= 0xf),
|
||||
NextState("IDLE"),
|
||||
).Else(
|
||||
NextValue(crc_packet, crc.value),
|
||||
If(dw == 64,
|
||||
If(data_width == 64,
|
||||
NextValue(last_be, sink.last_be >> 4),
|
||||
).Else (
|
||||
NextValue(last_be, sink.last_be),
|
||||
|
@ -229,16 +212,17 @@ class LiteEthMACCRCInserter(Module):
|
|||
)
|
||||
)
|
||||
)
|
||||
ratio = crc.width//dw
|
||||
if ratio > 1:
|
||||
cnt = Signal(max=ratio, reset=ratio-1)
|
||||
cnt = Signal(max=ratio, reset=ratio-1)
|
||||
cnt_done = Signal()
|
||||
fsm.act("CRC",
|
||||
source.valid.eq(1),
|
||||
chooser(crc_packet, cnt, source.data, reverse=True),
|
||||
If(cnt_done,
|
||||
source.last.eq(1),
|
||||
If(source.ready, NextState("IDLE"))
|
||||
If(source.ready,
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
)
|
||||
self.comb += cnt_done.eq(cnt == 0)
|
||||
|
@ -255,18 +239,15 @@ class LiteEthMACCRCInserter(Module):
|
|||
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"))
|
||||
source.data.eq(crc_packet[-(e+1)*8:])) for e in range(data_width//8)],
|
||||
If(source.ready,
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
|
||||
# MAC CRC32 Checker --------------------------------------------------------------------------------
|
||||
|
||||
class LiteEthMACCRC32Inserter(LiteEthMACCRCInserter):
|
||||
def __init__(self, description):
|
||||
LiteEthMACCRCInserter.__init__(self, LiteEthMACCRC32, description)
|
||||
|
||||
# MAC CRC Checker ----------------------------------------------------------------------------------
|
||||
|
||||
class LiteEthMACCRCChecker(Module):
|
||||
class LiteEthMACCRC32Checker(LiteXModule):
|
||||
"""CRC Checker
|
||||
|
||||
Check CRC at the end of each packet.
|
||||
|
@ -286,7 +267,7 @@ class LiteEthMACCRCChecker(Module):
|
|||
error : out
|
||||
Pulses every time a CRC error is detected.
|
||||
"""
|
||||
def __init__(self, crc_class, description):
|
||||
def __init__(self, description):
|
||||
self.sink = sink = stream.Endpoint(description)
|
||||
self.source = source = stream.Endpoint(description)
|
||||
|
||||
|
@ -294,17 +275,16 @@ class LiteEthMACCRCChecker(Module):
|
|||
|
||||
# # #
|
||||
|
||||
dw = len(sink.data)
|
||||
assert dw in [8, 32, 64]
|
||||
crc = crc_class(dw)
|
||||
self.submodules += crc
|
||||
ratio = ceil(crc.width/dw)
|
||||
# Parameters.
|
||||
data_width = len(sink.data)
|
||||
ratio = ceil(32/data_width)
|
||||
assert data_width in [8, 32, 64]
|
||||
|
||||
fifo = ResetInserter()(stream.SyncFIFO(description, ratio + 1))
|
||||
self.submodules += fifo
|
||||
# CRC32 Checker.
|
||||
self.crc = crc = LiteEthMACCRC32(data_width)
|
||||
|
||||
fsm = FSM(reset_state="RESET")
|
||||
self.submodules += fsm
|
||||
# FIFO.
|
||||
self.fifo = fifo = ResetInserter()(stream.SyncFIFO(description, ratio + 1))
|
||||
|
||||
fifo_in = Signal()
|
||||
fifo_out = Signal()
|
||||
|
@ -320,6 +300,8 @@ class LiteEthMACCRCChecker(Module):
|
|||
self.sink.ready.eq(fifo_in),
|
||||
]
|
||||
|
||||
# FSM.
|
||||
self.fsm = fsm = FSM(reset_state="RESET")
|
||||
fsm.act("RESET",
|
||||
crc.reset.eq(1),
|
||||
fifo.reset.eq(1),
|
||||
|
@ -327,7 +309,7 @@ class LiteEthMACCRCChecker(Module):
|
|||
)
|
||||
self.comb += [
|
||||
crc.data.eq(sink.data),
|
||||
crc.last_be.eq(sink.last_be),
|
||||
crc.be.eq(sink.last_be),
|
||||
]
|
||||
fsm.act("IDLE",
|
||||
If(sink.valid & sink.ready,
|
||||
|
@ -335,37 +317,37 @@ class LiteEthMACCRCChecker(Module):
|
|||
NextState("COPY")
|
||||
)
|
||||
)
|
||||
last_be = Signal().like(sink.last_be)
|
||||
last_be = Signal().like(sink.last_be)
|
||||
crc_error = Signal()
|
||||
self.comb += fifo.source.connect(source, omit={"valid", "ready", "last", "last_be"})
|
||||
fsm.act("COPY",
|
||||
fifo.source.ready.eq(fifo_out),
|
||||
source.valid.eq(sink.valid & fifo_full),
|
||||
source.payload.eq(fifo.source.payload),
|
||||
|
||||
If(dw <= 32,
|
||||
If(data_width <= 32,
|
||||
source.last.eq(sink.last),
|
||||
source.last_be.eq(sink.last_be),
|
||||
# For dw == 64 bit, we need to look wether the last word contains only the crc value or both crc and data
|
||||
# For data_width == 64 bit, we need to look wether the last word contains only the crc value or both crc and data
|
||||
# In the latter case, the last word also needs to be output
|
||||
# In both cases, last_be needs to be adjusted for the new end position
|
||||
).Elif(sink.last_be & 0xF,
|
||||
source.last.eq(sink.last),
|
||||
source.last_be.eq(sink.last_be << (dw//8 - 4)),
|
||||
source.last_be.eq(sink.last_be << (data_width//8 - 4)),
|
||||
).Else(
|
||||
NextValue(last_be, sink.last_be >> 4),
|
||||
NextValue(crc_error, crc.error),
|
||||
),
|
||||
|
||||
# `source.error` has a width > 1 for dw > 8, but since the crc error
|
||||
# `source.error` has a width > 1 for data_width > 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 & sink.last, dw//8)),
|
||||
source.error.eq(sink.error | Replicate(crc.error & sink.last, data_width//8)),
|
||||
self.error.eq(sink.valid & sink.last & crc.error),
|
||||
|
||||
If(sink.valid & sink.ready,
|
||||
crc.ce.eq(1),
|
||||
# Can only happen for dw == 64
|
||||
# Can only happen for data_width == 64
|
||||
If(sink.last & (sink.last_be > 0xF),
|
||||
NextState("COPY_LAST"),
|
||||
).Elif(sink.last,
|
||||
|
@ -375,17 +357,12 @@ class LiteEthMACCRCChecker(Module):
|
|||
)
|
||||
|
||||
# If the last sink word contains both data and the crc value, shift out
|
||||
# the last value here. Can only happen for dw == 64
|
||||
# the last value here. Can only happen for data_width == 64
|
||||
fsm.act("COPY_LAST",
|
||||
fifo.source.connect(source),
|
||||
source.error.eq(fifo.source.error | Replicate(crc_error, dw//8)),
|
||||
fifo.source.connect(source, keep={"valid", "ready", "last"}),
|
||||
source.error.eq(fifo.source.error | Replicate(crc_error, data_width//8)),
|
||||
source.last_be.eq(last_be),
|
||||
If(source.valid & source.ready,
|
||||
NextState("RESET")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class LiteEthMACCRC32Checker(LiteEthMACCRCChecker):
|
||||
def __init__(self, description):
|
||||
LiteEthMACCRCChecker.__init__(self, LiteEthMACCRC32, description)
|
||||
|
|
Loading…
Reference in New Issue