Skip to content

Verilog · Chapter 5.1.4 · Data & Variables

trireg Nets in Verilog

A normal wire has no memory: when every driver releases, the net floats to Z. A trireg does have memory, so when every driver releases it retains its last driven value as if a real capacitor were holding the charge. That capacitor is physical, appearing in dynamic-CMOS gates, precharge and evaluate domino logic, charge-shared bit-lines, and DRAM sense amplifiers. These are circuits that lean on parasitic capacitance to store information for a few nanoseconds before the next refresh. Verilog gives the trireg net three capacitor strengths, large, medium, and small, to rank the held charge against any later driver that might overwrite it. The mechanism is precise and the use cases are narrow, and the synthesis limitation is what keeps trireg in switch-level libraries rather than synthesisable RTL.

Foundation21 min readVerilogtriregCharge StorageDynamic CMOSSwitch-Level

Chapter 5 · Section 5.1.4 · Data & Variables

1. The Engineering Problem

You are modelling a dynamic-CMOS precharge / evaluate domino gate. During the precharge phase, a PMOS pulls the output node HIGH. During the evaluate phase, the precharge transistor turns off and the NMOS pull-down network either pulls the node LOW (if the input condition is true) or leaves it floating. The "leaves it floating" case is the whole point of dynamic logic — the output node holds its precharged HIGH value through the rest of the evaluate phase by relying on the parasitic capacitance of the node itself.

The straightforward translation to Verilog uses a regular wire:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire eval_node;
 
assign eval_node = precharge ? 1'b1 : 1'bz;   // PMOS precharge
assign eval_node = (eval & cond) ? 1'b0 : 1'bz; // NMOS evaluate

The simulation runs. During precharge, eval_node = 1. During evaluate-with-cond-false, both drivers release (drive Z), and the simulator reports eval_node = Z for the rest of the evaluate phase. Downstream logic samples Z and propagates X through every operator. The model is wrong — but the bug is not in the drivers; it's in the net type. A real silicon precharged node does not float to Z when both transistors are off; it holds its last-driven value because the parasitic capacitance keeps the charge. A wire has no such memory.

The fix is the trireg net type:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
trireg (medium) eval_node;
 
assign eval_node = precharge ? 1'b1 : 1'bz;
assign eval_node = (eval & cond) ? 1'b0 : 1'bz;

Same drivers, same statements — but now eval_node is a trireg (medium). When both drivers release, the resolution function does not return Z; it returns the last driven value at strength medium. Downstream logic sees 1 through the evaluate phase (the precharged HIGH is preserved), and the dynamic-gate model now matches the silicon.

This page is about the trireg net type, its three capacitor strengths, the resolution rules that distinguish "currently driven" from "released and holding," the optional charge-decay timing for modelling capacitor leakage, and the (narrow but real) places where trireg shows up in modern Verilog work.

2. Why trireg Exists

Static-CMOS digital logic has a simple invariant: at every cycle, every gate's output is being actively driven by either the pull-up network (PMOS, output = 1) or the pull-down network (NMOS, output = 0). The output node is never floating; a wire modelling the node is always driven by exactly one transistor.

Dynamic-CMOS logic breaks the invariant. The output node is driven during one phase of the clock and not driven during the other phase. The phases:

  • Precharge phase — the clock is LOW. A dedicated precharge PMOS pulls the output node HIGH. The evaluate NMOS network is disabled (the foot transistor at the bottom is off). The node is actively driven HIGH.
  • Evaluate phase — the clock is HIGH. The precharge PMOS turns off. The evaluate NMOS network turns on, conditional on the foot transistor. If the input condition forms a path from output to GND, the output node is pulled LOW. If the input condition does not form a path, no transistor drives the node — and the node retains its precharged HIGH value via the parasitic capacitance of the metal route, the gate inputs of downstream consumers, and the source/drain diffusion capacitance of the precharge transistor itself.

The "retains its precharged value" case is what trireg exists to model. A wire would return Z (floating) in this case; the silicon returns 1 (held by the capacitor). Without trireg, every dynamic-logic family — domino logic, NORA logic, true-single-phase clocked (TSPC) logic, charge-sharing SRAM cells, DRAM bit-lines — would simulate wrong in Verilog. The net would float to Z, downstream logic would propagate X, and the entire circuit class would be unrepresentable.

The original Verilog spec (1985) added trireg for exactly these circuit families. The keyword has lived in the language for forty years for one reason: the simulator needs to distinguish "actively driven net" from "released net with stored charge," and wire / tri cannot do that.

3. Mental Model

