Skip to content

VHDL · Chapter 7.9 · Sequential Logic Design

Signals vs Variables in Clocked Processes

A subtlety runs through every clocked process: signals and variables can both become registers, but they behave differently in time. A signal assigned under the clock edge is scheduled, so it always reads its old value within the cycle and updates next cycle, making it a register with one cycle of latency. A variable updates immediately, so it is a same-cycle intermediate when written before it is read, and becomes a register only when it is read before it is written and carries state across cycles. Getting this right is the difference between a computation that completes this cycle and one that takes an extra cycle, and it is central to writing correct pipelines. This closing lesson makes the distinction precise in the clocked context and works the accumulator example step by step.

Foundation14 min readVHDLSignalsVariablesRegistersPipelineClocked

1. Engineering intuition — same edge, different timing

Inside a clocked process, both <= (signal) and := (variable) can end up as flip-flops, so it is easy to assume they are interchangeable. They are not. A signal is scheduled: it keeps its old value for the rest of this cycle and updates at the edge, so reading it back always gives last cycle's value — it is inherently a one-cycle-latency register. A variable is immediate: assign it and the next line sees the new value, so it is a within-cycle intermediate — unless you read it before assigning it, in which case it must remember last cycle's value and becomes a register. The practical effect is whether a multi-step computation finishes this cycle or slips to the next.

2. Formal explanation — three cases

three_cases.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
process (clk)
  variable v : unsigned(7 downto 0);
begin
  if rising_edge(clk) then
    -- (1) SIGNAL: scheduled → register, value next cycle.
    s2 <= s1;                    -- s2 gets the OLD s1 → one cycle of latency (a flip-flop)
 
    -- (2) VARIABLE written-before-read: same-cycle intermediate (combinational inside the reg).
    v := unsigned(a) + unsigned(b);
    y <= std_logic_vector(v);    -- uses THIS cycle's v → no extra latency
 
    -- (3) VARIABLE read-before-write: carries state across cycles → a register.
    v := v + 1;                  -- reads last cycle's v → v is itself a register
  end if;
end process;

In a clocked process: a signal <= is always a register (scheduled, next-cycle value); a variable := written before read is a same-cycle intermediate (combinational logic feeding the registered outputs); a variable := read before written is a register (it must hold across cycles). The same syntax, three different timings — decided by scheduled-vs-immediate and access order (lessons 5.3, 5.7).

3. Production RTL — variable vs signal accumulator

accumulator_timing.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
 
-- VARIABLE intermediate: compute and register the result in ONE cycle.
var_acc : process (clk)
  variable t : unsigned(15 downto 0);
begin
  if rising_edge(clk) then
    t := unsigned(acc) + unsigned(x);    -- immediate
    acc <= std_logic_vector(t);          -- result available next cycle, computed from THIS x
  end if;
end process;
 
-- SIGNAL intermediate: an extra register stage → one more cycle of latency.
sig_acc : process (clk)
begin
  if rising_edge(clk) then
    t_s <= unsigned(acc2) + unsigned(x); -- scheduled (registered intermediate)
    acc2 <= std_logic_vector(t_s);       -- uses the OLD t_s → acc2 lags by an extra cycle
  end if;
end process;

What hardware does this become? The variable version is an adder feeding one register (acc) — the intermediate t is combinational. The signal version inserts an extra register (t_s) before acc2, adding a pipeline stage and one more cycle of latency. Same arithmetic; the signal-vs-variable choice decides whether you get one register or two.

4. Hardware interpretation — intermediate wire vs extra register

variable intermediate versus signal extra register in a clocked processvariable(write-before-read)combinational intermediate→ one register (samecycle)adder feeds output FFsignal intermediatescheduled = its ownregister→ extra register (+1cycle)a pipeline stage12
In a clocked process, a variable written before read is a combinational intermediate feeding the output register (one register, same-cycle compute); a variable read before written is itself a register (state across cycles). A signal intermediate is always its own register, so chaining signal assignments adds a pipeline stage and a cycle of latency per stage. Choose a variable for same-cycle intermediate computation and a signal when you deliberately want a registered (pipelined) stage.

5. Simulation interpretation — the one-cycle difference

Variable accumulator (same cycle) vs signal-intermediate accumulator (extra cycle)

8 cycles
Variable accumulator (same cycle) vs signal-intermediate accumulator (extra cycle)variable acc: adds THIS cycle's x → updates with no extra delayvariable acc: adds THI…signal-intermediate acc2: lags one extra cycle (extra register stage)signal-intermediate ac…clkx22334411acc022559910acc200225599t0t1t2t3t4t5t6t7
Both accumulate x, but the variable version (acc) registers the sum in one cycle, while the signal-intermediate version (acc2) inserts an extra register and lags by one more clock. The numbers are the same running total — only the latency differs, entirely due to immediate (variable) versus scheduled (signal) updates.

6. Debugging example — the accidental extra pipeline stage

Expected: a clocked computation completes with the intended latency. Observed: the result is one cycle later than expected (or a value is a cycle stale). Root cause: an intermediate was held in a signal and read back in the same clocked process, so it read the old value and added an unintended register stage; the designer expected variable-like same-cycle behaviour. Fix: use a variable for same-cycle intermediates inside a clocked process, reserving signal intermediates for when you genuinely want a pipeline register. Engineering takeaway: in a clocked process, each signal intermediate you read back is an extra register (a pipeline stage); use variables for same-cycle math and count your signal stages deliberately.

extra_stage_fix.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- BUG: signal intermediate read back → extra register, +1 cycle latency.
-- t_s <= a + b;  y <= t_s + 1;            -- reads OLD t_s
-- FIX: variable intermediate → same-cycle, one register.
process (clk)
  variable t : unsigned(7 downto 0);
begin
  if rising_edge(clk) then
    t := unsigned(a) + unsigned(b);        -- immediate
    y <= std_logic_vector(t + 1);          -- uses NEW t → no extra stage
  end if;
end process;

7. Common mistakes & what to watch for

  • Reading a signal intermediate back in a clocked process. It is the old value and adds a register stage; use a variable for same-cycle intermediates.
  • Assuming a variable is never a register. Read-before-write across cycles makes it a register; that is fine when intended (an accumulator), a surprise when not.
  • Miscounting pipeline depth. Each signal stage is one cycle; chains of signal assignments add latency per stage.
  • Using variables for cross-process state. Variables are process-local; observable state shared across processes must be a signal.
  • Mixing same-cycle and pipelined intent unclearly. Decide deliberately where you want registers (signals) versus combinational intermediates (variables).

8. Engineering insight & continuity

In a clocked process the signal-vs-variable choice is really a latency choice: signals are scheduled and thus registers (one stage each), variables are immediate and thus same-cycle intermediates unless read before written. Use variables for the math you want to finish this cycle and signals (or explicit extra stages) where you want pipeline registers — and always count your registered signal stages. With this, Module 7 is complete: edges, flip-flops, registers, enables, counters, shift registers, the gated-clock hazard, and now register timing. All of this state needs a defined starting point, which is the subject of the next module — Reset Design — beginning with why reset matters and the synchronous-versus- asynchronous choice.