VHDL · Chapter 15.3 · Debugging and Simulation
Debugging X and U Propagation
When a waveform fills with red, it is almost always an X or a U propagating from a single source. A U means a signal was never assigned, from a missing reset, a missing driver, or an unconnected port, so it still holds its start value. An X means unknown, caused by a driver conflict where two drivers fight, an undriven resolved net, or the result of arithmetic on a metavalued input. The defining property is propagation: almost any operation with an X or U operand yields X, so one bad bit poisons everything downstream. That looks catastrophic but is actually a gift, because all that red leads back to one root. The method mirrors waveform debugging: trace the metavalue backward to the first signal that is unknown despite having defined inputs. This lesson covers the causes, how they spread, and the fixes.
Foundation14 min readVHDLDebuggingX PropagationMetavaluesstd_logicSimulation
1. Engineering intuition — red spreads from one wound
A screen full of Xs feels like the whole design broke, but metavalues do not appear everywhere independently —
they spread. One unknown bit feeds an adder, the sum is unknown; that feeds a comparator, its result is
unknown; a metavalue in a condition makes a whole branch unknown. So a field of red is the shadow of a single
upstream wound. That is good news: instead of fixing a thousand Xs, you find the one signal where X/U
originates — the first place a signal is metavalued even though everything feeding it is defined — and fix that.
The skill is resisting the panic of widespread red and going straight to the source.
2. Formal explanation — what X and U mean and how they spread
-- U (uninitialized): a signal that was NEVER ASSIGNED still holds std_logic's default 'U'.
-- causes: no reset / register never written, a SIGNAL with no driver, an UNCONNECTED input port.
--
-- X (unknown): a defined-but-indeterminate value.
-- causes: MULTIPLE DRIVERS in conflict on a resolved net (resolution → 'X'),
-- an undriven resolved net, or an operation whose INPUT was 'U'/'X'.
--
-- PROPAGATION rule: almost any operation with an X/U operand yields X.
-- '1' and 'X' = 'X'; ('0','U') + 1 = 'X'/'U'; if X then ... → both branches unknown.
-- → one metavalue poisons every signal in its downstream cone.
--
-- TRACE METHOD: follow the X/U BACKWARD. The SOURCE is the first signal that is X/U while
-- ALL its inputs are defined ('0'/'1'). Everything past it is just propagation.U is never assigned; X is unknown (driver conflict, undriven resolved net, or computed from U/X).
Both propagate: an operation with a metavalued operand produces a metavalue. So the source is the first
node that is X/U while its inputs are all defined — found by tracing backward (the method from 15.2).
3. Production usage — the three classic sources and their fixes
-- (1) U from NO RESET: a register read before it is ever written.
-- FIX: reset it (or initialize), so it starts defined.
process (clk) begin if rising_edge(clk) then
if rst='1' then cnt <= (others => '0'); -- without this, cnt is 'U' until first write
else cnt <= cnt + 1; end if;
end if; end process;
-- (2) X from MULTIPLE DRIVERS: two processes/assignments drive the same resolved signal → conflict='X'.
-- bus <= a; -- driver 1
-- bus <= b; -- driver 2 → resolution yields 'X' when they disagree
-- FIX: a single driver (mux the sources, or use one process).
bus <= a when sel='1' else b; -- one driver
-- (3) U from UNCONNECTED PORT: an input port left open in a port map floats to 'U'.
-- u : entity work.blk port map (clk => clk, en => open); -- en undriven → 'U'
-- FIX: connect every input port.
u : entity work.blk port map (clk => clk, en => en_sig);What hardware does this become? X and U are simulation values — real hardware has only 0/1 (plus real
high-Z) — so they are diagnostics, not gates. A U flags logic that depends on an uninitialized value (a real
bug: actual silicon would power up to some unpredictable state). An X flags a genuine conflict or
indeterminate result (e.g. bus contention that would be real electrical trouble). Fixing them — reset/initialize,
single driver, connect ports — removes both the simulation red and the underlying hardware hazard it represents.
4. Structural interpretation — one source, a downstream shadow
5. Simulation interpretation — U before reset, X from conflict
U until first assignment; X from a driver conflict
8 cycles6. Debugging example — fixing X downstream instead of at the source
Expected: clearing the metavalues quickly. Observed: patching Xs at the output (masking, forcing) makes
red reappear elsewhere, and the design still misbehaves. Root cause: the fix targeted a downstream
metavalue — a symptom of propagation — instead of the source; the real X/U originates upstream (an
unreset register, a driver conflict, an open port) and keeps poisoning the cone. Fix: trace the metavalue
backward to the first signal that is X/U while its inputs are all defined, and fix that — add a
reset/initialization for a U, reduce a conflicting net to a single driver for an X, or connect the open
port. Engineering takeaway: never mask an X/U downstream — find the one source node (metavalued with
defined inputs) and fix the root; everything downstream clears on its own.
-- BUG: masking the X at the output — source still poisons everything upstream of here.
-- y <= '0' when is_x(sum) else sum; -- hides the symptom, not the cause
-- FIX: eliminate the source (e.g. reset the register feeding 'sum').
if rst='1' then acc <= (others => '0'); else acc <= acc + d; end if; -- 'sum' now defined7. Common mistakes & what to watch for
- Fixing X/U downstream. Trace to the source (first metavalue with defined inputs); masking symptoms just moves the red.
- No reset/initialization. Unwritten registers read
U; reset or initialize anything read before it is first assigned. - Multiple drivers. Conflicting drivers on a resolved net give
X; reduce to a single driver (mux or one process). - Unconnected input ports. Open inputs float to
U; connect every input in the port map. - Ignoring metavalues in conditions. An
X/Uin anif/comparison makes the result unknown — check conditions, not just data, for metavalue sources.
8. Engineering insight & continuity
X (unknown/conflict) and U (uninitialized) propagate from a single source through their downstream cone,
so a sea of red traces back to one root: an unreset register, a driver conflict, or an open port. Find it by
tracing the metavalue backward to the first node that is X/U with defined inputs, then fix the cause —
reset/initialize, single driver, connect the port — and the whole cone clears. These metavalues are value
problems; the next class of simulation bug is about timing within an instant — the delta cycle — where
signals are defined but settle in a confusing order, the focus of the next lesson, Delta-Cycle and Race Bugs.