The mental-model has three practical consequences for any engineer touching trireg:

  • A trireg always has a value. Once any driver has charged the capacitor, the value is held indefinitely (or until the charge-decay timeout, or until another driver overwrites it). There is no "floating Z" state for a trireg once it has been driven at least once.
  • The capacitor size matters for resolution against subsequent drivers. A large capacitor (rank 4) holds against any weak (rank 3) or medium / small (ranks 2 / 1) subsequent driver. A small capacitor (rank 1) gets overridden by anything stronger than highz. The size is a strength the held value carries; the 5.1.2 strength lattice fully integrates trireg into the resolution function.
  • The initial value is X. At t = 0, before any driver has activated, the trireg's capacitor is "uncharged" — the simulator returns X to mark the absence of a stored value. Designs that rely on trireg for charge storage need a reset / precharge phase to drive every trireg to a defined state before downstream logic samples it.

4. The Three Capacitor Strengths

IEEE 1364-2005 §6.1.2 defines exactly three capacitor strengths for trireg. They occupy the middle of the strength lattice (5.1.2 §4), ranked between pull (rank 5) and weak (rank 3, also called weak1 / weak0 for direction).

KeywordRankPhysical analogue
large4Long-life dynamic node — large parasitic capacitor (e.g. a wide metal route with many gate-input loads)
medium2Default; typical-size capacitor in a dynamic gate output
small1Short-life dynamic node — minimum parasitic (e.g. a single-gate-load wire, fast to discharge)

A trireg declaration with no explicit capacitor strength defaults to medium. Most working trireg declarations use the default and only reach for large or small when the modelled circuit genuinely requires the distinction (a wide bit-line vs a single inverter input).

The three strengths matter in two places:

  1. Resolution against subsequent drivers. When a driver tries to overwrite the held charge, the strength lattice ranks driver-strength vs capacitor-strength. A pull driver (rank 5) beats a large capacitor (rank 4) — the held value is overwritten. A weak driver (rank 3) loses to a large capacitor (rank 4) — the held value persists.
  2. Charge decay (optional). A trireg can be declared with a charge-decay timeouttrireg #(charge_decay_value) (medium) node; — which specifies how many simulation time units the capacitor holds its charge before decaying to X. The decay only fires when the net is in the "held" state (no active driver); when a driver is active, the decay timer resets. The 5.1.2 page's strength axis governs the resolution behaviour during the hold; the decay timing governs how long the hold lasts.

The vast majority of trireg usage skips the decay timer and relies on the held value persisting indefinitely. The decay timer is mainly for DRAM bit-line modelling, where the simulation needs to assert "if the refresh cycle is missed, the cell forgets" within a defined window. Switch-level dynamic-logic models usually assume the next clock phase will overwrite the held value before any decay matters.

5. Syntax

trireg-syntax.v — every legal form
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
// <trireg> [#(charge_decay)] [(strength)] [range] name [= continuous-assign];
 
trireg                              q0;          // medium (default), scalar
trireg            (large)           q1;          // explicit large
trireg            (medium) [7:0]    bus;         // medium 8-bit vector
trireg            (small)  [WIDTH-1:0] payload;  // small parametric width
 
trireg #(1000)    (medium)          q_decay;     // 1000-time-unit charge decay
trireg #(500)     (small)  [3:0]    dram_cell;   // small + 500-tu decay (DRAM-like)
 
// Continuous-assign shorthand
trireg            (large)           held = drv;  // first driver inline

Three syntax constraints the language forces:

  • Capacitor strength is in its own parenthesised group. It must not be confused with the (strength1, strength0) pair used by assign statements (5.1.2 §6). A trireg's strength specifier is a single keyword (large, medium, or small), not a pair — because the capacitor's stored charge has only one strength per cycle, not two.
  • Charge-decay precedes capacitor strength. Write trireg #(decay) (strength) name; in that order. The #(decay) is a delay specifier following the same syntax as other Verilog # delay specifiers; the parenthesised capacitor strength comes next, then the range, then the identifier.
  • signed is not legal on trireg. Unlike wire / tri / wand / wor, the trireg net type does not accept a signed modifier. The held value is always treated as unsigned for resolution purposes.

5.1 The default capacitor strength is medium

If you write trireg q0; with no explicit strength, the simulator treats it as trireg (medium) q0;. Most code relies on this — explicit (medium) is unusual and reads as if the author was unsure of the default.

5.2 Charge decay is rare

