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
triregnet 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.
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
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.
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.
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
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.
%balso does not show strength;%vwould print, for example,St1forstrong1_netandWe1forweak1_netat T=20. - The highz drivers produce z.
highz1_netiszwheneverais 1, for example at T=20, andhighz0_netiszwheneverbis 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 % 2returns -1, 0 or 1, as explained in Verilog $random. Whenidxis -1,values[idx]is out of range and reads as x. Using{$random} % 2would 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
| Case | Drivers | line | %v |
|---|---|---|---|
| (a) en = 1, data = 1 | strong1, pull0, weak1 | 1 | St1 |
| (b) en = 1, data = 0 | strong0, pull0, weak1 | 0 | St0 |
| (c) en = 0 | z, pull0, weak1 | 0 | Pu0 |
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
triregnets, 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
pullupmodel open-drain buses such as I2C, and%vprints the strength of a net - Strengths only affect simulation; synthesis ignores them