Kmila
All lessons
Easy Reading ~12 min

Signal vs. variable

This is the single most misunderstood distinction in VHDL. Students read "a variable is like a local variable in C" and sprint off in the wrong direction. Let's ground it.

Two kinds of names in VHDL

VHDL has signals (declared at the architecture level with signal) and variables (declared inside a process with variable). Both can hold values. Both can be assigned. They are not interchangeable.

The golden rule

  • Signal assignments happen at the end of the current simulation tick.
  • Variable assignments happen immediately, inside the process.

That's it. Every "gotcha" with signals vs variables is a restatement of this rule.

The demo

architecture demo of ex is
  signal s : integer := 0;
begin
  process(clk)
  begin
    if rising_edge(clk) then
      s <= 1;           -- request: set s to 1 (not visible yet)
      s <= 2;           -- request: set s to 2 (overrides the first)
      -- what is s here?  Still 0. Neither assignment has taken effect.
    end if;
  end process;
end architecture demo;

After the clock edge completes, s becomes 2 (only the last assignment "wins" when multiple happen in the same tick). Inside the process, after both assignments, s is still 0 — because the new value doesn't propagate until the process is done.

Now with a variable:

architecture demo of ex is
begin
  process(clk)
    variable v : integer := 0;   -- variable lives inside the process
  begin
    if rising_edge(clk) then
      v := 1;           -- takes effect immediately
      v := 2;           -- takes effect immediately
      -- v is 2 here, right now, during this tick
    end if;
  end process;
end architecture demo;

Notice two things:

  • Variables use :=; signals use <=.
  • Variables are local to a single process.

Why this matters for hardware

A signal models a wire or a flip-flop. You can't change the value of a wire twice at the same instant — whatever drives the wire during one tick settles to one value at the end of that tick.

A variable models a temporary computation inside a process. The synthesizer is free to implement it as combinational logic, or optimise it away entirely.

When to use which

  • Signals — the default. Every port, every register, every bus you're going to see on the waveform viewer: signal.
  • Variables — for local loop counters or intermediate arithmetic results inside one process, where you want C-like semantics inside that process.

A common trap

Some students write:

process(clk)
begin
  if rising_edge(clk) then
    a <= b;    -- register 1
    b <= a;    -- register 2 — "swaps" a and b
  end if;
end process;

They expect this to swap a and b. Because signal assignments happen at the end of the tick, it does: on the edge, a gets the old value of b, and b gets the old value of a. This is actually the idiomatic way to swap two registers in one cycle.

But if you switch to variables the semantics change:

process(clk)
  variable a, b : integer;
begin
  if rising_edge(clk) then
    a := b;    -- a now equals b
    b := a;    -- b gets the NEW a — so b keeps its old value, and a changes
  end if;
end process;

No swap. Just both equal to the old b.

What to remember

  • signal: updates at end of tick. <= assignment. Models wires and registers.
  • variable: updates immediately. := assignment. Local to a process. Models intermediate computation.
  • When in doubt — use a signal.
1 / 6
t = 0
Restart Step back Play Step forward
Signals
a 0
b 5
tmp 0
result U
Cold start. tmp is a variable (initial 0); result is a signal (uninitialised).
An unhandled error has occurred. Reload 🗙