The #(decay) form is uncommon in production Verilog. When you do see it, it's almost always in:

  • DRAM cell models — where the decay simulates "if refresh is delayed past N time units, the cell forgets its value."
  • Switch-level dynamic-logic libraries — where the model needs to flag "if the clock pulse is too slow, the precharged value leaks before evaluate fires."
  • Academic / research codebases — modelling charge-redistribution analog effects.

Synthesisable RTL never uses #(decay) — it's a simulation-only timing primitive with no gate-level analogue.

6. The Two States

A trireg is always in one of two simulator-tracked states. The state determines what value the net presents and at what strength.

6.1 Driven state

At least one driver is outputting a non-Z value. The trireg behaves like a wire:

  • The resolution function runs across all active drivers per the strength lattice (5.1.2 §7).
  • The resolved value is presented to consumers at the resolved strength.
  • The capacitor is implicitly "being charged" by the active driver — the capacitor's stored value at this instant equals the resolved value.
  • The charge-decay timer (if any) is inhibited — the simulator does not run it while a driver is active.

6.2 Hold state

Every driver is outputting Z. The trireg switches to its capacitor-held value:

  • The simulator presents the last value the net carried during its previous driven state.
  • The strength becomes the declared capacitor strength (large / medium / small).
  • The charge-decay timer (if any) starts counting from the moment the last driver released.

The transition from driven to hold is "released the last driver." The transition from hold back to driven is "any driver started outputting a non-Z value." Both transitions are events that wake the simulator's resolution function.

The interesting edge case: at t = 0, before any driver has ever been active, the trireg is in a third state — "never driven." The simulator presents X at the declared capacitor strength. As soon as the first driver activates, the third state ends and is never re-entered.

7. Visual Explanation

Three figures cover the trireg mechanism.

7.1 Visual A — driven vs hold transitions

The cycle-by-cycle picture. While precharge is active, the PMOS drives node = 1. While eval is active and cond is true, the NMOS drives node = 0. While both are inactive (released), the trireg retains the last driven value at capacitor strength.

trireg state transitions across precharge / evaluate phases

data flow
trireg state transitions across precharge / evaluate phases1 or Z0 or Zdriven orheldPMOSdrives 1 during precharge phaseNMOSdrives 0 during evaluate (cond true)triregholds last value when both releaseloadsamples held value
Both transistors release during the dynamic-logic 'no-pull' state. A wire would float to Z. The trireg holds the last driven value — typically the precharged 1 — through the rest of the phase, exactly modelling the parasitic capacitance of the silicon node.

7.2 Visual B — capacitor strength vs subsequent driver

The strength lattice in action. A trireg (large) holds its value at rank 4. A subsequent weak driver (rank 3) loses to the capacitor — the held value persists. A subsequent pull driver (rank 5) overrides the capacitor — the new driver's value wins.

Capacitor strength vs subsequent driver strengthtrireg (large)held: 1 at large1 (rank 4)weak0 driverdrives 0 at weak (rank 3)resolved1 (capacitor wins)trireg (large)held: 1 at large1 (rank 4)pull0 driverdrives 0 at pull (rank 5)resolved0 (driver wins)12
Two scenarios on the same large-capacitor trireg. Top: a weak driver (rank 3) is overridden by the held large-capacitor value (rank 4); the trireg keeps its previous value. Bottom: a pull driver (rank 5) overrides the held value; the trireg adopts the new value and would hold it after the pull releases.

7.3 Visual C — initial X and the first driver

At t = 0, the trireg has never been driven. The simulator presents X at the declared capacitor strength. The first driver to activate ends this "never driven" state; subsequent releases enter the hold state at the last driven value.

trireg initial X state at simulation startt = 0no driver ever activetriregX at mediumt = 5first driver activates: 1trireg1 at strong1 (driven)t = 10driver releasestrireg1 at medium (held)12
The 'never driven' state is the gotcha at simulation start. Designs that read a trireg before its first precharge will see X. The fix is a reset / precharge cycle that drives every trireg to a defined value before downstream logic samples it.

8. Dynamic-Logic Domino Gate Example

The canonical use case. A 2-input dynamic NAND in domino form: precharge HIGH, evaluate by pulling LOW if both inputs are true.

