VHDL · Chapter 3.6 · Signals & Hardware Behavior
Multiple Drivers and Contention
A signal with one driver is a plain wire, but put two sources on the same net and you now have two drivers, so something must decide what the net actually carries. For a resolved type like the standard logic type that decision is made by a resolution function. High-impedance yields to any real driver, strong values beat weak ones, and two conflicting strong values produce an X, which is modelled contention. For an unresolved type, a second driver is simply illegal and is caught before simulation even starts. This lesson shows how multiple drivers become tristate buses, where exactly one source is active at a time, and how to read and debug the X that a driver fight produces.
Foundation15 min readVHDLMultiple DriversResolutionTristatestd_logicContention
1. Engineering intuition — two sources, one wire, a referee
Electrically, tying two outputs to one wire is a question, not a contradiction: if one is driving and the other is released (high-impedance), the wire follows the active one; if both push opposite values hard, they fight and the result is indeterminate. VHDL models exactly this. Each source of a signal is a driver (lesson 3.5), and when there is more than one, a resolution function plays referee — combining the drivers into the single value the net carries.
2. Formal explanation — resolved vs unresolved types
Whether multiple drivers are even allowed depends on the type (lesson 2.5):
std_logicis a resolved subtype ofstd_ulogic: it carries a resolution function, so multiple drivers are legal and combined.std_ulogicis unresolved: a second driver on the same signal is an elaboration error, caught before simulation even starts.
The std_logic resolution combines two drivers roughly as: 'Z' (high-Z) yields to any driver;
a strong value ('0'/'1') overrides a weak one ('L'/'H'/'W'); equal values agree; two
opposing strong values ('0' vs '1') resolve to 'X'; and 'U' (uninitialised) or 'X'
contaminates the result.
-- conceptual outcomes of resolving two std_logic drivers:
-- '1' and 'Z' -> '1' (Z yields to the real driver)
-- '0' and 'Z' -> '0'
-- '1' and '1' -> '1' (agreement)
-- '0' and '1' -> 'X' (conflict — both drive hard, opposite values)
-- '1' and 'H' -> '1' (strong beats weak)
-- 'U' and x -> 'U' (uninitialised contaminates)3. Production-quality RTL — the tristate bus
The intended use of multiple drivers is a tristate bus: several masters share one set of
wires, each driving 'Z' (released) except the one currently granted, which drives real data.
-- two drivers on 'bus_o'; each is high-Z unless its enable is asserted.
bus_o <= drv_a when en_a = '1' else (others => 'Z'); -- driver 1
bus_o <= drv_b when en_b = '1' else (others => 'Z'); -- driver 2
-- Correct protocol: en_a and en_b are NEVER both '1' at once, so exactly one
-- driver is active and the other is 'Z'; resolution then just passes the active value.What hardware does this become? Two tristate buffers wired to a shared bus. The arbitration
that guarantees one enable at a time is the contract that keeps the bus from 'X'. (Modern
on-chip designs usually replace tristate buses with muxes; the resolution model still explains
the behaviour.)
4. Hardware interpretation — resolution models electrical reality
5. Simulation interpretation — when resolution runs
Whenever any driver of a resolved signal changes, the simulator re-runs the resolution function over all of that signal's drivers and applies the combined result — one delta later, exactly like any other signal update.
Tristate bus: clean handoff (one driver active) vs overlap (both drive) → 'X'
8 cycles6. Debugging example — the bus stuck at 'X'
Expected: a shared bus carries the granted master's data. Observed: the bus reads 'X'
during a window, and downstream logic samples garbage. Root cause: two drivers were active at
once — an arbitration/enable overlap (en_a and en_b both '1'), or a default branch that
drove '0' instead of 'Z', so a "released" driver was actually still driving. Fix: ensure
mutually exclusive enables and that every inactive driver outputs 'Z'. Engineering takeaway:
an 'X' on a multiply-driven net almost always means more than one driver was active — trace
each driver's enable and confirm only one is on (and the rest are truly high-Z).
-- WRONG: inactive driver outputs '0', not 'Z' → it always contends.
bus_o <= drv_b when en_b = '1' else (others => '0'); -- '0' keeps DRIVING the bus
-- RIGHT: release with 'Z' so resolution lets the active driver win.
bus_o <= drv_b when en_b = '1' else (others => 'Z');
-- And guarantee en_a, en_b are mutually exclusive in the arbiter.7. Common mistakes & what to watch for
- Driving
'0'instead of'Z'when "inactive." A'0'is a strong driver and contends; an idle tristate source must output'Z'. - Allowing overlapping enables. Two active drivers conflict to
'X'; arbitration must keep exactly one active. - Using
std_ulogicfor a multi-driver net. It is unresolved — a second driver is an elaboration error, not contention. - Reading
'X'as a don't-care.'X'means unknown/conflict; treating it as'0'/'1'hides a real driver fight. - Assuming one process can "share" a signal with another by both assigning it. That creates two drivers and resolution, usually not what you intended — use a single driver or a mux.
8. Engineering insight
Multiple drivers are the natural extension of the driver model: the net's value is no longer one
driver's value but the resolution of all of them. Resolution encodes electrical truth — released
outputs yield, strong beats weak, and a genuine fight is 'X' rather than a silently-picked
winner. That honesty is the point: std_logic's 'X' surfaces contention you would otherwise
chase in silicon, and std_ulogic's refusal to resolve catches accidental multi-drive at compile
time. Prefer a single driver (or an explicit mux) by default, and reach for resolved multi-driver
nets only where the hardware truly is a shared bus — then guarantee one active driver at a time.
9. Summary
A net with multiple sources has multiple drivers; a resolution function combines them. std_logic
resolves ('Z' yields, strong beats weak, opposing strong values give 'X'), while std_ulogic
forbids a second driver. Tristate buses are multiple drivers with exactly one active and the rest
'Z'. An 'X' on such a net is the model's signal that two drivers were active at once.
10. Learning continuity
You now understand how value contention is resolved across drivers. The next question is about time: how a driver's scheduled transactions interact when delays are involved — does a brief glitch propagate or get swallowed? The next lesson, Inertial vs Transport Delay, explains the two delay models a driver can use, why a real gate rejects pulses shorter than its delay (inertial) while an ideal wire passes them (transport), and how that shapes the projected waveform you met in the driver model.