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:

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

Worked example: Tile capacity

Given: GCell 2 µm wide, M3 pitch 40 nm

Find: Horizontal tracks per tile on M3.

Answer: 50 tracks

Worked example: Wire resistance

Given: 0.08 Ω/□, 1 mm long, 0.1 µm wide

Find: R.

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.

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 ps

5× longer wire → 25× more delay. Long nets are routed on thick upper layers (lower R) and buffered.

Common mistakes

Applications

Key takeaways

Going further