|
| 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