tri-state-buffer

Tri-State Buffers and Bus Arbitration Explained

Denny Denny
6 min read
Multiple drivers feeding a shared horizontal data bus through tri-state output stages, only one active at a time.

TL;DR: A tri-state buffer is a logic gate with three possible outputs — 0, 1, and high-impedance (Hi-Z). The Hi-Z state effectively disconnects the buffer from the wire, allowing multiple drivers to share one bus without short-circuiting each other. Bus arbitration logic ensures only one driver is enabled at a time.

A standard logic gate has two output states: it drives the wire to 0 or to 1. Connect two such gates to the same wire with one driving 0 and the other driving 1, and the wire is short-circuited — current flows directly from supply to ground through both gates and one or both will burn out. The tri-state buffer solves this by adding a third state, Hi-Z, in which the output transistor is fully off and the buffer simply does not drive the wire at all. That third state is what makes shared buses possible. Every CPU data bus, every memory address bus, every PCI or AXI lane is arbitrated tri-state logic underneath.

What is a tri-state buffer?

A tri-state (or three-state) buffer is a non-inverting digital buffer with an enable input. When enabled, the output equals the input. When disabled, the output is electrically disconnected from the wire — the high-impedance state, written Z.

The truth table:

EnableInputOutput
00Z
01Z
100
111

The Hi-Z state is not a logic value. A wire in Hi-Z is floating: its voltage is determined by whatever else is connected to it, not by the disabled buffer. If nothing else drives the wire, its voltage is undefined and may pick up noise. That is why bus designs include weak pull-up or pull-down resistors — to set a defined idle level when every driver is in Hi-Z.

For the broader role of buffers in driving capacitive loads and isolating signals, see The Unsung Hero: Why Digital Buffers Are Essential in Circuit Design.

Inside the cell

A CMOS tri-state buffer consists of two output transistors (a PMOS pulling up to VDD and an NMOS pulling down to GND) and a pair of AND/NOR gates that enforce a third condition: when Enable = 0, both output transistors are off simultaneously. With both transistors off, the output node is connected to neither supply nor ground — it is floating, which is exactly the Hi-Z state.

The schematic in compact form:

EN ──┐
     AND ── PMOS gate (pull-up)
IN ──┤

     NOR ── NMOS gate (pull-down)
EN ──┘

When EN = 1, the buffer behaves like a normal CMOS inverter pair: IN = 0 turns on the PMOS and off the NMOS, driving output high; IN = 1 does the reverse. When EN = 0, both gates force their respective transistor off regardless of IN, leaving the output node floating.

A standalone gate-level model is at /docs/TRI_STATE_BUFFER, and the 8-bit version, used to gate an entire byte onto a bus in one move, is at /docs/TRI_STATE_BUFFER_8BIT.

Why Hi-Z matters: bus sharing

A shared bus is a single set of wires that multiple devices can drive at different times. Without tri-state, sharing is impossible — two devices driving opposite values short the bus. With tri-state, the rule becomes simple:

At any instant, at most one driver on the bus has its enable asserted. All others are in Hi-Z.

This rule is enforced by bus arbitration logic. As long as it is enforced, the bus carries the value driven by whichever device currently owns it. Switching ownership is just a matter of disabling one driver and enabling another — done in nanoseconds.

Contrast this with a wire-OR scheme, where every driver pulls down (open-collector or open-drain) and a single pull-up resistor returns the wire to high when no one pulls. Wire-OR is slower (the pull-up must charge the line through the resistor) and only carries one logic value (a low overrides a high). Tri-state is faster and bidirectional, which is why it dominates internal CPU buses and modern memory interfaces.

Bus contention and the consequences of getting it wrong

If two tri-state drivers are enabled at the same time and drive opposite values, the bus is in contention: a low-impedance path forms from VDD through one driver’s PMOS, across the wire, through the other driver’s NMOS, and to GND. Currents in the tens to hundreds of milliamps flow until one transistor burns out or the metal traces sustain damage. Even brief, accidental contention raises chip temperature, generates supply noise, and can corrupt nearby logic.

