Processes
Processes define when a block of logic runs. DFHDL supports processes in two domains:
- RT domain: A clock-bound process used to describe finite-state machines (FSMs). The process runs in lockstep with the domain clock and uses steps, waits, and control flow that the compiler lowers to registers and combinational logic.
- ED domain: Sensitivity-driven processes that run when listed signals change (or all read signals with
process(all)), giving the same level of control asprocessin VHDL oralwaysin Verilog.
Processes are not available in the dataflow (DF) domain. Processes cannot be nested inside another process.
RT domain: clock-bound FSM process
In an RT design, a process is used to describe a finite-state machine that is clock-bound: it advances on the domain clock and is compiled to a state register plus combinational next-state and output logic.
Syntax: process / process.forever
Use the shorthand process: (or process.forever) inside an RTDesign or RTDomain. The block contains either plain combinational logic (assignments, no steps) or step definitions that form an FSM.
Step-based FSM
Define states as def Name: Step = ... and control flow with:
NextStep— advance to the next step in definition order.ThisStep— stay in the current step for another cycle.FirstStep— go to the first step (e.g. reset to initial state).- Step name (e.g.
S1,S2) — jump to that step.
You can optionally name the process (e.g. val my_fsm = process:) so the compiler uses that name for the generated state enum and state register.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
The compiler lowers this to a state enum, a state register, and a match on the state; see Design Domains for the compilation flow.
fallThrough
A step can define def fallThrough = cond where cond is a Boolean/Bit expression. When the condition holds, the step advances to the next step in the same cycle (conditional advancement); when it does not, the FSM stays in the current step.
onEntry and onExit
Inside a step you can define:
def onEntry = ...— run when entering the step (once per transition into this state).def onExit = ...— run when leaving the step (once per transition out of this state).
1 2 3 4 5 6 7 8 | |
Waits and loops
RT processes can use cycle waits (e.g. 1.cy.wait, n.cy.wait) and wait conditions (waitUntil(cond), waitWhile(cond)). The compiler converts these into step blocks and counters so that the behavior remains clock-bound and synthesizable.
Process with no steps
If the process body has no step definitions, it is purely combinational and runs every cycle:
1 2 | |
ED domain: sensitivity-driven processes
In an ED design, processes are sensitivity-driven: they run when an event occurs on one of their sensitivity signals (or on any read signal with process(all)).
ED process forms
Sensitivity list: process(sig1, sig2, ...)
The process runs whenever any of the listed signals change.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
You can list multiple signals, including edge-qualified signals (see Edge sensitivity).
Combinational-style: process(all)
The process is sensitive to all signals that are read in the block. Use this for combinational logic that should react to any input change. The compiler infers the actual sensitivity list from the block body.
1 2 3 4 5 6 7 | |
Forever process: process.forever / process
A process with no sensitivity list runs continuously. It is allowed in RT and ED, but not in DF. The shorthand process: (no arguments) is rewritten by the compiler to process.forever.
- In RT:
process:is the clock-bound FSM process described above (steps, waits, etc.). - In ED: Use it for testbenches or clock generation (e.g. toggling a clock with
wait).
1 2 3 4 5 | |
Edge sensitivity
For sequential (clocked) logic you typically want the process to run only on a specific clock edge. You can either:
- List the clock and check the edge inside the block (VHDL-style):
1 2 3 | |
- Put the edge in the sensitivity list (Verilog-style; compiler may normalize to this):
1 2 | |
Edge options are .rising and .falling on clock (or bit) signals. When reset is used, list both clock and reset and branch on reset then clock edge:
1 2 3 4 5 | |
Assignments inside processes
Blocking assignment :=
Takes effect immediately within the process. Use for combinational logic and for intermediate values that are read later in the same process.
1 2 3 | |
Non-blocking assignment :==
Schedules an update at the end of the current evaluation step. Use for registers and outputs that should not create combinational feedback within the same process.
1 2 3 | |
Rule of thumb
Use := for combinational (e.g. in process(all) or combinational branches). Use :== for register and sequential outputs in clocked processes.
Local variables
You can declare local variables inside a process with VAR; they are visible only within that process and help structure combinational or sequential logic.
1 2 3 4 5 6 7 | |
You can also use plain Scala val declarations (without <> VAR or <> CONST) inside process blocks to name intermediate sub-expressions. These are DFHDL values created inline -- they do not declare new ports or variables but serve as readable names for parts of a computation:
1 2 3 4 5 6 | |
Do not use <> CONST or <> VAR modifiers inside processes for these intermediates -- plain val name = expr is sufficient.
Local VAR in clocked processes become registers
Local VAR declared inside a clocked process(clk): block are synthesized as flip-flop registers in the generated Verilog, not combinational wires. This is because the DFHDL compiler treats any variable written inside a clocked process as sequential storage.
1 2 3 4 5 6 | |
If you need a purely combinational intermediate inside a clocked process, use a plain Scala val (without <> VAR) for simple expressions, or compute the intermediate in a separate process(all): block and read the result in the clocked process.
Relation to design domains
| Domain | Processes |
|---|---|
| DF | No processes. Behavior is expressed with dataflow and .prev; the compiler introduces registers and eventually ED processes. |
| RT | Clock-bound FSM process: process: (or process.forever) with optional step definitions (def Name: Step = ...), onEntry/onExit, and waits. Compiled to a state register and match logic. Plain RT register code (no process) is also lowered to ED processes by the compiler. |
| ED | Sensitivity-driven: process(sig1, sig2, ...), process(all), and process.forever / process. Full control over sensitivity and blocking vs non-blocking assignment. |
See Design Domains for the overall flow from DF → RT → ED and how processes fit into compilation.
Summary
- RT: Use
process:in RTDesign / RTDomain for a clock-bound FSM withdef Name: Step = ...,NextStep/ThisStep/FirstStep, and optionalonEntry/onExitand waits. - ED: Use
process(sig1, sig2, ...)orprocess(all)in EDDesign / EDDomain to define when a block runs;process(all)for combinational logic;process(clk)(and optionallyprocess(clk, rst)) withclk.rising/clk.fallingfor sequential logic. - Use
:=for immediate (blocking) updates and:==for register (non-blocking) updates in ED processes. - Processes cannot be nested and are not available in the DF domain.