Skip to content

Commit 17de834

Browse files
committed
Update CRC RFC with latest design
1 parent 71daa10 commit 17de834

File tree

1 file changed

+129
-47
lines changed

1 file changed

+129
-47
lines changed

text/0006-stdlib-crc.md

Lines changed: 129 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -24,76 +24,161 @@ See the [Wikipedia page on CRCs] for more background and use cases.
2424
# Guide-level explanation
2525
[guide-level-explanation]: #guide-level-explanation
2626

27-
The standard library includes a generator for a cyclic redundancy check (CRC)
28-
module, which can be used to compute and/or validate most common CRCs used by
29-
transmission and storage protocols.
30-
31-
There are many variations on CRCs in use, but they can almost all be described
32-
by the following parameters:
33-
34-
* The bit width of the checksum, commonly (but not always) a power of 2
35-
* The generator polynomial
36-
* The initial value of the CRC register
37-
* Whether to process input words least- or most-significant-bit first
27+
The Amaranth standard library includes a generator for a cyclic redundancy
28+
check (CRC) module, which can be used to compute and/or validate most common
29+
CRCs used by transmission and storage protocols.
30+
31+
There are many different CRC algorithms in use, but they can almost all be
32+
described by the following parameters:
33+
34+
* The bit width of the CRC, commonly (but not always) a power of 2,
35+
* The generator polynomial, represented as an integer where each bit is a 0 or
36+
1 term in a binary-valued polynomial with as many terms as the CRC width,
37+
* The initial value of the CRC register, commonly non-zero to allow detection
38+
of additional 0-valued data words at the start of a message,
39+
* Whether to process input words least- or most-significant-bit first,
40+
allowing the CRC processing order to match the transmission or storage order
41+
of the data bits,
3842
* Whether to flip the final output so that its least-significant-bit becomes
39-
the most-significant bit
40-
* What value, if any, to XOR the output bits with before using the CRC value
43+
the most-significant bit, set for the same reason as reflecting the input
44+
when the CRC value will also be transmitted or stored with a certain bit
45+
order,
46+
* What value, if any, to XOR the output bits with before using the CRC value,
47+
often used to invert all bits of the output.
4148

4249
This set of parameters is commonly known as the Williams or Rocksoft model. For
4350
more information, refer to ["A Painless Guide to CRC Error Detection Algorithms"].
4451

4552
["A Painless Guide to CRC Error Detection Algorithms"]: http://www.ross.net/crc/download/crc_v3.txt
4653

47-
The generator in the Amaranth standard library computes CRCs from an input
48-
data stream of configurable width, and can therefore be use for both validating
49-
existing CRCs and creating new ones. It can handle any CRC parameterised by
50-
the Williams model above.
51-
5254
For a list of parameters to use for standard CRCs, refer to:
5355

5456
* [reveng]'s catalogue, which uses the same parameterisation
55-
* [crcmod]'s predefined list, but removing the leading `1` from the
56-
polynomials, and where `Reversed` is `True`, set both `ref_in` **and**
57+
* [crcmod]'s predefined list, but remove the leading `1` from the
58+
polynomials, XOR the "Init-value" with "XOR-out" to obtain `initial_crc`,
59+
and where `Reversed` is `True`, set both `ref_in` **and**
5760
`ref_out` to `True`.
5861
* [CRC Zoo], which only lists polynomials; use the "explicit +1" form but
5962
remove the leading `1`.
6063

64+
The CRC algorithms described in the [reveng] catalogue are also available
65+
in the Amaranth standard library in the `crc.Catalog` class.
66+
6167
[reveng]: https://reveng.sourceforge.io/crc-catalogue/all.htm
6268
[crcmod]: http://crcmod.sourceforge.net/crcmod.predefined.html
6369
[CRC Zoo]: https://users.ece.cmu.edu/~koopman/crc/
6470

