4. Pull-ups and undefined states

A wire nothing drives has no voltage, which is stranger than it sounds. Why that needs a resistor, and why one bus fails completely without it.

Ask a software engineer what an unconnected input reads and you usually get “zero”. It is a reasonable guess, and it is wrong in a way that turns out to matter.

A wire nothing drives does not sit at zero volts. It does not sit anywhere. This chapter is about why, and about the large family of resistors that exist because of it and otherwise look like they are doing nothing.

Prerequisites: Chapter 1 for the idea that a part’s job is readable from what it connects to.

Levels on this page: EE3, EE4, EE5. Each links to what that level means.

Nothing is a voltage nothing has (EE3)

A logic input on a modern chip is the gate of a MOSFET, and a gate is one plate of a very small capacitor with an insulating layer underneath. Almost no current flows into it. The input leakage on a typical part is measured in nanoamps, which means the input presents an impedance of hundreds of megohms or more.

That is the surprising part, and everything follows from it. A node with enormous impedance has its voltage set by whatever tiny currents happen to reach it. Capacitive coupling from a trace running alongside. Leakage across the board surface. Charge left over from the last time something did drive it. None of those are big enough to matter anywhere else on the board, and on a floating input they are the only thing there is.

So the line drifts. It can sit anywhere between ground and the rail, including the region in the middle that is neither a valid low nor a valid high.

That middle region is worse than it sounds, and this is the part people miss. A CMOS input stage is a pair of transistors, one pulling toward the rail and one toward ground. At a valid high, one is on and the other is off. At a valid low, the reverse. Held in the middle, both are partly on, which opens a direct path from the rail to ground through the input stage. The part draws current it was never meant to draw, warms up, and the input can oscillate as it drifts back and forth across the threshold.

Hence the rule every datasheet states and nobody explains: do not leave a CMOS input floating. A resistor to a rail (pull-up) or to ground (a pull-down) gives the node somewhere to sit. It is weak enough that anything genuinely driving the line overrides it easily, and strong enough that stray coupling cannot.

The tool has a rule for this, and its verdicts are more interesting than its findings:

$ agni check --verdicts --rule floating-input examples/tutorial-project/designs/gateway/gateway.edn
floating-input  4 not-considered, 3 pass
    not-considered  GND       the net carries a passive part, which may be the pull that fixes
                              it or a series element hiding the driver, so it is not provably
                              floating
    not-considered  MCU_NRST  the net carries a passive part, which may be the pull that fixes
                              it or a series element hiding the driver, so it is not provably
                              floating
    not-considered  PMIC_EN   the net carries a passive part, which may be the pull that fixes
                              it or a series element hiding the driver, so it is not provably
                              floating
    not-considered  XTAL_IN   the net carries a passive part, which may be the pull that fixes
                              it or a series element hiding the driver, so it is not provably
                              floating
    pass            CAN1_RXD  1 of the net's 2 pins are neither input nor no-connect, so the net
                              is not input-only
    pass            CAN1_TXD  1 of the net's 2 pins are neither input nor no-connect, so the net
                              is not input-only
    pass            I2C_SCL   1 of the net's 2 pins are neither input nor no-connect, so the net
                              is not input-only

7 verdicts across 1 rule(s), 3 pass, 4 not-considered

Read the four not-considered rows closely, because you already know how to answer them and the tool does not.

MCU_NRST and PMIC_EN are the two nets chapter 1 used to teach the decision procedure. PMIC_EN carries R2, a 10k to the 12V rail, which chapter 1 read as a pull-up. MCU_NRST carries R3, a 10k to power-good, which chapter 1 read as a series resistor. The rule sees only “there is a passive part on this net” and stops, because from the netlist alone a resistor might be the pull that fixes the float or a series element with the real driver on the far side of it.

That is a rule declining to guess rather than a rule failing. You can look at the net names and resolve it; the tool cannot, and reports which of the two situations it is in rather than picking one. Chapter 9 of the tutorials is entirely about reading that distinction.

Some pins can only pull one way (EE3)

There is a second reason a pull-up exists, and it is not about floating at all.

Some outputs are built to drive only low. The pin can connect the line to ground or it can let go, and letting go is all it does at the top. These are called open-drain, and the point of them is that many devices can share one wire without ever fighting. If two pull low at once, nothing is damaged and the line is simply low. Compare that with two ordinary outputs on one net, one driving high and one driving low, which is a short across the supply through two transistors. (That is output-output-conflict, and it is chapter 5.)

The catch is that if nothing drives high, nothing brings the line back up. An open-drain bus needs an external resistor to a rail, and it does not work at all without one.

A supply rail across the top and a ground line across the bottom, with one shared bus line between them. A single pull-up resistor joins the rail to the bus. Three open-drain devices hang off the bus, and each one can only connect the line to ground, never to the rail. Any device pulling low simply makes the line low, so several can share the wire without fighting, and with the resistor removed nothing can bring the line back up. +3V3 the pull-up the only thing that can raise the line SDA U1 U2 U3 pulls low only pulls low only pulls low only ground two devices pulling low at once is simply low, so they can share the wire take the resistor away and the line never comes back up

I2C is the standard example. Two lines, both open-drain, both needing a pull-up:

