Line Drivers: Push-Pull and Open-Drain

Say what a push-pull and an open-drain output each do to a shared wire, read a fight as x and as a voltage, and measure what the pull-up costs in rise time.

Lesson
P5.4
Time
30 min
Runs on
Your browser
Check
4 questions

A new I/O expander joined an interrupt line shared by three other chips, with its output set to push-pull. Whenever another chip asserted the interrupt, the line sat halfway between the rails and the processor never saw it. The expander was driving a 1 into a wire that the others expected to pull low freely.

TransientTwo Push-Pull Outputs DisagreeOutput A drives the line high from the start. Output B drives it low from 1 µs to 2 µs. Each output is a 50 Ω switch; the line has 100 pF.
0.5 V1.0 V1.5 V2.0 V2.5 V3.0 V3.5 Vv(line)00.5 µs1.0 µs1.5 µs2.0 µs2.5 µs3.0 µsTime0.3 VDD0.7 VDD0.5 V1.0 V1.5 V2.0 V2.5 V3.0 V3.5 Vv(line)01 µs2 µs3 µsTime0.3 VDD0.7 VDD
  1. Time 500 nsv(line) = 3.3 V

    A alone: the line is at 3.3 V, a clean 1.

  2. Time 1.5 µsv(line) = 1.65 V

    B switches on. The line settles at 1.65 V, between the two thresholds, and 33 mA flows from the supply through both outputs to ground.

  3. Time 2.5 µsv(line) = 3.3 V

    B lets go, and A pulls the line back to a 1.

Read the plot as a table
Timev(line)
0 s3.3 V
500 ns3.3 V
1 µs3.3 V
1.5 µs1.65 V
2 µs1.65 V
2.5 µs3.3 V
3 µs3.3 V

Simulated on the Analog engine (ngspice 42). The 0.3 and 0.7 VDD lines are the input thresholds UM10204 §3.1.2 uses: between them an input may read either value. A logic simulator draws this as x.

Two Ways to Drive a Wire

Push-Pull and Open-Drain Output StagesArrows show which way each part moves the line. Left: one output with two switches. Right: a resistor and one switch per device.
Push-Pull and Open-Drain Output StagesArrows show which way each part moves the line. Left: one output with two switches. Right: a resistor and one switch per device. component High-side switch on for a 1 component Line one owner component Low-side switch on for a 0 component Pull-up resistor to the supply component Line shared component Device A pulls low or lets go component Device B pulls low or lets go drives 1drives 0raises it slowlypulls 0pulls 0
  1. Push-pull

    One switch to the supply, one to ground, never both on. Both edges are driven hard, so it is fast, but only one output may own the line.

  2. Open drain

    Each device has only the switch to ground. It pulls the line low or lets go, and a resistor brings it high. Any number of devices can share the line.

Read the diagram as text
  • High-side switch: on for a 1
  • Line: one owner
  • Low-side switch: on for a 0
  • Pull-up resistor: to the supply
  • Line: shared
  • Device A: pulls low or lets go
  • Device B: pulls low or lets go
  • High-side switch → Line: drives 1
  • Low-side switch → Line: drives 0
  • Pull-up resistor → Line: raises it slowly
  • Device A → Line: pulls 0
  • Device B → Line: pulls 0

A push-pull output that can also let go, with an enable, is a tri-state output: SPI's MISO is one.

Push-pull buys speed and pays in sharing: two push-pull outputs on one wire must never both be on. Open drain buys sharing and pays in speed: the rise is only as fast as the resistor can charge the wire.

Who Pulls Low: The Wired-AND

Who Pulls the Line LowTwo open-drain outputs and a pull-up. Each output pulls the line low or lets go.
VDD · pull-upSDADevice Aa_low = pull lowDevice Bb_low = pull low

A low lane of 1 pulls the net down. The pull-up raises it only when every device releases.

01234SDAa_lowb_low
  1. Cycle 0

    Nobody pulls. The pull-up holds the line at 1.

  2. Cycle 1

    A pulls, and the line is 0.

  3. Cycle 2

    Both pull. They agree, so nothing fights.

  4. Cycle 3

    A lets go but B still pulls: the line stays 0, and A can read that back.

  5. Cycle 4

    B lets go too. Only now does the line rise.

Measured: the example's first five lines. The line is 1 only when every device has let go, a wired-AND.

a_lowb_lowWith the pull-upWithout it
001z
1000
1100
0100

Measured: the example on Icarus Verilog. With nobody pulling and no pull-up, the line is z: nothing sets its level.

The line reads 1 only when every device lets go, which is an AND of the devices' released states. A device can always pull, never fight, and read the line back to see whether someone else pulls too. I2C builds clock stretching and arbitration on this (P8.5, P8.6).

Two Push-Pull Outputs on One Wire

The same program puts two push-pull outputs on one wire. Driving the same value is harmless: 1. Driving opposite values gives x, the simulator's word for the fight in the opening plot.

CaseFour-state (Icarus Verilog)Two-state (Verilator)
Open drain, nobody pulls, no pull-upz0
Push-pull 1 against push-pull 0x1

Measured: the same program on both simulators. A two-state simulator has no z or x, so it shows a legal-looking value for both faults.

A bench for a shared line should run on a four-state simulator, or check for contention itself. Otherwise a missing pull-up and two drivers fighting both pass.

The Price of Open Drain: The Rise

TransientA Driven Rise and Three Released RisesEach line starts at 0 V and is let go at 0 (or driven high, for the push-pull output). 3.3 V supply.
  • push-pull, 50 Ω, 100 pF
  • 1 kΩ pull-up, 100 pF
  • 4.7 kΩ pull-up, 100 pF
  • 4.7 kΩ pull-up, 400 pF