65-
The computed CRC value is updated on any clock cycle where the `valid` input
66-
is asserted, with the updated value available on the `crc` output on the
67-
subsequent clock cycle. The latency is one cycle, and the throughput is
68-
one data word per clock cycle.
71+
In Amaranth, the `crc.Parameters` class holds the parameters that describe a
72+
CRC algorithm:
73+
74+
* `crc_width`: the bit width of the CRC
75+
* `data_width`: the width of the input data words the CRC is computed over;
76+
commonly 8 for processing byte-wise data but can be any length greater than 0
77+
* `polynomial`: the generator polynomial of the CRC, excluding an implicit
78+
leading 1 for the "x^n" term
79+
* `initial_crc`: the initial value of the CRC, loaded when computation of a
80+
new CRC begins
81+
* `reflect_input`: if True, input words are bit-reversed so that the least
82+
significant bit is processed first
83+
* `reflect_output`: if True, the final output is bit-reversed so that its
84+
least-significant bit becomes the most-significant bit of output
85+
* `xor_output`: a value to XOR the output with
86+
87+
The `crc.Parameters` class may be constructed manually, or via a
88+
`crc.Predefined` entry in `crc.Catalog`, which contains many commonly
89+
used CRC algorithms. The data width is not part of the `crc.Predefined`
90+
class, so it must be specified to create a `crc.Parameters`, for example:
91+
92+
```python
93+
from amaranth.lib import crc
94+
params1 = crc.Parameters(8, 8, 0x2f, 0xff, False, False, 0xff)
95+
params2 = crc.Catalog.CRC8_AUTOSAR(data_width=8)
96+
```
97+
98+
If not specified, the data width defaults to 8 bits.
99+
100+
The `crc.Parameters` class can be used to compute CRCs in software using its
101+
`compute()` method, which is passed an iterable of integer data words and
102+
returns the resulting CRC value.
103+
104+
```python
105+
from amaranth.lib import crc
106+
params = crc.Parameters(8, 8, 0x2f, 0xff, False, False, 0xff)
107+
assert params.compute(b"123456789") == 0xdf
108+
```
109+
110+
To generate a hardware CRC module, either call `create()` on `crc.Parameters`
111+
or manually construct a `crc.Processor`:
112+
113+
```python
114+
from amaranth.lib import crc
115+
params = crc.Parameters(8, 8, 0x2f, 0xff, False, False, 0xff)
116+
crc1 = m.submodules.crc1 = crc.Processor(params)
117+
crc2 = m.submodules.crc2 = crc.Catalog.CRC8_AUTOSAR().create()
118+
```
119+
120+
The `crc.Processor` module begins computation of a new CRC whenever its `first`
121+
input is asserted. Input on `data` is processed whenever `valid` is asserted,
122+
which may occur in the same clock cycle as `first`. The updated CRC value is
123+
available on the `crc` output on the clock cycle after `valid`.
69124

70125
With the data width set to 1, a traditional bit-serial CRC is implemented
71126
for the given polynomial in a Galois-type shift register. For larger values
72127
of data width, a similar architecture computes every new bit of the CRC in
73128
parallel.
74129

75-
The `match` output signal may be used to validate data that contains a trailing
76-
CRC. If the most recently processed word(s) form a valid CRC for all the data
77-
processed since reset, the CRC register will always contain a fixed value which
78-
can be computed in advance, and the `match` output indicates whether the CRC
79-
register currently contains this value.
130+
The `match_detected` output signal may be used to validate data that contains a
131+
trailing CRC. If the most recently processed word(s) form a valid CRC for all
132+
the data processed since reset, the CRC register will always contain a fixed
133+
value which can be computed in advance, and the `match_detected` output
134+
indicates whether the CRC register currently contains this value.
80135

81136
# Reference-level explanation
82137
[reference-level-explanation]: #reference-level-explanation
83138