rtl/dynamic_nand_domino.v — precharge/evaluate domino NAND
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module dynamic_nand_domino (
    input  wire        clk,        // precharge HIGH; evaluate LOW
    input  wire        a,
    input  wire        b,
    output wire        y           // domino output (static buffered)
);
    // The dynamic node — needs a trireg because the evaluate phase may
    // leave both transistors off, and the silicon node holds its
    // precharged HIGH value through the rest of the phase.
    trireg (medium) dyn_node;
 
    // PMOS precharge: when clk is LOW, drive dyn_node to 1.
    //   (drives 1 actively; otherwise Z)
    assign dyn_node = ~clk ? 1'b1 : 1'bz;
 
    // NMOS evaluate: when clk is HIGH AND a AND b, drive dyn_node to 0.
    //   (drives 0 actively; otherwise Z)
    assign dyn_node = (clk & a & b) ? 1'b0 : 1'bz;
 
    // Static output buffer — the actual cell output.  Required: dynamic
    // nodes never drive downstream gates directly; always buffer.
    assign y = ~dyn_node;
endmodule

Walk the resolution function for the three phases:

  • Precharge (clk = 0). PMOS active, driving 1. NMOS released, driving Z. Resolution: 1 at strong1. dyn_node is driven; the capacitor charges. y = ~1 = 0.
  • Evaluate, condition true (clk = 1, a = 1, b = 1). PMOS released. NMOS active, driving 0. Resolution: 0 at strong0. dyn_node is driven; the capacitor discharges. y = ~0 = 1.
  • Evaluate, condition false (clk = 1, a = 0 or b = 0). PMOS released. NMOS released. Resolution: hold statedyn_node retains its last driven value (1 from the most recent precharge) at medium strength. y = ~1 = 0.

The trireg-vs-wire distinction is sharp on the third case. A wire would resolve to Z, propagating X through ~. The trireg (medium) resolves to 1 at medium, propagating cleanly through the buffer. The static-cell output y is now valid throughout the evaluate phase — exactly the silicon's behaviour.

The required pattern: the dynamic node feeds only a static inverter (or some other static buffer). Cascading dynamic gates directly violates the noise-margin assumption of dynamic logic (the next gate's precharge transistor can fight the held charge of the previous gate's output during the next cycle). Domino logic enforces this by ending every dynamic stage with a static inverter, which is why the family is named for the cascading pattern.

9. Bit-Line Charge Sharing Example

The canonical SRAM bit-line model. A precharged bit-line is shared by N storage cells; during read, exactly one cell discharges the line through its access transistor. The bit-line capacitance is much larger than any single cell's storage capacitance, so the read voltage swing is small — typically modelled with a sense amplifier.

rtl/sram_bitline.v — 4-cell SRAM bit-line read
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module sram_bitline (
    input  wire        precharge,         // precharge phase enable
    input  wire        rd_en,             // read enable
    input  wire [3:0]  word_select,       // one-hot row select
    input  wire [3:0]  cell_data,         // stored bits in the four cells
    output wire        bl_out             // bit-line output (to sense amp)
);
    // Bit-line — large capacitor because the metal route is long and
    // every cell's access transistor adds a junction capacitance.
    trireg (large) bit_line;
 
    // Precharge phase: drive bit_line HIGH at strong strength.
    assign bit_line = precharge ? 1'b1 : 1'bz;
 
    // Read phase: the selected cell drives bit_line to its stored value;
    // the unselected cells release.
    assign bit_line = (rd_en & word_select[0]) ? cell_data[0] : 1'bz;
    assign bit_line = (rd_en & word_select[1]) ? cell_data[1] : 1'bz;
    assign bit_line = (rd_en & word_select[2]) ? cell_data[2] : 1'bz;
    assign bit_line = (rd_en & word_select[3]) ? cell_data[3] : 1'bz;
 
    assign bl_out = bit_line;
endmodule

The walk:

  • Precharge active. bit_line driven to 1 at strong1. The capacitor charges to 1.
  • Read with no selected cell. All five drivers (precharge + four cells) release. bit_line holds 1 at large1 (the precharged value, at the large capacitor strength).
  • Read with selected cell storing 1. The selected cell drives 1 at strong1. The other four drivers release. The driven value wins over the held capacitor strength; bit_line = 1 at strong1.
  • Read with selected cell storing 0. The selected cell drives 0 at strong0. Resolution: strong0 beats the held large1 (rank 6 > rank 4). bit_line = 0 at strong0 — and on the next cycle when the cell releases, the capacitor now holds 0 instead of 1.

The large capacitor strength matters because real bit-lines have very large parasitic capacitance — typically modelled as large rather than medium to reflect that a weak keeper (e.g. a leakage-replacement cell on the bit-line) cannot override the precharged value. The strength-aware resolution function is what makes this model accurate.

10. Charge-Decay Modelling

