In short: Concurrent statements all run in parallel and re-evaluate whenever any of their inputs change, modeling real hardware that is always "on," while sequential statements run top-to-bottom only inside a process, subprogram, or block and execute in the order written when that region is triggered.
If you have ever stared at a VHDL file and wondered why one signal assignment updates "instantly" while another waits for a clock edge, you have run into the single most important distinction in the language: concurrent versus sequential statements. Getting this wrong is the root cause of a huge share of simulation-vs-synthesis mismatches, inferred latches, and race-condition surprises. This article explains exactly when each kind of statement runs, how VHDL's simulation engine schedules them, and why a process block behaves so differently from a plain signal assignment.
The mental model: hardware is parallel, code is a description
VHDL describes hardware, and hardware does not have a program counter. Every gate, mux, and flip-flop is physically present and reacting at the same time. The concurrent region of a VHDL architecture mirrors this: everything between begin and end architecture that is not inside a process executes conceptually in parallel. Order of appearance is irrelevant—you can write the output assignment before the one that feeds it and the result is identical.
Sequential statements exist because sometimes you want to describe behavior using familiar imperative constructs—if, case, loops, variable updates. VHDL allows this only inside a sequential region: a process, a procedure, or a function. Inside that region, statements execute one after another, like software, every time the region is activated.
Concurrent statements: when do they execute?
A concurrent signal assignment such as y <= a and b; has an implicit sensitivity list: every signal on the right-hand side. In this case, a and b. Whenever either one changes, the simulator re-evaluates the expression and schedules an update to y. The three main concurrent forms are the simple assignment, the conditional assignment (y <= a when sel='1' else b;), and the selected assignment (with sel select ...). A process statement is itself a concurrent statement—that is the key to the whole puzzle.
An architecture with 500 concurrent assignments does not run them "500 times per iteration." Each is a standalone driver that only wakes when its own inputs change—exactly like 500 independent gates.
Sequential statements: only inside a process
Inside a process, statements run in textual order using variables (immediate update, with :=) and signals (deferred update, with <=). The process runs from top to bottom once, then suspends—either at a wait statement or at the end if it has a sensitivity list. This is where the famous signal-vs-variable confusion lives; our companion article on VHDL signals vs variables covers the update timing in depth, and it is required reading alongside this one.
process(clk)
begin
if rising_edge(clk) then
count <= count + 1; -- signal: new value applied at end of delta
end if;
end process;
The delta cycle: why order feels invisible for signals
VHDL's simulation kernel uses a two-phase model of delta cycles. In phase one, all active processes and concurrent statements evaluate and compute the new values they want to drive onto signals. In phase two, those new values are committed simultaneously. Because signal updates are deferred, a signal you assign on line 1 of a process still holds its old value when you read it on line 2—within the same process execution. This is precisely why re-ordering concurrent statements, and re-ordering signal assignments within a process, often produces the same result: the commit happens at the delta boundary, not at the line.
A delta cycle has zero simulation time. A cascade of ten delta cycles all occur at, say, t = 20 ns—they order causality without advancing the clock. Variables, by contrast, update mid-process with no delta delay at all.
Side-by-side: the practical differences
| Aspect | Concurrent statement | Sequential statement |
|---|---|---|
| Where it lives | Architecture body (outside any process) | Inside process / procedure / function |
| Execution order | Order-independent (parallel) | Top-to-bottom as written |
| When it runs | Whenever an RHS signal changes | When the process is triggered (sensitivity list or wait) |
| Constructs allowed | Simple / conditional / selected assignment, process, instances, generate | if, case, for/while loops, variables, wait |
| Typical hardware | Combinational nets, wiring | Registers, FSMs, complex combinational logic |
Why the process behaves differently
A process is a concurrent statement on the outside and a sequential program on the inside. It is "called" by the kernel whenever a signal in its sensitivity list changes (or a wait condition is met), runs its body once, then suspends. This dual nature is deliberate: it lets you use software-style control flow to describe a block of hardware, while still slotting into the parallel fabric of the design. Synthesis tools from Synopsys (Design Compiler, VCS for simulation), Cadence (Genus, Xcelium), and Siemens EDA (ModelSim/Questa) all interpret a clocked process with rising_edge(clk) as flip-flops, and an unclocked process with a complete sensitivity list as combinational logic. Omit a signal from a combinational process's sensitivity list and you get the classic mismatch: simulation shows stale values while synthesis quietly infers a latch.
For deeper FSM coding patterns that build on this, see our guide to the two-process VHDL state machine. And if you are weighing structured training versus self-study, our partners at ChipXpert's VLSI training programs run instructor-led RTL labs that drill these exact concepts.
Ready to master RTL design end-to-end? Explore CourseTron's hands-on FPGA and VHDL courses with simulator labs, waveform debugging, and synthesis projects.
Frequently asked questions
Is a process a concurrent or sequential statement?
Both, depending on the viewpoint. From the architecture's perspective a process is a single concurrent statement that runs in parallel with everything else. Internally, the statements between its begin and end process execute sequentially, top to bottom, each time the process is activated.
Can I use an if statement outside a process?
No. if, case, and loops are sequential statements and are only legal inside a process, procedure, or function. For concurrent logic use the conditional assignment (y <= a when cond else b;) or the selected assignment (with sel select), which are the concurrent equivalents.
Why do my signal assignments inside a process not take effect immediately?
Signal assignments are scheduled, not applied instantly. The new value is committed at the end of the current delta cycle, so a signal read later in the same process pass still returns its old value. If you need an immediate in-process update, use a variable with := instead.
Does the order of concurrent statements matter?
No. Concurrent statements model parallel hardware, so they produce identical results regardless of the order you write them in. Each simply re-evaluates whenever one of its right-hand-side signals changes.