VERILOG SERIES · MODULE 14

Switch Level Modelling — VLSI Trainers
Verilog Series · Module 14

Switch Level Modelling

The lowest abstraction level in Verilog — model circuits at the MOS transistor level using nmos, pmos, cmos, and bidirectional switch primitives with truth tables, circuit diagrams, and complete examples.

Introduction

Switch level modelling is the lowest abstraction level available in Verilog. It models circuits as networks of MOS transistor switches connected by nodes — exactly as a circuit designer thinks when drawing a transistor-level schematic.

At this level, the fundamental building block is the transistor switch — a three-terminal device where a gate signal controls whether current flows between drain and source. Verilog provides a complete set of built-in switch primitives that directly correspond to NMOS and PMOS transistors in CMOS technology.

🔬
Transistor Level
Models individual MOS transistors — the actual physical devices that implement all digital logic in silicon.
🏗
Standard Cell Design
Used to describe the internals of standard cells (inverter, NAND, NOR, mux) before they are characterised and placed in a library.
Propagates z
Unlike gate primitives (where z inputs become x), switch primitives propagate z through — modelling real transistor high-impedance behaviour.
🧪
Simulation Only
Switch level is used for custom cell characterisation and simulation verification. Synthesis tools operate at gate or RTL level, not switch level.
When to use switch level: Primarily for designing and verifying standard cell internals, SRAM bit cells, sense amplifiers, charge pumps, and other circuits where transistor-level behaviour (charge sharing, weak drives, body effects) matters for correctness. In regular RTL design flows, gate or data-flow level is used instead.

🏗 Where Switch Level Fits

Switch level is the lowest and most detailed abstraction in Verilog — directly below gate level. The same circuit can be described at any of these levels:

Behavioral
always / if / case
Data Flow
assign y = ~a
Gate Level
not g1(y, a)
Switch Level ▶
pmos + nmos
Fig 1 — Same inverter at all four abstraction levels
// Behavioral
always @(*) y = ~a;

// Data Flow
assign y = ~a;

// Gate Level
not g1(y, a);

// Switch Level — explicit transistors
pmos p1(y, vdd, a);   // pull-up: ON when a=0
nmos n1(y, gnd, a);   // pull-dn: ON when a=1
Key difference from gate primitives: Switch primitives model the transistor, not the logic function. They are directional (source → drain) when conducting, and they propagate z through instead of converting it to x — matching real silicon behaviour.

🔌 Basic Transistor Switches

Verilog provides six unidirectional MOS switch primitives — three ideal (fully conducting) and three resistive (partially conducting, lower signal strength). All have the same three-terminal structure:

output
Drain
signal exits here
input
Source
signal enters here
input
Gate
controls conduction
Port order for all MOS switches: primitive_name instance_name (drain, source, gate); — drain first, then source, then gate. The gate controls whether drain-source conduct.
nmos N-channel MOSFET (ideal)
Conducts (ON) when gate = 1. Blocks (OFF) when gate = 0. Passes signal from source to drain at full strength.
nmos n1(drain, source, gate);
pmos P-channel MOSFET (ideal)
Conducts (ON) when gate = 0. Blocks (OFF) when gate = 1. Passes signal from source to drain at full strength.
pmos p1(drain, source, gate);
cmos Complementary pair (ideal)
Combines nmos and pmos in one primitive with complementary enables. Four ports: drain, source, ngate, pgate.
cmos c1(out, data, nctrl, pctrl);
rnmos N-channel MOSFET (resistive)
Same as nmos but attenuates signal strength by one level when conducting. Models a high-resistance NMOS path.
rnmos rn1(drain, source, gate);
rpmos P-channel MOSFET (resistive)
Same as pmos but attenuates signal strength. Models a high-resistance PMOS path (e.g., pass transistor with body effect).
rpmos rp1(drain, source, gate);
rcmos Complementary pair (resistive)
Resistive version of cmos — attenuates signal strength. Used for pass-gate logic where reduced strength is significant.
rcmos rc1(out, data, nctrl, pctrl);