The optional #(decay) form models capacitor leakage — the held value decays toward X after the specified number of simulation time units. This is the right model for DRAM cells (where refresh must arrive before the leakage time) and for long-held dynamic nodes in silicon-debug investigations.

rtl/dram_cell_model.v — DRAM cell with charge-decay timeout
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module dram_cell_model (
    input  wire        precharge,
    input  wire        write_en,
    input  wire        write_data,
    output wire        cell_q
);
    // Small capacitor + 1000-tu charge-decay timeout.
    // If no refresh / write arrives within 1000 tu, the cell decays to X.
    // Models the leakage current through the access transistor's
    // sub-threshold path on a real DRAM cell.
    trireg #(1000) (small) cell_node;
 
    // Precharge: drive HIGH for one cycle.
    assign cell_node = precharge ? 1'b1 : 1'bz;
 
    // Write: overwrite the cell with write_data.
    assign cell_node = write_en ? write_data : 1'bz;
 
    assign cell_q = cell_node;
endmodule

The decay behaviour:

  • While any driver is active, the decay timer is inhibited.
  • The moment all drivers release, the decay timer starts.
  • If a new driver activates before the timer expires, the timer resets when the new driver releases (the cell is "refreshed").
  • If the timer expires with no driver having activated, cell_node transitions from "held last value at small" to "decayed to X at small."

