In real hardware, several drivers can be connected to the same wire. A strong transistor can overpower a weak resistor, and a wire that nobody drives can keep its voltage for a while as stored charge. Verilog models this with strength: every value on a net carries a strength level as well as its logic value, and when drivers disagree, the stronger one decides the result. Net declarations and continuous assignments can specify two kinds of strength: charge strength and drive strength.

12 min read | Beginner Level

What You'll Learn

  • The eight strength levels, from supply down to highz
  • How charge strength lets a trireg net hold its value when it is not driven
  • How drive strength decides the value of a net with several drivers
  • How to model pull-ups and open-drain buses, and how to print strengths with %v

Verilog defines eight strength levels. Five are drive strengths, used by gates and continuous assignments that actively drive a net. Three are charge strengths, used only by trireg nets to model the charge stored on a wire.

The eight strength levels from strongest to weakest: 7 supply (supply0, supply1, drive), 6 strong (strong0, strong1, the default drive strength), 5 pull (pull0, pull1, drive), 4 large (charge), 3 weak (weak0, weak1, drive), 2 medium (the default charge strength for trireg), 1 small (charge), 0 highz (highz0, highz1, drive). When two drivers disagree, the higher level wins

Charge Strength

A trireg net models a wire with capacitance. When all of its drivers turn off, instead of becoming z like a normal wire, it keeps the last value it was driven to, stored as charge. The size of that charge is given as small, medium or large, and medium is the default.


trireg                          a_net;    // strength medium by default
trireg   (medium) #(0, 0, 100)  cap1;     // strength medium, charge decay time of 100 time units
trireg   (large)  [3:0]         cap2;     // strength large, no decay time

The third value in the delay of a trireg is the charge decay time: how long the stored value lasts after the drivers turn off. When it runs out, the net becomes x, because the real voltage would slowly drift to an undefined level. Without a decay time, as for cap2, the value is kept forever.


bufif1 (cap1, data, en);    // Drives cap1 with data while en is 1
Waveforms of en, data and the trireg cap1: while en is 1, cap1 follows data and is 1; when en goes to 0 at 30, the buffer stops driving and cap1 keeps the value 1 as stored charge, even when data changes at 60; after the charge decay time of 100, at 130, cap1 becomes x

The stored charge never competes with the drivers of the same trireg: as soon as any of its drivers drives a 0 or 1, even with weak strength, the net takes the driven value, and the charge only matters while all drivers are off. Charge strengths come into play when two trireg nets are connected, for example through a tran switch, and share their charge: the net with the larger charge then decides the value.

Drive Strength

Drive strength describes how strongly a gate or continuous assignment drives its output. When a net has several drivers with different values, the driver with the higher strength sets the value of the net. If the strongest drivers have the same strength but different values, the result is x.

The strength of a continuous assignment is written in parentheses after assign:


assign  (strength1, strength0) net = expression;
  • strength1 is the strength used when the expression is 1
  • strength0 is the strength used when the expression is 0

The two can be written in either order, because each keyword already says which value it belongs to. Without a strength specification, a driver uses (strong1, strong0).


wire out;

assign (strong1, weak0) out = a & b; // Drives 'out' with strong1 when true

Here out is driven with strength strong1 when a & b is 1, and with weak0 when it is 0. On its own, the logic value of out is the same as without the strength; the difference only shows when another driver on out disagrees: a pull1 driver could override its weak 0, but nothing below supply strength can override its strong 1.

Three cases of two drivers on one net: strong1 and pull0 give net = 1 with strength St1, because the stronger driver wins; pull1 and pull1 give net = 1 with strength Pu1, because the values agree; strong1 and strong0 give net = x with strength StX, because equal strengths with different values conflict

The %v format specifier prints the strength and value of a net as three characters, such as St1 for strong 1, Pu0 for pull 0, We1 for weak 1, HiZ for high impedance and StX for a conflict between strong drivers.

Quick Check

A net has two drivers: assign (pull1, pull0) n = 1'b0; and assign (weak1, weak0) n = 1'b1;. What is the value of n?

Show Answer