🟠 nmos — N-Channel MOSFET Switch

The nmos primitive models an N-channel MOSFET transistor. Its gate terminal is the control input — when the gate is high (logic 1), the transistor turns ON and conducts, passing the source signal to the drain. When the gate is low, the transistor turns OFF and the drain floats to z.

Fig 2 — nmos transistor symbol, truth table, and Verilog
Circuit Symbol
drain source gate N
Truth Table
gatesourcedrain (out)state
0anyzOFF
100ON → passes 0
111ON → passes 1
1zzON → passes z
xanyx/zUnknown
// Syntax: nmos instance_name(drain, source, gate);
nmos n1(drain, source, gate);

// Example: pull-down in a CMOS inverter
supply0 GND;
nmos n_inv(out, GND, in);  // when in=1 → out pulled to GND (0)

// Multiple nmos in series (stacked pull-down)
wire mid;
nmos na(out,  mid, a);   // both must be ON to pull out low
nmos nb(mid,  GND, b);   // implements NAND pull-down network
nmos propagates z: When an nmos switch is ON and the source is z (floating), it passes z to the drain. This is the key difference from gate primitives — a not gate receiving z produces x, but an nmos switch receiving z passes z through.

🔵 pmos — P-Channel MOSFET Switch

The pmos primitive models a P-channel MOSFET. It is the complement of nmos — the gate signal is inverted in effect: gate = 0 turns the transistor ON, gate = 1 turns it OFF. PMOS transistors form the pull-up network in CMOS logic, connecting the output to VDD when the input is low.

Fig 3 — pmos transistor: symbol, truth table, and Verilog
Circuit Symbol
drain source gate P
Truth Table
gatesourcedrain (out)state
1anyzOFF
000ON → passes 0
011ON → passes 1
0zzON → passes z
xanyx/zUnknown
// Syntax: pmos instance_name(drain, source, gate);
pmos p1(drain, source, gate);

// Example: pull-up in a CMOS inverter
supply1 VDD;
pmos p_inv(out, VDD, in);  // when in=0 → out pulled to VDD (1)

// Multiple pmos in parallel (pull-up network for NAND)
pmos pa(out, VDD, a);    // either one pulls out high
pmos pb(out, VDD, b);    // if a=0 OR b=0, out=1 (NAND pull-up)

〰️ Resistive Switches — rnmos, rpmos

The resistive switch variants (rnmos, rpmos) behave identically to their ideal counterparts in terms of conduction logic — but when conducting, they reduce the signal strength by one level. A strong input becomes pull, a pull becomes weak, and so on.

🔵 nmos — Ideal (strength preserved)

nmos n1(out, in, g);
// in = strong1 → out = strong1
// Signal strength unchanged

〰️ rnmos — Resistive (strength reduced)

rnmos rn1(out, in, g);
// in = strong1 → out = pull1
// Strength drops one level
Fig 4 — Resistive switch usage: pass-gate with strength reduction
// rnmos: models a resistive pass transistor
// Common in dynamic logic and pass-gate networks
rnmos rn1(out, in, enable);