The 1000 is in time units (the `timescale directive's primary unit). A `timescale 1ns/1ps then makes the decay 1000 nanoseconds. Real DRAM cells decay over milliseconds; the simulation typically scales the decay aggressively for runtime — accepting that "decay in 1000 ns" simulates the same logical behaviour as "decay in 64 ms" in the silicon, just compressed.

#(decay) is rare. Use it only when the simulation needs to verify "did the refresh arrive in time?" — which is essentially DRAM controller verification. Dynamic-CMOS modelling skips the decay entirely; the assumption is that the next clock phase will overwrite the held value before any decay matters.

11. Simulation Behavior

The simulator tracks each trireg's state (driven or hold), the last driven value, the capacitor strength, and (optionally) the decay timer. Updates happen on three events:

  1. A driver changes value or strength. The simulator re-runs the resolution function over all active drivers. If at least one driver is non-Z, the net is in driven state with the resolved value; if all drivers are Z, the net transitions to hold state with the most-recently-driven value at capacitor strength.
  2. The decay timer expires. Only fires when the net has been in hold state for the full decay window with no driver activity. The net transitions to X at capacitor strength.
  3. Re-activation. Any driver becoming non-Z while the net is in hold or decayed state transitions back to driven state; the decay timer (if any) is cancelled and resets.

The simulator's internal trireg data structure typically carries: current state (driven / hold / decayed / never-driven), last driven value, last driven strength, capacitor strength (compile-time), decay window (compile-time), pending decay event handle (runtime). Every event causes a state-machine transition.

12. Waveform Analysis

Two waveforms cover the trireg behaviour.

12.1 Waveform #1 — domino dynamic NAND

Dynamic NAND in domino form — trireg (medium) dyn_node

12 cycles
Dynamic NAND in domino form — trireg (medium) dyn_nodePrecharge — PMOS drives dyn_node = 1Precharge — PMOS drive…Evaluate, a=0 → no pull-down, dyn_node HELD at 1Evaluate, a=0 → no pul…Evaluate, a=b=1 → NMOS drives dyn_node = 0; y = 1Evaluate, a=b=1 → NMOS…Precharge again — dyn_node back to 1Precharge again — dyn_…Evaluate, a=b=1 → dyn_node = 0; y = 1Evaluate, a=b=1 → dyn_…Evaluate, a=b=1 → dyn_node = 0; y = 1Evaluate, a=b=1 → dyn_…clkabdyn_nodeyphasepreeval-hpreeval-hpreevalpreeval-hpreevalpreevalt0t1t2t3t4t5t6t7t8t9t10t11
The 'phase' row labels precharge vs evaluate. The 'eval-h' label marks evaluate phases where the NMOS condition is false — both transistors are off, and dyn_node would float to Z on a wire but HOLDS the precharged 1 on the trireg. The downstream static buffer y reads 0 (= ~1) throughout these held phases. This is the entire reason trireg exists.

The interesting cycles are the held evaluate phases (cycles 1, 3, 7). Both PMOS and NMOS are off; on a wire, dyn_node would resolve to Z and y would propagate X through ~. On the trireg (medium), the resolution function detects all drivers released and returns the last driven value (1 from the most recent precharge) at medium strength. y = ~1 = 0 throughout the hold, and the downstream consumer never sees X.

12.2 Waveform #2 — held value overridden by stronger driver

trireg (medium) held value vs subsequent pull and weak drivers

12 cycles
trireg (medium) held value vs subsequent pull and weak driversStrong driver active — tr_node = 1 at strong1Strong driver active —…Strong releases — tr_node HELDS 1 at mediumStrong releases — tr_n…Pull driver active — beats medium (rank 5>4); tr_node = 0 at pull0Pull driver active — b…Pull releases — tr_node HOLDS 0 at mediumPull releases — tr_nod…Weak driver active — weak0 (rank 3) beats medium (rank 2)Weak driver active — w…Weak releases — tr_node HOLDS 0 at mediumWeak releases — tr_nod…clkdrv_strongdrv_pulldrv_weaktr_nodetr_strstrong1strong1mediummediumpull0pull0mediummediummediummediummediummediumt0t1t2t3t4t5t6t7t8t9t10t11
The 'tr_str' row shows the strength the trireg is currently presenting. When driven, it's the driver's strength. When held, it's the capacitor strength (medium = rank 2). A pull driver (rank 5) easily overrides medium; a weak driver (rank 3) overrides medium too (rank 3 > rank 2). To make a held value resist a weak driver, declare the trireg as large (rank 4) — see §7.2 for the visual.

The cycle-8 case is the lattice-aware lesson: medium is rank 2, weaker than weak (rank 3). A medium capacitor is not strong enough to resist a weak driver. To model a circuit where the dynamic node's stored charge should resist a weak keeper or leakage-replacement driver, declare the trireg as large (rank 4) — which beats weak (rank 3) but loses to pull (rank 5). The capacitor-strength choice is exactly the strength-lattice trade-off: pick the rank that matches the real physical capacitance relative to all other drivers in the circuit.

13. Synthesis Insight

trireg is rejected by every modern synthesis tool.

The reason is the same as wand / wor (5.1.3) and internal tri (5.1.1): no standard-cell library provides cells that implement capacitor-storage behaviour. The synthesis tool has no way to map a trireg to gates. The mapping options would be:

  1. A flip-flop or latch with an enable signal. The tool could infer a storage element from the multi-driver-with-release pattern, but the inference would be fragile (it has to recognise the "all drivers Z" hold condition) and the result would not match the dynamic-logic behaviour (a flop is clocked; a trireg is event-driven by driver releases).
  2. Direct mapping to a custom dynamic-logic cell. A few foundries provide characterised dynamic-logic cells (typically in their datapath / multiplier macros), but these are not part of the standard-cell library and the synthesis tool does not infer them.

Most synthesis tools therefore reject trireg with an explicit error:

Error: Unsupported net type trireg at instance dyn_node. Use a flip-flop or latch with an enable signal for dynamic storage, or instantiate a dynamic-logic library cell.

The practical consequence: trireg lives only in simulation-only models — testbench BFMs, switch-level libraries, hand-characterised dynamic-logic cells. Synthesisable RTL never declares trireg.

The replacement pattern for synthesisable storage is straightforward — replace the dynamic node with a clocked flip-flop and explicit enable:

static-replacement.v — flop replaces trireg for synthesis
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Original dynamic-logic pattern (simulation-only):
trireg (medium) dyn_node;
assign dyn_node = ~clk ? 1'b1 : 1'bz;
assign dyn_node = (clk & a & b) ? 1'b0 : 1'bz;
 
// Synthesisable rewrite — static logic with a registered output:
reg dyn_node_q;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)        dyn_node_q <= 1'b1;          // precharge equivalent
    else if (a & b)    dyn_node_q <= 1'b0;          // evaluate equivalent
    else               dyn_node_q <= dyn_node_q;    // hold equivalent (no-op)
end
assign y = ~dyn_node_q;

The rewrite produces the same logical behaviour but on static CMOS gates — no dynamic-logic noise margin concerns, no charge-decay timing, no trireg. The performance / area trade-off (dynamic logic was historically faster than static for the same gate count) is no longer competitive on modern process nodes; static logic plus a deeper pipeline beats dynamic logic on every metric except very specialised low-leakage circuits.

14. Industry Use Cases

The three places trireg actually shows up in modern engineering work:

14.1 Dynamic-logic cell models

A handful of foundries still provide custom dynamic-logic cells for high-speed datapath blocks — wide multipliers, dynamic adders, register-file decoders. The Verilog behavioural model of these cells uses trireg to model the precharge / evaluate / hold cycle. The synthesis flow does not actually run on these models; the standard-cell library carries the hand-characterised cell at a layout/timing level, and the synthesis tool instantiates the cell directly. The trireg model is for simulation only — gate-level and SDF-annotated simulation use the foundry's library cell.