0. The first driver drives 0 with pull strength (5), and the second drives 1 with weak strength (3). The stronger driver wins, so n is 0, and %v prints Pu0.

Verilog Strength Keywords

supply0, supply1, strong0, strong1, pull0, pull1, weak0, weak1, highz0, highz1

supply0 and supply1 are the strongest levels. They model connections to ground and power, which no other driver can override. They are mostly used through the net types supply0 and supply1, as in switch level modeling.

strong0 and strong1 model a driver that actively pulls the net low or high with full force, like the output transistors of a logic gate. This is the default strength of every gate and continuous assignment, and it overrides pull and weak drivers.


assign (strong1, weak0) my_net = some_signal;    // Drives my_net high with strong strength
assign (weak1, strong0) my_net = another_signal; // Drives my_net low with strong strength

These two assignments drive the same net. When some_signal is 1, it drives a strong 1; when another_signal is 0, it drives a strong 0. If both happen at the same time, the two strong values conflict and my_net is x. In every other case, the strong value overrides the weak one from the other assignment.

pull0 and pull1 model resistive drivers, such as pull-up and pull-down resistors. They set the value of a net when no stronger driver is active, and lose against any strong driver.


assign (pull1, pull0) my_net = some_signal; // Pulls up to 1 unless driven low

The built-in primitives pullup and pulldown are a shorter way to put a constant pull driver on a net: pullup (my_net); drives pull1, and pulldown (my_net); drives pull0.

weak0 and weak1 are weaker than pull. They model drivers that only hold a value when nothing else is driving, such as a small keeper circuit on a bus.


assign (weak1, weak0) my_net = some_signal; // Drives my_net with weak strengths

highz0 and highz1 mean that the driver does not drive the net at all for that value: the driver's output is z. This is how a driver that can only pull in one direction is modeled.


assign (highz1, pull0) my_net = some_signal; // Drives z when some_signal is 1, pull0 when it is 0

Two rules apply to strength specifications:

  • (highz1, highz0) and (highz0, highz1) are illegal, because a driver that never drives anything makes no sense.
  • When no strength is given, the default is (strong1, strong0).

Synthesis result: Synthesis tools ignore drive and charge strengths. In real hardware, strength comes from the size of transistors and resistors. Only pullup and pulldown on top-level ports are sometimes translated into pad configuration for an FPGA or chip I/O; everything else about strength is for simulation.

Modeling an Open-Drain Bus

The combination of highz1, strong0 drivers and a pull-up is how buses such as I2C, interrupt lines and reset lines are built: every device can pull the line low, nobody drives it high, and a resistor pulls it up to 1 when all devices let go.

Open-drain bus: two drivers d1 and d2, each with strength (highz1, strong0), and a pull-up resistor with pull1 strength from vdd are connected to the same bus. When d1 and d2 are both 1, both drivers release the bus and the pull-up makes it 1 with strength Pu1. When d1 or d2 is 0, the strong0 driver beats the pull-up and the bus is 0 with strength St0

module tb;
  reg  d1, d2;
  wire bus;

  assign (highz1, strong0) bus = d1;   // Open-drain driver 1
  assign (highz1, strong0) bus = d2;   // Open-drain driver 2
  pullup (bus);                        // Pull-up resistor

  initial begin
    $monitor ("[%0t] d1=%b d2=%b bus=%b (%v)", $time, d1, d2, bus, bus);
    d1 = 1; d2 = 1;
    #10 d1 = 0;
    #10 d1 = 1; d2 = 0;
    #10 d2 = 1;
  end
endmodule
[0] d1=1 d2=1 bus=1 (Pu1)
[10] d1=0 d2=1 bus=0 (St0)
[20] d1=1 d2=0 bus=0 (St0)
[30] d1=1 d2=1 bus=1 (Pu1)

The bus is 1 only while both drivers release it, so it computes the AND of d1 and d2 without any gate, which is called a wired-AND. Two drivers pulling low at the same time cause no conflict, because they drive the same value.

I2C and Shared Interrupt Lines