// Resistive pull-up using rpmos
supply1 VDD;
rpmos   rp1(net, VDD, 1'b0); // always-on weak pull-up (gate tied low)
// net has a weak pull-up — any strong driver overrides it

// Practical use: pseudo-NMOS inverter
// rpmos acts as a weak pull-up load
rpmos load(out, VDD, 1'b0);  // weak1 pull-up
nmos  pull(out, GND, in);   // strong0 pull-down when in=1
// When in=1: strong0 from nmos beats weak1 from rpmos → out=0
// When in=0: nmos OFF, rpmos drives weak1 → out=1 (weak)
Strength level reduction: supplystrongpullweakmediumsmallhighz. A resistive switch drops the strength one step. If the signal is already at the lowest driving strength (weak), it becomes medium (capacitive).

🟢 CMOS Switch (cmos, rcmos)

The cmos primitive is a convenience primitive that combines an nmos and a pmos switch in a single instantiation. It models a CMOS transmission gate — a pass gate that conducts in both polarities, giving full-rail signal transmission for both logic 0 and logic 1.

It has four ports: output (drain), input (source), n-gate control, and p-gate control. For a true complementary switch, ngate and pgate should be complementary (pgate = ~ngate).

out
Drain
output terminal
data
Source
input terminal
nctrl
nMOS Gate
1 = nmos ON
pctrl
pMOS Gate
0 = pmos ON
Fig 5 — cmos transmission gate: circuit, truth table, and Verilog
CMOS Transmission Gate
data out nctrl pctrl
cmos Truth Table
nctrlpctrldataout
1000
1011
10zz
01anyz
xxanyx/z
nctrl=1, pctrl=0 → switch ON
nctrl=0, pctrl=1 → switch OFF
// cmos primitive: 4 ports — (out, data, nctrl, pctrl)
cmos tg1(out, data, nctrl, pctrl);

// Equivalent using separate nmos + pmos:
nmos n1(out, data, nctrl);
pmos p1(out, data, pctrl);

// Resistive version
rcmos rtg1(out, data, nctrl, pctrl);

// 2-to-1 MUX using two CMOS transmission gates
wire sel_n;
not g_inv(sel_n, sel);
cmos tg_a(out, a, sel,   sel_n); // a passes when sel=1
cmos tg_b(out, b, sel_n, sel);  // b passes when sel=0
Why use cmos over nmos alone? An nmos-only pass gate transmits logic 1 weakly — it drops Vdd - Vth, causing a threshold voltage loss. A CMOS transmission gate (nmos + pmos complementary) passes both 0 and 1 at full rail voltage, making it superior for high-speed and low-power signal transmission.

🏗 CMOS Gate Designs at Switch Level

Using nmos and pmos primitives together, complete CMOS logic gates can be built from scratch. Every CMOS gate follows the same complementary topology: a pull-up network (PUN) of PMOS transistors connected to VDD, and a pull-down network (PDN) of NMOS transistors connected to GND.

Fig 6 — CMOS Inverter (complete switch-level model)

Inverter: pmos pull-up + nmos pull-down — the fundamental CMOS cell
VDD (supply1) in GND (supply0) out PMOS NMOS
module inv_switch (
  input  in,
  output out
);
  supply1 VDD;
  supply0 GND;

  pmos p1(out, VDD, in);  // pull-up:  ON when in=0 → out=1
  nmos n1(out, GND, in);  // pull-dn: ON when in=1 → out=0
endmodule

Fig 7 — CMOS NAND Gate (switch level)

NAND: parallel PMOS pull-up + series NMOS pull-down
module nand2_switch (
  input  a, b,
  output y
);
  supply1 VDD;
  supply0 GND;
  wire    mid;

  // Pull-up network (PUN) — PMOS in PARALLEL
  // If a=0 OR b=0 → at least one PMOS ON → y=1
  pmos pa(y,   VDD, a);  // ON when a=0
  pmos pb(y,   VDD, b);  // ON when b=0

  // Pull-down network (PDN) — NMOS in SERIES
  // Only if a=1 AND b=1 → both ON → y=0
  nmos na(y,   mid, a);  // ON when a=1
  nmos nb(mid, GND, b);  // ON when b=1
endmodule

Fig 8 — CMOS NOR Gate (switch level)

NOR: series PMOS pull-up + parallel NMOS pull-down
module nor2_switch (
  input  a, b,
  output y
);
  supply1 VDD;
  supply0 GND;
  wire    mid;

  // Pull-up network (PUN) — PMOS in SERIES
  // Only if a=0 AND b=0 → both ON → y=1
  pmos pa(mid, VDD, a);  // ON when a=0
  pmos pb(y,   mid, b);  // ON when b=0

  // Pull-down network (PDN) — NMOS in PARALLEL
  // If a=1 OR b=1 → at least one ON → y=0
  nmos na(y, GND, a);    // ON when a=1
  nmos nb(y, GND, b);    // ON when b=1
endmodule
CMOS topology rule: NAND uses parallel PMOS / series NMOS. NOR uses series PMOS / parallel NMOS. In general: the pull-up and pull-down networks are topological duals — wherever the PDN has transistors in series, the PUN has them in parallel, and vice versa.

↔️ Bidirectional Gates — tran, tranif0, tranif1

Unlike nmos/pmos (unidirectional — source to drain only), bidirectional switch primitives pass signals equally in both directions. They model the bidirectional nature of a physical pass transistor where current can flow either way. Bidirectional switches have no source or drain — just two interchangeable terminals and (for conditional versions) a control gate.

tran Always-ON bidirectional switch
Always conducting — no gate control. Connects two nodes permanently. Used to model resistive shorts, ESD protection, and test structures.
tran t1(node_a, node_b);
tranif1 Active-high bidirectional switch
Conducts bidirectionally when enable = 1. Floats both nodes to z when enable = 0. Models a CMOS pass gate with active-high enable.
tranif1 t1(node_a, node_b, enable);
tranif0 Active-low bidirectional switch
Conducts bidirectionally when enable = 0. Floats both nodes to z when enable = 1. Models a CMOS pass gate with active-low enable.
tranif0 t1(node_a, node_b, enable);
rtran Resistive always-ON bidirectional
Always-ON bidirectional switch that reduces signal strength when conducting. Models a resistive pass element.
rtran rt1(node_a, node_b);
rtranif1 Resistive active-high bidirectional
Active-high bidirectional switch that also reduces signal strength. Models a resistive pass transistor.
rtranif1 rt1(node_a, node_b, enable);
rtranif0 Resistive active-low bidirectional
Active-low bidirectional switch that also reduces signal strength.
rtranif0 rt1(node_a, node_b, enable);

Bidirectional Switch Truth Tables

tran (always ON)
node_anode_b (result)
00
11
zz
xx
Bidirectional — symmetric
tranif1 (active-high)
enableinout
100
111
1zz
0anyz
xanyx/z
tranif0 (active-low)
enableinout
000
011
0zz
1anyz
xanyx/z

💡 Bidirectional Gate Examples

Fig 9 — SRAM bit cell (6-transistor)

Classic 6T SRAM cell: cross-coupled inverters + access transistors
module sram_6t (
  inout  bl, blb,    // bit line and bit line bar (complementary)
  input  wl         // word line (row select)
);
  supply1 VDD;
  supply0 GND;
  wire    q, qn;    // internal storage nodes

  // Cross-coupled inverter pair (storage element)
  pmos p1(q,  VDD, qn);  nmos n1(q,  GND, qn);  // INV1: qn→q
  pmos p2(qn, VDD, q);   nmos n2(qn, GND, q);   // INV2: q→qn

  // Access transistors (controlled by word line)
  // tranif1: bidirectional — enables read AND write on same path
  tranif1 t1(q,  bl,  wl);  // q  ↔ bl  when wl=1
  tranif1 t2(qn, blb, wl);  // qn ↔ blb when wl=1
endmodule

Fig 10 — Bidirectional I/O pad (inout port)

Bidirectional pad: uses tranif gates to control direction
module bidir_pad (
  inout  pad,          // connects to external pin
  input  data_out,     // data to drive onto pad
  output data_in,      // data read from pad
  input  oe            // output enable: 1=drive, 0=receive
);
  // Output path: drive pad when oe=1
  tranif1 tx(pad, data_out, oe);   // pad ↔ data_out when oe=1

  // Input path: always read from pad
  tran    rx(data_in, pad);       // data_in always connected to pad

  // When oe=0: tranif1 disconnected → pad floats to external signal
  // When oe=1: tranif1 drives pad with data_out
endmodule

Fig 11 — Dynamic logic using tran (charge sharing model)

Domino-style dynamic gate: precharge + evaluate using switches
module dynamic_and (
  input  clk, a, b,
  output out
);
  supply1 VDD;
  supply0 GND;
  wire    clkn, mid;

  not inv_clk(clkn, clk);

  // Precharge: when clk=0 (clkn=1), pmos charges out to VDD
  pmos    pre(out, VDD, clk);    // ON when clk=0

  // Evaluate: when clk=1, nmos PDN can discharge out
  nmos    na(out, mid, a);      // stack: a AND b pull out low
  nmos    nb(mid, GND, b);

  // Keeper: weak pmos holds precharged value if a or b = 0
  rpmos   keep(out, VDD, clkn); // weak pull-up during evaluate
endmodule

🔺 pullup and pulldown Primitives

Verilog provides two convenience primitives for modelling resistor pull-ups and pull-downs — commonly used in open-drain bus topologies (I²C, JTAG, 1-Wire).

🔼 pullup — Resistive pull to VDD

pullup   pu1(net);
// Connects net to VDD at pull strength
// When no strong driver: net = pull1
// Any strong 0 driver: net = 0

🔽 pulldown — Resistive pull to GND

pulldown pd1(net);
// Connects net to GND at pull strength
// When no strong driver: net = pull0
// Any strong 1 driver: net = 1
Fig 12 — Open-drain I²C bus with pullup primitive
// I2C SDA line: open-drain topology
// Multiple devices drive low; external resistor pulls high
module i2c_bus_sw (
  inout sda,
  input drv_a, drv_b   // 0 = device wants to pull low
);
  // External pull-up resistor (modelled by pullup)
  pullup r_ext(sda);          // pull1 strength — weak

  // Open-drain drivers: nmos pulls low, no pull-high inside device
  nmos dev_a(sda, 1'b0, !drv_a); // drv_a=0 → nmos ON → sda=0
  nmos dev_b(sda, 1'b0, !drv_b); // drv_b=0 → nmos ON → sda=0

  // Result: sda = 0 if any device drives; 1 (weak) otherwise
  // → open-drain wired-AND behaviour
endmodule

📋 Switch Primitives Complete Reference

Primitive Type Ports Conducts when Direction Strength
nmos N-MOS (drain, source, gate) gate = 1 Unidirectional Full (unchanged)
pmos P-MOS (drain, source, gate) gate = 0 Unidirectional Full (unchanged)
cmos CMOS pair (out, data, ngate, pgate) ngate=1, pgate=0 Unidirectional Full (unchanged)
rnmos N-MOS (R) (drain, source, gate) gate = 1 Unidirectional Reduced by 1 level
rpmos P-MOS (R) (drain, source, gate) gate = 0 Unidirectional Reduced by 1 level
rcmos CMOS pair (R) (out, data, ngate, pgate) ngate=1, pgate=0 Unidirectional Reduced by 1 level
tran Bidir switch (node_a, node_b) Always Bidirectional Full (unchanged)
tranif1 Bidir switch (node_a, node_b, ctrl) ctrl = 1 Bidirectional Full (unchanged)
tranif0 Bidir switch (node_a, node_b, ctrl) ctrl = 0 Bidirectional Full (unchanged)
rtran Bidir switch (R) (node_a, node_b) Always Bidirectional Reduced by 1 level
rtranif1 Bidir switch (R) (node_a, node_b, ctrl) ctrl = 1 Bidirectional Reduced by 1 level
rtranif0 Bidir switch (R) (node_a, node_b, ctrl) ctrl = 0 Bidirectional Reduced by 1 level
pullup Pull resistor (net) Always Drives pull1 Pull strength
pulldown Pull resistor (net) Always Drives pull0 Pull strength
Key distinction — z propagation: Switch primitives (nmos, pmos, tran) propagate z when conducting. Gate primitives (and, or, buf) convert a z input into x. This is the fundamental modelling difference — use switch level when you need accurate high-impedance and charge-sharing behaviour.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top