Skip to content

Commit 3608d76

Browse files
committed
Add first draft of stdlib LFSR RFC.
1 parent f4e3de1 commit 3608d76

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed

text/0072-stdlib-lfsr.md

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
- Start Date: 2024-08-04
2+
- RFC PR: [amaranth-lang/rfcs#72](https://github.com/amaranth-lang/rfcs/pull/72)
3+
- Amaranth Issue: [amaranth-lang/amaranth#0000](https://github.com/amaranth-lang/amaranth/issues/0000)
4+
5+
# LFSR generator
6+
7+
## Summary
8+
[summary]: #summary
9+
10+
Add a linear feedback shift register (LFSR) generator to the Amaranth standard
11+
library, with pre-defined generators for standard PRBS sequences.
12+
13+
## Motivation
14+
[motivation]: #motivation
15+
16+
Many digital designs require the generation of pseudo-random bit sequences,
17+
for purposes such as link error detection, data whitening, generating training
18+
signals, or low-overhead generation of random noise signals. One common and
19+
efficient way to generate maximum-length pseudo-random bit sequences is a
20+
linear feedback shift register (LFSR).
21+
22+
Because an LFSR is fully characterised by its generating polynomial (or,
23+
equivalently, by the set of feedback taps), it is straightforward to support
24+
a wide range of use cases in an optimal implementation that can be used by
25+
most Amaranth users. In particular, the well-known PRBS sequences can be
26+
provided as pre-defined LFSRs.
27+
28+
See the [Wikipedia page on LFSRs] for more background and use cases.
29+
30+
[Wikipedia page on LFSRs]: https://en.wikipedia.org/wiki/Linear-feedback_shift_register
31+
32+
## Guide-level explanation
33+
[guide-level-explanation]: #guide-level-explanation
34+
35+
The Amaranth standard library includes a generator for a linear feedback shift
36+
register (LFSR) module, which can be used to compute many common bit sequences.
37+
38+
An LFSR is characterised by two properties, stored in the `lfsr.Algorithm`
39+
class:
40+
41+
* `width`: the bit-width of the feedback register
42+
* `polynominal`: the polynomial that defines which bits are used for feedback,
43+
excluding an implicit leading 1 for the "x^n" term
44+
* `reverse`: whether to generate the time-reversed output sequence, equivalent
45+
to mirroring the polynominal
46+
47+
Common PRBS sequences are generated using LFSRs and are provided as pre-defined
48+
`lfsr.Algorithm` objects in `lfsr.catalog`, including PRBS7, PRBS9, PRBS11,
49+
PRBS13, PRBS15, PRBS20, PRBS23, and PRBS31.
50+
51+
Settings specific to a particular instantiation of an LFSR are contained in the
52+
`lfsr.Parameters` class, which is constructed by calling an `lfsr.Algorithm`
53+
instance and providing the additional settings:
54+
55+
* `output_width`: by default 1, but may be increased to generate multiple
56+
output bits in parallel per clock cycle
57+
* `structure`: either `"galois"` (the default) or `"fibonacci"` to select the
58+
implementation technique; both generate the same sequence but with a
59+
different time offset and sequence of internal states, and one or the
60+
other may be preferable on some architectures
61+
* `init`: by default 1, but may be set to any non-zero initial state matching
62+
the width of the LFSR register.
63+
64+
The `lfsr.Parameters` class can generate output bit sequences in software using
65+
its `compute()` method, which returns an infinite iterator.
66+
67+
To generate a hardware LFSR module, either call `create()` on `lfsr.Parameters`
68+
or manually construct an `lfsr.Processor`:
69+
70+
```python
71+
from amaranth.lib import lfsr
72+
algo = lfsr.Algorithm(width=7, polynomial=0x20)
73+
params = algo(output_width=4)
74+
m.submodules.prbs7 = lfsr.Processor(params)
75+
m.submodules.prbs9 = lfsr.catalog.PRBS9().create()
76+
```
77+
78+
The LFSR module generates a new output on its `out` output every clock cycle.
79+
Use `ResetInserter` and `EnableInserter` if additional sequence control is
80+
required. The internal state is available as the `state` output.
81+
82+
## Reference-level explanation
83+
[reference-level-explanation]: #reference-level-explanation
84+
85+
The proposed new library items are:
86+
87+
* The `lfsr.Algorithm` class to hold the generic parameters, which are all passed
88+
to the constructor by name:
89+
* `width`: positive integer width of LFSR state register
90+
* `polynominal`: integer constant indicating which feedback taps to use,
91+
excluding implicit 1 in the most-significant bit position
92+
* `reverse`: default-false boolean indicating whether the output sequence
93+
should be time-reversed, which can be achieved by mirroring the
94+
polynomial before logic generation
95+
* `lfsr.Algorithm` implements `__call__()` which is used to create `lfsr.Parameters` instances
96+
* The `lfsr.Parameters` class which holds an `lfsr.Algorithm` alongside
97+
implementation-specific settings, provided as named arguments to the
98+
`__call__()` method on `lfsr.Algorithm`:
99+
* `output_width`: positive integer number of output bits to generate per clock cycle, default 1
100+
* `structure`: either `"galois"` or `"fibonacci"` (default), which type of
101+
implementation to generate
102+
* `init`: positive integer initial state, default 1
103+
* `lfsr.Parameters` has the following methods:
104+
* `compute()` to compute output words in software, returning an infinite iterator
105+
* `create()` to generate an `lfsr.Processor`
106+
* `algorithm()` returns the `lfsr.Algorithm` used to create this instance
107+
* The `lfsr.Processor` class which is a `lib.wiring.Component` and implements
108+
the hardware generator, with the following signature:
109+
* `out: Out(parameters.output_width)`
110+
* `state: Out(algorithm.width)`
111+
* An `lfsr.catalog` module which contains instances of `lfsr.Algorithm`
112+
corresponding to commonly used PRBS sequences and the 64b66b and 128b130b
113+
training sequences.
114+
115+
Further technical detail will be added as the draft implementation is developed.
116+
117+
## Drawbacks
118+
[drawbacks]: #drawbacks
119+
120+
* LFSR generation could exist outside of the Amaranth stdlib; implementing it
121+
here may discourage experimentation in alternative interfaces.
122+
* Some hardware or design patterns may still require other implementation
123+
techniques, although all options I'm aware of are covered in the proposed
124+
design or the unresolved questions and future possibilities below.
125+
126+
## Rationale and alternatives
127+
[rationale-and-alternatives]: #rationale-and-alternatives
128+
129+
This design implements LFSRs (and thus PRBS sequence generation) in a standard
130+
way that should be useful to almost all users of LFSRs. There is a fairly small
131+
design space for the actual hardware implementation and so it is unlikely an
132+
alternative design would be significantly more efficient.
133+
134+
Some design alternatives include:
135+
136+
* We could simplify the implementation and API by only supporting Galois or
137+
only Fibonacci implementations, with the downside of potentially less
138+
efficient implementations on some platforms.
139+
I haven't researched how serious an impact this would be.
140+
* We could not support multi-bit generation, but this is a useful feature for
141+
many applications.
142+
* The split between Algorithm and Parameters mirrors that in `lib.crc`,
143+
but with fewer parameters we could consider merging them or making the
144+
Parameters settings be part of the Processor creation.
145+
146+
## Prior art
147+
[prior-art]: #prior-art
148+
149+
LFSR generation is well established, some references are:
150+
151+
* Wikipedia articles:
152+
* https://en.wikipedia.org/wiki/Linear-feedback_shift_register
153+
* https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence
154+
* "Efficient Shift Registers, LFSR Counters, and Long Pseudo- Random Sequence Generators", application note by AMD:
155+
* https://docs.amd.com/v/u/en-US/xapp052
156+
* CCITT O.151 and O.152 define some PRBS sequences
157+
158+
Implementations in Verilog and VHDL are widespread, including:
159+
160+
* https://github.com/alexforencich/verilog-lfsr/blob/master/rtl/lfsr.v
161+
* https://opencores.org/projects/lfsrcountergenerator
162+
163+
The Glasgow project includes an implementation in Amaranth:
164+
165+
* https://github.com/GlasgowEmbedded/glasgow/blob/main/software/glasgow/gateware/lfsr.py
166+
* Uses `degree` instead of `width`, and a list of tap positions instead of
167+
`polynominal`, and `reset` instead of `init`
168+
* Also uses `EnableInserter` and `ResetInserter`
169+
* Does not support generation of multi-bit outputs, but the entire internal
170+
state might be used instead
171+
* Only supports Fibonacci implementations
172+
173+
## Unresolved questions
174+
[unresolved-questions]: #unresolved-questions
175+
176+
Before this RFC is ready to merge, we should resolve:
177+
178+
* It might be useful to add logic for LFSR matching/error detection, which is
179+
a small addition and often useful
180+
* Not sure if we should add explicit ready/valid signals or a reset signal,
181+
or leave those to EnableInserter/ResetInserter.
182+
* Should we support internally-inverted LFSRs using XNOR operations, permitting
183+
a valid all-0s internal state to make initialisation easier e.g. on ASICs?
184+
* Parallel generation of bits might change the internal state representation,
185+
so we may need to only guarantee it matching expected values with single-bit
186+
outputs.
187+
188+
The specific details of the parallel LFSR generation will be resolved during
189+
implementation - there are a couple of possible ways to implement this, either
190+
a similar matrix-based approach to the CRC module or by a virtual extension of
191+
the shift register length.
192+
193+
## Future possibilities
194+
[future-possibilities]: #future-possibilities
195+
196+
Some unresolved questions above may become a future possibility instead.
197+
Beyond those, I don't foresee other future extensions.

0 commit comments

Comments
 (0)