reASITIC cookbook¶
Worked design recipes for common RF inductor / transformer scenarios.
Each section includes a runnable Python snippet using the reasitic
public API and shows the expected output. All examples assume the
BiCMOS technology file is at ../run/tek/BiCMOS.tek.
1. Pick a 1-nH inductor for a 5 GHz LO¶
For an LO tank, you want L to set the resonant frequency, Q
high to reduce phase noise, and footprint small enough to fit the
floorplan. Use OptSq to find the geometry that maximises Q for
your L target.
import reasitic
from reasitic.optimise import optimise_square_spiral
tech = reasitic.parse_tech_file("../run/tek/BiCMOS.tek")
res = optimise_square_spiral(
tech, target_L_nH=1.0, freq_ghz=5.0, metal="m3",
length_bounds=(50, 300), # tighten footprint
)
print(f"L={res.length_um:.0f} W={res.width_um:.1f} S={res.spacing_um:.1f}"
f" N={res.turns:.2f} → L={res.L_nH:.2f} nH, Q={res.Q:.0f}")
L=205 W=30.0 S=2.7 N=2.04 → L=1.04 nH, Q=27
The optimiser picks a wide-trace, low-turn geometry — minimising series resistance dominates Q for small-L designs.
2. 5 nH for 1 GHz with substrate-loss-limited Q¶
At 1 GHz the substrate cap shunts the spiral terminals, capping Q
even with thick metal. The REPORT command exposes both the metal-
loss Q and the self-resonance frequency:
sp = reasitic.square_spiral(
"L5",
length=400, width=20, spacing=2, turns=4,
tech=tech, metal="m3",
)
from reasitic.report import design_report
print(design_report(sp, tech, freqs_ghz=[1.0]).format_text())
=== Design report for <L5> ===
L_dc = 7.34 nH
R_dc = 4.55 Ω
Area = 419 200 μm²
f_SR = 6.5 GHz
f_GHz L_nH R_ac Q C_p1 C_p2
1.000 7.34 4.86 9.5 254.0 254.0
The 254 fF substrate cap puts f_SR around 6.5 GHz — fine for 1 GHz
operation. To raise f_SR, reduce the area: use OPTAREA to find
the smallest spiral meeting the L target.
3. Differential balun with 1:1 turns ratio¶
A planar balun is two stacked counter-wound coils. balun() builds
the geometry; CALCTRANS reports the differential parameters.
b = reasitic.balun(
"BAL",
length=200, width=10, spacing=2, turns=3,
tech=tech, metal="m3", metal2="m2",
)
# Treat the two coils as separate shapes for analysis
from reasitic.network.analysis import calc_transformer
import reasitic.geometry as geo
# Re-build as two separate coils for CalcTrans
pri = reasitic.square_spiral("PRI", length=200, width=10, spacing=2,
turns=3, tech=tech, metal="m3")
sec = reasitic.square_spiral("SEC", length=200, width=10, spacing=2,
turns=3, tech=tech, metal="m2")
res = calc_transformer(pri, sec, tech, freq_ghz=2.4)
print(f"L_pri={res.L_pri_nH:.2f}, L_sec={res.L_sec_nH:.2f},"
f" k={res.k:.3f}, n={res.n_turns_ratio:.3f}")
L_pri=2.32, L_sec=2.32, k=0.654, n=1.000
For a balun the goal is k as close to 1 as possible. To raise k,
move the two coils closer (or use the same metal layer with
interleaving — see :func:reasitic.transformer).
4. Frequency-swept S-parameter export for SPICE¶
To use a reASITIC-extracted spiral in SPICE, dump a Touchstone S2P file or a SPICE Pi-model sub-circuit at the operating point:
from reasitic.network import linear_freqs, two_port_sweep, write_touchstone_file
from reasitic.exports import write_spice_subckt_file
sp = reasitic.square_spiral(
"L1", length=200, width=10, spacing=2, turns=3,
tech=tech, metal="m3",
)
# Touchstone .s2p across the 0.1 - 10 GHz band
fs = linear_freqs(0.1, 10.0, 0.1)
sweep = two_port_sweep(sp, tech, fs)
write_touchstone_file("L1.s2p", sweep.to_touchstone_points(param="S"))
# SPICE Pi-model at the operating point only
write_spice_subckt_file("L1.sub", sp, tech, freq_ghz=2.4)
The L1.sub file is a complete .subckt L1_pi p1 p2 gnd ... .ends
block that drops directly into ngspice / Hspice / LTspice.
5. Cross-validate against the legacy ASITIC binary¶
reASITIC ships with a binary-driver that compares geometry output against the original ASITIC binary. Useful for catching regressions when you change a kernel.
from reasitic.validation import BinaryRunner
runner = BinaryRunner.auto() # uses ../run/asitic + xvfb-run
result = runner.geom("SQ NAME=L:LEN=200:W=10:S=2:N=3:METAL=m3", "L")
print(f"Binary reports: spiral_l1={result.spiral_l1_um}, segments={result.n_segments}")
The binary’s numerical commands (Ind, Cap, 2Port)
segfault in headless mode (legacy library bug), so we cross-check
geometry only. For numerical correctness, see tests/test_physics_validation.py
which validates against published Greenhouse / Mohan formulas.
6. Optimisation sweep across multiple operating points¶
For a multi-band radio you may want different spirals at each band.
BatchOpt runs OptSq across a list of (L, f) targets:
from reasitic.optimise import batch_opt_square
targets = [
(1.0, 5.0), # 1 nH at 5 GHz
(2.0, 2.4), # 2 nH at 2.4 GHz (Wi-Fi)
(5.0, 1.0), # 5 nH at 1 GHz
]
arr = batch_opt_square(tech, targets=targets, metal="m3")
print(arr)
Each row gives the best (length, width, spacing, turns, L_nH, Q)
for its (target_L, freq) pair.
7. Differential transformer with optimal coupling¶
A differential pair is two interleaved coils on the same metal. The
transformer builder creates them; CalcTrans reports k:
import reasitic
from reasitic.network.analysis import calc_transformer
tech = reasitic.parse_tech_file("../run/tek/BiCMOS.tek")
t = reasitic.transformer(
"T",
length=200, width=10, spacing=2, turns=3,
tech=tech, metal_primary="m3", metal_secondary="m3",
)
# Re-extract primary/secondary as separate coils for analysis
pri = reasitic.square_spiral(
"P", length=200, width=10, spacing=2, turns=3,
tech=tech, metal="m3",
)
sec = reasitic.square_spiral(
"S", length=200, width=10, spacing=2, turns=3,
tech=tech, metal="m3",
x_origin=210, # adjacent
)
print(calc_transformer(pri, sec, tech, freq_ghz=2.4))
A typical adjacent-coil planar transformer has k ≈ 0.4–0.6. Move the coils closer (or interleave them on alternating metals) to push k upward.
8. MIM capacitor with proper dielectric stack¶
For decoupling / RF tank caps, ASITIC’s MIM model is two stacked metal plates with the inter-layer oxide forming the dielectric:
cap = reasitic.capacitor(
"C1",
length=50, width=50, # 50 × 50 μm² plate
metal_top="m3", # 2 μm thick m3
metal_bottom="m2", # 0.8 μm thick m2
tech=tech,
)
# Substrate cap from each plate
from reasitic.substrate import shape_shunt_capacitance
print(f"Total shunt cap: {shape_shunt_capacitance(cap, tech) * 1e15:.2f} fF")
The shunt cap captures the m2-to-substrate path (m3 sits above m2 so its substrate path is longer).
9. Multi-metal series inductor (boosting L for fixed footprint)¶
The MMSquare inductor stacks multiple metal layers in series.
For the BiCMOS stack you can boost L by ~3× without changing
footprint by stacking m2+m3:
mm = reasitic.multi_metal_square(
"MM",
length=200, width=10, spacing=2, turns=3,
tech=tech, metals=["m2", "m3"],
)
print(f"L = {reasitic.compute_self_inductance(mm):.2f} nH")
# Compare with single-metal m3 only
single = reasitic.square_spiral(
"S", length=200, width=10, spacing=2, turns=3,
tech=tech, metal="m3",
)
print(f"Single-metal L = {reasitic.compute_self_inductance(single):.2f} nH")
Each additional metal layer adds roughly L_single nH (with mutual coupling between the layers ~1, since they’re co-located).
10. Parametric L-Q sweep visualisation¶
Sweep the spiral length and turns count, plot L and Q:
import numpy as np
from reasitic.optimise import sweep_square_spiral
arr = sweep_square_spiral(
tech,
length_um=np.arange(100, 401, 50).tolist(),
width_um=[10.0],
spacing_um=[2.0],
turns=np.arange(1.0, 6.0, 0.5).tolist(),
freq_ghz=2.4,
metal="m3",
)
# Now plot Q vs (L, N) using matplotlib (optional dep)
try:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
Ls = sorted(set(arr["length_um"]))
Ns = sorted(set(arr["turns"]))
Q = arr["Q"].reshape(len(Ls), len(Ns))
im = ax.imshow(Q, origin="lower",
extent=(min(Ns), max(Ns), min(Ls), max(Ls)),
aspect="auto", cmap="viridis")
plt.colorbar(im, label="Q")
ax.set_xlabel("Turns N")
ax.set_ylabel("Length (μm)")
plt.savefig("LQ_sweep.png", dpi=120)
except ImportError:
pass
See also¶
examples/— runnable scripts that mirror these recipes.MAPPING.md— line-by-line port table to the reverse-engineered C source. Useful when chasing numerical discrepancies against the binary.PLAN.md— implementation phases and what’s deferred.