VHDL · Chapter 3.8 · Signals & Hardware Behavior
Initial Values, U, and Uninitialized Hardware
Hardware does not power up at zero. A flip-flop comes out of reset-less power-on in an unknown state, and VHDL models that honestly with two special values: U means a signal has never been driven, and X means its value is unknown or the result of a conflict. The dangerous part is propagation, because feeding a U or X into any logic makes the output U or X too, so one uninitialised register can turn a whole design red in simulation. This lesson explains why that is actually a useful feature that catches missing resets, why a signal initialiser is a simulation and FPGA convenience rather than an ASIC guarantee, and how to keep unknown values from contaminating your real logic.
Foundation15 min readVHDLstd_logicMetalogical ValuesResetX PropagationInitialization
1. Engineering intuition — silicon powers up unknown
When a chip powers on, every flip-flop settles to some level, but which one is not designed in —
it depends on process, voltage, and noise. Treating that as "0" is wishful thinking. VHDL refuses
to pretend: an undriven std_logic signal is 'U' (uninitialised), and any genuinely-unknown
value is 'X'. These are metalogical values — they exist to model uncertainty, not to be
built. The engineering consequence is simple and strict: a design must drive every flip-flop to a
known state (via reset) before its value is trusted.
2. Formal explanation — U, X, and propagation
From the nine std_logic values (lesson 2.2), two are central here:
'U'— uninitialised: the signal has had no driver since elaboration. It is the default initial value of anystd_logicwith no explicit:=.'X'— unknown/forcing unknown: a value that cannot be determined, including the result of driver contention (lesson 3.6).
Both propagate: operate on a 'U'/'X' and the result is metalogical too.
-- if any operand is 'U' or 'X', the result is generally 'U'/'X':
y <= a and b; -- a='X' → y='X' (unless b forces it: '0' and 'X' = '0' for AND)
sum <= unsigned(a) + unsigned(b); -- a has any 'U'/'X' bit → that arithmetic result is 'X'
-- A comparison against an unknown is itself unknown: ('X' = '1') is not TRUE.The few exceptions are controlling values ('0' and 'X' = '0', '1' or 'X' = '1') where one
operand forces the result regardless of the unknown — useful, but not something to rely on for
correctness.
3. Production-quality RTL — reset is how you escape U/X
The cure for power-on unknown is an explicit reset that drives state to a defined value:
signal count : unsigned(7 downto 0); -- powers up 'U' in simulation (no init)
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
count <= (others => '0'); -- reset DRIVES count to a known 0 → leaves 'U'
else
count <= count + 1; -- only meaningful once count is known
end if;
end if;
end process;What hardware does this become? An 8-bit register with a reset. Until rst asserts, count
is 'U' in simulation (and genuinely unknown in silicon); after reset it is defined. Without the
reset branch, count would stay 'U' and count + 1 would be 'U' forever — visible immediately
in simulation, which is exactly the point.
4. Hardware interpretation — initializer ≠ reset
A signal initializer (signal s : std_logic := '0';) is not a universal hardware guarantee:
- Simulation always honours it:
sstarts at'0'at time zero. - FPGA synthesis can honour it: the bitstream presets flip-flops, so power-on state is defined.
- ASIC synthesis generally cannot: standard-cell flip-flops have no power-on preset, so an initializer is ignored and the real power-on state is unknown — you must use a reset.
signal valid : std_logic := '0'; -- sim: starts '0'. FPGA: preset '0'. ASIC: NOT guaranteed.
-- Portable, synthesizable rule: define power-on state with an explicit reset, not the ':='.5. Simulation interpretation — U at time zero, then reset
In simulation every signal starts at its initial value — 'U' if none was given — and stays there
until driven. A register therefore shows 'U'/'X' from time zero until reset defines it:
A register starts unknown ('U'/'X') and only a reset brings it to a known value
8 cycles6. Debugging example — the X that will not clear
Expected: outputs become valid shortly after start-up. Observed: some outputs sit at 'X'
indefinitely and spread to everything they touch. Root cause: a register was never reset (no
reset branch, reset never asserted, or reset on the wrong polarity), so it stays 'U'/'X' and
propagates through the datapath. Fix: ensure every state element is driven to a known value by
reset (correct polarity, asserted long enough), and trace the 'X' upstream to the first
undriven register. Engineering takeaway: 'X' propagation is a gift — follow the red back to
its source and you find the exact register that lacks a reset; never mask it by forcing '0'.
-- WRONG: no reset → state_reg is 'U' forever, and every output derived from it is 'X'.
process (clk) begin
if rising_edge(clk) then
state_reg <= next_state; -- never initialised to a known state
end if;
end process;
-- RIGHT: reset to a defined state so the FSM starts known.
process (clk) begin
if rising_edge(clk) then
if rst = '1' then state_reg <= IDLE;
else state_reg <= next_state;
end if;
end if;
end process;7. Common mistakes & what to watch for
- Relying on
:= '0'for ASIC power-on state. ASIC flip-flops have no preset; use a reset. The initializer only helps simulation and FPGA. - Masking
'X'by forcing'0'/'1'. That hides the missing reset and ships a real bug; fix the source instead. - Assuming
('X' = '1')is false-and-safe. A comparison against an unknown is itself not a clean boolean; unknowns leak into control flow. - Forgetting
'U'≠'X'.'U'means never driven (often a connectivity/reset gap);'X'means unknown/conflict. The distinction points to different root causes. - Resetting only some state. A single unreset register can contaminate everything downstream; every state element needs a defined power-on value.
8. Engineering insight
'U' and 'X' make VHDL pessimistic on purpose. By refusing to assume a power-on 0, the
model forces you to define state with reset and then loudly propagates any gap so it cannot hide.
That pessimism is your best ally: an 'X' storm in simulation is a precise pointer to an
uninitialised or contended node, and chasing the red upstream lands you on the exact defect.
Designs that respect this — reset every state element, never mask unknowns — behave the same in
simulation and silicon. Designs that paper over 'X' with forced constants are trading a visible
simulation failure for an invisible bring-up failure.
9. Summary
'U' marks an undriven signal (the default initial value); 'X' marks an unknown or conflicting
value. Both propagate, so one uninitialised register can contaminate a whole design — which is the
point: it makes missing resets visible. A := … initializer is honoured by simulation and FPGA
bitstreams but not by ASIC standard cells, so the portable way to define power-on state is an
explicit reset, not the initializer.
10. Learning continuity
You have now completed the signal model's edge cases — contention, delay, and the unknown. The module's final lesson steps back to the unifying picture: Signals as the Model of Real Wires ties declaration, drivers, delta cycles, resolution, delay, and initialization into one mental model of the physical net, and frames how that model maps onto synthesized hardware — the bridge from "how signals behave" into the next module on concurrency and the processes that drive them.