Skip to content

Verilog · Chapter 5.1.2 · Data & Variables

Signal Strengths in Verilog

When two drivers push different values onto the same wire, Verilog does not just compare the values. It also compares how strongly each driver pushes, using an eight-level strength scale that runs from supply down through strong, pull, and weak to high impedance. This lesson explains that strength scale, shows how the resolution rules pick a winner between conflicting drivers, and walks through where drive strength actually shows up in practice, such as pull-up modeling, transmission gates, and chip pad I/O. Modern synthesizable RTL rarely writes explicit strengths, yet the same rules still govern how a simulator resolves nets. Learning the model lets you read switch-level and pad-level code with confidence and debug multi-driver contention correctly.

Foundation24 min readVerilogSignal StrengthDrive StrengthResolutionSwitch-Level

Chapter 5 · Section 5.1.2 · Data & Variables

1. The Engineering Problem

Two assign statements drive the same wire. Both are active on the same cycle. Both push different values. What does the simulator do?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire bus;
 
assign (strong1, strong0) bus = drv_a;     // standard CMOS driver
assign (weak1,   weak0)   bus = drv_b;     // weak pull-up keeper

The naïve answer is "contention — bus = X." The naïve answer is wrong. The two drivers carry different strengths — one is strong, the other is weak — and Verilog's resolution function reads the strength annotations and silently picks the strong driver. Bus follows drv_a; the weak driver is overridden without a warning. Drop the strength annotations and the same code produces X on every conflicting cycle.

This page is about the eight-level strength lattice that sits underneath every wire and tri declaration in the language, the resolution function that walks the lattice, and the (narrow but real) set of cases where a working RTL engineer reaches for strength specifiers in shipping code. The model is small once you see it; getting it wrong produces simulations that look fine and silicon that doesn't.

2. Why Strengths Exist

Pre-Verilog hardware description languages had a one-axis resolution function: each driver contributes a value (0, 1, Z, X); the resolution function combines them into a final net value. That works for simple CMOS gates driving point-to-point nets. It fails the moment the design uses any of the following:

  • Pull-up / pull-down resistors. A weak resistor pulling a line HIGH is electrically a driver — it puts current onto the wire — but a strong CMOS driver pulling the line LOW must override it. The simulator needs a rule that lets the strong driver win.
  • Open-drain / open-collector buses. I²C, OneWire, and every shared-bus protocol since TTL relies on an external pull-up resistor being weaker than every node's pull-down transistor. The resolution function needs to model "weak source loses to strong sink."
  • Switch-level transistor models. A pass-transistor (NMOS or PMOS) implementing a transmission gate doesn't drive its output the way a CMOS buffer does — the output strength is reduced ("attenuated") by the channel resistance. The simulator needs to track that the output is strong if the input is supply, but only pull if the input is strong.
  • Charge-storage nodes (trireg). A capacitive node holds its last driven value at a much lower strength than the original driver. The simulator needs to remember "this node has value 1 at strength medium" so that a subsequent weak driver can override it.
  • Power and ground rails. The supply0 / supply1 net types model the chip's power and ground — drivers that nothing else in the design can possibly override. The resolution function needs an "infinite" strength level for these.

The eight-level lattice is the answer to all five cases simultaneously. It gives every drive value a place on the strength axis, and a resolution function that ranks drivers by strength before considering their values. The whole machinery is what makes Verilog a gate-and-switch simulator and not just a logic simulator.

3. Mental Model

The mental-model has three practical consequences for any engineer touching strength-aware code:

  • Strength is per-driver, not per-net. A wire does not have a strength; every driver feeding the net has one. The resolution function sorts the driver list by strength and picks the winner.
  • Strength is not synthesised. No standard cell library has a "weak1 / strong0" output cell. Strength specifiers exist in RTL for simulation accuracy and have no effect on the gates produced by synthesis. The synthesised netlist treats every driver as strong.
  • Strength is a simulator-only fidelity knob. Use it when you need the simulator to model an electrical effect (pull-up override, switch-level attenuation, charge storage) that the value axis alone cannot capture. Do not use it as a structural primitive in synthesisable RTL.

4. The 8-Level Strength Lattice

IEEE 1364-2005 §7 defines exactly eight strength levels. Each is a discrete rank, listed from strongest (supply) to weakest (highz). The lattice applies independently to drives toward 0 and drives toward 1 — every level has a 0 half and a 1 half (so supply0 and supply1, strong0 and strong1, and so on).