139+
The proposed new interface is:
140+
141+
* A `crc.Parameters` class which holds the parameters for a CRC algorithm,
142+
all of which are passed to the constructor:
143+
* `crc_width`: bit width of the CRC register
144+
* `data_width`: bit width of input data words
145+
* `polynomial`: generator polynomial for CRC algorithm
146+
* `initial_crc`: initial value of CRC at start of computation
147+
* `reflect_input`: if True, input words are bit-reversed
148+
* `reflect_output`: if True, output values are bit-reversed
149+
* `xor_output`: value to XOR the CRC value with at output
150+
* The class has the following methods:
151+
* `compute(data)` performs a software CRC computation on `data`, an
152+
iterable of input data words, and returns the CRC value
153+
* `create()` returns a `crc.Processor` instance preconfigured to use
154+
these parameters
155+
* `residue()` returns the residue value for these parameters, which is
156+
the value left in the CRC register after processing an entire valid
157+
codeword (data followed by its own valid CRC)
158+
* `check()` returns the CRC computation of the ASCII string "123456789",
159+
a defacto standard for validating CRC correctness
160+
* A `crc.Predefined` class which is constructed using all the parameters of
161+
`crc.Parameters` except `data_width`, which is application-specific. This
162+
class implements `__call__(data_width=8)` which is used to create a
163+
`crc.Parameters` with the specified data width
164+
* A `crc.Catalog` class which contains instances of `crc.Predefined` as
165+
class attributes
166+
* A `crc.Processor` class which inherits from `Elaboratable` and implements
167+
the hardware generator
168+
169+
The hardware implementation uses the property that CRCs are linear, and so the
170+
new value of any bit of the CRC register can be found as a linear combination
171+
of the current state and all the input bits. By expressing the CRC computation
172+
as a linear system like this, we can then determine the boolean equation used
173+
to update each bit of the CRC in parallel. A software CRC calculator is
174+
implemented in Python in order to find these logic equations.
175+
84176
The proposed CRC generator is already implemented and available in [PR 681].
85177
The docstrings and comments in it should explain its operation to a suitably
86178
technical level of detail.
87179

88180
[PR 681]: https://github.com/amaranth-lang/amaranth/pull/681
89181

90-
The proposed implementation uses the fact that CRCs are a linear calculation,
91-
and so the new value of any bit of the CRC register can be found as a linear
92-
combination of the current state and all the input bits. By expressing the CRC
93-
computation as a linear system like this, we can then determine the logic
94-
equation used to update each bit of the CRC in parallel. A software CRC
95-
calculator is implemented in Python in order to find these logic equations.
96-
97182
# Drawbacks
98183
[drawbacks]: #drawbacks
99184

@@ -144,21 +229,18 @@ computations.
144229
# Unresolved questions
145230
[unresolved-questions]: #unresolved-questions
146231

147-
- There is a `reset` signal to reset the CRC state to its initial value.
148-
In practice, users could replace this with a `ResetInserter` wrapper.
149-
Since all users will need this signal, it seems easier to include it
150-
directly in the module interface.
151-
152-
- We could include a number of standard CRC parameters directly in the standard
153-
library, to make using them more convenient. I don't know what the preferred
154-
interface for this would be, perhaps just a Python module with some
155-
constants?
232+
- Currently the design uses a `first` signal to signal the beginning of a new
233+
CRC computation (previously called `reset`). Requiring that it may be
234+
asserted simultaneously with the first valid data word of the new CRC adds
235+
muxes to the design that would not be needed if `first` and `valid` cannot
236+
be asserted together. Is this a worthwhile tradeoff, or is there a way to
237+
avoid the extra muxes?
156238

157239
# Future possibilities
158240
[future-possibilities]: #future-possibilities
159241

160-
- The data interface uses a `data` and `valid` signal. Eventually, this should
161-
be replaced with a Stream, once they are finalised.
242+
- The data interface uses `first`, `data`, and `valid` signals.
243+
Eventually, this should be replaced with a Stream, once they are finalised.
162244

163245
- Currently the entire input data word must be valid together; there is no
164246
support for masking some bits off. In particular, such a feature could be

0 commit comments

Comments
 (0)