Implement RFC 6: CRC Generator

See amaranth-lang/rfcs#6 and #681.
This commit is contained in:
Adam Greig 2022-01-30 19:28:10 +00:00 committed by Catherine
parent 60c2a1b4b8
commit 45b9730786
7 changed files with 1717 additions and 0 deletions

View file

@ -0,0 +1,426 @@
"""
Utilities for computing cyclic redundancy checks (CRCs) in software and in
hardware.
CRCs are specified using the :py:class:`Algorithm` class, which contains
settings for CRC width, polynomial, initial value, input/output reflection, and
output XOR. Many commonly used CRC algorithms are available in the
:py:mod:`~amaranth.lib.crc.catalog` module, while most other CRC designs can be
accommodated by manually constructing :py:class:`Algorithm`.
Call the :py:class:`Algorithm` with a ``data_width`` to obtain a
:py:class:`Parameters` class, which fully defines a CRC computation. The
:py:class:`Parameters` class provides the :py:meth:`~Parameters.compute` method
to perform software computations, and the :py:meth:`~Parameters.create` method
to create a hardware CRC module, :py:class:`Processor`.
.. code-block::
# Create a predefined CRC16-CCITT hardware module, using the default
# 8-bit data width (in other words, bytes).
from amaranth.lib.crc.catalog import CRC16_CCITT
crc = m.submodules.crc = CRC16_CCITT().create()
# Create a custom CRC algorithm, specify the data width explicitly,
# and use it to compute a CRC value in software.
from amaranth.lib.crc import Algorithm
algo = Algorithm(crc_width=16, polynomial=0x1021, initial_crc=0xffff,
reflect_input=False, reflect_output=False,
xor_output=0x0000)
assert algo(data_width=8).compute(b"123456789") == 0x29b1
"""
from ... import *
__all__ = ["Algorithm", "Parameters", "Processor", "catalog"]
class Algorithm:
"""
Settings for a CRC algorithm, excluding data width.
The parameter set is based on the Williams model from
"A Painless Guide to CRC Error Detection Algorithms":
http://www.ross.net/crc/download/crc_v3.txt
For a reference of standard CRC parameter sets, refer to:
* `reveng`_'s catalogue, which uses an identical parameterisation,
* `crcmod`_'s list of predefined functions, but remove the leading '1'
from the polynominal, XOR the "Init-value" with "XOR-out" to obtain
``initial_crc``, and where "Reversed" is True, set both ``reflect_input``
and ``reflect_output`` to True,
* `CRC Zoo`_, which contains only polynomials; use the "explicit +1"
form of polynomial but remove the leading '1'.
.. _reveng: https://reveng.sourceforge.io/crc-catalogue/all.htm
.. _crcmod: http://crcmod.sourceforge.net/crcmod.predefined.html
.. _CRC Zoo: https://users.ece.cmu.edu/~koopman/crc/
Many commonly used CRC algorithms are available in the
:py:mod:`~amaranth.lib.crc.catalog` module, which includes
all entries in the `reveng`_ catalogue.
To create a :py:class:`Parameters` instance, call the :py:class:`Algorithm`
object with the required data width, which defaults to 8 bits.
Parameters
----------
crc_width : int
Bit width of CRC word. Also known as "width" in the Williams model.
polynomial : int
CRC polynomial to use, ``crc_width`` bits long, without the implicit
``x**crc_width`` term. Polynomial is always specified with the highest
order terms in the most significant bit positions; use
``reflect_input`` and ``reflect_output`` to perform a least
significant bit first computation.
initial_crc : int
Initial value of CRC register at reset. Most significant bit always
corresponds to the highest order term in the CRC register.
reflect_input : bool
If True, the input data words are bit-reflected, so that they are
processed least significant bit first.
reflect_output : bool
If True, the output CRC is bit-reflected, so the least-significant bit
of the output is the highest-order bit of the CRC register.
Note that this reflection is performed over the entire CRC register;
for transmission you may want to treat the output as a little-endian
multi-word value, so for example the reflected 16-bit output 0x4E4C
would be transmitted as the two octets 0x4C 0x4E, each transmitted
least significant bit first.
xor_output : int
The output CRC will be the CRC register XOR'd with this value, applied
after any output bit-reflection.
"""
def __init__(self, *, crc_width, polynomial, initial_crc, reflect_input,
reflect_output, xor_output):
self.crc_width = int(crc_width)
self.polynomial = int(polynomial)
self.initial_crc = int(initial_crc)
self.reflect_input = bool(reflect_input)
self.reflect_output = bool(reflect_output)
self.xor_output = int(xor_output)
if self.crc_width <= 0:
raise ValueError("crc_width must be greater than 0")
if not 0 <= self.polynomial < 2 ** self.crc_width:
raise ValueError("polynomial must be between 0 and 2**crc_width - 1")
if not 0 <= self.initial_crc < 2 ** self.crc_width:
raise ValueError("initial_crc must be between 0 and 2**crc_width - 1")
if not 0 <= self.xor_output < 2 ** self.crc_width:
raise ValueError("xor_output must be between 0 and 2**crc_width - 1")
def __call__(self, data_width=8):
"""
Constructs a :py:class:`Parameters` instance from this
:py:class:`Algorithm` with the specified ``data_width``.
Parameters
----------
data_width : int
Bit width of data words, default 8.
"""
return Parameters(self, data_width)
def __repr__(self):
return f"Algorithm(crc_width={self.crc_width}," \
f" polynomial=0x{self.polynomial:0{self.crc_width//4}x}," \
f" initial_crc=0x{self.initial_crc:0{self.crc_width//4}x}," \
f" reflect_input={self.reflect_input}," \
f" reflect_output={self.reflect_output}," \
f" xor_output=0x{self.xor_output:0{self.crc_width//4}x})"
class Parameters:
"""
Full set of parameters for a CRC computation.
Contains the settings from :py:class:`Algorithm` and additionally
``data_width``. Refer to :py:class:`Algorithm` for details of what each
parameter means and how to construct them.
From this class, you can directly compute CRCs with the
:py:meth:`~Parameters.compute` method, or construct a hardware module with
the :py:meth:`~Parameters.create` method.
Parameters
----------
algorithm : Algorithm
CRC algorithm to use. Specifies the CRC width, polynomial,
initial value, whether to reflect the input or output words,
and any output XOR.
data_width : int
Bit width of data words.
"""
def __init__(self, algorithm, data_width=8):
self._crc_width = algorithm.crc_width
self._polynomial = algorithm.polynomial
self._initial_crc = algorithm.initial_crc
self._reflect_input = algorithm.reflect_input
self._reflect_output = algorithm.reflect_output
self._xor_output = algorithm.xor_output
self._data_width = int(data_width)
if self._data_width <= 0:
raise ValueError("data_width must be greater than 0")
@property
def algorithm(self):
"""
Returns an :py:class:`Algorithm` with the CRC settings from this
instance.
"""
return Algorithm(
crc_width=self._crc_width,
polynomial=self._polynomial,
initial_crc=self._initial_crc,
reflect_input=self._reflect_input,
reflect_output=self._reflect_output,
xor_output=self._xor_output)
def residue(self):
"""
Compute the residue value for this CRC, which is the value left in the
CRC register after processing any valid codeword.
"""
# Residue is computed by initialising to (possibly reflected)
# xor_output, feeding crc_width worth of 0 bits, then taking
# the (possibly reflected) output without any XOR.
if self._reflect_output:
init = self._reflect(self._xor_output, self._crc_width)
else:
init = self._xor_output
algo = self.algorithm
algo.initial_crc = init
algo.reflect_input = False
algo.xor_output = 0
return algo(data_width=self._crc_width).compute([0])
def create(self):
"""
Returns a ``Processor`` configured with these parameters.
"""
return Processor(self)
def compute(self, data):
"""
Computes and returns the CRC of all data words in ``data``.
Parameters
----------
data : iterable of integers
The CRC is computed over this complete set of data.
Each item is an integer of bitwidth equal to ``data_width``.
"""
# Precompute some constants we use every iteration.
word_max = (1 << self._data_width) - 1
top_bit = 1 << (self._crc_width + self._data_width - 1)
crc_mask = (1 << (self._crc_width + self._data_width)) - 1
poly_shifted = self._polynomial << self._data_width
# Implementation notes:
# We always compute most-significant bit first, which means the
# polynomial and initial value may be used as-is, and the reflect_in
# and reflect_out values have their usual sense.
# However, when computing word-at-a-time and MSbit-first, we must align
# the input word so its MSbit lines up with the MSbit of the previous
# CRC value. When the CRC width is smaller than the word width, this
# would normally truncate data bits.
# Instead, we shift the initial CRC left by the data width, and the
# data word left by the crc width, lining up their MSbits no matter
# the relation between the two widths.
# The new CRC is then shifted right by the data width before output.
crc = self._initial_crc << self._data_width
for word in data:
if not 0 <= word <= word_max:
raise ValueError("data word must be between 0 and {}".format(word_max - 1))
if self._reflect_input:
word = self._reflect(word, self._data_width)
crc ^= word << self._crc_width
for _ in range(self._data_width):
if crc & top_bit:
crc = (crc << 1) ^ poly_shifted
else:
crc <<= 1
crc &= crc_mask
crc >>= self._data_width
if self._reflect_output:
crc = self._reflect(crc, self._crc_width)
crc ^= self._xor_output
return crc
@staticmethod
def _reflect(word, n):
"""
Bitwise-reflects an n-bit word ``word``.
"""
return int(f"{word:0{n}b}"[::-1], 2)
def _matrices(self):
"""
Computes the F and G matrices for parallel CRC computation, treating
the CRC as a linear time-invariant system described by the state
relation x(t+1) = F.x(i) + G.u(i), where x(i) and u(i) are column
vectors of the bits of the CRC register and input word, F is the n-by-n
matrix relating the old state to the new state, and G is the n-by-m
matrix relating the new data to the new state, where n is the CRC
width and m is the data word width.
The matrices are ordered least-significant-bit first; in other words
the first entry, with index (0, 0), corresponds to the effect of the
least-significant bit of the input on the least-significant bit of the
output.
For convenience of implementation, both matrices are returned
transposed: the first index is the input bit, and the second index is
the corresponding output bit.
The matrices are used to select which bits are XORd together to compute
each bit i of the new state: if F[j][i] is set then bit j of the old
state is included in the XOR, and if G[j][i] is set then bit j of the
new data is included.
These matrices are not affected by ``initial_crc``, ``reflect_input``,
``reflect_output``, or ``xor_output``.
"""
f = []
g = []
algo = self.algorithm
algo.reflect_input = algo.reflect_output = False
algo.xor_output = 0
crc = Parameters(algo, self._data_width)
for i in range(self._crc_width):
crc._initial_crc = 2 ** i
w = crc.compute([0])
f.append([int(x) for x in reversed(f"{w:0{self._crc_width}b}")])
for i in range(self._data_width):
crc._initial_crc = 0
w = crc.compute([2 ** i])
g.append([int(x) for x in reversed(f"{w:0{self._crc_width}b}")])
return f, g
def __repr__(self):
return f"Parameters({self.algorithm!r}, data_width={self._data_width})"
class Processor(Elaboratable):
"""
Cyclic redundancy check (CRC) processor module.
This module generates CRCs from an input data stream, which can be used
to validate an existing CRC or generate a new CRC. It is configured by
the :py:class:`Parameters` class, which can handle most forms of CRCs.
Refer to that class's documentation for a description of the parameters.
The CRC value is updated on any clock cycle where ``valid`` is asserted,
with the updated value available on the ``crc`` output on the subsequent
clock cycle. The latency is therefore one clock cycle, and the throughput
is one data word per clock cycle.
The CRC is reset to its initial value whenever ``start`` is asserted.
``start`` and ``valid`` may be asserted on the same clock cycle, in which
case a new CRC computation is started with the current value of ``data``.
With ``data_width=1``, a classic bit-serial CRC is implemented for the
given polynomial in a Galois-type shift register. For larger values of
``data_width``, a similar architecture computes every new bit of the
CRC in parallel.
The ``match_detected`` output may be used to validate data with a trailing
CRC (also known as a codeword). If the most recently processed word(s) form
the valid CRC of all the previous data since ``start`` was asserted, the
CRC register will always take on a fixed value known as the residue. The
``match_detected`` output indicates whether the CRC register currently
contains this residue.
Parameters
----------
parameters : Parameters
CRC parameters.
Attributes
----------
start : Signal(), in
Assert to indicate the start of a CRC computation, re-initialising
the CRC register to the initial value. May be asserted simultaneously
with ``valid`` or by itself.
data : Signal(data_width), in
Data word to add to CRC when ``valid`` is asserted.
valid : Signal(), in
Assert when ``data`` is valid to add the data word to the CRC.
crc : Signal(crc_width), out
Registered CRC output value, updated one clock cycle after ``valid``
becomes asserted.
match_detected : Signal(), out
Asserted if the current CRC value indicates a valid codeword has been
received.
"""
def __init__(self, parameters):
if not isinstance(parameters, Parameters):
raise TypeError("Algorithmn parameters must be of type Parameters, "
"not {!r}"
.format(parameters))
self._crc_width = parameters._crc_width
self._data_width = parameters._data_width
self._polynomial = parameters._polynomial
self._initial_crc = Const(parameters._initial_crc, self._crc_width)
self._reflect_input = parameters._reflect_input
self._reflect_output = parameters._reflect_output
self._xor_output = parameters._xor_output
self._matrix_f, self._matrix_g = parameters._matrices()
self._residue = parameters.residue()
self.start = Signal()
self.data = Signal(self._data_width)
self.valid = Signal()
self.crc = Signal(self._crc_width)
self.match_detected = Signal()
def elaborate(self, platform):
m = Module()
crc_reg = Signal(self._crc_width, reset=self._initial_crc.value)
data_in = Signal(self._data_width)
# Optionally bit-reflect input words.
if self._reflect_input:
m.d.comb += data_in.eq(self.data[::-1])
else:
m.d.comb += data_in.eq(self.data)
# Optionally bit-reflect and then XOR the output.
if self._reflect_output:
m.d.comb += self.crc.eq(crc_reg[::-1] ^ self._xor_output)
else:
m.d.comb += self.crc.eq(crc_reg ^ self._xor_output)
# Compute next CRC state.
source = Mux(self.start, self._initial_crc, crc_reg)
with m.If(self.valid):
for i in range(self._crc_width):
bit = 0
for j in range(self._crc_width):
if self._matrix_f[j][i]:
bit ^= source[j]
for j in range(self._data_width):
if self._matrix_g[j][i]:
bit ^= data_in[j]
m.d.sync += crc_reg[i].eq(bit)
with m.Elif(self.start):
m.d.sync += crc_reg.eq(self._initial_crc)
# Check for residue match, indicating a valid codeword.
if self._reflect_output:
m.d.comb += self.match_detected.eq(crc_reg[::-1] == self._residue)
else:
m.d.comb += self.match_detected.eq(crc_reg == self._residue)
return m
# Imported after Algorithm is defined to prevent circular imports.
from . import catalog