14.2 DRAM cell behavioural models

Verification environments for memory controllers include a behavioural model of the DRAM array. Each cell is a trireg with a charge-decay timeout that simulates the refresh requirement. The decay timeout is typically scaled aggressively (1000 ns instead of 64 ms) to keep the simulation tractable, but the logical behaviour — "refresh must arrive before decay or the cell forgets" — is preserved exactly.

14.3 Switch-level cell libraries

Pre-2000 standard-cell libraries from Intel, IBM, and Motorola included switch-level Verilog models that used trireg to represent the parasitic capacitance on every internal node. The 5.1.2 strength-attenuation pattern from the pass-transistor section combines with trireg to model "value passes through, attenuated; node retains its charge after the pass-transistor turns off." Modern flows skip this layer — synthesis works directly on the standard-cell library — but maintenance work on legacy IP may still touch these models.

In all three cases, trireg is doing a modelling job, not generating a circuit. Real silicon uses real capacitors; trireg makes the simulator behave the same way.

15. Common Mistakes

Five pitfalls that catch engineers reading or writing trireg code.

15.1 Using trireg for digital state

The most common misuse. An engineer wants a signal to "remember its last value" — i.e., act as digital state — and reaches for trireg because the keyword's behaviour sounds right. The result is a trireg declaration in RTL that the synthesis tool then rejects. The right answer for digital state is a flip-flop (reg declared inside an always @(posedge clk) block) or a latch (always @(*) with conditional assignment). trireg models analog charge storage on a physical capacitor — not digital state.

15.2 Forgetting the initial X

At t = 0, every trireg is X until the first driver activates. Testbenches that read a trireg before the precharge cycle has run will see X and propagate it. Add a reset / precharge phase at simulation start that drives every trireg to a defined value before downstream logic samples it. (This is the same discipline as for any reg — initialise before reading.)

15.3 Mismatched capacitor strength vs subsequent driver