On an I2C bus, the clock and data lines are open-drain, so any device on the bus can hold the clock low to slow the transfer down, and a device that sends a 1 while another sends a 0 can detect the collision by reading the line. Shared interrupt and reset lines on circuit boards work the same way. Simulation models of these buses use (highz1, strong0) drivers and pullup, so that the model behaves like the real wire.

Bus Keepers

A tri-state bus that is not driven floats, and a floating input can make a receiver switch randomly and waste power. Many designs add a bus keeper: a weak driver that holds the bus at its last value until a real driver takes over. In a model, it is a driver with weak strength fed back from the bus, which any normal driver easily overrides.

Verilog Strength Example

The example below drives several nets, each with a different strength specification, from randomly chosen inputs.


module tb;

    // Declare nets with different strengths
    reg a, b, c, d;
    reg [1:0] values = {1'b1, 1'b0};
    integer idx;

    wire strong0_net, strong1_net;
    wire pull0_net, pull1_net;
    wire weak0_net, weak1_net;
    wire highz0_net, highz1_net;

    // Assign strong drivers
    assign (strong1, weak0) strong1_net = a; // Strongly drives high if 'a' is 1
    assign (weak1, strong0) strong0_net = b; // Strongly drives low if 'b' is 0

    // Assign pull drivers
    assign (pull1, pull0) pull1_net = c; // Pulls up to 1 unless driven low
    assign (pull0, pull1) pull0_net = d; // Pulls down to 0 unless driven high

    // Assign weak drivers
    assign (weak1, weak0) weak1_net = a; // Weakly drives high if 'a' is 1
    assign (weak0, weak1) weak0_net = b; // Weakly drives low if 'b' is 0

    // Assign high impedance drivers
    assign (highz1, pull0) highz1_net = a; // z when 'a' is 1, pull0 when 'a' is 0
    assign (highz0, pull1) highz0_net = b; // z when 'b' is 0, pull1 when 'b' is 1

    initial begin
      repeat (10) begin
        #10;
        idx = $random % 2;
        a = values[idx];
        idx = $random % 2;
        b = values[idx];
        idx = $random % 2;
        c = values[idx];
        idx = $random % 2;
        d = values[idx];
      end
    end

    initial
      $monitor("[%0t] a=%0b b=%0b c=%0b d=%0b strong1=%0b strong0=%0b pull1=%0b pull0=%0b weak1=%0b weak0=%0b highz1=%0b highz0=%0b", $time, a, b, c, d, strong1_net, strong0_net, pull1_net, pull0_net, weak1_net, weak0_net, highz1_net, highz0_net);

endmodule
Output
xcelium> run
[0] a=x b=x c=x d=x strong1=x strong0=x pull1=x pull0=x weak1=x weak0=x highz1=x highz0=x
[10] a=0 b=x c=x d=x strong1=0 strong0=x pull1=x pull0=x weak1=0 weak0=x highz1=0 highz0=x
[20] a=1 b=1 c=x d=0 strong1=1 strong0=1 pull1=x pull0=0 weak1=1 weak0=1 highz1=z highz0=1
[30] a=1 b=1 c=0 d=1 strong1=1 strong0=1 pull1=0 pull0=1 weak1=1 weak0=1 highz1=z highz0=1
[40] a=1 b=0 c=1 d=0 strong1=1 strong0=0 pull1=1 pull0=0 weak1=1 weak0=0 highz1=z highz0=z
[50] a=x b=0 c=1 d=x strong1=x strong0=0 pull1=1 pull0=x weak1=x weak0=0 highz1=x highz0=z
[60] a=0 b=1 c=0 d=0 strong1=0 strong0=1 pull1=0 pull0=0 weak1=0 weak0=1 highz1=0 highz0=1
[70] a=0 b=x c=0 d=x strong1=0 strong0=x pull1=0 pull0=x weak1=0 weak0=x highz1=0 highz0=x
[80] a=x b=x c=x d=0 strong1=x strong0=x pull1=x pull0=0 weak1=x weak0=x highz1=x highz0=x
[90] a=0 b=0 c=0 d=x strong1=0 strong0=0 pull1=0 pull0=x weak1=0 weak0=0 highz1=0 highz0=z
[100] a=0 b=x c=x d=x strong1=0 strong0=x pull1=x pull0=x weak1=0 weak0=x highz1=0 highz0=x
xmsim: *W,RNQUIE: Simulation is complete.

