Skip to content

VHDL · Chapter 2.2 · Data Types

The Nine Values of std_logic

Module 1 introduced the standard logic type as a nine-valued wire type and listed what each value means. This lesson explains the system behind those nine values, a strength model in which forcing levels beat weak levels, which in turn beat high-impedance. That single idea is what lets the type resolve multiple drivers on one wire, propagate an unknown through logic to flag trouble, and mark anything never driven as uninitialised. A transistor drives a wire hard, a pull resistor drives it gently, and an undriven wire drives it not at all, and the strongest driver present wins. Understanding strengths turns the nine values from a table to memorise into a model you can reason with about resolution and contention.

Foundation15 min readVHDLstd_logicResolutionStrengthX-propagationTypes

1. Intuition — strength, not just value

In Module 1 you saw the nine values — '0' '1' 'Z' 'X' 'U' 'W' 'L' 'H' '-' — and what each means. The deeper idea is that they are organised by strength: how hard a driver is pushing on the wire. A real wire driven by a transistor pushes hard; the same wire pulled by a resistor pushes gently; an undriven wire pushes not at all.

std_logic encodes exactly this. The values split into forcing (strong), weak (resistive), and high-impedance (no drive), plus two non-electrical markers. Strength is what decides what happens when more than one driver touches the same wire — and that is the whole reason the type exists.

2. The strength hierarchy

Group the nine values by how hard they drive, strongest first:

std_logic strength hierarchy: forcing over weak over high-impedanceForcing: 0 1 Xtransistor drive —strongestWeak: L H Wpull resistor — yields toforcingHigh-Z: Zno drive — loses to allU · -uninitialised · don't-careResolutionstrongest present wins12
The nine std_logic values are ordered by drive strength. FORCING values (0, 1, X) come from a transistor actively driving the wire and win over everything weaker. WEAK values (L, H, W) come from pull resistors and only win when nothing forcing is present. HIGH-IMPEDANCE (Z) is no drive at all and loses to everything. Two values are non-electrical: U marks a wire never driven (uninitialised), and '-' is a designer's don't-care. When drivers combine on one wire, the strongest present wins.
  • Forcing — '0', '1', 'X'. A transistor actively pulling the wire. '0' and '1' are clean strong drives; 'X' is a forcing unknown — strongly driven, but the simulator cannot tell to what (typically because two forcing drivers disagree).
  • Weak — 'L', 'H', 'W'. A pull-down/pull-up resistor or a weak conflict. These set the wire only when nothing forcing is present.
  • High-impedance — 'Z'. The driver has let go; the wire floats. 'Z' loses to any real drive — which is exactly how a tri-state bus releases the line for someone else.
  • 'U' and '-'. Not electrical levels: 'U' is the power-on value of any signal never assigned, and '-' is a don't-care you write to let synthesis optimise.

3. How each value arises in hardware

  • '0' / '1' — a gate or driver outputs a clean logic level.
  • 'Z' — a tri-state driver is disabled (bus <= data when oe = '1' else 'Z';), so the wire is undriven and free for another driver.
  • 'X' — two forcing drivers fight (one says '0', one says '1'); the result is a real electrical contention the simulator reports as unknown.
  • 'U' — a signal that was never reset or driven; seeing it means "this has no defined value yet."
  • 'L' / 'H' — a pull-down / pull-up resistor gently holds a line at 0 / 1 when nothing is actively driving it.

4. Strength decides resolution — over time

When two drivers share one wire, std_logic is a resolved type: a resolution function combines the drivers using strength. The waveform makes the rule concrete — watch one net with two drivers a and b as their strengths change:

Resolution by strength on one shared net (a and b both drive 'net')

8 cycles
Resolution by strength on one shared net (a and b both drive 'net')forcing '1' vs 'Z' → net = '1'forcing '1' vs 'Z' → n…only weak 'H' present → net = 'H'only weak 'H' present …forcing '0' beats weak 'H' → net = '0'forcing '0' beats weak…forcing '1' vs forcing '0' → net = 'X' (contention)forcing '1' vs forcing…abnetXXt0t1t2t3t4t5t6t7
Strength is the tie-breaker: a forcing value beats a weak one, a weak value beats Z, and two forcing values that disagree resolve to X. This is the electrical behaviour of a real shared wire, captured by the nine-value type.

This is why std_logic (and not bit) is used for buses: only a value set with strengths can express "driven", "released", "pulled", and "fought over" on the same wire.

5. X-propagation — unknown is contagious (on purpose)

A forcing 'X' is not random; it is unknown, and unknown spreads through logic. If one input to a gate is 'X', the output is 'X' whenever that input could change the result:

x_propagation.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
y <= a and b;
-- a = 'X', b = '1'  ->  y = 'X'   (a could be 0 or 1, so y is unknown)
-- a = 'X', b = '0'  ->  y = '0'   (0 forces the AND low regardless of a)

That propagation is a feature: an 'X' that reaches an output is the simulator tracing an unknown back to its source for you. Masking it (forcing it to '0') throws away the very signal that locates the bug.

6. Debugging example — an X on a result signal

You run a simulation and a data signal shows 'X' partway through. Read it as a strength story:

  • 'X' from cycle zero, never recovering → usually contention: two processes or drivers assign the same signal, fighting forcing values. Search for the second driver.
  • 'X' appearing after a 'U' → an uninitialised value propagated into logic; the real fault is a missing reset upstream, not the gate showing 'X'.
  • 'X' only on some bits of a vector → contention or unknown on exactly those bit positions — narrow the search to the drivers of those bits.

The discipline: do not "fix" the 'X' at the output — trace it upstream to the forcing conflict or the 'U' that produced it.

7. Common mistakes & what to watch for

  • Treating 'X' as a value to clamp. 'X' is a diagnostic; clamping it to '0' hides the contention or missing reset that caused it.
  • Confusing 'Z' and 'X'. 'Z' is no drive (intentional, on a bus); 'X' is conflicting drive (a fault). They look similar in a list and mean opposite things.
  • Ignoring 'U'. 'U' is the loudest possible "no reset / not driven" signal — never background noise.
  • Using weak values ('L'/'H') on purpose inside a chip. Pull strengths model board resistors and pads; on-chip RTL should drive forcing values and use multiplexers, not resistive pulls.

8. Engineering insight

The nine values are not nine arbitrary symbols — they are a small, complete model of what a wire can do, ordered by how hard it is being driven. Once you see them as forcing > weak > Z, plus U and '-', resolution stops being a lookup table and becomes obvious: the strongest driver present wins, and disagreement among equals is 'X'. That model is also a debugging map — every stray 'X' or 'U' points back along the strength chain to its cause.

9. Summary & next step

std_logic's nine values encode a strength model: forcing (0 1 X) beats weak (L H W) beats high-impedance (Z), with U for uninitialised and '-' for don't-care. Strength decides multi-driver resolution, X propagates to flag unknowns, and U/X are diagnostics that point to their own causes. This is the electrical realism std_logic buys over a two-value type.

Which raises the natural question: if std_logic is so capable, why does VHDL also have bit and boolean? The next lesson compares the three — and shows when each is the right type to reach for.