.. role:: raw-html-m2r(raw)
:format: html
Registers
=========
Introduction
------------
Creating registers in SpinalHDL is very different than in VHDL or Verilog.
In Spinal, there are no process/always blocks. Registers are explicitly defined at declaration.
This difference from traditional event-driven HDL has a big impact:
* You can assign registers and wires in the same scope, meaning the code doesn't need to be split between process/always blocks
* It make things much more flexible (see :ref:`Functions `)
Clocks and resets are handled separately, see the :ref:`Clock domain ` chapter for details.
Instantiation
-------------
There are 4 ways to instantiate a register:
.. list-table::
:header-rows: 1
:widths: 50 55
* - Syntax
- Description
* - ``Reg(type : Data)``
- Register of the given type
* - ``RegInit(resetValue : Data)``
- Register loaded with the given ``resetValue`` when a reset occurs
* - ``RegNext(nextValue : Data)``
- Register that samples the given ``nextValue`` each cycle
* - ``RegNextWhen(nextValue : Data, cond : Bool)``
- Register that samples the given ``nextValue`` when a condition occurs
Here is an example declaring some registers:
.. code-block:: scala
// UInt register of 4 bits
val reg1 = Reg(UInt(4 bits))
// Register that samples reg1 each cycle
val reg2 = RegNext(reg1 + 1)
// UInt register of 4 bits initialized with 0 when the reset occurs
val reg3 = RegInit(U"0000")
reg3 := reg2
when(reg2 === 5) {
reg3 := 0xF
}
// Register that samples reg3 when cond is True
val reg4 = RegNextWhen(reg3, cond)
The code above will infer the following logic:
.. image:: /asset/picture/register.svg
:align: center
.. note::
The ``reg3`` example above shows how you can assign the value of a ``RegInit`` register.
It's possible to use the same syntax to assign to the other register types as well (``Reg``, ``RegNext``, ``RegNextWhen``).
Just like in combinational assignments, the rule is 'Last assignment wins', but if no assignment is done, the register keeps its value.
Also, ``RegNext`` is an abstraction which is built over the ``Reg`` syntax. The two following sequences of code are strictly equivalent:
.. code-block:: scala
// Standard way
val something = Bool()
val value = Reg(Bool())
value := something
// Short way
val something = Bool()
val value = RegNext(something)
Reset value
-----------
In addition to the ``RegInit(value : Data)`` syntax which directly creates the register with a reset value,
you can also set the reset value by calling the ``init(value : Data)`` function on the register.
.. code-block:: scala
// UInt register of 4 bits initialized with 0 when the reset occurs
val reg1 = Reg(UInt(4 bits)) init(0)
If you have a register containing a Bundle, you can use the ``init`` function on each element of the Bundle.
.. code-block:: scala
case class ValidRGB() extends Bundle{
val valid = Bool()
val r, g, b = UInt(8 bits)
}
val reg = Reg(ValidRGB())
reg.valid init(False) // Only the valid if that register bundle will have a reset value.
Initialization value for simulation purposes
--------------------------------------------
For registers that don't need a reset value in RTL, but need an initialization value for simulation (to avoid x-propagation), you can ask for a random initialization value by calling the ``randBoot()`` function.
.. code-block:: scala
// UInt register of 4 bits initialized with a random value
val reg1 = Reg(UInt(4 bits)) randBoot()
Register vectors
----------------
As for wires, it is possible to define a vector of registers with ``Vec``.
.. code-block:: scala
val vecReg1 = Vec(Reg(UInt(8 bits)), 4)
val vecReg2 = Vec.fill(8)(Reg(Bool()))
Initialization can be done with the ``init`` method as usual, which can be combined with the ``foreach`` iteration on the registers.
.. code-block:: scala
val vecReg1 = Vec(Reg(UInt(8 bits)) init(0), 4)
val vecReg2 = Vec.fill(8)(Reg(Bool()))
vecReg2.foreach(_ init(False))
In case where the initialization must be deferred since the init value is not known, use a function as in the example below.
.. code-block:: scala
case class ShiftRegister[T <: Data](dataType: HardType[T], depth: Int, initFunc: T => Unit) extends Component {
val io = new Bundle {
val input = in (dataType())
val output = out(dataType())
}
val regs = Vec.fill(depth)(Reg(dataType()))
regs.foreach(initFunc)
for (i <- 1 to (depth-1)) {
regs(i) := regs(i-1)
}
regs(0) := io.input
io.output := regs(depth-1)
}
object SRConsumer {
def initIdleFlow[T <: Data](flow: Flow[T]): Unit = {
flow.valid init(False)
}
}
class SRConsumer() extends Component {
//...
val sr = ShiftRegister(Flow(UInt(8 bits)), 4, SRConsumer.initIdleFlow[UInt])
}
Transforming a wire into a register
-----------------------------------
Sometimes it is useful to transform an existing wire into a register. For
instance, when you are using a Bundle, if you want some outputs of the bundle to
be registers, you might prefer to write ``io.myBundle.PORT := newValue`` without
declaring registers with ``val PORT = Reg(...)`` and connecting their output to
the port with ``io.myBundle.PORT := PORT``. To do this, you just need to use
``.setAsReg()`` on the ports you want to control as registers:
.. code-block:: scala
val io = new Bundle {
val apb = master(Apb3(apb3Config))
}
io.apb.PADDR.setAsReg()
io.apb.PWRITE.setAsReg() init (False)
when(someCondition) {
io.apb.PWRITE := True
}
Notice in the code above that you can also specify an initialization value.
.. note::
The register is created in the clock domain of the wire, and does not depend
on the place where ``.setAsReg()`` is used.
In the example above, the wire is defined in the ``io`` Bundle, in the same
clock domain as the component. Even if ``io.apb.PADDR.setAsReg()`` was
written in a ``ClockingArea`` with a different clock domain, the register
would use the clock domain of the component and not the one of the
``ClockingArea``.