Routing: Global, Track & Detailed
Routing turns connections into real metal wires and vias on up to 15+ layers. Learn metal stacks and preferred directions, global routing and congestion, track assignment, detailed routing with maze (Lee) and A* search, design rules, vias and double patterning, timing- and SI-driven routing, and a working Lee router in Python.
Each net in the placed design must become a connected set of wire segments and vias that obey hundreds of design rules. Modern chips have 10–15+ metal layers: thin lower layers for local connections, thick upper layers for long global wires, clocks and power.
Routing is done hierarchically: global routing assigns nets to coarse tiles (and exposes congestion), track assignment places them on tracks, and detailed routing draws exact geometry and fixes design-rule violations.
An analogy
Routing is like building a city's road system over many levels of flyovers: each level has a preferred direction, vias are ramps between levels, and traffic jams (congestion) must be avoided.
Metal stack and preferred directions
Layers alternate preferred direction (e.g. M2 vertical, M3 horizontal) so wires on adjacent layers cross cleanly via vias. Lower layers are thin and dense (local connections); upper layers are thick and wide (long nets, clocks, power). Every via adds resistance and a potential yield risk.
Global routing and congestion
The chip is divided into tiles (GCells); global routing chooses which tiles each net passes through, like planning roads on a city map. Where demand exceeds track capacity, congestion hotspots appear — the placer must spread cells or the floorplan must change.
Detailed routing and design rules
Detailed routing draws exact shapes while satisfying rules such as:
- Minimum width, spacing and area
- Via enclosure and via-to-via spacing
- End-of-line and minimum-step rules
- Multi-patterning colouring (SADP/SAQP) at advanced nodes
- Antenna rules (see signoff)
Timing- and SI-aware routing
Critical nets get priority, shorter paths and upper layers (layer promotion). Clock and sensitive nets use NDRs and shielding. Crosstalk-aware routing spaces aggressive nets apart. After routing, parasitic extraction (SPEF) feeds sign-off STA; ECO routing fixes the last violations. Redundant vias improve yield and EM robustness.
Formulas
- Tile capacity:
capacity = tile width / pitch (per layer) - Congestion:
overflow = max(0, demand − capacity) - Wire resistance:
R = R_sheet · L / W - Distributed RC delay:
t₅₀ ≈ 0.38 · R · C - Lee algorithm cost:
O(grid cells) per net; guaranteed shortest path
Worked example: Tile capacity
Given: GCell 2 µm wide, M3 pitch 40 nm
Find: Horizontal tracks per tile on M3.
- 2000 / 40
Answer: 50 tracks
Worked example: Wire resistance
Given: 0.08 Ω/□, 1 mm long, 0.1 µm wide
Find: R.
- Squares = 1000/0.1 = 10 000
Answer: 800 Ω
Worked example: Layer promotion
Given: Same 1 mm wire on an upper layer: 0.02 Ω/□, 0.4 µm wide, 0.25 fF/µm
Find: Delay.
- R = 0.02 × 2500 = 50 Ω, C = 250 fF
- 0.38 × 50 × 250 fF
Answer: ≈ 4.8 ps vs 61 ps on the thin layer
Lee maze router: wave expansion + backtrace
from collections import deque
grid = [
"S....#....",
".##..#.##.",
".#...#..#.",
".#.###..#.",
".#......#T",
]
rows, cols = len(grid), len(grid[0])
find = lambda ch: next((r, c) for r in range(rows) for c in range(cols) if grid[r][c] == ch)
src, dst = find("S"), find("T")
# Lee algorithm: breadth-first wave expansion, then backtrace
dist = {src: 0}
q = deque([src])
while q:
r, c = q.popleft()
if (r, c) == dst:
break
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] != "#" and (nr, nc) not in dist:
dist[(nr, nc)] = dist[(r, c)] + 1
q.append((nr, nc))
path, cur = [dst], dst
while cur != src: # backtrace to the source
r, c = cur
cur = next(n for n in ((r+1, c), (r-1, c), (r, c+1), (r, c-1)) if dist.get(n) == dist[cur] - 1)
path.append(cur)
out = [list(row) for row in grid]
for r, c in path[1:-1]:
out[r][c] = "*"
print(f"shortest route length: {dist[dst]} grid units")
print("\n".join("".join(row) for row in out))
# shortest route length: 23 grid units
# S***.#****
# .##*.#*##*
# .#**.#*.#*
# .#*###*.#*
# .#*****.#T'#' cells are blockages (other wires, macros). The breadth-first wave guarantees the shortest path around them. Real detailed routers use A* with costs for vias, wrong-way wires and congestion, and rip-up-and-reroute to resolve conflicts between nets.
Wire delay versus length
# RC of an on-chip copper wire (distributed line)
Rs = 0.08 # sheet resistance, ohm/square
c_per_um = 0.2e-15 # capacitance per um, F
W = 0.1 # width, um
for L in (100, 1000, 5000): # length, um
R = Rs * L / W
C = c_per_um * L
t50 = 0.38 * R * C # 50 % delay of a distributed RC line
print(f"L={L:5d} um: R={R:7.0f} ohm C={C*1e15:6.0f} fF delay={t50*1e12:8.1f} ps")
# L= 100 um: R= 80 ohm C= 20 fF delay= 0.6 ps
# L= 1000 um: R= 800 ohm C= 200 fF delay= 60.8 ps
# L= 5000 um: R= 4000 ohm C= 1000 fF delay= 1520.0 ps5× longer wire → 25× more delay. Long nets are routed on thick upper layers (lower R) and buffered.
Common mistakes
- Wrong: Ignoring congestion reports until detailed routing. — Right: Fix congestion at placement or floorplan.
- Wrong: Routing long critical nets on M2. — Right: Promote to thick upper layers and buffer.
- Wrong: Single vias everywhere. — Right: Use redundant vias for yield and EM.
Applications
- Digital ASICs: Billions of wire segments on 10–15 layers.
- PCB autorouting: Same maze/A* ideas on boards.
- FPGA routing: PathFinder negotiated congestion on programmable switches.
- Package & interposer: Routing bumps to balls and die-to-die links.
Key takeaways
- Global → track → detailed routing.
- Congestion = demand > capacity.
- Wire delay ∝ L²; promote and buffer long nets.
- Extraction feeds sign-off timing.
Going further
- Negotiated congestion (PathFinder): Nets bid for shared resources iteratively.
- Multi-patterning aware routing: Colour assignment during routing at 7 nm and below.
- Backside routing: Power (and future signals) on the wafer backside.