RankKeyword (1-side)Keyword (0-side)Numeric (4-bit)Physical analogue
7 (strongest)supply1supply00111Power / ground rail tie
6strong1strong00110Default CMOS driver
5pull1pull00101Resistive pull-up / pull-down
4large1large00100Large capacitor (charge storage)
3weak1weak00011High-resistance keeper
2medium1medium00010Medium capacitor
1small1small00001Small capacitor
0 (weakest)highz1highz00000High-impedance / undriven

A few observations the simulator's resolution function relies on, that working engineers carry in their heads:

  • supply is electrically unbreakable. A supply1 net is the chip's VDD rail; nothing in the design can override it. The synthesis tool replaces supply0 / supply1 nets with constant-0 / constant-1 ties to the power planes — never with logic.
  • strong is the default for everything else. Every plain wire-driving assign, every gate-primitive output, every continuous driver runs at strong unless you explicitly say otherwise. This is the "normal RTL" rank.
  • pull models a resistor. A pull1 driver is a pull-up resistor tied to VDD; a pull0 driver is a pull-down to GND. The tri1 and tri0 net types use pull strength implicitly — a tri1 net resolves to pull1 when no other driver is active.
  • large, medium, small are the three capacitive strengths used only by trireg charge-storage nets. They rank the charge on the node by capacitance — large for a long-life storage node, small for a fragile dynamic node.
  • weak is a stronger-than-resistor driver but weaker than pull. Some keeper-cell cells in standard libraries model their output at weak strength so a real strong driver can override them without a glitch.
  • highz is "no current flowing" — the strength of Z. The resolution function uses it as a tie-breaker: a highz driver always loses to anything else.

The numeric column matters when you read simulator dumps. Some simulators (Verilator, Icarus) print resolved net values as the 4-bit strength encoded together with the value (stx for "strong-X", we1 for "weak-1", etc.). The 4-bit number is the strength rank.

5. The Two Halves — Why Each Strength Has a 0 and a 1 Side

Strength is directional. A pull-up resistor exerts force toward 1; a pull-down resistor exerts force toward 0. The two are the same physical component (a resistor) but with opposite ends tied to opposite rails — they are not the same driver.

This is why every strength level has two keyword variants:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
supply1 / supply0   ← rail ties (VDD / GND)
strong1 / strong0   ← default CMOS driver
pull1   / pull0     ← pull-up / pull-down resistors
weak1   / weak0     ← weak keepers
large1  / large0    ← large-capacitor storage
medium1 / medium0   ← medium-capacitor storage
small1  / small0    ← small-capacitor storage
highz1  / highz0    ← high-impedance "no-driver" markers

The resolution function compares strengths independently for the 0 direction and the 1 direction. If one driver is pull1 and another is strong0, the resolution does not "subtract" them — it asks: "what's the strongest 1-side driver?" → pull1. "What's the strongest 0-side driver?" → strong0. Then it compares the two ranks: strong0 (rank 6) beats pull1 (rank 5), so the net resolves to 0 at strength strong0.

The two-axis model is what makes the resolution function able to handle "weak source losing to strong sink" — exactly the I²C / open-drain case.

6. Syntax — Strength Specifiers on Drivers

Strength specifiers can be attached to three constructs: gate primitives, assign statements, and net declarations (when the declaration includes a continuous-assign shorthand). The syntax is always a parenthesised pair (strength1, strength0) placed before the value or the driver.

strength-syntax.v — every place a strength specifier is legal
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
// 1. Gate primitive output drive strength
//    Form: gate (strength1, strength0) [delay] instance(...);
buf  (strong1, strong0)  u_buf  (out1, in);   // default — same as no specifier
nand (pull1,   pull0)    u_nand (out2, a, b); // weaker output drive
 
// 2. Continuous-assign strength
//    Form: assign (strength1, strength0) target = expression;
assign (weak1, weak0)    keep_q = last_q;     // weak keeper feedback
assign (strong1, strong0) out_q = data_in;    // explicit default
 
// 3. Net declaration with continuous-assign shorthand
//    Form: wire (strength1, strength0) name = expression;
wire   (pull1, pull0)    pullup_line = 1'b1;  // models an external pull-up
 
// 4. trireg with capacitor-strength (covered in detail on the 5.1.4 page)
trireg (medium) cap_node;  // medium-capacitor charge storage