Real designs guard against contention with:

  • Mutually exclusive enables. A single decoder produces one-hot enable signals, so only one driver is ever enabled.
  • Dead time between enables. When ownership changes, both drivers are briefly disabled (both in Hi-Z) before the new driver takes over. This is the equivalent of dead-time in a half-bridge power circuit and prevents shoot-through.
  • Bus turnaround cycles in synchronous protocols. DDR memory, for instance, inserts explicit cycles when the bus changes from read to write so the previous driver releases before the new one takes over.

Arbitration: who gets the bus?

Bus arbitration is the policy and circuit that decides which device drives the bus when multiple want to. Three classical schemes:

Centralized arbiter. A single arbiter chip receives request lines from every device, applies a priority policy (fixed priority, round-robin, or fairness-weighted), and asserts a single grant line back to the winner. The winner enables its tri-state buffer; everyone else stays in Hi-Z. Used in PCI’s REQ#/GNT# protocol and in early multiprocessor backplanes.

Daisy chain. A grant signal propagates serially through devices in priority order. Each device either claims the grant (and stops the propagation) or passes it along. Simpler hardware but unfair — the device closest to the arbiter always wins.

Distributed / collision detect. Every device drives an open-collector request line and watches the bus. If a higher-priority requester is detected, lower-priority requesters back off. Ethernet’s CSMA/CD is the classical example, though most modern buses have moved away from this pattern.

Inside a CPU, arbitration is usually a tiny centralized arbiter built into the control unit: the decoder that turns the current opcode into one-hot enable signals for register file outputs, the ALU result, the memory data register, and the immediate operand. Only one of those drivers is enabled per cycle; the rest are in Hi-Z.

The CPU’s data bus, address bus, and control bus

A textbook CPU has three internal buses, all tri-state:

  • Data bus — bidirectional. Registers, the ALU, RAM, and the input/output ports all hang off it. The data bus is the most active tri-state structure in the chip; every cycle, exactly one driver wins. The 8-bit version is modeled in DigiSim at /docs/DATA_BUS_8BIT.
  • Address bus — usually unidirectional from the CPU outward, so tri-state matters only when the CPU shares the address bus with a DMA controller or another bus master.
  • Control bus — a mix of dedicated and shared lines. Read/Write strobes are typically dedicated; status lines often use open-collector wire-OR.

To see the data bus drive in motion, follow an instruction step-by-step in Case Study: Visualizing the Fetch-Decode-Execute Cycle — every register-to-bus and bus-to-register move in that walkthrough is a tri-state enable flipping.

Tri-state vs open-collector vs pull-up

PropertyTri-stateOpen-collector
States0, 1, Z0, Z (with external pull-up gives weak 1)
SpeedFast (active drive both ways)Slower (pull-up RC limits rise)
Multiple driversAllowed if one enabled at a timeAllowed (wire-AND)
PowerLow when idleConstant pull-up current when low
Use casesFast internal buses, memory dataI2C, interrupt request lines, status flags

Pull-up resistors are not an alternative to tri-state — they complement it. A pull-up keeps the bus at a defined level when every tri-state driver is in Hi-Z (idle). Without a pull-up, an idle bus is floating and may pick up noise that is interpreted as glitches by receiving logic.

Build it, then break it

Open /template/single-bit-tri-state-buffer for a one-bit cell with toggleable enable, and /template/buffer-and-tri-state-demo for two drivers contending for one bus. Try enabling both at once and observe what happens to the readout — that is the digital equivalent of the smoke that comes out of real silicon when two drivers fight. Once tri-state is intuitive, the next conceptual layer is what gets driven onto the bus and when, which is the CPU’s program counter and instruction pipeline; that walkthrough begins in Case Study: Visualizing the Fetch-Decode-Execute Cycle and the CPU flags register post that pairs with it.