| name | gmoverid |
| description | gm/ID transistor characterization and design methodology, based on ngspice + Python. Two independent workflows: (1) Characterization — generates three standard curve sets for any MOSFET model: gate capacitance (Cgg/Cgs/Cgd/Cgb vs Vgs), gm/ID four-quadrant characteristics (gm/Id vs Vov, Id/W vs gm/Id, fT vs gm/Id, gm·ro vs gm/Id), and IV characteristics (linear/log Id vs Vov, output curves). Supports 180 nm single-node and 45/22 nm HP multi-node flows with built-in PTM model files (180/45/22 nm) — no extra downloads required. (2) Design — the GmIdTable class builds a lookup table from simulation data (cached to logs/cache/) and provides lookup(), size(), size_from_ft(), size_from_gmro() APIs for NMOS/PMOS transistor sizing using the gm/ID methodology. Only depends on the ngspice skill. Use this skill when setting up or extending a gm/ID characterization project, generating characteristic curves, interpreting design curves, or sizing transistors by the gm/ID method. |
gm/ID Characterization and Design Skill
Important — do not modify skill files during normal use.
All code edits, new scripts, plots, and simulation outputs should go into the user's project working directory (outside .claude/), not into this skill folder. Only modify the skill assets (assets/, SKILL.md, references/) when the user explicitly asks to improve or extend the skill itself.
Dependency: ngspice skill (netlist execution, wrdata parsing). Model files are built in — the transistor-models skill is not required.
Asset Files
assets/
├── simulate_gmoverid.py — ngspice simulation engine, data extraction, analytical caps, MODEL_INFO registry
├── plot_gmoverid.py — all matplotlib plotting functions
├── run_gmoverid.py — 180 nm single-node orchestration (NMOS/PMOS + channel-length sweep)
├── run_multinode.py — multi-node orchestration (45/22 nm HP)
├── design_gmoverid.py — GmIdTable lookup/sizing API + print_op()
├── models/ — built-in PTM model files (ready to use)
│ ├── nmos180.lib — 180 nm BSIM3v3 (VDD = 1.8 V)
│ ├── pmos180.lib
│ ├── nmos45hp.lib — 45 nm HP BSIM4 (VDD = 1.0 V)
│ ├── pmos45hp.lib
│ ├── nmos22hp.lib — 22 nm HP BSIM4 (VDD = 0.8 V)
│ └── pmos22hp.lib
└── netlist/
├── gmoverid_vgs.cir.tmpl — NMOS Vgs sweep (fixed Vds)
├── gmoverid_vds.cir.tmpl — NMOS Vds sweep (fixed Vgs)
├── gmoverid_pmos_vsg.cir.tmpl — PMOS |Vsg| sweep (fixed |Vsd|)
└── gmoverid_pmos_vsd.cir.tmpl — PMOS |Vsd| sweep (fixed |Vsg|)
Built-in models are from PTM — Arizona State University (ptm.asu.edu), free for academic research. When citing, use: W. Zhao and Y. Cao, "New Generation of Predictive Technology Model for Sub-45 nm Early Design Exploration," IEEE Trans. Electron Devices, vol. 53, no. 11, pp. 2816–2823, Nov. 2006. doi: 10.1109/TED.2006.884077. For nodes beyond the three built-in ones, install the transistor-models skill (full PTM library, requires manual MODEL_INFO configuration) or download from mec.umn.edu/ptm and copy the .lib file into the project's models/ directory.
Workflow 1: Characterization (Simulation + Plotting)
Deployment
- Copy all assets (including the
models/ subdirectory) to <project>/
- Create empty directories
plots/ and logs/
- Run
python run_gmoverid.py (180 nm) or python run_multinode.py (HP multi-node)
For additional nodes, simply copy the extra .lib file into <project>/models/.
All paths resolve automatically via Path(__file__).resolve().parent — no path configuration needed.
Three Standard Plot Sets (per model)
| Set | Function | Output file |
|---|
| Gate capacitance | plot_caps() | gmoverid_caps_{model}_L{node}nm.png |
| gm/ID four-quadrant | plot_main() | gmoverid_{model}_L{node}nm.png |
| IV characteristics | plot_iv() | gmoverid_iv_{model}_L{node}nm.png |
gm/ID four-quadrant layout (plot_main, 2×2):
(0,0) gm/Id [V⁻¹] vs Vov | (0,1) Id/W [uA/um] vs gm/Id (log Y)
(1,0) fT [GHz] vs gm/Id | (1,1) gm·ro vs gm/Id
- All four panels use Vds as the curve parameter (VDS_LIST)
- gm/Id X-axis: xlim = [4, 24], one grid division per 2 V⁻¹
- Panels (0,1), (1,0), (1,1) apply
_fall_mask(gmid) before plotting — the gm/ID array is double-valued (same gm/ID appears on the subthreshold rising side and the inversion falling side); only the falling (inversion) side is kept to prevent curve fold-back. Panel (0,0) is exempt because its X-axis is Vov, which is monotonic.
Adding a New Technology Node
- Copy the model
.lib into models/ (download from mec.umn.edu/ptm or install the transistor-models skill)
- Add an entry to
MODEL_INFO in simulate_gmoverid.py (see conventions.md §3)
- Add an entry to
NODE_CFG in run_multinode.py
Plotting Conventions (required when creating new figures)
When generating figures for a new project or plotting for a design report, follow the style of the existing figures produced by this skill.
Format requirements:
- Never call
plt.show(); always use fig.savefig(path, dpi=150, bbox_inches='tight') and save to plots/
- Colors and line styles: cycle through
COLORS/LSTYLE from plot_gmoverid.py in order (blue solid, orange dashed, green dash-dot, purple dotted)
- X-axis for gm/ID: always
xlim = [4, 24] with one tick per 2 V⁻¹; Y-axis ylim(bottom=0)
- Fold-back prevention: whenever gm/ID is the X-axis, prepend
_fall_mask(gmid) to the boolean mask. The gm/ID vs Vgs curve peaks and then descends — both sides can fall within [4, 24], causing matplotlib to draw a fold-back. _fall_mask discards the subthreshold rising side (before the peak); only the inversion falling side is plotted. This rule applies to plot_main panels (0,1)/(1,0)/(1,1) and to the equivalent panels in plot_comparison.
- Titles and axis labels: use ASCII + LaTeX (e.g.
$g_m/I_D$); do not write Chinese characters directly in matplotlib labels
µ (U+00B5) fails to render in some terminals and fonts — always use u in axis labels (e.g. uA/um, W=10um) or LaTeX $\\mu$; use Unicode µ only when the user explicitly requests it
Chinese font warning:
matplotlib does not include Chinese fonts by default; Chinese characters render as boxes (□□). If Chinese is needed, add at the top of the script:
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'DejaVu Sans']
matplotlib.rcParams['axes.unicode_minus'] = False
On Windows prefer Microsoft YaHei; on Linux/macOS verify the font is installed, otherwise boxes still appear. The most robust approach is to use only ASCII/LaTeX labels and avoid font dependencies entirely.
Workflow 2: Design (Lookup-Table Sizing)
Core Idea of the gm/ID Methodology
gm/ID (transconductance efficiency) is the pivot quantity linking circuit specifications to device dimensions.
Traditional design relies on long-channel model equations (gm = 2Id/Vov) to compute device sizes; errors are severe in advanced nodes (≤ 180 nm). The gm/ID method abandons equations in favor of simulation-generated design charts (lookup tables), using gm/ID as the single independent variable to express all key device performance parameters:
gm/ID ──► Id/W (current-density curve → determines W)
──► fT (transit frequency → determines speed)
──► gm·ro (intrinsic gain → determines gain ceiling)
──► Vgs (uniquely determines the bias point)
All four curves are independent of transistor width W — devices of different W share exactly the same Id/W, fT, and gm·ro values at the same gm/ID point. This is the foundation of the methodology: the design chart needs to be generated only once (at unit width), and it applies directly to any W.
gm/ID is the trade-off axis for three design objectives:
| Design priority | Direction of gm/ID | Reason |
|---|
| High speed (fT↑) | Low gm/ID (strong inversion) | High Vov → fast |
| High gain (gm·ro↑) | High gm/ID (weak/moderate inversion) | Closer to subthreshold |
| Low power (Id↓, same W) | High gm/ID | Id/W decreases as gm/ID increases |
| Minimum area (W↓, same Id) | Low gm/ID | Id/W decreases as gm/ID increases |
Note: area and power optimization point in opposite directions — this is the central trade-off in gm/ID design.
Design Flow (Five Steps)
① Choose topology → ② Set L → ③ Choose gm/ID → ④ Derive gm / Id → ⑤ Look up Id/W → get W
① Choose topology
Select the circuit structure (common-source, differential pair, cascode, common-gate, etc.) based on gain, bandwidth, and swing requirements.
② Set channel length L
- Need high speed (high fT) → choose minimum L
- Need high gain (high gm·ro) → increase L moderately (each doubling of L raises gm·ro by roughly 2–4×, with a corresponding fT reduction)
- Verify feasibility first:
tbl.lookup('gmro', gmid_target) to check the upper bound; if unsatisfied, increase L before considering cascode or multi-stage topologies
- With a PMOS load, also verify PMOS ro: effective gain = gm_n × (ro_n ∥ ro_p)
③ Choose gm/ID
Select the trade-off point on the fT–gm/ID and gm·ro–gm/ID curves based on the design priority:
tbl = GmIdTable('nmos45hp', W=1.0, L=0.045, vds=0.5)
print(f"fT @ gmid=6 : {tbl.lookup('ft', 6.0)/1e9:.1f} GHz")
print(f"fT @ gmid=15 : {tbl.lookup('ft', 15.0)/1e9:.1f} GHz")
print(f"gmro @ gmid=6 : {tbl.lookup('gmro', 6.0):.1f}")
print(f"gmro @ gmid=15 : {tbl.lookup('gmro', 15.0):.1f}")
④ Derive the required gm from circuit specs, then compute Id
Rout = 1 / (2 * 3.14159 * BW * CL)
gm = Av / Rout
Id = gm / gmid
⑤ Look up Id/W to get W, round to 100 nm grid
id_w = tbl.lookup('id_w', gmid)
W_exact = Id / (id_w * 1e-6)
W = round(W_exact / 0.1) * 0.1
After rounding, recompute Id = W × id_w and verify Av and BW; a ΔW < 5% deviation is usually negligible.
API Reference: GmIdTable
from design_gmoverid import GmIdTable, print_op
tbl = GmIdTable('nmos180', W=10.0, L=0.18, vds=0.9)
Constructor parameters:
| Parameter | Type | Default | Description |
|---|
model | str | — | Key in MODEL_INFO, e.g. 'nmos180', 'pmos45hp' |
W | float | — | Simulation reference width [um] (results are W-independent; any value works) |
L | float | — | Channel length [um] |
vds | float | VDD/2 | Fixed Vds (NMOS) or |Vsd| (PMOS) during Vgs sweep [V] |
force_resim | bool | False | If True, ignores cache and re-runs simulation |
Single-quantity lookup (W-independent, depends only on gm/ID):
tbl.lookup('id_w', gmid)
tbl.lookup('ft', gmid)
tbl.lookup('gmro', gmid)
tbl.lookup('vgs', gmid)
tbl.lookup('vov', gmid)
tbl.lookup('gm', gmid)
Sizing: fix gm/ID + one constraint
op = tbl.size(gmid=15.0, Id=100e-6)
op = tbl.size(gmid=15.0, W=20.0)
op = tbl.size(gmid=15.0, gm=1.5e-3)
print_op(op)
Constrained sizing: auto-search (highest gm/ID = lowest current)
op = tbl.size_from_ft(8e9, W=20.0)
op = tbl.size_from_gmro(35, Id=50e-6)
size_from_ft: suited for known-width, speed-constrained scenarios (LNA transconductor, OTA GBW)
size_from_gmro: suited for high-gain stages (prefer fixing W to avoid excessively large W in weak inversion)
Keys returned by size():
model, L_um, W_um, Id_A, Vgs_V, Vov_V, gmid, gm_S, ft_Hz, gmro, id_w_Apm
Typical gm/ID Design Parameters by Node
nmos180 (Vds = 0.9 V, L = 180 nm)
| gm/ID [V⁻¹] | Id/W [uA/um] | fT [GHz] | gm·ro | Typical use |
|---|
| 5–8 | 42–81 | 20–25 | 27–36 | High-speed circuits, sampling switches |
| 10–12 | 20–29 | 15–18 | 39 | OTA output stage, drivers |
| 13–16 | 8–17 | 9–13 | 39–46 | OTA input differential pair (balanced) |
| 18–20 | 3–6 | 4–6 | 53 | Low-power analog, voltage references |
nmos45hp / nmos22hp (HP, minimum L)
| Node | gm/ID [V⁻¹] | Id/W [uA/um] | fT [GHz] | gm·ro | Notes |
|---|
| 45 nm HP | 6–10 | 150–300 | 200–400 | 6–8 | Strong CLM, low gain — ro must be considered |
| 22 nm HP | 6–10 | 200–500 | 400–700 | 3–4 | Very strong DIBL, extremely low gain |
Vds has a minor effect on Id/W (advanced nodes have lower output impedance): ignore it during initial design, then fine-tune after simulation.
Design Example: 45 nm HP Common-Source Amplifier
Specifications (from textbook §5.2.1, 40 nm process, using PTM 45 nm HP as surrogate model):
| Specification | Value |
|---|
| VDD | 1.1 V |
| Low-frequency voltage gain Av | 2 (linear, i.e. 6 dB) |
| −3 dB bandwidth BW | 100 MHz |
| Load capacitance CL | 10 pF |
| Total current Id | ≤ 2 mA |
| Channel length L | 45 nm (minimum gate length) |
| Design target | gm/ID = 10 V⁻¹ (balanced speed vs. power) |
1. Derive Design Constraints (ro-aware)
The gain equation includes the ro term and cannot be neglected:
|A_DC| = gm·(RL ∥ ro)
1/|A_DC| = 1/(gm·RL) + 1/(gm·ro)
=> gm·RL = 1 / (1/Av − 1/(gm·ro))
For 45 nm HP, intrinsic gain gm·ro ≈ 7 (confirmed via tbl.lookup('gmro', gmid)). Substituting:
gm·RL = 1 / (1/2 − 1/7) ≈ 2.80
Bandwidth determines the total output-node impedance (RL ∥ ro), i.e. the actual Rout:
Rout = RL ∥ ro = 1 / (2pi × BW × CL)
= 1 / (2pi × 100 MHz × 10 pF) ≈ 159 Ohm
gm = Av / Rout = 2 / 159 ≈ 12.6 mA/V
RL = gm·RL / gm = 2.80 / 12.6 mS ≈ 222 Ohm
Why not simply set RL = Rout? Rout = 159 Ω is RL∥ro, not RL itself. RL must be larger than Rout and is back-calculated from the gain equation using the gm·ro term.
2. Size with GmIdTable
from design_gmoverid import GmIdTable
import math
VDD = 1.1; Av = 2.0; BW = 100e6; CL = 10e-12
tbl = GmIdTable('nmos45hp', W=1.0, L=0.045, vds=0.5)
gmid = 10.0
gmro = tbl.lookup('gmro', gmid)
id_w = tbl.lookup('id_w', gmid)
vgs = tbl.lookup('vgs', gmid)
vov = tbl.lookup('vov', gmid)
Rout = 1 / (2 * 3.14159 * BW * CL)
gm_RL = 1 / (1/Av - 1/gmro)
gm = Av / Rout
RL = gm_RL / gm
Id = gm / gmid
W_exact = Id / (id_w * 1e-6)
W = round(W_exact / 0.1) * 0.1
Id_r = W * id_w * 1e-6
gm_r = Id_r * gmid
ro_r = gmro / gm_r
Av_r = gm_r * (RL * ro_r / (RL + ro_r))
Vd_DC = VDD - Id_r * RL
3. Design Summary
| Parameter | Value |
|---|
| gm/ID | 10 V⁻¹ |
| W / L | 8.1 um / 45 nm |
| Id | 1.26 mA (< 2 mA ✓) |
| gm | 12.6 mA/V |
| gm·ro | 7.11 |
| RL | 222 Ohm |
| Av (verified) | 2.00 ✓ |
| Vgs (bias) | 0.499 V |
| Vd_DC | 0.821 V |
4. Post-Design Simulation (hand off to ngspice skill)
With W, RL, and Vgs in hand, use the ngspice skill to build a .control block netlist and simulate:
- DC operating point: verify
@m1[id], @m1[gm], @m1[gds] against design values
- AC frequency response:
ac dec 200 1e5 1e10 → read vdb(vout) → verify low-frequency gain and −3 dB frequency
- Plotting: save frequency-gain data with
wrdata, plot a Bode magnitude response using matplotlib
Self-Validation
Two complementary checks — run both after generating characterization plots.
Step 1 — Numerical self-check
python validate_gmoverid.py [model] [L_um]
python validate_gmoverid.py
python validate_gmoverid.py nmos45hp 0.045
Five scalar tests, each prints PASS / FAIL:
| # | Test | Pass criterion | Failure meaning |
|---|
| 1 | Weak-inversion limit | peak gm/ID in [25, 32] V^-1 | gm extraction error near Id ≈ 0 |
| 2 | ID/W monotonicity | zero non-monotone steps | Interpolation oscillation in gm or Id |
| 3a | L-doubling gain | gmro(2L)/gmro(L) ≥ 1.5 | Model ignores CLM |
| 3b | L-doubling fT | fT(2L)/fT(L) in [0.15, 0.70] | Cgg scaling wrong |
| 4 | fT×gm/ID peak | peak at gm/ID in [8, 18] V^-1 | Cgg or gm mismatch |
| 5 | Vds sensitivity | gmro change ≥ 8% across 0.25–0.75 VDD | Model ignores channel-length modulation |
For physics derivation and failure diagnosis see references/validation.md.
Step 2 — Visual inspection by LLM
Generate the four-quadrant plot, then show it to Claude and ask for a
physics assessment. The expected trends for each panel are listed below —
use them as the inspection checklist.
Panel (0,0) — gm/ID vs Vov
| Zone | Expected trend | Typical values |
|---|
| Vov < 0 (subthreshold) | gm/ID approaches a constant ceiling from below | 25–32 V^-1 (= 1/(n·Ut), n=1.2–1.5) |
| Vov ≈ 0 (threshold) | smooth peak, then monotone descent | peak ≈ 25–32 V^-1 |
| Vov > 0 (strong inversion) | follows 2/Vov (dashed reference line) closely | drops to ~5 V^-1 at Vov=0.4V |
Red flags: peak > 35 (gm noise at low Id), plateau that never decays
(missing strong-inversion region), kink at Vov=0 (Vth extraction error).
Note: for HP minimum-L nodes, DIBL raises the slope factor n toward 1.4–1.5,
so the subthreshold ceiling legitimately sits closer to 25 V^-1 rather than
32 V^-1 — this is correct, not a model defect.
Panel (0,1) — Id/W vs gm/ID (log Y)
| Feature | Expected |
|---|
| Direction | strictly decreasing left to right (strong→weak inversion) |
| Shape on log scale | approximately linear (exponential decay in weak inversion) |
| Range | spans ≥ 2 decades across gm/ID = [4, 24] |
| Multiple Vds curves | nearly overlapping (Id/W is weakly Vds-dependent in saturation) |
Red flags: any upward hook (fold-back from subthreshold rising side —
fixed by _fall_mask), flat segment (resolution too coarse), curves
widely separated by Vds (device not in saturation).
Panel (1,0) — fT vs gm/ID
| Feature | Expected |
|---|
| Direction | monotonically decreasing left to right |
| Shape | roughly follows a power law; steeper decay toward high gm/ID |
| Magnitude | 180nm: 5–30 GHz; 45nm HP: 100–400 GHz |
| Multiple Vds curves | nearly overlapping (fT is Vds-insensitive in saturation) |
Red flags: fT increases with gm/ID (sign inversion in gm or Cgg),
non-monotone wiggles (ngspice convergence issue near threshold), fT > 1 THz
or < 1 GHz at reasonable bias (Cgg extraction error).
Panel (1,1) — gm·ro vs gm/ID
| Feature | Expected |
|---|
| Direction | generally increasing left to right (weak inversion has higher gain) |
| Magnitude | 180nm: 20–60; 45nm HP min-L: 5–15 |
| Vds dependence | curves separated — higher Vds → lower gmro (stronger CLM) |
| Shape | smooth, no kinks; may plateau in strong inversion |
Red flags: completely flat across all gm/ID (CLM ignored), gmro > 200 at
moderate gm/ID for short-channel nodes (gds underestimated), kinks at
specific Vgs points (sparse Vds-sweep interpolation artefact — use
vgs_gds_results to enable finite-difference gds path).
Interpreting LLM visual feedback
LLM image analysis is powerful for shape and smoothness but requires
physical context to interpret correctly:
- "Peak below BJT limit 38.6 V^-1" — expected; MOSFETs have n > 1.
Correct ceiling is 1/(n·Ut) ≈ 25–32 V^-1.
- "Curves widely separated in panel (0,1) or (1,0)" — check Vds values;
if all are well into saturation the separation should be < 20%.
- "gmro is very low (5–10)" — normal for HP minimum-L nodes; not a defect.
- "Curve does not reach weak inversion" — increase Vgs sweep range or
lower id_thresh in
sweep_vgs.
Reference Documents
See references/conventions.md for full details:
- §1–5 Project structure, symbol conventions, MODEL_INFO, netlist templates, data-dict keys
- §6–7 Sweep configuration constants (NODE_CFG), plotting conventions and figure list
- §8–9 Physical sanity-check values, common errors and fixes
- §10 Extending the skill (new nodes, new plot types, new channel lengths)
- §11 Full design API reference (GmIdTable, print_op, cache naming, unit conventions)