918
amaranth/lib/crc/catalog.py Normal file
View file

@ -0,0 +1,918 @@
"""
This module contains a catalog of predefined CRC algorithms.
All entries are from `reveng`_, accessed on 2023-05-25.
.. _reveng: https://reveng.sourceforge.io/crc-catalogue/all.htm
To use an entry, call it with an optional ``data_width`` which defaults
to 8. For example:
.. code-block::
crc8 = m.submodules.crc8 = crc.catalog.CRC8_AUTOSAR().create()
"""
from . import Algorithm
# Note: The trailing `#:` gives Sphinx an empty documentation string for each
# constant, allowing it to be documented with `automodule` (which otherwise
# ignores undocumented module constants) and also preventing it from using
# the Algorithm docstring if otherwise forced to document the constants.
CRC3_GSM = Algorithm(
crc_width=3,
polynomial=0x3,
initial_crc=0x0,
reflect_input=False,
reflect_output=False,
xor_output=0x7) #:
CRC3_ROHC = Algorithm(
crc_width=3,
polynomial=0x3,
initial_crc=0x7,
reflect_input=True,
reflect_output=True,
xor_output=0x0) #:
CRC4_G_704 = CRC4_ITU = Algorithm(
crc_width=4,
polynomial=0x3,
initial_crc=0x0,
reflect_input=True,
reflect_output=True,
xor_output=0x0) #:
CRC4_INTERLAKEN = Algorithm(
crc_width=4,
polynomial=0x3,
initial_crc=0xf,
reflect_input=False,
reflect_output=False,
xor_output=0xf) #:
CRC5_EPC_C1G2 = CRC5_EPC = Algorithm(
crc_width=5,
polynomial=0x09,
initial_crc=0x09,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC5_G_704 = CRC5_ITU = Algorithm(
crc_width=5,
polynomial=0x15,
initial_crc=0x00,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC5_USB = Algorithm(
crc_width=5,
polynomial=0x05,
initial_crc=0x1f,
reflect_input=True,
reflect_output=True,
xor_output=0x1f) #:
CRC6_CDMA2000_A = Algorithm(
crc_width=6,
polynomial=0x27,
initial_crc=0x3f,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC6_CDMA2000_B = Algorithm(
crc_width=6,
polynomial=0x07,
initial_crc=0x3f,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC6_DARC = Algorithm(
crc_width=6,
polynomial=0x19,
initial_crc=0x00,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC6_G_704 = CRC6_ITU = Algorithm(
crc_width=6,
polynomial=0x03,
initial_crc=0x00,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC6_GSM = Algorithm(
crc_width=6,
polynomial=0x2f,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0x3f) #:
CRC7_MMC = Algorithm(
crc_width=7,
polynomial=0x09,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC7_ROHC = Algorithm(
crc_width=7,
polynomial=0x4f,
initial_crc=0x7f,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC7_UMTS = Algorithm(
crc_width=7,
polynomial=0x45,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_AUTOSAR = Algorithm(
crc_width=8,
polynomial=0x2f,
initial_crc=0xff,
reflect_input=False,
reflect_output=False,
xor_output=0xff) #:
CRC8_BLUETOOTH = Algorithm(
crc_width=8,
polynomial=0xa7,
initial_crc=0x00,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC8_CDMA2000 = Algorithm(
crc_width=8,
polynomial=0x9b,
initial_crc=0xff,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_DARC = Algorithm(
crc_width=8,
polynomial=0x39,
initial_crc=0x00,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC8_DVB_S2 = Algorithm(
crc_width=8,
polynomial=0xd5,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_GSM_A = Algorithm(
crc_width=8,
polynomial=0x1d,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_GSM_B = Algorithm(
crc_width=8,
polynomial=0x49,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0xff) #:
CRC8_HITAG = Algorithm(
crc_width=8,
polynomial=0x1d,
initial_crc=0xff,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_I_432_1 = CRC8_ITU = Algorithm(
crc_width=8,
polynomial=0x07,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0x55) #:
CRC8_I_CODE = Algorithm(
crc_width=8,
polynomial=0x1d,
initial_crc=0xfd,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_LTE = Algorithm(
crc_width=8,
polynomial=0x9b,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_MAXIM_DOW = CRC8_MAXIM = Algorithm(
crc_width=8,
polynomial=0x31,
initial_crc=0x00,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC8_MIFARE_MAD = Algorithm(
crc_width=8,
polynomial=0x1d,
initial_crc=0xc7,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_NRSC_5 = Algorithm(
crc_width=8,
polynomial=0x31,
initial_crc=0xff,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_OPENSAFETY = Algorithm(
crc_width=8,
polynomial=0x2f,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_ROHC = Algorithm(
crc_width=8,
polynomial=0x07,
initial_crc=0xff,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC8_SAE_J1850 = Algorithm(
crc_width=8,
polynomial=0x1d,
initial_crc=0xff,
reflect_input=False,
reflect_output=False,
xor_output=0xff) #:
CRC8_SMBUS = Algorithm(
crc_width=8,
polynomial=0x07,
initial_crc=0x00,
reflect_input=False,
reflect_output=False,
xor_output=0x00) #:
CRC8_TECH_3250 = CRC8_AES = CRC8_ETU = Algorithm(
crc_width=8,
polynomial=0x1d,
initial_crc=0xff,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC8_WCDMA = Algorithm(
crc_width=8,
polynomial=0x9b,
initial_crc=0x00,
reflect_input=True,
reflect_output=True,
xor_output=0x00) #:
CRC10_ATM = CRC10_I_610 = Algorithm(
crc_width=10,
polynomial=0x233,
initial_crc=0x000,
reflect_input=False,
reflect_output=False,
xor_output=0x000) #:
CRC10_CDMA2000 = Algorithm(
crc_width=10,
polynomial=0x3d9,
initial_crc=0x3ff,
reflect_input=False,
reflect_output=False,
xor_output=0x000) #:
CRC10_GSM = Algorithm(
crc_width=10,
polynomial=0x175,
initial_crc=0x000,
reflect_input=False,
reflect_output=False,
xor_output=0x3ff) #:
CRC11_FLEXRAY = Algorithm(
crc_width=11,
polynomial=0x385,
initial_crc=0x01a,
reflect_input=False,
reflect_output=False,
xor_output=0x000) #:
CRC11_UMTS = Algorithm(
crc_width=11,
polynomial=0x307,
initial_crc=0x000,
reflect_input=False,
reflect_output=False,
xor_output=0x000) #:
CRC12_CDMA2000 = Algorithm(
crc_width=12,
polynomial=0xf13,
initial_crc=0xfff,
reflect_input=False,
reflect_output=False,
xor_output=0x000) #:
CRC12_DECT = Algorithm(
crc_width=12,
polynomial=0x80f,
initial_crc=0x000,
reflect_input=False,
reflect_output=False,
xor_output=0x000) #:
CRC12_GSM = Algorithm(
crc_width=12,
polynomial=0xd31,
initial_crc=0x000,
reflect_input=False,
reflect_output=False,
xor_output=0xfff) #:
CRC12_UMTS = CRC12_3GPP = Algorithm(
crc_width=12,
polynomial=0x80f,
initial_crc=0x000,
reflect_input=False,
reflect_output=True,
xor_output=0x000) #:
CRC13_BBC = Algorithm(
crc_width=13,
polynomial=0x1cf5,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC14_DARC = Algorithm(
crc_width=14,
polynomial=0x0805,
initial_crc=0x0000,
reflect_input=True,
reflect_output=True,
xor_output=0x0000) #:
CRC14_GSM = Algorithm(
crc_width=14,
polynomial=0x202d,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x3fff) #:
CRC15_CAN = Algorithm(
crc_width=15,
polynomial=0x4599,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC15_MPT1327 = Algorithm(
crc_width=15,
polynomial=0x6815,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0001) #:
CRC16_ARC = CRC16_IBM = Algorithm(
crc_width=16,
polynomial=0x8005,
initial_crc=0x0000,
reflect_input=True,
reflect_output=True,
xor_output=0x0000) #:
CRC16_CDMA2000 = Algorithm(
crc_width=16,
polynomial=0xc867,
initial_crc=0xffff,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_CMS = Algorithm(
crc_width=16,
polynomial=0x8005,
initial_crc=0xffff,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_DDS_110 = Algorithm(
crc_width=16,
polynomial=0x8005,
initial_crc=0x800d,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_DECT_R = Algorithm(
crc_width=16,
polynomial=0x0589,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0001) #:
CRC16_DECT_X = Algorithm(
crc_width=16,
polynomial=0x0589,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_DNP = Algorithm(
crc_width=16,
polynomial=0x3d65,
initial_crc=0x0000,
reflect_input=True,
reflect_output=True,
xor_output=0xffff) #:
CRC16_EN_13757 = Algorithm(
crc_width=16,
polynomial=0x3d65,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0xffff) #:
CRC16_GENIBUS = CRC16_DARC = CRC16_EPC = CRC16_EPC_C1G2 = CRC16_I_CODE = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0xffff,
reflect_input=False,
reflect_output=False,
xor_output=0xffff) #:
CRC16_GSM = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0xffff) #:
CRC16_IBM_3740 = CRC16_AUTOSAR = CRC16_CCITT_FALSE = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0xffff,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_IBM_SDLC = CRC16_ISO_HDLC = CRC16_ISO_IEC_14443_3_B = CRC16_X25 = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0xffff,
reflect_input=True,
reflect_output=True,
xor_output=0xffff) #:
CRC16_ISO_IEC_14443_3_A = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0xc6c6,
reflect_input=True,
reflect_output=True,
xor_output=0x0000) #:
CRC16_KERMIT = CRC16_BLUETOOTH = CRC16_CCITT = CRC16_CCITT_TRUE = CRC16_V_41_LSB = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0x0000,
reflect_input=True,
reflect_output=True,
xor_output=0x0000) #:
CRC16_LJ1200 = Algorithm(
crc_width=16,
polynomial=0x6f63,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_M17 = Algorithm(
crc_width=16,
polynomial=0x5935,
initial_crc=0xffff,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_MAXIM_DOW = CRC16_MAXIM = Algorithm(
crc_width=16,
polynomial=0x8005,
initial_crc=0x0000,
reflect_input=True,
reflect_output=True,
xor_output=0xffff) #:
CRC16_MCRF4XX = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0xffff,
reflect_input=True,
reflect_output=True,
xor_output=0x0000) #:
CRC16_MODBUS = Algorithm(
crc_width=16,
polynomial=0x8005,
initial_crc=0xffff,
reflect_input=True,
reflect_output=True,
xor_output=0x0000) #:
CRC16_NRSC_5 = Algorithm(
crc_width=16,
polynomial=0x080b,
initial_crc=0xffff,
reflect_input=True,
reflect_output=True,
xor_output=0x0000) #:
CRC16_OPENSAFETY_A = Algorithm(
crc_width=16,
polynomial=0x5935,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_OPENSAFETY_B = Algorithm(
crc_width=16,
polynomial=0x755b,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_PROFIBUS = CRC16_IEC_61158_2 = Algorithm(
crc_width=16,
polynomial=0x1dcf,
initial_crc=0xffff,
reflect_input=False,
reflect_output=False,
xor_output=0xffff) #:
CRC16_RIELLO = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0xb2aa,
reflect_input=True,
reflect_output=True,
xor_output=0x0000) #:
CRC16_SPI_FUJITSU = CRC16_AUG_CCITT = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0x1d0f,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_T10_DIF = Algorithm(
crc_width=16,
polynomial=0x8bb7,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_TELEDISK = Algorithm(
crc_width=16,
polynomial=0xa097,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_TMS37157 = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0x89ec,
reflect_input=True,
reflect_output=True,
xor_output=0x0000) #:
CRC16_UMTS = CRC16_BUYPASS = CRC16_VERIFONE = Algorithm(
crc_width=16,
polynomial=0x8005,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC16_USB = Algorithm(
crc_width=16,
polynomial=0x8005,
initial_crc=0xffff,
reflect_input=True,
reflect_output=True,
xor_output=0xffff) #:
CRC16_XMODEM = CRC16_ACORN = CRC16_LTE = CRC16_V_41_MSB = CRC16_ZMODEM = Algorithm(
crc_width=16,
polynomial=0x1021,
initial_crc=0x0000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000) #:
CRC17_CAN_FD = Algorithm(
crc_width=17,
polynomial=0x1685b,
initial_crc=0x00000,
reflect_input=False,
reflect_output=False,
xor_output=0x00000) #:
CRC21_CAN_FD = Algorithm(
crc_width=21,
polynomial=0x102899,
initial_crc=0x000000,
reflect_input=False,
reflect_output=False,
xor_output=0x000000) #:
CRC24_BLE = Algorithm(
crc_width=24,
polynomial=0x00065b,
initial_crc=0x555555,
reflect_input=True,
reflect_output=True,
xor_output=0x000000) #:
CRC24_FLEXRAY_A = Algorithm(
crc_width=24,
polynomial=0x5d6dcb,
initial_crc=0xfedcba,
reflect_input=False,
reflect_output=False,
xor_output=0x000000) #:
CRC24_FLEXRAY_B = Algorithm(
crc_width=24,
polynomial=0x5d6dcb,
initial_crc=0xabcdef,
reflect_input=False,
reflect_output=False,
xor_output=0x000000) #:
CRC24_INTERLAKEN = Algorithm(
crc_width=24,
polynomial=0x328b63,
initial_crc=0xffffff,
reflect_input=False,
reflect_output=False,
xor_output=0xffffff) #:
CRC24_LTE_A = Algorithm(
crc_width=24,
polynomial=0x864cfb,
initial_crc=0x000000,
reflect_input=False,
reflect_output=False,
xor_output=0x000000) #:
CRC24_LTE_B = Algorithm(
crc_width=24,
polynomial=0x800063,
initial_crc=0x000000,
reflect_input=False,
reflect_output=False,
xor_output=0x000000) #:
CRC24_OPENPGP = Algorithm(
crc_width=24,
polynomial=0x864cfb,
initial_crc=0xb704ce,
reflect_input=False,
reflect_output=False,
xor_output=0x000000) #:
CRC24_OS_9 = Algorithm(
crc_width=24,
polynomial=0x800063,
initial_crc=0xffffff,
reflect_input=False,
reflect_output=False,
xor_output=0xffffff) #:
CRC30_CDMA = Algorithm(
crc_width=30,
polynomial=0x2030b9c7,
initial_crc=0x3fffffff,
reflect_input=False,
reflect_output=False,
xor_output=0x3fffffff) #:
CRC31_PHILIPS = Algorithm(
crc_width=31,
polynomial=0x04c11db7,
initial_crc=0x7fffffff,
reflect_input=False,
reflect_output=False,
xor_output=0x7fffffff) #:
CRC32_AIXM = Algorithm(
crc_width=32,
polynomial=0x814141ab,
initial_crc=0x00000000,
reflect_input=False,
reflect_output=False,
xor_output=0x00000000) #:
CRC32_AUTOSAR = Algorithm(
crc_width=32,
polynomial=0xf4acfb13,
initial_crc=0xffffffff,
reflect_input=True,
reflect_output=True,
xor_output=0xffffffff) #:
CRC32_BASE91_D = Algorithm(
crc_width=32,
polynomial=0xa833982b,
initial_crc=0xffffffff,
reflect_input=True,
reflect_output=True,
xor_output=0xffffffff) #:
CRC32_BZIP2 = CRC32_AAL5 = CRC32_DECT_B = Algorithm(
crc_width=32,
polynomial=0x04c11db7,
initial_crc=0xffffffff,
reflect_input=False,
reflect_output=False,
xor_output=0xffffffff) #:
CRC32_CD_ROM_EDC = Algorithm(
crc_width=32,
polynomial=0x8001801b,
initial_crc=0x00000000,
reflect_input=True,
reflect_output=True,
xor_output=0x00000000) #:
CRC32_CKSUM = CRC32_POSIX = Algorithm(
crc_width=32,
polynomial=0x04c11db7,
initial_crc=0x00000000,
reflect_input=False,
reflect_output=False,
xor_output=0xffffffff) #:
CRC32_ISCSI = CRC32_BASE91_C = CRC32_CASTAGNOLI = CRC32_INTERLAKEN = Algorithm(
crc_width=32,
polynomial=0x1edc6f41,
initial_crc=0xffffffff,
reflect_input=True,
reflect_output=True,
xor_output=0xffffffff) #:
CRC32_ISO_HDLC = CRC32_ADCCP = CRC32_V_42 = CRC32_XZ = CRC32_PKZIP = CRC32_ETHERNET = Algorithm(
crc_width=32,
polynomial=0x04c11db7,
initial_crc=0xffffffff,
reflect_input=True,
reflect_output=True,
xor_output=0xffffffff) #:
CRC32_JAMCRC = Algorithm(
crc_width=32,
polynomial=0x04c11db7,
initial_crc=0xffffffff,
reflect_input=True,
reflect_output=True,
xor_output=0x00000000) #:
CRC32_MEF = Algorithm(
crc_width=32,
polynomial=0x741b8cd7,
initial_crc=0xffffffff,
reflect_input=True,
reflect_output=True,
xor_output=0x00000000) #:
CRC32_MPEG_2 = Algorithm(
crc_width=32,
polynomial=0x04c11db7,
initial_crc=0xffffffff,
reflect_input=False,
reflect_output=False,
xor_output=0x00000000) #:
CRC32_XFER = Algorithm(
crc_width=32,
polynomial=0x000000af,
initial_crc=0x00000000,
reflect_input=False,
reflect_output=False,
xor_output=0x00000000) #:
CRC40_GSM = Algorithm(
crc_width=40,
polynomial=0x0004820009,
initial_crc=0x0000000000,
reflect_input=False,
reflect_output=False,
xor_output=0xffffffffff) #:
CRC64_ECMA_182 = Algorithm(
crc_width=64,
polynomial=0x42f0e1eba9ea3693,
initial_crc=0x0000000000000000,
reflect_input=False,
reflect_output=False,
xor_output=0x0000000000000000) #:
CRC64_GO_ISO = Algorithm(
crc_width=64,
polynomial=0x000000000000001b,
initial_crc=0xffffffffffffffff,
reflect_input=True,
reflect_output=True,
xor_output=0xffffffffffffffff) #:
CRC64_MS = Algorithm(
crc_width=64,
polynomial=0x259c84cba6426349,
initial_crc=0xffffffffffffffff,
reflect_input=True,
reflect_output=True,
xor_output=0x0000000000000000) #:
CRC64_REDIS = Algorithm(
crc_width=64,
polynomial=0xad93d23594c935a9,
initial_crc=0x0000000000000000,
reflect_input=True,
reflect_output=True,
xor_output=0x0000000000000000) #:
CRC64_WE = Algorithm(
crc_width=64,
polynomial=0x42f0e1eba9ea3693,
initial_crc=0xffffffffffffffff,
reflect_input=False,
reflect_output=False,
xor_output=0xffffffffffffffff) #:
CRC64_XZ = CRC64_ECMA = Algorithm(
crc_width=64,
polynomial=0x42f0e1eba9ea3693,
initial_crc=0xffffffffffffffff,
reflect_input=True,
reflect_output=True,
xor_output=0xffffffffffffffff) #:
CRC82_DARC = Algorithm(
crc_width=82,
polynomial=0x0308c0111011401440411,
initial_crc=0x000000000000000000000,
reflect_input=True,
reflect_output=True,
xor_output=0x000000000000000000000) #:

View file

@ -35,6 +35,9 @@ Implemented RFCs
.. _RFC 3: https://amaranth-lang.org/rfcs/0003-enumeration-shapes.html
.. _RFC 4: https://amaranth-lang.org/rfcs/0004-const-castable-exprs.html
.. _RFC 5: https://amaranth-lang.org/rfcs/0005-remove-const-normalize.html
.. _RFC 6: https://amaranth-lang.org/rfcs/0006-stdlib-crc.html
.. _RFC 8: https://amaranth-lang.org/rfcs/0008-aggregate-extensibility.html
.. _RFC 9: https://amaranth-lang.org/rfcs/0009-const-init-shape-castable.html
.. _RFC 8: https://amaranth-lang.org/rfcs/0008-aggregate-extensibility.html
.. _RFC 9: https://amaranth-lang.org/rfcs/0009-const-init-shape-castable.html
.. _RFC 10: https://amaranth-lang.org/rfcs/0010-move-repl-to-value.html
@ -44,6 +47,9 @@ Implemented RFCs
* `RFC 3`_: Enumeration shapes
* `RFC 4`_: Constant-castable expressions
* `RFC 5`_: Remove Const.normalize
* `RFC 6`_: CRC generator
* `RFC 8`_: Aggregate extensibility
* `RFC 9`_: Constant initialization for shape-castable objects
* `RFC 8`_: Aggregate extensibility
* `RFC 9`_: Constant initialization for shape-castable objects
* `RFC 10`_: Move Repl to Value.replicate
@ -79,6 +85,7 @@ Standard library changes
* Added: :mod:`amaranth.lib.enum`. (`RFC 3`_)
* Added: :mod:`amaranth.lib.data`. (`RFC 1`_)
* Added: :mod:`amaranth.lib.crc`. (`RFC 6`_)
Toolchain changes

View file

@ -12,4 +12,5 @@ Standard library
stdlib/data
stdlib/coding
stdlib/cdc
stdlib/crc
stdlib/fifo

11
docs/stdlib/crc.rst Normal file
View file

@ -0,0 +1,11 @@
Cyclic redundancy checks
########################
.. automodule:: amaranth.lib.crc
:special-members: __call__
The following pre-defined CRC algorithms are available:
.. toctree::
crc/catalog

View file

@ -0,0 +1,4 @@
Predefined CRC Algorithms
#########################
.. automodule:: amaranth.lib.crc.catalog

350
tests/test_lib_crc.py Normal file
View file

@ -0,0 +1,350 @@
# amaranth: UnusedElaboratable=no
import unittest
from amaranth.sim import *
from amaranth.lib.crc import Algorithm, Processor, catalog
# Subset of catalogue CRCs used to test the hardware CRC implementation,
# as testing all algorithms takes a long time for little benefit.
# Selected to ensure coverage of CRC width, initial value, reflection, and
# XOR output.
CRCS = [
"CRC3_GSM", "CRC4_INTERLAKEN", "CRC5_USB", "CRC8_AUTOSAR", "CRC8_BLUETOOTH",
"CRC8_I_432_1", "CRC12_UMTS", "CRC15_CAN", "CRC16_ARC", "CRC16_IBM_3740",
"CRC17_CAN_FD", "CRC21_CAN_FD", "CRC24_FLEXRAY_A", "CRC32_AUTOSAR",
"CRC32_BZIP2", "CRC32_ISO_HDLC", "CRC40_GSM"
]
# All catalogue CRCs with their associated check values and residues from
# the reveng catalogue. Used to verify our computation of the check and
# residue values.
CRC_CHECKS = {
"CRC3_GSM": (0x4, 0x2),
"CRC3_ROHC": (0x6, 0x0),
"CRC4_G_704": (0x7, 0x0),
"CRC4_ITU": (0x7, 0x0),
"CRC4_INTERLAKEN": (0xb, 0x2),
"CRC5_EPC_C1G2": (0x00, 0x00),
"CRC5_EPC": (0x00, 0x00),
"CRC5_G_704": (0x07, 0x00),
"CRC5_ITU": (0x07, 0x00),
"CRC5_USB": (0x19, 0x06),
"CRC6_CDMA2000_A": (0x0d, 0x00),
"CRC6_CDMA2000_B": (0x3b, 0x00),
"CRC6_DARC": (0x26, 0x00),
"CRC6_G_704": (0x06, 0x00),
"CRC6_ITU": (0x06, 0x00),
"CRC6_GSM": (0x13, 0x3a),
"CRC7_MMC": (0x75, 0x00),
"CRC7_ROHC": (0x53, 0x00),
"CRC7_UMTS": (0x61, 0x00),
"CRC8_AUTOSAR": (0xdf, 0x42),
"CRC8_BLUETOOTH": (0x26, 0x00),
"CRC8_CDMA2000": (0xda, 0x00),
"CRC8_DARC": (0x15, 0x00),
"CRC8_DVB_S2": (0xbc, 0x00),
"CRC8_GSM_A": (0x37, 0x00),
"CRC8_GSM_B": (0x94, 0x53),
"CRC8_HITAG": (0xb4, 0x00),
"CRC8_I_432_1": (0xa1, 0xac),
"CRC8_ITU": (0xa1, 0xac),
"CRC8_I_CODE": (0x7e, 0x00),
"CRC8_LTE": (0xea, 0x00),
"CRC8_MAXIM_DOW": (0xa1, 0x00),
"CRC8_MAXIM": (0xa1, 0x00),
"CRC8_MIFARE_MAD": (0x99, 0x00),
"CRC8_NRSC_5": (0xf7, 0x00),
"CRC8_OPENSAFETY": (0x3e, 0x00),
"CRC8_ROHC": (0xd0, 0x00),
"CRC8_SAE_J1850": (0x4b, 0xc4),
"CRC8_SMBUS": (0xf4, 0x00),
"CRC8_TECH_3250": (0x97, 0x00),
"CRC8_AES": (0x97, 0x00),
"CRC8_ETU": (0x97, 0x00),
"CRC8_WCDMA": (0x25, 0x00),
"CRC10_ATM": (0x199, 0x000),
"CRC10_I_610": (0x199, 0x000),
"CRC10_CDMA2000": (0x233, 0x000),
"CRC10_GSM": (0x12a, 0x0c6),
"CRC11_FLEXRAY": (0x5a3, 0x000),
"CRC11_UMTS": (0x061, 0x000),
"CRC12_CDMA2000": (0xd4d, 0x000),
"CRC12_DECT": (0xf5b, 0x000),
"CRC12_GSM": (0xb34, 0x178),
"CRC12_UMTS": (0xdaf, 0x000),
"CRC12_3GPP": (0xdaf, 0x000),
"CRC13_BBC": (0x04fa, 0x0000),
"CRC14_DARC": (0x082d, 0x0000),
"CRC14_GSM": (0x30ae, 0x031e),
"CRC15_CAN": (0x059e, 0x0000),
"CRC15_MPT1327": (0x2566, 0x6815),
"CRC16_ARC": (0xbb3d, 0x0000),
"CRC16_IBM": (0xbb3d, 0x0000),
"CRC16_CDMA2000": (0x4c06, 0x0000),
"CRC16_CMS": (0xaee7, 0x0000),
"CRC16_DDS_110": (0x9ecf, 0x0000),
"CRC16_DECT_R": (0x007e, 0x0589),
"CRC16_DECT_X": (0x007f, 0x0000),
"CRC16_DNP": (0xea82, 0x66c5),
"CRC16_EN_13757": (0xc2b7, 0xa366),
"CRC16_GENIBUS": (0xd64e, 0x1d0f),
"CRC16_DARC": (0xd64e, 0x1d0f),
"CRC16_EPC": (0xd64e, 0x1d0f),
"CRC16_EPC_C1G2": (0xd64e, 0x1d0f),
"CRC16_I_CODE": (0xd64e, 0x1d0f),
"CRC16_GSM": (0xce3c, 0x1d0f),
"CRC16_IBM_3740": (0x29b1, 0x0000),
"CRC16_AUTOSAR": (0x29b1, 0x0000),
"CRC16_CCITT_FALSE": (0x29b1, 0x0000),
"CRC16_IBM_SDLC": (0x906e, 0xf0b8),
"CRC16_ISO_HDLC": (0x906e, 0xf0b8),
"CRC16_ISO_IEC_14443_3_B": (0x906e, 0xf0b8),
"CRC16_X25": (0x906e, 0xf0b8),
"CRC16_ISO_IEC_14443_3_A": (0xbf05, 0x0000),
"CRC16_KERMIT": (0x2189, 0x0000),
"CRC16_BLUETOOTH": (0x2189, 0x0000),
"CRC16_CCITT": (0x2189, 0x0000),
"CRC16_CCITT_TRUE": (0x2189, 0x0000),
"CRC16_V_41_LSB": (0x2189, 0x0000),
"CRC16_LJ1200": (0xbdf4, 0x0000),
"CRC16_M17": (0x772b, 0x0000),
"CRC16_MAXIM_DOW": (0x44c2, 0xb001),
"CRC16_MAXIM": (0x44c2, 0xb001),
"CRC16_MCRF4XX": (0x6f91, 0x0000),
"CRC16_MODBUS": (0x4b37, 0x0000),
"CRC16_NRSC_5": (0xa066, 0x0000),
"CRC16_OPENSAFETY_A": (0x5d38, 0x0000),
"CRC16_OPENSAFETY_B": (0x20fe, 0x0000),
"CRC16_PROFIBUS": (0xa819, 0xe394),
"CRC16_IEC_61158_2": (0xa819, 0xe394),
"CRC16_RIELLO": (0x63d0, 0x0000),
"CRC16_SPI_FUJITSU": (0xe5cc, 0x0000),
"CRC16_AUG_CCITT": (0xe5cc, 0x0000),
"CRC16_T10_DIF": (0xd0db, 0x0000),
"CRC16_TELEDISK": (0x0fb3, 0x0000),
"CRC16_TMS37157": (0x26b1, 0x0000),
"CRC16_UMTS": (0xfee8, 0x0000),
"CRC16_BUYPASS": (0xfee8, 0x0000),
"CRC16_VERIFONE": (0xfee8, 0x0000),
"CRC16_USB": (0xb4c8, 0xb001),
"CRC16_XMODEM": (0x31c3, 0x0000),
"CRC16_ACORN": (0x31c3, 0x0000),
"CRC16_LTE": (0x31c3, 0x0000),
"CRC16_V_41_MSB": (0x31c3, 0x0000),
"CRC16_ZMODEM": (0x31c3, 0x0000),
"CRC17_CAN_FD": (0x04f03, 0x00000),
"CRC21_CAN_FD": (0x0ed841, 0x000000),
"CRC24_BLE": (0xc25a56, 0x000000),
"CRC24_FLEXRAY_A": (0x7979bd, 0x000000),
"CRC24_FLEXRAY_B": (0x1f23b8, 0x000000),
"CRC24_INTERLAKEN": (0xb4f3e6, 0x144e63),
"CRC24_LTE_A": (0xcde703, 0x000000),
"CRC24_LTE_B": (0x23ef52, 0x000000),
"CRC24_OPENPGP": (0x21cf02, 0x000000),
"CRC24_OS_9": (0x200fa5, 0x800fe3),
"CRC30_CDMA": (0x04c34abf, 0x34efa55a),
"CRC31_PHILIPS": (0x0ce9e46c, 0x4eaf26f1),
"CRC32_AIXM": (0x3010bf7f, 0x00000000),
"CRC32_AUTOSAR": (0x1697d06a, 0x904cddbf),
"CRC32_BASE91_D": (0x87315576, 0x45270551),
"CRC32_BZIP2": (0xfc891918, 0xc704dd7b),
"CRC32_AAL5": (0xfc891918, 0xc704dd7b),
"CRC32_DECT_B": (0xfc891918, 0xc704dd7b),
"CRC32_CD_ROM_EDC": (0x6ec2edc4, 0x00000000),
"CRC32_CKSUM": (0x765e7680, 0xc704dd7b),
"CRC32_POSIX": (0x765e7680, 0xc704dd7b),
"CRC32_ISCSI": (0xe3069283, 0xb798b438),
"CRC32_BASE91_C": (0xe3069283, 0xb798b438),
"CRC32_CASTAGNOLI": (0xe3069283, 0xb798b438),
"CRC32_INTERLAKEN": (0xe3069283, 0xb798b438),
"CRC32_ISO_HDLC": (0xcbf43926, 0xdebb20e3),
"CRC32_ADCCP": (0xcbf43926, 0xdebb20e3),
"CRC32_V_42": (0xcbf43926, 0xdebb20e3),
"CRC32_XZ": (0xcbf43926, 0xdebb20e3),
"CRC32_PKZIP": (0xcbf43926, 0xdebb20e3),
"CRC32_ETHERNET": (0xcbf43926, 0xdebb20e3),
"CRC32_JAMCRC": (0x340bc6d9, 0x00000000),
"CRC32_MEF": (0xd2c22f51, 0x00000000),
"CRC32_MPEG_2": (0x0376e6e7, 0x00000000),
"CRC32_XFER": (0xbd0be338, 0x00000000),
"CRC40_GSM": (0xd4164fc646, 0xc4ff8071ff),
"CRC64_ECMA_182": (0x6c40df5f0b497347, 0x0000000000000000),
"CRC64_GO_ISO": (0xb90956c775a41001, 0x5300000000000000),
"CRC64_MS": (0x75d4b74f024eceea, 0x0000000000000000),
"CRC64_REDIS": (0xe9c6d914c4b8d9ca, 0x0000000000000000),
"CRC64_WE": (0x62ec59e3f1a4f00a, 0xfcacbebd5931a992),
"CRC64_XZ": (0x995dc9bbdf1939fa, 0x49958c9abd7d353f),
"CRC64_ECMA": (0x995dc9bbdf1939fa, 0x49958c9abd7d353f),
"CRC82_DARC": (0x09ea83f625023801fd612, 0x000000000000000000000),
}
class CRCTestCase(unittest.TestCase):
def test_checks(self):
"""
Verify computed check values and residues match catalogue entries.
"""
for name in dir(catalog):
if name.startswith("CRC"):
crc = getattr(catalog, name)(data_width=8)
check, residue = CRC_CHECKS[name]
assert crc.compute(b"123456789") == check
assert crc.residue() == residue
def test_repr(self):
algorithm = catalog.CRC8_AUTOSAR
assert repr(algorithm) == "Algorithm(crc_width=8, polynomial=0x2f," \
" initial_crc=0xff, reflect_input=False, reflect_output=False," \
" xor_output=0xff)"
params = algorithm(data_width=8)
assert repr(params) == "Parameters(Algorithm(crc_width=8," \
" polynomial=0x2f, initial_crc=0xff, reflect_input=False," \
" reflect_output=False, xor_output=0xff), data_width=8)"
def test_processor_typecheck(self):
with self.assertRaises(TypeError):
proc = Processor(12)
def test_algorithm_range_checks(self):
with self.assertRaises(ValueError):
Algorithm(crc_width=0, polynomial=0x3, initial_crc=0x0,
reflect_input=False, reflect_output=False, xor_output=0x7)
with self.assertRaises(ValueError):
Algorithm(crc_width=3, polynomial=0x8, initial_crc=0x0,
reflect_input=False, reflect_output=False, xor_output=0x7)
with self.assertRaises(ValueError):
Algorithm(crc_width=3, polynomial=0x3, initial_crc=0x8,
reflect_input=False, reflect_output=False, xor_output=0x7)
with self.assertRaises(ValueError):
Algorithm(crc_width=3, polynomial=0x3, initial_crc=0x0,
reflect_input=False, reflect_output=False, xor_output=0x8)
def test_parameter_range_checks(self):
with self.assertRaises(ValueError):
catalog.CRC8_AUTOSAR(data_width=0)
with self.assertRaises(ValueError):
crc = catalog.CRC8_AUTOSAR()
crc.compute([3, 4, 256])
def test_crc_bytes(self):
"""
Verify CRC generation by computing the check value for each CRC
in the catalogue with byte-sized inputs.
"""
for name in CRCS:
crc = getattr(catalog, name)(data_width=8).create()
check = CRC_CHECKS[name][0]
def process():
for word in b"123456789":
yield crc.start.eq(word == b"1")
yield crc.data.eq(word)
yield crc.valid.eq(1)
yield
yield crc.valid.eq(0)
yield
self.assertEqual((yield crc.crc), check)
sim = Simulator(crc)
sim.add_sync_process(process)
sim.add_clock(1e-6)
sim.run()
def test_crc_words(self):
"""
Verify CRC generation for non-byte-sized data by computing a check
value for 1, 2, 4, 16, 32, and 64-bit inputs.
"""
# We can't use the catalogue check value since it requires 8-bit
# inputs, so we'll instead use an input of b"12345678".
data = b"12345678"
# Split data into individual bits. When input is reflected, we have
# to reflect each byte first, then form the input words, then let
# the CRC module reflect those words, to get the same effective input.
bits = "".join(f"{x:08b}" for x in data)
bits_r = "".join(f"{x:08b}"[::-1] for x in data)
for name in CRCS:
for m in (1, 2, 4, 16, 32, 64):
algo = getattr(catalog, name)
crc = algo(data_width=m).create()
# Use a SoftwareCRC with byte inputs to compute new checks.
swcrc = algo(data_width=8)
check = swcrc.compute(data)
# Chunk input bits into m-bit words, reflecting if needed.
if algo.reflect_input:
d = [bits_r[i : i+m][::-1] for i in range(0, len(bits), m)]
else:
d = [bits[i : i+m] for i in range(0, len(bits), m)]
words = [int(x, 2) for x in d]
def process():
yield crc.start.eq(1)
yield
yield crc.start.eq(0)
for word in words:
yield crc.data.eq(word)
yield crc.valid.eq(1)
yield
yield crc.valid.eq(0)
yield
self.assertEqual((yield crc.crc), check)
sim = Simulator(crc)
sim.add_sync_process(process)
sim.add_clock(1e-6)
sim.run()
def test_crc_match(self):
"""Verify match_detected output detects valid codewords."""
for name in CRCS:
algo = getattr(catalog, name)
n = algo.crc_width
m = 8 if n % 8 == 0 else 1
crc = algo(data_width=m).create()
check = CRC_CHECKS[name][0]
if m == 8:
# For CRCs which are multiples of one byte wide, we can easily
# append the correct checksum in bytes.
check_b = check.to_bytes(n // 8, "little" if algo.reflect_output else "big")
words = b"123456789" + check_b
else:
# For other CRC sizes, use single-bit input data.
if algo.reflect_output:
check_b = check.to_bytes((n + 7)//8, "little")
if not algo.reflect_input:
# For cross-endian CRCs, flip the CRC bits separately.
check_b = bytearray(int(f"{x:08b}"[::-1], 2) for x in check_b)
else:
shift = 8 - (n % 8)
check_b = (check << shift).to_bytes((n + 7)//8, "big")
# No catalogue CRCs have ref_in but not ref_out.
codeword = b"123456789" + check_b
words = []
for byte in codeword:
if algo.reflect_input:
words += [int(x) for x in f"{byte:08b}"[::-1]]
else:
words += [int(x) for x in f"{byte:08b}"]
words = words[:72 + n]
def process():
yield crc.start.eq(1)
yield
yield crc.start.eq(0)
for word in words:
yield crc.data.eq(word)
yield crc.valid.eq(1)
yield
yield crc.valid.eq(0)
yield
self.assertTrue((yield crc.match_detected))
sim = Simulator(crc)
sim.add_sync_process(process)
sim.add_clock(1e-6)
sim.run()