$ agni check --verdicts --rule i2c-pull-up examples/tutorial-project/designs/gateway/gateway.edn
i2c-pull-up  2 fail
    fail  I2C_SCL  no rail is reachable from I2C_SCL through a resistor within 3 hops
    fail  I2C_SDA  no rail is reachable from I2C_SDA through a resistor within 3 hops

2 verdicts across 1 rule(s), 2 fail

The witness is worth a second look. It says “within 3 hops” rather than “no resistor to a rail”, because the rule walks outward through series elements rather than demanding a resistor wired straight to the rail. A pull-up sitting on the far side of a series element or a filter is still a pull-up, and a check that insisted on a direct connection would report a defect on a board that is fine.

Why this one is an error (EE4)

Here is the same board with three rules at once, and the interesting thing is the severity column:

$ agni check --rule i2c-pull-up --rule floating-input --rule decoupling-present examples/tutorial-project/designs/gateway/gateway.edn
findings by rule:
  decoupling-present     1
  i2c-pull-up            2

first 3:
  [warning] decoupling-present: PMIC_MAIN_12V0 (power rail has no decoupling capacitor)
  [error] i2c-pull-up: I2C_SCL (I2C net has no pull-up resistor to a rail)
  [error] i2c-pull-up: I2C_SDA (I2C net has no pull-up resistor to a rail)

3 finding(s) total
8 subject(s) considered by 3 rule(s), 4 not considered (--verdicts for the detail)

decoupling-present is a warning. i2c-pull-up is an error. Both are missing passives on a board that will be built either way, and chapter 3 argued at some length that missing decoupling is genuinely serious. So why the difference?

Because severity in this catalog answers “does it work at all”, not “how sure are we” and not “how much do we care”.

A board with no decoupling on a rail powers up and runs. It fails later, intermittently, under load or heat, on some units. An I2C bus with no pull-up does not work once. Every device on it can pull the line down and nothing can bring it back up, so after the first low the bus stays low forever. Nothing communicates, at any temperature, on every board built. The rule’s own Impact calls it a total-function failure.

Keep that pair in mind, because it defines the whole scale. error means the thing cannot work. warning means it usually indicates a defect and the board still runs. Neither says anything about how confident the check is, which the verdicts carry on a separate axis.

Choosing the resistor (EE5)

At EE3 the answer was “a pull-up”. At EE5 you have to pick a value, and it is a trade-off with a limit at each end.

Too weak (too large a resistance) and the line rises too slowly. Everything on the bus has capacitance, and the pull-up has to charge all of it through itself, so the rise is an RC curve. I2C caps total bus capacitance at 400pF and sets a maximum rise time per speed mode, and a large pull-up on a heavily loaded bus misses it.

Too strong (too small a resistance) and the device pulling the line down has to sink all the current the pull-up passes. Every open-drain output has a maximum sink current in its datasheet, and going under it is how you exceed the low-level output voltage the receivers need.

The usual landing zone is a few kilohms, with weaker values on short lightly loaded buses and stronger ones as the bus grows. (If you ever meet a board where I2C works at room temperature and stops when it warms up, a marginal pull-up against a slow rise time is one of the first things worth measuring.) The catalog checks presence rather than value, because value needs the bus capacitance and the sink ratings, which is a datasheet-tier question and the subject of chapter 7.

Two pull-ups is also wrong

Resistors in parallel are one smaller resistor, and the arithmetic is the ordinary kind: two 2.2k pulling the same line give an effective 1.1k. So a bus with a second pull-up on it sinks roughly twice the current it was sized for, and a device holding the line low has to sink all of it while still keeping its output below VOL. Past that point the low level creeps up and a receiver starts reading it as neither state.

It arrives the same way nearly every time. A module carries its own termination so it works standalone, the board it plugs into already pulls the same bus, and neither schematic is wrong on its own. Nothing about the board looks unusual, and it usually still works with whichever parts happen to be fitted, which is why it is a warning rather than an error.

Two pull-ups to two DIFFERENT rails is the worse version, and a different defect. The bus then ties those supplies together through its resistors whenever one is up and the other is not, so a rail that is meant to be off is fed through the bus during sequencing. On the bench every supply comes up at once and nothing is seen; it appears as a board that fails on one particular power-up order.

What you can now answer

  • Why an unconnected input does not read zero, and what it does instead. (EE3)
  • Why a floating CMOS input costs supply current rather than just reading unpredictably. (EE3)
  • Why an open-drain bus needs a resistor to work at all, and why several devices can share one line safely. (EE3)
  • Why one missing passive is an error and another is a warning, on the same board. (EE4)
  • Which way a pull-up value can be wrong, in both directions. (EE5)

The rules this page explains

Rule Severity What it catches
i2c-pull-up error an I2C line reaching no rail through a resistor
i2c-redundant-pull-up warning one bus pulled to one rail by two resistors
i2c-pull-up-split-rail warning one bus pulled to two different rails
floating-input warning a net whose pins are all inputs, with nothing driving or pulling it
profile/missing-pullup warning the same question for any bus a declared interface says needs one
unspecified-pin-with-driver warning a pin whose electrical type the design never stated, sitting on a driven net

Next: who drives this net, the other half of the question this chapter started, and the case where two parts both answer.