A trireg (medium) is rank 2, which is weaker than weak (rank 3). A weak keeper driver downstream of the trireg will override the held value. If the intent was "the capacitor's stored charge should resist a weak driver," the right declaration is trireg (large) (rank 4 > weak's rank 3). The capacitor-strength choice has to match the modelled circuit's relative capacitance, not just feel like a reasonable default.

15.4 Charge-decay timer never firing

The #(decay) timer resets every time a driver activates. If the driver activates frequently — e.g. every clock cycle — the decay timer never has time to expire, and the trireg behaves as if no decay timer existed. This is usually fine (the simulation matches the silicon), but it confuses engineers who expect the decay to fire on a fixed schedule. The decay only fires when the net stays in hold state continuously for the full window.

15.5 Vector trireg resolves per-bit

A 4-bit trireg is four independent capacitors. Each bit holds its last driven value independently — and each bit's decay timer (if any) runs independently. Don't reason about a vector trireg as a single stored value; reason bit by bit.

16. Debugging Lab

Three trireg debug post-mortems

17. Coding Guidelines

  1. Use trireg only when a physical capacitor is doing physical work. Dynamic CMOS, precharge / evaluate, DRAM cells, bit-line modelling, switch-level libraries. Not for digital state — that's a flop or latch.
  2. Match the capacitor strength to the modelled circuit's relative capacitance. large for wide bit-lines and long metal routes; medium (default) for typical dynamic gate outputs; small for fast-decay nodes. The choice has to make the trireg-vs-other-driver resolution come out right.
  3. Every trireg needs a precharge / reset / first-driver phase before the read. The initial X at t = 0 does not disappear on its own; downstream logic that samples before precharge will see X and propagate it.
  4. Drivers output Z for "released." Using 1'b1 or 1'b0 to mean "released" overrides the held value instead of letting it persist — the trireg's hold-state semantic depends on every non-active driver outputting Z.
  5. Skip #(decay) unless the simulation genuinely needs to verify refresh timing. Most dynamic-logic models do not need the decay; the next clock phase will overwrite the held value before any decay matters.
  6. trireg is simulation-only. Synthesis tools reject it. Synthesisable RTL uses a clocked flop or latch with an enable for any digital state; the trireg declaration belongs in testbenches and behavioural models only.
  7. Vector trireg resolves per-bit. A 4-bit trireg is four independent capacitors. Each bit holds and decays independently.
  8. Document the modelled circuit. A one-line comment naming the dynamic-logic family ("domino NAND output node — held across evaluate when no NMOS pull-down path") distinguishes intentional trireg use from misuse for digital state.

18. Interview Q&A

19. Exercises

Three exercises that turn the trireg model into reflex.

Exercise 1 — Predict the resolved value

For each scenario, predict the trireg's state and resolved value.

#trireg strengthLast driven valueCurrent driversResolved (value, strength)
1large1all releasing?
2medium0one pull1 driver?
3small1one weak0 driver?
4large0one pull1 and one weak0?
5mediumX (never driven)one strong1 driver?
6large1one weak0 driver?

Hints. Compare driver strengths to the capacitor strength using the 5.1.2 lattice. If no driver is active, the trireg is in hold state.

Exercise 2 — Model a 2-input dynamic AND in domino form

Write a Verilog module dynamic_and_domino that implements a 2-input dynamic AND gate in domino logic. Specifications:

  • Inputs: wire clk (precharge LOW, evaluate HIGH), wire a, wire b.
  • Output: wire y.
  • Behaviour: during precharge, the dynamic node is HIGH. During evaluate, the dynamic node falls to 0 only if a & b is true. The static output is y = ~dyn_node (the domino convention).

Hints. A 2-input dynamic AND needs the NMOS pull-down to fire when a & b is true — drive the dynamic node to 0 when clk & a & b. The precharge drives the dynamic node to 1 when ~clk. Use trireg (medium) dyn_node; for the dynamic node and a static assign y = ~dyn_node; for the buffered output.

Exercise 3 — Spot the trireg misuse

The following module appears in a synthesisable file and the synthesis tool errors out. Identify the misuse and rewrite the module without trireg while preserving the behaviour.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module sample_and_hold (
    input  wire        sample,
    input  wire        data_in,
    output wire        held_q
);
    trireg (large) hold_node;
    assign hold_node = sample ? data_in : 1'bz;
    assign held_q = hold_node;
endmodule

What to produce. (a) Explain why the synthesis tool rejects this module. (b) Rewrite using a clocked flip-flop (with a clock input added) and explicit held_q output. (c) Identify one behavioural difference between the trireg version and the flop version — specifically, what happens at t = 0 before sample first asserts.

20. Summary

trireg is Verilog's capacitive-storage net — the language's primitive for modelling parasitic charge storage on dynamic-CMOS nodes. When at least one driver is active, a trireg behaves like a wire and resolves to the driven value at the driver's strength. When all drivers release, the trireg transitions to hold state and presents its last driven value at the declared capacitor strengthlarge (rank 4), medium (rank 2, the default), or small (rank 1).

The three capacitor strengths sit in the middle of the IEEE 1364 strength lattice between pull and weak, so the held charge resists a weak driver (only large capacitor) and yields to a pull driver. An optional #(decay) form models capacitor leakage — the held value decays to X after the specified simulation time if no driver activates.

The use cases are narrow:

  • Dynamic-CMOS gate models — precharge / evaluate, domino logic, NORA, TSPC.
  • DRAM cell models — with #(decay) to verify refresh timing.
  • SRAM bit-line modelslarge capacitor on the bit-line, pull0 from the selected cell, strong1 from precharge.
  • Switch-level library cells — modelling parasitic capacitance on every internal node.

The synthesis story is the reason trireg stays in simulation-only code: every modern synthesis tool rejects trireg because no standard-cell library provides capacitor-storage cells. Synthesisable RTL that needs "remember last value" uses a flip-flop or latch with an enable — never trireg.

The day-to-day discipline:

  • Use trireg only when a physical capacitor is doing physical work. Dynamic CMOS, charge sharing, dynamic memory. Not for digital state — that's a flop.
  • Match the capacitor strength to the circuit's relative capacitance. large for wide bit-lines; medium for typical dynamic gates; small for fast-decay nodes.
  • Every trireg needs a precharge / reset phase before the read. The initial X does not disappear on its own.
  • Drivers output Z for "released." Active 0 or 1 overrides the held value instead of letting it persist.
  • #(decay) is rare. Use it only for verifying refresh timing — most dynamic-logic models don't need it.
  • trireg cannot be synthesised. Synthesisable RTL replaces it with a clocked flop / latch.

The sibling sub-pages drill further into the strength-aware net family: 5.1.5 tri0 / tri1 Nets covers implicit pull-up / pull-down drivers; 5.1.6 supply0 / supply1 covers the power and ground rail-tie nets at supply strength. Chapter 12 Switch-Level Modeling picks up the pmos / nmos / cmos primitives that combine with trireg to model full dynamic-logic cells.