00.5 V1.0 V1.5 V2.0 V2.5 V3.0 V3.5 VVoltage0.5 µs1.0 µs1.5 µs2.0 µs2.5 µs3.0 µsTime0.3 VDD0.7 VDD01 V2 V3 V4 VVoltage1 µs2 µs3 µsTime0.3 VDD0.7 VDD
Read the plot as a table
Timepush-pull, 50 Ω, 100 pF1 kΩ pull-up, 100 pF4.7 kΩ pull-up, 100 pF4.7 kΩ pull-up, 400 pF
500 ns3.3 V3.278 V2.161 V770.6 mV
1 µs3.3 V3.3 V2.907 V1.361 V
1.5 µs3.3 V3.3 V3.164 V1.814 V
2 µs3.3 V3.3 V3.253 V2.161 V
2.5 µs3.3 V3.3 V3.284 V2.427 V
3 µs3.3 V3.3 V3.294 V2.631 V

Simulated on the Analog engine (ngspice 42) from the library's pull-up netlist (UM10204 §7.1). A rise time is read between the 0.3 VDD and 0.7 VDD crossings.

OutputLoadR × CRise, 0.3 to 0.7 VDDMeets the tr limit of
push-pull, 50 Ω100 pF5.0 ns4.2 nsSm, Fm, Fm+
1 kΩ pull-up100 pF100 ns85 nsSm, Fm, Fm+
4.7 kΩ pull-up100 pF470 ns398 nsSm
4.7 kΩ pull-up400 pF1880 ns1593 nsnone

Measured rise times; the limits are UM10204 Table 11, tr maximum: Sm 1000 ns, Fm 300 ns, Fm+ 120 ns. Table 11 also caps the load at 400 pF for Sm and Fm.

Every released rise is the same curve scaled by R × C. The 1 kΩ, 100 pF run measures 0.847 × RC, the constant UM10204 §7.1 derives from the 0.3 and 0.7 VDD thresholds. A smaller resistor is faster but makes every pull-down sink more current. Lesson P8.7 sizes it.

Where You Will Meet It

LinkOutput styleLessons
UART TXPush-pull, one owner per wireP6.1
SPIPush-pull; MISO lets go when its select is highP7.4
QSPI data pinsPush-pull, turned around between the two sidesP7.7
I2C SDA and SCLOpen drain with pull-ups, the wired-AND (UM10204 §3.1.1)P8.1, P8.7, P8.13
I2C Ultra Fast-modePush-pull, one direction only (§3.2.1)P8.12
I3COpen drain while devices may compete, push-pull for dataP9.3

UM10204 §3.1.1 also lets a single controller drive SCL push-pull when no device on the bus stretches the clock.

Try It

The sda net is declared tri1: a pull-up the simulator models. The bare net is a plain wire with the same two open-drain outputs and nothing pulling it up. fight has two push-pull outputs.

Change tri1 sda; to wire sda;: the released rows of sda read z too. Then try tri0 sda;, a pull-down, and see which rows change.

RUNIcarus Verilog · in your browser
Edit the code, then run it
Expected: a_low 0, b_low 0: sda 1, without the pull-up z a_low 1, b_low 0: sda 0, without the pull-up 0 a_low 1, b_low 1: sda 0, without the pull-up 0 a_low 0, b_low 1: sda 0, without the pull-up 0 a_low 0, b_low 0: sda 1, without the pull-up z push-pull 1 and 1: 1 push-pull 1 against 0: x
Read included file: drivers.sv
`timescale 1ns/1ns
module od(input wire low, inout wire line); assign line = low ? 1'b0 : 1'bz; endmodule
module pp(input wire en, input wire v, inout wire line); assign line = en ? v : 1'bz; endmodule

Check Yourself

Predict the outputSDA has a pull-up (tri1). The controller has an open-drain output; a target has a push-pull output driving 1. The program reads SDA with only the target driving, then again after the controller pulls low, and prints both. What does it print?
`timescale 1ns/1ns
module tb;
  tri1 sda;                         // the pull-up
  reg ctrl_low = 0, tgt_en = 0;
  od ctrl(ctrl_low, sda);           // the controller: open drain
  pp tgt(tgt_en, 1'b1, sda);        // a target whose output is push-pull, driving 1
  reg first;
  initial begin
    tgt_en = 1;      #1 first = sda;   // the target drives 1; nobody pulls
    ctrl_low = 1;    #1 $display("%b %b", first, sda);
    $finish;
  end
endmodule
Read included file: drivers.sv
`timescale 1ns/1ns
module od(input wire low, inout wire line); assign line = low ? 1'b0 : 1'bz; endmodule
module pp(input wire en, input wire v, inout wire line); assign line = en ? v : 1'bz; endmodule
Spot the bugEach option is one device's output stage on a pulled-up shared line (low = 1 pulls the line to 0). Two copies of the device sit on the line; the bench tries all four combinations and checks the line is the AND of the released states. Which stage is wrong?
ConceptOn Verilator the line with no pull-up reads 0 and the two fighting outputs read 1. What should a bench writer take from that?
ConceptOn an open-drain line, what does halving the pull-up resistor do?

Remember

  • A push-pull output drives both levels and is fast, but only one may own a wire.
  • An open-drain output only pulls low or lets go; with a pull-up, the line is the AND of every device.
  • Two push-pull outputs that disagree sit mid-rail and pass a large current; a four-state simulator shows x, a two-state one hides it.
  • An open-drain rise is an RC charge, 0.85 RC from 0.3 to 0.7 VDD, so the pull-up and the bus capacitance set the speed.