Three syntax constraints that catch new readers:

  • Strength order. The pair is always (strength1, strength0)1-side first. Writing (strong0, strong1) is a syntax error in some simulators and a silent reorder in others; just always write 1-side first.
  • Both sides required. You cannot specify only one half — (pull1) is illegal. If you only need a 1-side specifier, write (pull1, highz0); the highz0 says "nothing on the 0 side."
  • supply is direction-locked. supply1 always means "tied to VDD"; you cannot say (supply1, supply1) — the 0-side must be a different keyword.

6.1 Implicit default

Every gate primitive and every assign runs at (strong1, strong0) if you don't specify otherwise. This is why you almost never see strength specifiers in synthesisable RTL — the default is "what every CMOS driver does."

The implicit default extends to the simulator's internal driver tracking: an assign statement without a strength specifier carries an implicit (strong1, strong0) annotation in the simulator's data structures, which the resolution function reads on every driver-change event.

7. Strength Resolution — The Algorithm

The IEEE 1364-2005 §7.2 resolution function for any net (wire, tri, tri0, tri1, trireg) runs on every cycle that any contributing driver changes. The algorithm:

  1. Collect all active drivers. For each driver, record its value (0, 1, Z, X) and its strength annotation (a (strength1, strength0) pair).
  2. Split into a 0-side max and a 1-side max.
    • For drivers carrying 0, take the maximum strength0 rank.
    • For drivers carrying 1, take the maximum strength1 rank.
    • Drivers carrying Z contribute nothing (their effective rank is highz).
    • Drivers carrying X contribute to both sides at their annotated strength.
  3. Compare the two maxima.
    • If 1-max strictly exceeds 0-max → net resolves to 1 at 1-max strength.
    • If 0-max strictly exceeds 1-max → net resolves to 0 at 0-max strength.
    • If they tie at the same strength → net resolves to X at that strength.
  4. If no driver is active, the net resolves to its default — Z for wire / tri, pull1 / pull0 for tri1 / tri0, the last-driven value at reduced strength for trireg.

The algorithm is what implements every special case in the rest of the language:

  • tri1 net = "implicit pull1 driver always active in addition to the explicit ones." A stronger explicit 0-driver wins (resolved value = 0); when all explicit drivers release to Z, the implicit pull1 wins (resolved value = 1).
  • Open-drain / I²C = nodes have one pull1 driver (the bus pull-up resistor) plus zero or more strong0 drivers (peripheral pull-down transistors). Any peripheral asserting 0 wins; with no peripheral active, the pull-up wins.
  • Switch-level NMOS pass-transistor = output strength is reduced one rank from input strength (a strong input produces a pull output through the transistor — the resolution function then compares against any other driver on the receiver-side node).

8. Visual Explanation

Three figures cover the three resolution patterns the lattice produces.

8.1 Visual A — pull-up resistor model

The canonical "weak source loses to strong sink" pattern. One driver represents an external pull-up resistor (pull1); peripheral drivers represent NMOS pull-downs (strong0). The strong driver always wins when active; the pull-up takes over when all peripherals release.

Open-drain bus — pull-up override

data flow
Open-drain bus — pull-up overridepull1strong0 or Zstrong0 or Zpull-uppull1 (resistor to VDD)wireresolution nodeperiph Adrives strong0 when activeperiph Bdrives strong0 when active
The pull-up runs continuously at pull1. Each peripheral drives strong0 when it wants to claim the bus and releases (Z) otherwise. Resolution rule: strong0 (rank 6) beats pull1 (rank 5), so any active peripheral pulls the bus to 0; when all release, pull1 wins and the bus floats HIGH.

8.2 Visual B — strength override (no contention)

When two drivers fight a plain wire with different strengths, there is no X. The stronger driver wins silently. This is the case the §1 example illustrated.

Strength override — strong driver beats weak driverdriver Astrong1driver Bweak0wireresolves to 1 (strong1)consumersamples 112
Driver A at strong1; driver B at weak0. Without strength specifiers, this would resolve to X (mid-axis tie). With strengths, the resolution function ranks strong (6) above weak (3) and the net resolves to 1 at strong1 — driver B is overridden silently.

8.3 Visual C — equal-strength contention

The only case the strength-axis cannot resolve. Two drivers at the same strength carry different values. The resolution function returns X at the tied strength.

