1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
// SPDX-License-Identifier: Apache-2.0
package chisel3.util.random
import chisel3._
import chisel3.util.Valid
/** Pseudo Random Number Generators (PRNG) interface
* @param n the width of the LFSR
* @groupdesc Signals The actual hardware fields of the Bundle
*/
class PRNGIO(val n: Int) extends Bundle {
/** A [[chisel3.util.Valid Valid]] interface that can be used to set the seed (internal PRNG state)
* @group Signals
*/
val seed: Valid[Vec[Bool]] = Input(Valid(Vec(n, Bool())))
/** When asserted, the PRNG will increment by one
* @group Signals
*/
val increment: Bool = Input(Bool())
/** The current state of the PRNG
* @group Signals
*/
val out: Vec[Bool] = Output(Vec(n, Bool()))
}
/** An abstract class representing a Pseudo Random Number Generator (PRNG)
* @param width the width of the PRNG
* @param seed the initial state of the PRNG
* @param step the number of state updates per cycle
* @param updateSeed if true, when loading the seed the state will be updated as if the seed were the current state, if
* false, the state will be set to the seed
*/
abstract class PRNG(val width: Int, val seed: Option[BigInt], step: Int = 1, updateSeed: Boolean = false)
extends Module {
require(width > 0, s"Width must be greater than zero! (Found '$width')")
require(step > 0, s"Step size must be greater than one! (Found '$step')")
val io: PRNGIO = IO(new PRNGIO(width))
/** Allow implementations to override the reset value, e.g., if some bits should be don't-cares. */
protected def resetValue: Vec[Bool] = seed match {
case Some(s) => VecInit(s.U(width.W).asBools)
case None => WireDefault(Vec(width, Bool()), DontCare)
}
/** Internal state of the PRNG. If the user sets a seed, this is initialized to the seed. If the user does not set a
* seed this is left uninitialized. In the latter case, a PRNG subclass *must do something to handle lockup*, e.g.,
* the PRNG state should be manually reset to a safe value. [[LFSR]] handles this by, based on the chosen reduction
* operator, either sets or resets the least significant bit of the state.
*/
private[random] val state: Vec[Bool] = RegInit(resetValue)
/** State update function
* @param s input state
* @return the next state
*/
def delta(s: Seq[Bool]): Seq[Bool]
/** The method that will be used to update the state of this PRNG
* @param s input state
* @return the next state after `step` applications of [[PRNG.delta]]
*/
final def nextState(s: Seq[Bool]): Seq[Bool] = (0 until step).foldLeft(s) { case (s, _) => delta(s) }
when(io.increment) {
state := nextState(state)
}
when(io.seed.fire) {
state := (if (updateSeed) { nextState(io.seed.bits) }
else { io.seed.bits })
}
io.out := state
}
/** Helper utilities related to the construction of Pseudo Random Number Generators (PRNGs) */
object PRNG {
/** Wrap a [[PRNG]] to only return a pseudo-random [[UInt]]
* @param gen a pseudo random number generator
* @param increment when asserted the [[PRNG]] will increment
* @return the output (internal state) of the [[PRNG]]
*/
def apply(gen: => PRNG, increment: Bool = true.B): UInt = {
val prng = Module(gen)
prng.io.seed.valid := false.B
prng.io.seed.bits := DontCare
prng.io.increment := increment
prng.io.out.asUInt
}
}
|