Three things stand out in this log:

  • Most nets simply follow their input. Each net has only one driver, so there is never a conflict, and the strength does not change the logic value. %b also does not show strength; %v would print, for example, St1 for strong1_net and We1 for weak1_net at T=20.
  • The highz drivers produce z. highz1_net is z whenever a is 1, for example at T=20, and highz0_net is z whenever b is 0, for example at T=40, because those drivers do not drive that value at all.
  • The x values come from the stimulus, not from strength. $random % 2 returns -1, 0 or 1, as explained in Verilog $random. When idx is -1, values[idx] is out of range and reads as x. Using {$random} % 2 would give only 0 and 1.

Quick Check

If a second driver assign (pull1, pull0) weak1_net = 1'b0; were added to the example, what would weak1_net be when a is 1?

Show Answer

0. The original driver drives weak1, and the new one drives pull0. Pull is stronger than weak, so the net is 0 whenever the original driver drives a weak value, which is always.

Common Beginner Mistakes

Mistake #1: Expecting strength to change the value of a net with one driver

Wrong: Writing assign (weak1, weak0) n = a; and expecting n to behave differently from assign n = a;
Right: Use strengths when a net has several drivers, and check them with %v

Why? Strength only decides between drivers that disagree. With a single driver, the net always takes that driver's value, as the strength example shows.

Mistake #2: Two drivers of equal strength driving different values

Wrong: Two plain assign statements to the same net, one driving 1 and the other 0
Right: Make all but one driver release the net (drive z), or give them different strengths on purpose

Why? Both drivers default to strong, so the net becomes x. In hardware, this is bus contention: two outputs fight, causing a large current and an undefined voltage.

Mistake #3: Using a strength that never drives

Wrong: assign (highz1, highz0) n = a;
Right: At most one of the two strengths can be highz, as in (highz1, strong0)

Why? A driver that is high impedance for both values never drives the net, so the compiler rejects the declaration.

Mistake #4: Expecting strengths to reach the hardware

Wrong: Relying on (weak1, weak0) or a trireg to make synthesized logic hold or resolve a value
Right: Describe the real circuit, such as a tri-state buffer with an enable, and use pull-ups at the pins where the board or I/O cell provides them

Why? Synthesis ignores strengths and charge. A design whose correct behavior depends on them works in simulation but not in the synthesized netlist.

Practice Exercise

Challenge: Bus with a Pull-Down and a Keeper

A net line has three drivers: a tri-state driver bufif1 (line, data, en);, a pulldown (line);, and a weak driver assign (weak1, weak0) line = 1'b1;. Predict the value of line, and the strength that %v prints, for these cases: (a) en = 1, data = 1; (b) en = 1, data = 0; (c) en = 0.

Hint

When en is 0, the bufif1 drives z. Then compare the remaining drivers: pull0 from the pulldown and weak1 from the assign.

Solution
CaseDriversline%v
(a) en = 1, data = 1strong1, pull0, weak11St1
(b) en = 1, data = 0strong0, pull0, weak10St0
(c) en = 0z, pull0, weak10Pu0

The bufif1 drives with the default strong strength, so it wins whenever it is enabled. When it is disabled, the pull-down (5) beats the weak driver (3), so the weak 1 never shows. To make the weak driver visible, the pull-down would have to be removed.

Summary

In this tutorial, you learned:

  • Every value on a net has a strength, from supply (7) through strong, pull, large, weak, medium and small down to highz (0)
  • Charge strengths (small, medium, large) apply to trireg nets, which keep their value as stored charge and can decay to x after a decay time
  • Drive strengths are written as assign (strength1, strength0), with (strong1, strong0) as the default
  • When drivers disagree, the stronger one wins; equal strengths with different values give x
  • highz strengths and pullup model open-drain buses such as I2C, and %v prints the strength of a net
  • Strengths only affect simulation; synthesis ignores them
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5