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?
wire bus;
assign (strong1, strong0) bus = drv_a; // standard CMOS driver
assign (weak1, weak0) bus = drv_b; // weak pull-up keeperThe 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
strongif the input issupply, but onlypullif the input isstrong. - 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 value1at strengthmedium" so that a subsequentweakdriver can override it. - Power and ground rails. The
supply0/supply1net 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
wiredoes 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).
| Rank | Keyword (1-side) | Keyword (0-side) | Numeric (4-bit) | Physical analogue |
|---|---|---|---|---|
| 7 (strongest) | supply1 | supply0 | 0111 | Power / ground rail tie |
| 6 | strong1 | strong0 | 0110 | Default CMOS driver |
| 5 | pull1 | pull0 | 0101 | Resistive pull-up / pull-down |
| 4 | large1 | large0 | 0100 | Large capacitor (charge storage) |
| 3 | weak1 | weak0 | 0011 | High-resistance keeper |
| 2 | medium1 | medium0 | 0010 | Medium capacitor |
| 1 | small1 | small0 | 0001 | Small capacitor |
| 0 (weakest) | highz1 | highz0 | 0000 | High-impedance / undriven |
A few observations the simulator's resolution function relies on, that working engineers carry in their heads:
supplyis electrically unbreakable. Asupply1net is the chip'sVDDrail; nothing in the design can override it. The synthesis tool replacessupply0/supply1nets with constant-0/ constant-1ties to the power planes — never with logic.strongis the default for everything else. Every plainwire-drivingassign, every gate-primitive output, every continuous driver runs atstrongunless you explicitly say otherwise. This is the "normal RTL" rank.pullmodels a resistor. Apull1driver is a pull-up resistor tied toVDD; apull0driver is a pull-down toGND. Thetri1andtri0net types usepullstrength implicitly — atri1net resolves topull1when no other driver is active.large,medium,smallare the three capacitive strengths used only bytriregcharge-storage nets. They rank the charge on the node by capacitance —largefor a long-life storage node,smallfor a fragile dynamic node.weakis a stronger-than-resistor driver but weaker thanpull. Some keeper-cell cells in standard libraries model their output atweakstrength so a realstrongdriver can override them without a glitch.highzis "no current flowing" — the strength ofZ. The resolution function uses it as a tie-breaker: ahighzdriver 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:
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" markersThe 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.
`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 storageThree 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 write1-side first. - Both sides required. You cannot specify only one half —
(pull1)is illegal. If you only need a1-side specifier, write(pull1, highz0); thehighz0says "nothing on the0side." supplyis direction-locked.supply1always means "tied toVDD"; you cannot say(supply1, supply1)— the0-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:
- Collect all active drivers. For each driver, record its value (
0,1,Z,X) and its strength annotation (a(strength1, strength0)pair). - Split into a
0-side max and a1-side max.- For drivers carrying
0, take the maximumstrength0rank. - For drivers carrying
1, take the maximumstrength1rank. - Drivers carrying
Zcontribute nothing (their effective rank ishighz). - Drivers carrying
Xcontribute to both sides at their annotated strength.
- For drivers carrying
- Compare the two maxima.
- If
1-max strictly exceeds0-max → net resolves to1at1-max strength. - If
0-max strictly exceeds1-max → net resolves to0at0-max strength. - If they tie at the same strength → net resolves to
Xat that strength.
- If
- If no driver is active, the net resolves to its default —
Zforwire/tri,pull1/pull0fortri1/tri0, the last-driven value at reduced strength fortrireg.
The algorithm is what implements every special case in the rest of the language:
tri1net = "implicitpull1driver always active in addition to the explicit ones." A stronger explicit0-driver wins (resolved value =0); when all explicit drivers release toZ, the implicitpull1wins (resolved value =1).- Open-drain / I²C = nodes have one
pull1driver (the bus pull-up resistor) plus zero or morestrong0drivers (peripheral pull-down transistors). Any peripheral asserting0wins; with no peripheral active, the pull-up wins. - Switch-level NMOS pass-transistor = output strength is reduced one rank from input strength (a
stronginput produces apulloutput 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 flow8.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.
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.
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).
`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;
endmoduleWalk the resolution function for the two interesting cases:
- No peripheral active. The
0-side max ishighz0(the pull-up's0-side, and every peripheral's1-side, contributes nothing). The1-side max ispull1(the resistor).pull1beatshighz0, sosda=1at strengthpull1. - One peripheral active. The
0-side max isstrong0(the peripheral's pull-down). The1-side max ispull1(the resistor).strong0(rank 6) beatspull1(rank 5), sosda=0at strengthstrong0.
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.
`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;
endmoduleWhy 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.
`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;
endmoduleThe 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 net | Waveform symbol | Meaning |
|---|---|---|
0 at supply0 | Su0 or Sup0 | rail tie — VDD-to-GND path |
1 at strong1 | St1 or 1 | default CMOS — usually printed plain |
0 at pull0 | Pu0 or pl0 | pull-down resistor active |
X at pull (tied 0/1 at pull) | PuX | pull-strength contention |
1 at weak1 | We1 or wk1 | weak keeper |
Z at highz | HiZ or Z | nothing 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 cyclesThe 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 cyclesThe 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.
// 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 driveSynthesis 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:
- 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
highz0release — disappears in gate-level simulation. If the design relies on the strength override for correctness, gate-level simulation will show different behaviour. - 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.
- 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*.vmodel 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
- 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. - Strength specifiers belong in non-synthesisable models. Testbench BFMs, pull-up / pull-down resistor models, switch-level cells,
triregcapacitive storage. Synthesisable RTL stays at default strength. - 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. - 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. - 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.
- Prefer
tri0/tri1net types over explicitpullspecifiers. Atri1net carries the implicitpull1driver and is shorter to read; the explicitassign (pull1, highz0)form is the textbook explanation. - For capacitive storage, prefer
triregwith explicit capacitor strength.trireg (medium) node;is clearer than emulating the same effect with a manually-managed strength specifier. - 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 A | Driver B | Resolved (value, strength) |
|---|---|---|---|
| 1 | 1 at strong1 | 0 at pull0 | ? |
| 2 | 1 at pull1 | 0 at weak0 | ? |
| 3 | 1 at strong1 | 0 at strong0 | ? |
| 4 | Z at highz | 1 at pull1 | ? |
| 5 | 1 at supply1 | 0 at strong0 | ? |
| 6 | 1 at weak1 | 1 at weak1 | ? |
| 7 | 1 at weak1 | 0 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_activeis high, the peripheral wants to assert the interrupt. - Output:
wire irq_n. Should be0when either peripheral is active,1when both release. - Use one explicit
pull1driver to model the resistor; usestrong0drivers 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.
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;
endmoduleWhat 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 anassignmodels an external pull-up resistor — weaker than a CMOS driver, stronger than nothing. - Switch-level pass-transistors.
pmos/nmos/cmosprimitives 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/tri1net. - 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.
Related Tutorials
- wire and tri Nets — Chapter 5.1.1; the resolution function this page extends.
- Physical Data Types — Chapter 5.1; the parent overview of the net family.
- Variables & Data Types — Chapter 5; nets and variables together.
- Number Representation — Chapter 4.4; the
1'bz,1'bxliterals strength-aware code relies on. - Operator Usage — Chapter 4.3; how
XandZpropagate through operators after strength resolution.