Rules ===== Introduction ------------ The semantics behind SpinalHDL are important to learn, so that you understand what is really happening behind the scenes, and how to control it. These semantics are defined by multiple rules: * Signals and registers are operating concurrently with each other (parallel behavioral, as in VHDL and Verilog) * An assignment to a combinational signal is like expressing a rule which is always true * An assignment to a register is like expressing a rule which is applied on each cycle of its clock domain * For each signal, the last valid assignment wins * Each signal and register can be manipulated as an object during hardware elaboration in a `OOP `_ manner Concurrency ----------- The order in which you assign each combinational or registered signal has no behavioral impact. For example, both of the following pieces of code are equivalent: .. code-block:: scala val a, b, c = UInt(8 bits) // Define 3 combinational signals c := a + b // c will be set to 7 b := 2 // b will be set to 2 a := b + 3 // a will be set to 5 This is equivalent to: .. code-block:: scala val a, b, c = UInt(8 bits) // Define 3 combinational signals b := 2 // b will be set to 2 a := b + 3 // a will be set to 5 c := a + b // c will be set to 7 More generally, when you use the ``:=`` assignment operator, it's like specifying a new rule for the left side signal/register. Last valid assignment wins -------------------------- If a combinational signal or register is assigned multiple times, the last valid one wins. As an example: .. code-block:: scala val x, y = Bool() // Define two combinational signals val result = UInt(8 bits) // Define a combinational signal result := 1 when(x) { result := 2 when(y) { result := 3 } } This will produce the following truth table: .. list-table:: :header-rows: 1 * - x - y - => - result * - False - False - - 1 * - False - True - - 1 * - True - False - - 2 * - True - True - - 3 Signal and register interactions with Scala (OOP reference + Functions) ------------------------------------------------------------------------ In SpinalHDL, each hardware element is modeled by a class instance. This means you can manipulate instances by using their references, such as passing them as arguments to a function. As an example, the following code implements a register which is incremented when ``inc`` is True and cleared when ``clear`` is True (``clear`` has priority over ``inc``) : .. code-block:: scala val inc, clear = Bool() // Define two combinational signals/wires val counter = Reg(UInt(8 bits)) // Define an 8 bit register when(inc) { counter := counter + 1 } when(clear) { counter := 0 // If inc and clear are True, then this assignment wins (Last valid assignment rule) } You can implement exactly the same functionality by mixing the previous example with a function that assigns to ``counter``: .. code-block:: scala val inc, clear = Bool() val counter = Reg(UInt(8 bits)) def setCounter(value : UInt): Unit = { counter := value } when(inc) { setCounter(counter + 1) // Set counter with counter + 1 } when(clear) { counter := 0 } You can also integrate the conditional check inside the function: .. code-block:: scala val inc, clear = Bool() val counter = Reg(UInt(8 bits)) def setCounterWhen(cond : Bool,value : UInt): Unit = { when(cond) { counter := value } } setCounterWhen(cond = inc, value = counter + 1) setCounterWhen(cond = clear, value = 0) And also specify what should be assigned to the function: .. code-block:: scala val inc, clear = Bool() val counter = Reg(UInt(8 bits)) def setSomethingWhen(something : UInt, cond : Bool, value : UInt): Unit = { when(cond) { something := value } } setSomethingWhen(something = counter, cond = inc, value = counter + 1) setSomethingWhen(something = counter, cond = clear, value = 0) All of the previous examples are strictly equivalent both in their generated RTL and also in the SpinalHDL compiler's perspective. This is because SpinalHDL only cares about the Scala runtime and the objects instantiated there, it doesn't care about the Scala syntax itself. In other words, from a generated RTL generation / SpinalHDL perspective, when you use functions in Scala which generate hardware, it is like the function was inlined. This is also true case for Scala loops, as they will appear in unrolled form in the generated RTL.