Equal-strength contention — X resultdriver Astrong1driver Bstrong0wireX at strongconsumersamples X12
Two strong drivers, conflicting values. Both contribute at strong (rank 6); neither side strictly dominates. The resolution function returns X at strong — the same X you'd see with no strength specifiers. The strength axis cannot break a same-strength tie.

9. Pull-Up Resistor Example

The canonical industry use case. An I²C-style SDA line is modelled as a wire with one pull1 driver (the external resistor) and multiple strong0 drivers (each peripheral's open-drain pull-down).

rtl/i2c_sda_model.v — open-drain SDA line with strength-aware pull-up
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module i2c_sda_model (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [3:0]  periph_drive_low,   // one bit per peripheral
    output wire        sda                 // shared open-drain line
);
    // Model the external pull-up resistor as a continuous pull1 driver.
    // The `(pull1, highz0)` specifier says: drive 1 at pull strength, but
    // do not drive 0 at all — the resistor is electrically asymmetric.
    assign (pull1, highz0) sda = 1'b1;
 
    // Each peripheral drives 0 at strong strength when active; releases
    // (drives Z) otherwise. `(strong0, highz1)` says: strong on the 0 side,
    // no 1-side drive at all — exactly an NMOS pull-down.
    assign (strong0, highz1) sda = periph_drive_low[0] ? 1'b0 : 1'bz;
    assign (strong0, highz1) sda = periph_drive_low[1] ? 1'b0 : 1'bz;
    assign (strong0, highz1) sda = periph_drive_low[2] ? 1'b0 : 1'bz;
    assign (strong0, highz1) sda = periph_drive_low[3] ? 1'b0 : 1'bz;
endmodule

Walk the resolution function for the two interesting cases:

  • No peripheral active. The 0-side max is highz0 (the pull-up's 0-side, and every peripheral's 1-side, contributes nothing). The 1-side max is pull1 (the resistor). pull1 beats highz0, so sda = 1 at strength pull1.
  • One peripheral active. The 0-side max is strong0 (the peripheral's pull-down). The 1-side max is pull1 (the resistor). strong0 (rank 6) beats pull1 (rank 5), so sda = 0 at strength strong0.

This is the working electrical model of every open-drain bus in the industry: I²C, OneWire, JTAG TDO, the ~INT line on most peripheral chips. The same model with tri1 instead of plain wire is even more common — the tri1 net type carries the implicit pull1 driver so you don't need the explicit assign (pull1, highz0). (The tri1 net type is the 5.1.5 sub-page; we use the explicit form here so the strength annotations are visible.)

10. Transmission Gate / Switch-Level Example

The historical workhorse of strength specifiers. A CMOS transmission gate (an NMOS in parallel with a PMOS, both controlled by the enable) passes the input value to the output when enabled, with attenuated strength. The NMOS half passes 0s well but 1s poorly (the output gets stuck a threshold below VDD); the PMOS half is the mirror image. The pair together passes both values cleanly — but the output of a pass-transistor is, electrically, one strength rank below the input.

rtl/tg_pass_strength.v — transmission gate with attenuated output
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module tg_pass_strength (
    input  wire        en,         // enable — turns the pass-gate ON
    input  wire        in,         // driver from upstream
    output wire        out         // pass-gate output (one rank weaker)
);
    // Model the transmission gate: when en=1, pass `in` to `out`;
    // when en=0, drive Z.  The output strength is `pull` — one rank
    // below the assumed `strong` strength of the input driver — to
    // model the channel-resistance attenuation of the pass transistors.
    assign (pull1, pull0) out = en ? in : 1'bz;
endmodule

Why this matters: if the downstream node is connected to a pull1 resistor (a real circuit), the resolution function compares the pass-gate's output at pull1 against the resistor at pull1 — a same-strength tie. If the two values disagree, the resolver returns X, which is correct — a same-strength fight in real silicon produces an indeterminate voltage. A "strong" pass-gate model would miss this case and let the pass-gate silently win, hiding a real circuit bug.

Real switch-level libraries from the 1980s and 1990s used pmos / nmos / cmos primitives that did this attenuation automatically (IEEE 1364 §29.4 — Pass-transistor strength reduction). Modern flows almost never use switch-level — the synthesis-and-place-and-route pipeline operates entirely above the transistor layer — but the strength-attenuation rule is preserved in the language for backward compatibility and for the rare custom-circuit project that still drops below the standard-cell line.

11. Trireg Capacitive Storage Example

The third real-world use of strengths: modelling dynamic-logic charge storage. A capacitive node holds its last driven value at a reduced strength after the driver releases, decaying with simulated time. The trireg net type implements this; the large / medium / small strengths annotate how big the storage capacitor is.

rtl/charge_storage.v — trireg capacitive storage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module charge_storage (
    input  wire        clk,
    input  wire        wr_en,
    input  wire        data_in,
    output wire        q
);
    // A `medium`-capacitance storage node — holds its last driven value
    // at `medium` strength once the driver releases. A subsequent `weak`
    // driver could not override it (weak < medium); a `pull` driver could.
    trireg (medium) storage_node;
 
    // Driver: drives storage_node at strong strength when wr_en is active;
    // releases (Z) otherwise.  After release, the node retains its last
    // value at the trireg's declared `medium` strength.
    assign (strong1, strong0) storage_node = wr_en ? data_in : 1'bz;
 
    assign q = storage_node;
endmodule

The semantics: while wr_en=1, storage_node carries the driven value at strong strength. When wr_en falls to 0, the driver releases (drives Z). Now the trireg resolution rule fires: the node remembers its last driven value at the declared capacitor strength (medium). Downstream q sees the held value at medium strength — not Z, and not at full strong strength. Any new driver weaker than medium (e.g. a weak keeper) would not override the held charge; a pull driver would.

The trireg net and its three capacitance strengths are the 5.1.4 sub-page. The takeaway here is that large / medium / small exist on the lattice purely for trireg — no other construct in the language uses them.

12. Simulation Behavior

The resolution function runs whenever any driver of a strength-aware net changes value or strength. The simulator's internal data structures track every driver's (value, strength1, strength0) triple, and the resolution function walks the driver list on every change to produce the resolved (value, strength) pair for the net.

The 4-bit strength encoding (column 4 in §4) is what some simulators print in waveform dumps and $display output when the resolution returns a non-strong value:

Resolved netWaveform symbolMeaning
0 at supply0Su0 or Sup0rail tie — VDD-to-GND path
1 at strong1St1 or 1default CMOS — usually printed plain
0 at pull0Pu0 or pl0pull-down resistor active
X at pull (tied 0/1 at pull)PuXpull-strength contention
1 at weak1We1 or wk1weak keeper
Z at highzHiZ or Znothing driving

The exact print form varies — VCS prints St1, Modelsim/Questa prints St 1, Verilator simplifies to plain 0/1/X/Z (Verilator ignores strengths). When you see Pu0 or We1 in a waveform, you are reading the strength-aware encoding and the resolution function ran something more interesting than the default rank-6-everywhere case.

13. Waveform Analysis

Two waveforms — one showing pull-up override (the I²C pattern), one showing strength contention (same-rank tie producing X).

13.1 Waveform #1 — Pull-up override

SDA line — pull-up resistor + peripheral pull-down

12 cycles
SDA line — pull-up resistor + peripheral pull-downno peripheral — pull1 wins, sda = 1 at pull1no peripheral — pull1 …P0 active — strong0 beats pull1, sda = 0 at strong0P0 active — strong0 be…P0 releases — pull1 wins againP0 releases — pull1 wi…P1 active — strong0 beats pull1P1 active — strong0 be…P1 releases — pull1 winsP1 releases — pull1 wi…clkp0_lowp1_lowsdastrengthpull1pull1strong0strong0pull1pull1strong0strong0pull1pull1pull1pull1t0t1t2t3t4t5t6t7t8t9t10t11
The bottom 'strength' row makes the resolution decision visible. When no peripheral is active, the only driver is the pull-up at pull1 — sda = 1 at strength pull1. When a peripheral asserts, the resolution function sees strong0 (rank 6) vs pull1 (rank 5); the strong wins, sda goes to 0 at strong0. When the peripheral releases, the resolution falls back to pull1.

The interesting cycles are the transitions. Between cycle 1 and cycle 2, p0_low rises and a new strong0 driver becomes active on sda. The resolution function reruns: 0-side max is strong0; 1-side max is pull1; strong0 > pull1, so sda falls to 0 at strong0. The exact same logic runs in reverse on cycles 4 and 8.

What's invisible on the waveform but real in the simulator: the strength of sda changes too. From cycle 0–1 the bus is 1 at pull1; from 2–3 it's 0 at strong0; from 4–5 back to 1 at pull1. Strength-aware downstream logic (another trireg, another strength-specifier-bearing driver) would see the strength change as well as the value change.

13.2 Waveform #2 — Strength contention (X result)

Same-strength contention — strong vs strong

10 cycles
Same-strength contention — strong vs strongA drives strong1 — bus = 1 at strong1A drives strong1 — bus…B also drives strong0 — same strength, conflict → X at strongB also drives strong0 …A releases — bus = 0 at strong0A releases — bus = 0 a…B releases — no driver, bus = ZB releases — no driver…clkdrv_adrv_bdata_aXX1111XXXXdata_bXXXX0000XXbusXXstrengthhighzhighzstrong1strong1strongstrongstrong0strong0highzhighzt0t1t2t3t4t5t6t7t8t9
The contention cycle (4–5) is the strength axis's failure mode. Both drivers are at strong; the resolution function cannot break the tie and returns X at strong — the same X a strength-less wire would produce. The strength axis only helps when the conflicting drivers carry different strengths. Same-strength fights still resolve to X.

The cycle-4–5 result is the lesson: strength is not a magic conflict-killer. Two drivers at the same strength still conflict, still produce X. The strength axis only adds resolution capability when the strengths are different. The system-design rule of thumb: if you reach for strengths to model a circuit, the circuit should have an inherent strength hierarchy — pull-up weaker than driver, weak keeper weaker than the next strong driver. Stamping (weak1, weak0) on a pair of equal CMOS drivers does not resolve the contention.

14. Synthesis Insight

Strength specifiers are ignored by synthesis.

synth-ignored.v — strength specifier vanishes in the netlist
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL: explicit pull-strength specifier on the driver
assign (pull1, pull0) keep_q = last_q;
 
// After synthesis, the netlist contains:
buf u_keep_q (keep_q, last_q);   // a plain buffer at strong drive

Synthesis tools assume every driver is (strong1, strong0) and produce gates accordingly. There is no standard-cell library that provides a "weak1 / weak0" output, and no synthesis tool has the modelling vocabulary to map the strength specifier to a cell. The strength specifier is therefore a simulator-only fidelity knob.

The practical consequences:

  1. RTL with strength specifiers diverges from the netlist. Any simulation behaviour that depends on a strength override — a weak keeper losing to a strong driver, a pull-up beating a highz0 release — disappears in gate-level simulation. If the design relies on the strength override for correctness, gate-level simulation will show different behaviour.
  2. Synthesisable RTL almost never uses strength specifiers. The pattern is: use strengths only in non-synthesised constructs — testbench BFMs, IP-vendor electrical models of external bus pull-ups, switch-level cells that are replaced by characterised standard cells before synthesis.
  3. Top-level pad models do use strengths. A bidirectional pad cell often has its electrical model in a (pull1, highz0) or (strong1, strong0) specifier inside an *.v model file. The synthesis tool replaces the pad model with the actual pad cell from the library, which has the electrical strength as a layout property — but the RTL model gets the simulation behaviour right.

15. Industry Use Cases

The three places strength specifiers actually show up in modern flows:

15.1 Pull-up / pull-down resistor models

Every chip-level testbench that models an external bus (I²C, SPI with open-drain CS, OneWire, JTAG, USB DP/DM at full-speed handshake) has a (pull1, highz0) driver somewhere modelling the external resistor. The pattern from §9 — explicit assign (pull1, highz0) line = 1'b1; — is the canonical form. Alternatively, the tri1 net type carries the implicit pull1 driver and the explicit assign is dropped (5.1.5 sub-page).

15.2 Switch-level cell models

Older standard-cell libraries (pre-90nm) and most custom-circuit projects (analog mixed-signal IP, RF, high-speed I/O retimers) provide switch-level Verilog models of their cells. These models use pmos / nmos / cmos primitives or assign statements with strength attenuation to model channel resistance. The 5.1.4 trireg sub-page and the Chapter 12 Switch-Level Modeling chapter cover this in detail; signal strengths are a prerequisite for both.

15.3 Power / ground rail models

supply0 / supply1 nets model the chip's power and ground rails (5.1.6 sub-page). Their strength is supply — the strongest possible — so nothing else in the design can override them. Synthesis replaces them with constant-0 / constant-1 ties to the actual power planes; simulation uses the strength to ensure that any "what-if some logic tied this signal to ground" experiment will resolve correctly.

In all three cases, the strength specifier exists to make the simulator's behaviour match the electrical reality. None of it produces extra gates — none of it can, because no library has the cells to map to.

16. Common Mistakes

Six pitfalls that catch engineers reading or writing strength-aware code.

16.1 Treating strength as a conflict-killer

The most common misunderstanding. Engineers see "drive strength" and assume that putting different strengths on two conflicting drivers will silently pick a winner. That's true only when the strengths are different. Same-strength contention still produces X (§13.2). Strength specifiers do not resolve contention between two CMOS drivers — they only resolve contention between drivers that genuinely have different electrical strengths (driver vs resistor, driver vs keeper, driver vs trireg).

16.2 Writing strength specifiers in synthesisable RTL

Strength specifiers are ignored by synthesis. Any RTL that uses them for functional correctness — not just simulation fidelity — has a gate-level mismatch. The right rule: use strengths only in (a) testbench BFMs modelling external electrical effects, (b) trireg charge storage, (c) supply0 / supply1 rail ties, (d) tri0 / tri1 pull-net implicit drivers. Everywhere else, write plain wire with explicit muxes and let synthesis worry about gates.

16.3 Confusing highz with Z

Z is a value. highz is a strength. A driver outputting 1'bz is also at highz strength (because there's nothing actually flowing through the driver) — but a driver outputting 1'b1 at highz1 strength is electrically nonsense and the simulator behaviour is undefined. The pair (highz1, highz0) is reserved for the "no driver" placeholder; do not stamp it on a real driver.

16.4 Forgetting the 0/1 directionality

Pull-ups and pull-downs are direction-specific. A model that says assign (pull1, pull0) line = 1'b1; is two drivers in one statement: a pull1 toward 1 and a pull0 toward 0. That's wrong for a resistor — a real pull-up only pulls toward 1. The right form is assign (pull1, highz0) line = 1'b1; — pull on the 1 side, nothing on the 0 side. The asymmetric specifier matches the asymmetric circuit.

16.5 Mixing strength axes across simulators

Verilator (popular for open-source flows) ignores strength specifiers entirely. Code that relies on strength resolution for correctness will produce different results on Verilator vs VCS / Questa / Xcelium. The IEEE 1364 spec mandates strength resolution; some implementations skip it for performance. If your CI runs Verilator, audit any strength-aware code paths and add a fallback that produces correct results under value-only resolution.

16.6 Using strengths to fix a multi-driver bug

If lint warns about a multi-driver wire, the right fix is almost never "add strength specifiers." The right fix is to (a) declare the net as tri if the multi-driver pattern is intentional and the contract enforces mutex (5.1.1 sub-page), or (b) rewrite as a single-driver mux if the intent is mutually exclusive selection. Strength specifiers are an electrical-modelling tool, not a lint-quieting tool.

17. Debugging Lab

Three signal-strength debug post-mortems

18. Coding Guidelines

  1. Default is (strong1, strong0) — don't write it. Explicit (strong1, strong0) specifiers add no information; they clutter the RTL. Write strength specifiers only when the strength is not the default.
  2. Strength specifiers belong in non-synthesisable models. Testbench BFMs, pull-up / pull-down resistor models, switch-level cells, trireg capacitive storage. Synthesisable RTL stays at default strength.
  3. Asymmetric drivers need asymmetric specifiers. A pull-up resistor is (pull1, highz0), not (pull1, pull0). A pull-down is (highz1, pull0). Match the specifier to the physical reality.
  4. Strength does not resolve same-strength contention. Same-rank conflicts still produce X. Use strengths only when the conflicting drivers have genuinely different electrical strengths.
  5. Verify on multiple simulators if strength is load-bearing. Verilator skips strength resolution; VCS/Questa/Xcelium honour it. CI that runs only Verilator will not catch strength-related bugs.
  6. Prefer tri0 / tri1 net types over explicit pull specifiers. A tri1 net carries the implicit pull1 driver and is shorter to read; the explicit assign (pull1, highz0) form is the textbook explanation.
  7. For capacitive storage, prefer trireg with explicit capacitor strength. trireg (medium) node; is clearer than emulating the same effect with a manually-managed strength specifier.
  8. Document every strength-modelled circuit. A one-line comment naming the electrical effect (pull-up, keeper, charge storage, pass-transistor attenuation) prevents the next engineer from "simplifying" the specifier away.

19. Interview Q&A

20. Exercises

Three exercises that turn the strength-lattice model into reflex.

Exercise 1 — Resolve the strengths

For each of the following two-driver scenarios, predict the resolved value and strength without running a simulator. Then explain which rule (driver dominance, equal-strength tie, etc.) produced the answer.

#Driver ADriver BResolved (value, strength)
11 at strong10 at pull0?
21 at pull10 at weak0?
31 at strong10 at strong0?
4Z at highz1 at pull1?
51 at supply10 at strong0?
61 at weak11 at weak1?
71 at weak10 at weak0?

Hints. Compare the 0-max and 1-max strengths. Higher rank wins; tie at the same rank with different values produces X.

Exercise 2 — Model an open-drain bus with two peripherals

Write a synthesisable Verilog module od_irq_bus that models a 1-bit interrupt line with two peripheral pull-downs and an external pull1 resistor. Specifications:

  • Inputs: wire periph_a_active, wire periph_b_active. When _active is high, the peripheral wants to assert the interrupt.
  • Output: wire irq_n. Should be 0 when either peripheral is active, 1 when both release.
  • Use one explicit pull1 driver to model the resistor; use strong0 drivers for the peripherals.

Hints. Three assign statements: one for the pull-up at (pull1, highz0), one for each peripheral at (strong0, highz1) releasing to Z when inactive. Verify the resolution rule manually: any strong0 beats the pull1, no strong0 lets pull1 win.

Exercise 3 — Spot the synthesis trap

The following module compiles and simulates correctly with all four-state simulators. Identify why post-synthesis gate-level simulation will diverge from RTL, and rewrite the module so RTL and gates match.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module bus_with_keeper (
    input  wire        wr_en,
    input  wire        wr_data,
    output wire        bus_q
);
    reg keep_ff;
    always @(*) keep_ff = wr_en ? wr_data : keep_ff;
 
    assign (weak1, weak0) bus_q = keep_ff;
    assign                bus_q = wr_en ? wr_data : 1'bz;
endmodule

What to produce. (a) Identify the two strength-related issues. (b) Rewrite the module using a real registered keeper (with posedge clk) and no strength specifiers. (c) Explain why the rewritten version matches between RTL and gates while the original does not.

21. Summary

Signal strengths are Verilog's mechanism for resolving electrically heterogeneous multi-driver contention. The language defines eight strength levels — supply, strong, pull, large, weak, medium, small, highz — each with a 0-side and a 1-side variant. The resolution function for any wire / tri / tri0 / tri1 / trireg net compares the maximum 0-side strength against the maximum 1-side strength; the stronger side wins, and a same-strength tie produces X.

The model is doing real electrical work in three places:

  • Pull-up / pull-down resistors. (pull1, highz0) on an assign models an external pull-up resistor — weaker than a CMOS driver, stronger than nothing.
  • Switch-level pass-transistors. pmos / nmos / cmos primitives attenuate output strength by one rank, modelling the channel-resistance loss through a pass gate.
  • Capacitive charge storage. trireg (large/medium/small) declares a node that retains its last value at the named capacitor strength after the driver releases.

Synthesisable RTL almost never uses explicit strength specifiers. Synthesis tools ignore them; standard-cell libraries provide no non-default-strength cells. The narrow exceptions are supply0 / supply1 rail ties (which synthesise to power-plane connections) and the implicit strengths inside tri0 / tri1 / trireg net types.

The day-to-day discipline:

  • Default strength is (strong1, strong0) — don't write it.
  • Strengths are simulator-only fidelity knobs. Use them in testbench BFMs and electrical models, not synthesisable RTL.
  • Asymmetric drivers need asymmetric specifiers(pull1, highz0) for a pull-up, (highz1, pull0) for a pull-down.
  • Same-strength fights still produce X. Strength does not resolve contention between two equal CMOS drivers.
  • Strengths and synthesis disagree. Any RTL whose functional correctness depends on a strength override has a gate-level mismatch — replace with a real mux, a real keeper cell, or a real tri0 / tri1 net.
  • Verilator skips strength resolution. CI relying on Verilator misses strength-related bugs.

The sibling sub-pages drill into the rest of the net family that uses the strength lattice: 5.1.3 Wired Nets covers wand / wor (logic-OR / logic-AND resolution overlaid on strength); 5.1.4 trireg Nets covers capacitive charge storage with large / medium / small strengths; 5.1.5 tri0 / tri1 Nets covers the implicit pull drivers; 5.1.6 supply0 / supply1 covers the supply strength rail-tie nets. Chapter 12 Switch-Level Modeling covers pmos / nmos / cmos primitives and their strength-attenuation rules.