The DSL in Hardcaml lets one describe circuits in a similar style to a Verilog block Always blocks allow hardware multiplexer structures to be described in a somewhat imperative manner using variable assignments if else conditions switches and a simple procedural macro construct This programming model often makes complicated logic such as state machines easier to reason about Always always
There are two key components when using the Always DSL 1 Variable Declarations There are two kinds of variable declarations namely wires and registers A is one whose value is sequentially updated on the edge of the clock signal provided within a type A is one whose value is updated combinationally meaning that the new value is visible at the same clock cycle as when it is assigned The new value will not persist to the next cycle If no such assignment exists the variable will possess the value Both kinds of variable will return the same type namely an Variables may be assigned within an Always block To read the value of a variable access the field which provides a that can be used to form expressions 2 Writing an Always Program Having declared the variables we can now write the actual procedural blocks An program comprises assignments to variables optionally guarded by or conditional statements For example Notice the call to the function surrounding the procedural block Two things going on here Notice that returns a unit This is because variable declarations create unassigned wires and the compile function assigns them to the appropriate multiplexers Notice that the Always DSL is just a list of This means we can play with various meta programming and abstraction tricks within these program blocks Example The following is an example of these pieces put together Differences to Verilog Always blocks The semantics are similar to Verilog with a few caveats Assignments are non blocking Hardcaml does not support blocking assignments In Verilog an Always block can describe either registers or combinational logic but not both With the Always DSL both can be defined in a single block Hardcaml Always blocks do not require a sensitivity list the clocks driving the registers are bound to the register variable themselves How values update The reader might be surprised by the following example The rule is the last assignment executed will set the next value open Base open Hardcaml reg Reg_spec t # * Creates a register variable * Always Variable reg width int > Always Variable t Signal with_register_spec <fun> wire default # * Creates a wire register that is the value of the wire * Always Variable wire default Signal t > unit > Always Variable t <fun> Always Variable t # let foo Always Variable wire default Signal gnd val foo Always Variable t Hardcaml Always Variable value wire width 1 internal <abstr> value Signal t # foo value Signal t wire width 1 Always if_ when switch let something let open Signal in let a input a 1 in let b input b 1 in let c Always Variable wire default gnd in let d Always Variable wire default gnd in let e Always Variable wire default gnd in Always compile * Assignments * c < a ^ b * if_ statements * if_ a b d < vdd d < gnd * when_ is like if_ with an empty else * when_ c value e < 1 compile # Always compile Always t list > unit <fun> compile Always t let clock Signal input clock 1 let clear Signal input clear 1 let a Signal input a 8 let b Signal input b 8 let r_sync Signal Reg_spec create clock clear let create let open Signal in * wire and register variable declarations * let c_wire Always Variable wire default Signal zero 8 in let c_reg Always Variable reg enable Signal vdd r_sync width 8 in * The program block with a call to compile * Always compile if_ a b c_wire < sll a by 1 c_reg < sll a by 1 c_wire < a b c_reg < a b * The c_wire value are assigned appropriately by the Always compiler * output c_wire c_wire value output c_reg c_reg value let counter_thing enable let open Signal in let a Always Variable reg enable vdd r_sync width 8 in Always compile * Here we might expect a to take the values 10 or 11 depending on enable But that is not the case While enable is high a will increment by one every clock cycle When enable is low a is set to 10 * a < 10 when_ enable a < a value 1