Static CMOS Gates, Sizing & Logical Effort
How to build and size fast CMOS logic. Learn pull-up/pull-down networks and duality, NAND vs NOR, transistor sizing for equal drive, the logical-effort delay model (d = gh + p), path effort, the optimum number of stages and stage effort ≈ 4, branching, and how to size a whole path by hand — with a Python calculator.
Any Boolean function can be built as a static CMOS gate: an NMOS pull-down network (series for AND, parallel for OR) and its dual PMOS pull-up network. The shape of these networks makes some gates slower than others — a NOR has series PMOS, the slowest devices, stacked on top of each other.
Logical effort, by Sutherland and Sproull, turns gate sizing into arithmetic: each gate has a logical effort g and parasitic delay p, and the fastest sizing of a path makes every stage bear the same effort. It gives near-optimal results in minutes with pencil and paper.
An analogy
Sizing a logic path is like a relay team carrying a heavy load uphill: the fastest team shares the work equally. Logical effort tells you how hard each runner's leg is and how many runners (stages) to use.
Building gates from networks
NAND2: two NMOS in series (pull-down only when both inputs are 1), two PMOS in parallel. NOR2: two NMOS in parallel, two PMOS in series. The pull-up is the dual (series ↔ parallel) of the pull-down, guaranteeing that exactly one network conducts. Static CMOS gates are naturally inverting (NAND, NOR, AOI, OAI).
Why NAND beats NOR
To give a gate the same drive as an inverter (NMOS width 1, PMOS 2), series transistors must be wider. NAND2 needs NMOS 2 and PMOS 2 → input capacitance 4 vs 3 for the inverter → g = 4/3. NOR2 needs PMOS 4 and NMOS 1 → 5/3. So CMOS designers prefer NAND-heavy logic and avoid wide NORs.
Path delay and optimum stages
Delay is minimised when every stage has the same effort f̂ = F^(1/N). Adding inverters can speed up a path that drives a huge load: the optimum is about one stage per factor of ~4 in path effort. Branching (a net driving several gates) multiplies the effort.
Beyond the basics
Real synthesis and sizing tools use characterised library tables (NLDM, CCS) instead of g and p, but logical effort remains the designer's intuition for datapaths, decoders and clock drivers. Skewed gates (favouring rise or fall), dynamic logic (domino, for speed at power cost) and pass-transistor logic extend the design space.
Formulas
- Stage delay:
d = g·h + p (in units of τ) - Logical effort of common gates:
INV 1, NAND2 4/3, NAND3 5/3, NOR2 5/3, NOR3 7/3 - Path effort:
F = G·B·H, G = Πgᵢ, B = Πbᵢ, H = C_out/C_in - Best stage effort:
f̂ = F^(1/N); minimum delay D = N·F^(1/N) + P - Best number of stages:
N ≈ log₄ F (stage effort ≈ 4, 'fan-out of 4') - Sizing (work backwards):
C_in,i = gᵢ · C_out,i / f̂
Worked example: Inverter chain
Given: Unit inverter drives a load 64× its input
Find: Best stages and delay.
- F = 64 → N = log₄64 = 3
- f̂ = 4; D = 3 × 4 + 3 × 1
Answer: 3 inverters sized 1 : 4 : 16; D = 15τ
Worked example: Mixed-gate path
Given: NAND2 → NOR2 → INV, H = 20
Find: Delay.
- G = 4/3 × 5/3 = 2.22; F = 44.4
- f̂ = 44.4^(1/3) = 3.54
- D = 3 × 3.54 + (2 + 2 + 1)
Answer: D ≈ 15.6τ
Worked example: Branching
Given: A gate drives 3 identical gates, only one on the critical path
Find: b.
- b = (C_on-path + C_off-path)/C_on-path = 3/1
Answer: b = 3 (triples the path effort)
Size a NAND2 → NOR2 → INV path with logical effort
# Logical effort: size a NAND2 -> NOR2 -> INV path driving a load 20x its input
g = [4/3, 5/3, 1.0] # logical effort per stage
p = [2.0, 2.0, 1.0] # parasitic delay per stage (units of tau)
H, B = 20, 1 # electrical effort, branching effort
G = 1.0
for gi in g:
G *= gi
F = G * B * H # path effort
N = len(g)
f_hat = F ** (1 / N) # best stage effort
D = N * f_hat + sum(p) # minimum path delay (tau)
print(f"G={G:.3f} F={F:.2f} stage effort={f_hat:.2f} delay={D:.2f} tau")
cin = 1.0 # input capacitance of first gate (units)
cout = H * cin
c = cout
sizes = []
for gi in reversed(g): # work backwards from the load
c = gi * c / f_hat
sizes.append(c)
print("input caps from first to last:", [round(x, 2) for x in reversed(sizes)])
# G=2.222 F=44.44 stage effort=3.54 delay=15.63 tau
# input caps from first to last: [1.0, 2.66, 5.65]Working backwards from the load, each gate's input capacitance is g·C_out/f̂. The first computed value equals the given input capacitance (1.0) — a built-in check that the sizing is consistent.
Common mistakes
- Wrong: Driving a big load directly from a small gate. — Right: Insert a tapered buffer chain (fan-out ≈ 4 per stage).
- Wrong: Wide NOR gates on critical paths. — Right: Use NAND-based logic or restructure with De Morgan.
- Wrong: Ignoring branching loads. — Right: Off-path loads still add effort.
Applications
- Decoders: Address decoders in SRAM sized for minimum delay.
- Clock and bus drivers: Inverter chains driving huge capacitive loads.
- Datapaths: Adder carry chains and multiplexers.
- Library design: Choosing drive strengths (X1, X2, X4…) in a cell library.
Key takeaways
- Pull-down and pull-up are duals; gates invert.
- d = gh + p; NAND is cheaper than NOR.
- Equal stage effort ≈ 4 minimises path delay.
- Buffer chains tame large loads.
Going further
- Domino logic: Precharged dynamic gates: fast but power-hungry and noise-sensitive.
- Asymmetric gates: Favour the critical input by sizing its transistors.
- Gate sizing optimisation: Convex (geometric programming) formulations used by tools.