VHDL · Chapter 4.2 · Concurrent Statements
Concurrent Signal Assignment
This is the atom of concurrent VHDL: a target signal assigned an expression, written directly in an architecture body. It is a continuously-live driver, so whenever any signal in the expression changes, the target is recomputed and updated one delta later. In other words it is combinational logic, exactly equivalent to a process with a sensitivity list containing every signal the expression reads. This lesson shows how a concurrent assignment maps straight to gates, why its implicit full sensitivity makes it safe by construction, why each assignment contributes exactly one driver so that two assignments to one target is a multiple-driver bug, and how a self-referential right-hand side turns into a combinational loop.
Foundation14 min readVHDLConcurrent AssignmentCombinational LogicDriversnumeric_stdRTL
1. Engineering intuition — a gate written as one line
A combinational gate continuously computes its output from its inputs. A concurrent signal
assignment is that gate in text: y <= a and b; means "y is always the AND of a and b." There
is no clock, no storage, no sequence — just a live function from inputs to output. Whenever an
input changes, the output follows (after the model's delta). If you can describe a piece of logic
as "output equals some expression of inputs," a concurrent assignment is its most direct form.
2. Formal explanation — equivalent to a process on all RHS signals
A simple concurrent signal assignment has the form target <= expression; placed in the
architecture body. Its defining property: it is equivalent to a process sensitive to every
signal read in the expression.
-- these two describe the SAME hardware and behaviour:
y <= (a and b) or c; -- concurrent signal assignment
process (a, b, c) -- the equivalent process: sensitive to ALL RHS signals
begin
y <= (a and b) or c;
end process;That implicit, automatically-complete sensitivity is why concurrent assignments cannot suffer the missing-sensitivity bug a hand-written combinational process can (lesson 3.5): the expression's inputs are the sensitivity, by definition. Each concurrent assignment is also exactly one driver for its target (lesson 3.5).
3. Production-quality RTL — gates, muxes, and arithmetic
Concurrent assignments express any combinational function directly:
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
-- logic gate:
y <= a and b;
-- a 2:1 mux (a conditional expression — the long form is lesson 4.3):
m <= d0 when sel = '0' else d1;
-- arithmetic (numeric_std): an adder driving an 8-bit bus
sum <= std_logic_vector(unsigned(x) + unsigned(y));
-- bit manipulation: concatenation and slicing are pure combinational nets
packed <= hi(3 downto 0) & lo(3 downto 0);What hardware does this become? Each line is a combinational block driving its target net: an AND gate, a 2:1 multiplexer, an 8-bit adder, and wiring (concatenation is free routing). None hold state; all recompute continuously.
4. Hardware interpretation — one driver, combinational logic
5. Simulation interpretation — re-evaluate on any input event
In simulation the assignment behaves as its equivalent process: any event on a signal it reads wakes it, it re-evaluates the expression, and it schedules the target one delta later (lesson 3.4). So the target tracks its inputs with a one-delta lag — the signature of combinational logic in the event-driven model.
y <= (a and b) or c — the target tracks its inputs one delta after each change
8 cycles6. Debugging example — the accidental combinational loop
Expected: a concurrent assignment computes a value. Observed: the simulator stalls with a
delta iteration-limit error, or the target is 'X'/oscillating. Root cause: the target appears
in its own expression with no register in between — y <= y xor a; — so it is sensitive to itself
and re-triggers every delta, a zero-delay combinational loop (lesson 3.4). (The sibling failure is
assigning the same target from two concurrent statements, creating two drivers and resolution,
lesson 3.6.) Fix: break the self-reference by registering the feedback in a clocked process, or
remove the unintended loop. Engineering takeaway: a concurrent assignment whose target feeds its
own expression is a comb loop; cross it with a register so each value lasts a clock.
-- WRONG: y depends on itself with no register → delta-cycle oscillation.
y <= y xor a; -- combinational loop, no fixed point
-- RIGHT: register the feedback so it advances once per clock.
process (clk) begin
if rising_edge(clk) then
y <= y xor a; -- a clocked toggle/accumulator, settles each cycle
end if;
end process;7. Common mistakes & what to watch for
- Self-referential expressions.
t <= f(t)with no register is a combinational loop; register the feedback. - Two concurrent assignments to one target. That is two drivers (resolution, lesson 3.6) — for ordinary logic, use one assignment, or a conditional/selected form (lessons 4.3/4.4).
- Expecting it to store a value. A concurrent assignment is combinational; it recomputes continuously and holds nothing across time on its own.
- Thinking it runs "in order" with neighbours. It is concurrent and order-independent (lesson 4.1); chained assignments are not sequential steps.
- Reaching for a process when one line suffices. Simple combinational functions read best as a concurrent assignment; reserve processes for clocked logic or genuinely sequential descriptions.
8. Engineering insight
The concurrent signal assignment is the cleanest expression of "hardware is always computing": one line, one driver, one combinational function, with a sensitivity that is automatically exactly its inputs. That automatic completeness is a real safety advantage over hand-written combinational processes, which can drop an input from the sensitivity list and mis-simulate. Use concurrent assignments as your default for combinational logic — they are concise, loop-and-latch resistant, and map transparently to gates — and step up to conditional/selected forms or processes only when the logic needs the extra structure they provide.
9. Summary
A concurrent signal assignment, target <= expression;, is the basic concurrent statement: a
continuously-live driver equivalent to a process sensitive to every signal it reads. It is
combinational logic, updates the target one delta after any input changes, and is exactly one
driver — so two assignments to one target is the multiple-driver case, and a self-referential
expression is a combinational loop.
10. Learning continuity
You now have the plain assignment. Real combinational logic usually needs to choose between
values, and the next lessons add that structure on top of this base: Conditional Signal
Assignment (when/else) expresses priority-style selection (a mux/priority network), and after
it Selected Signal Assignment (with/select) expresses a clean case-style decode — both still
concurrent, still single-driver, still combinational, just more expressive than a bare expression.