// SPDX-License-Identifier: Apache-2.0 package chisel3.util.random import chisel3._ /** Galois Linear Feedback Shift Register (LFSR) generator. * * A Galois LFSR can be generated by defining a width and a set of tap points. Optionally, an initial seed and a * reduction operation ([[XOR]], the default, or [[XNOR]]) can be used to augment the generated hardware. The resulting * hardware has support for a run-time programmable seed (via [[PRNGIO.seed]]) and conditional increment (via * [[PRNGIO.increment]]). * * $seedExplanation * * In the example below, a 4-bit LFSR Galois LFSR is constructed. The tap points are defined as four and three * (using LFSR convention of indexing from one). This results in the hardware configuration shown in the diagram. * * {{{ * val lfsr4 = Module(new GaloisLFSR(4, Set(4, 3)) * // +-----------------+---------------------------------------------------------+ * // | | | * // | +-------+ v +-------+ +-------+ +-------+ | * // | | | +---+ | | | | | | | * // +-->| x^4 |-->|XOR|-->| x^3 |---------->| x^2 |---------->| x^1 |---+ * // | | +---+ | | | | | | * // +-------+ +-------+ +-------+ +-------+ * }}} * * If you require a maximal period Galois LFSR of a specific width, you can use [[MaxPeriodGaloisLFSR]]. If you only * require a pseudorandom [[UInt]] you can use the [[GaloisLFSR$ GaloisLFSR companion object]]. * @see [[https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Galois_LFSRs]] * $paramWidth * $paramTaps * $paramSeed * $paramReduction * $paramStep * $paramUpdateSeed */ class GaloisLFSR( width: Int, taps: Set[Int], seed: Option[BigInt] = Some(1), val reduction: LFSRReduce = XOR, step: Int = 1, updateSeed: Boolean = false) extends PRNG(width, seed, step, updateSeed) with LFSR { def delta(s: Seq[Bool]): Seq[Bool] = { val first = s.head (s.tail :+ first).zipWithIndex.map { case (a, i) if taps(i + 1) && (i + 1 != s.size) => reduction(a, first) case (a, _) => a } } } /** A maximal period Galois Linear Feedback Shift Register (LFSR) generator. The maximal period taps are sourced from * [[LFSR.tapsMaxPeriod LFSR.tapsMaxPeriod]]. * {{{ * val lfsr8 = Module(new MaxPeriodGaloisLFSR(8)) * }}} * $paramWidth * $paramSeed * $paramReduction */ class MaxPeriodGaloisLFSR(width: Int, seed: Option[BigInt] = Some(1), reduction: LFSRReduce = XOR) extends GaloisLFSR(width, LFSR.tapsMaxPeriod.getOrElse(width, LFSR.badWidth(width)).head, seed, reduction) /** Utility for generating a pseudorandom [[UInt]] from a [[GaloisLFSR]]. * * For example, to generate a pseudorandom 8-bit [[UInt]] that changes every cycle, you can use: * {{{ * val pseudoRandomNumber = GaloisLFSR.maxPeriod(8) * }}} * * @define paramWidth @param width of pseudorandom output * @define paramTaps @param taps a set of tap points to use when constructing the LFSR * @define paramIncrement @param increment when asserted, a new random value will be generated * @define paramSeed @param seed an initial value for internal LFSR state * @define paramReduction @param reduction the reduction operation (either [[XOR]] or * [[XNOR]]) */ object GaloisLFSR { /** Return a pseudorandom [[UInt]] generated from a [[GaloisLFSR]]. * $paramWidth * $paramTaps * $paramIncrement * $paramSeed * $paramReduction */ def apply( width: Int, taps: Set[Int], increment: Bool = true.B, seed: Option[BigInt] = Some(1), reduction: LFSRReduce = XOR ): UInt = PRNG(new GaloisLFSR(width, taps, seed, reduction), increment) /** Return a pseudorandom [[UInt]] generated using a maximal period [[GaloisLFSR]] * $paramWidth * $paramIncrement * $paramSeed * $paramReduction */ def maxPeriod( width: Int, increment: Bool = true.B, seed: Option[BigInt] = Some(1), reduction: LFSRReduce = XOR ): UInt = PRNG(new MaxPeriodGaloisLFSR(width, seed, reduction), increment) }