HeiChips 2026 Analog Workshop
The slides presented during the workshop are available here: HeiChips2026_Analog-Workshop_Slides.pdf.
This workshop is based on a more advanced chip design tutorial for the ihp-sg13g2 PDK and the IIC-OSIC-TOOLS container: A Tutorial on Open-Source Analog-Mixed Signal Chip Design with the ihp-sg13g2 Open-PDK. If you want to go beyond this workshop, for example digital macros, top-level assembly, padframe generation, and tapeout preparation, that tutorial is the recommended next step.
1 Workshop Overview
Welcome to the analog design workshop of HeiChips 2026, the Summer School on Open-Source Chip Design taking place from August 3 to 7, 2026 at the European Institute for Neuromorphic Computing (EINC) in Heidelberg, Germany. In one week of lectures, hands-on workshops, and a hackathon, you design your own chip and tape it out on the IHP 130nm CMOS5L Open Source PDK.
This workshop covers the analog mixed-signal track on Wednesday, August 5, 2026:
| Time | Session |
|---|---|
| 09:00 – 09:15 | Open-Source Analog Mixed-Signal: Overview |
| 09:15 – 10:30 | Open-Source Analog Mixed-Signal: Schematic (Xschem, Ngspice) |
| 11:00 – 12:30 | Open-Source Analog Mixed-Signal: Layout (KLayout, DRC, LVS, PEX) |
| 13:15 – 14:45 | Open-Source Analog Mixed-Signal: Exercises |
Throughout the day we implement one complete analog macro, a CMOS inverter used as an analog amplifier, in the ihp-sg13cmos5l Open-PDK. We go through the full flow:
- Schematic entry and symbol creation in Xschem (see Section 4)
- Simulation with Ngspice using dc, ac, and transient testbenches (see Section 4.4)
- Layout in KLayout (see Section 5)
- Verification with DRC, LVS, and PEX using KLayout and Magic + Netgen (see Section 5.4)
- Post-layout simulation with the extracted parasitics (see Section 5.5)
- Exercises where you apply the flow on your own (see Section 6)
All required tools are provided through a Nix shell, so no container is needed. Follow the Prerequisites in the repository README before the workshop:
git clone https://github.com/HeiChips/heichips26-analog-workshop.git
cd heichips26-analog-workshop
nix-shell # enter the tool environment (do this in every new terminal)
make clone-pdk # install the ihp-sg13cmos5l Open-PDK into this repository
make klayout-setup # install the KLayout productivity pluginsThe HeiChips VM has Nix already pre-installed. If you work on your own machine, follow LibreLane’s Nix-based installation guide first.
Inside the Nix shell, export the PDK environment variables once per terminal from the repository root:
export PDK_ROOT=$(pwd)/IHP-Open-PDK
export PDK=ihp-sg13cmos5l1.1 Guidelines & Cheatsheets
The following guidelines and cheatsheets are helpful during the workshop and for your own projects afterwards. Some of them are located directly in the doc/ folder of this repository.
- Designer’s Guidelines
- Linux Cheatsheet
- Xschem Cheatsheet
- Ngspice Cheatsheet
- KLayout Cheatsheet (in
doc/klayout/) - KLayout Productivity Suite / Plugins
- ihp-sg13cmos5l Layout Calculation Cheatsheet (in
doc/ihp-sg13cmos5l-Open-PDK/) - ihp-sg13cmos5l Layout Rules and Process Specification (in
doc/ihp-sg13cmos5l-Open-PDK/) - ihp-sg13cmos5l LV and HV NMOS / PMOS Techsweeps (in
doc/sizing/)
2 Repository and Makefile Overview
2.1 Directory Structure
The workshop repository is organized as follows:
📁 heichips26-analog-workshop/
├─ 📁 config/ Shared tool configuration (.spiceinit for Ngspice, klayoutrc)
├─ 📁 doc/ Cheatsheets, PDK documentation, and sizing techsweeps
├─ 📁 inverter/ The analog macro implemented in this workshop
├─ 📁 workshop/ Sources of this website
├─ flake.nix Nix environment with all required tools
├─ Makefile PDK setup and tool launch targets
└─ README.md Quick-start instructions
The root Makefile provides the setup and launch targets. Running make without arguments prints all available targets.
| Target | Description |
|---|---|
make clone-pdk |
Clone the IHP Open-PDK plus the ihp-sg13cmos5l PDK into IHP-Open-PDK/ and compile the Verilog-A models with OpenVAF |
make klayout-setup |
Install the KLayout plugins into your user directory ~/.klayout/salt/, once per machine (see Section 5.2) |
make klayout |
Open KLayout in edit mode with the sg13cmos5l technology preloaded |
2.2 The Inverter Macro Folder
The inverter/ folder contains the complete analog macro with the following structure:
inverter macro.
| Folder | Content |
|---|---|
schematic/xschem/ |
Xschem schematic and symbols (inverter.sch, inverter.sym, inverter_pex.sym) |
testbenches/xschem/ |
The dc, ac, and transient testbenches plus Python plotting scripts |
layout/ |
KLayout GDS files (inverter.gds) |
netlist/ |
Exported netlists: schematic/ (for LVS), layout/ (extracted), pex/ (parasitic) |
verification/ |
DRC and LVS reports (drc/, lvs/) |
scripts/ |
Sizing notebooks (sizing/), vendored sak-* verification scripts, rendering |
final/ |
Build deliverables for top-level integration (GDS, LEF, LIB, Verilog stub, and the layout render in render/) |
Read the inverter README first to get familiar with the folder layout and the available Makefile targets. Everything below is documented there in more detail.
2.3 Makefile Targets of the Macro
All design steps are invoked through the macro Makefile from within the inverter/ folder. Running make without arguments prints all targets. The most important ones for this workshop are:
| Target | Description |
|---|---|
make sim-xschem [TB=<tb>] |
Run one Xschem testbench in batch mode (netlist + ngspice -b) |
make sim-view-xschem [SCRIPT=<script>] |
Plot the exported simulation data with Python |
make sim-all |
Run all three testbenches in sequence |
make klayout-drc |
Run KLayout DRC on the layout |
make magic-drc |
Run Magic DRC on the layout |
make klayout-lvs |
Export the schematic netlist and run KLayout LVS |
make magic-lvs |
Export the schematic netlist and run Magic + Netgen LVS |
make magic-pex |
Run parasitic extraction with Magic |
make klayout-verify |
DRC and LVS with KLayout in one command |
make magic-verify |
DRC, LVS, and PEX with Magic in one command |
make build-top |
Build the deliverables (LEF, LIB, Verilog stub, GDS copy, render) |
make all |
Verify, build, and simulate everything |
make clean |
Delete all generated files and folders (deliverables, netlists, reports, simulation outputs) |
Most targets accept optional variables, for example CELL=<cellname> to select a specific cell (defaults to inverter), EXT_MODE=<1|2|3> for the PEX mode, and DRC_LEVEL=<precheck|macro|regular> for the KLayout DRC rule set. TB and SCRIPT default to <CELL>_tb_tran and plot_<CELL>, so both simulation targets also run without arguments. See the inverter README for the full list.
KLayout-PEX (kpex) does not support the ihp-sg13cmos5l PDK yet, so the klayout-pex target currently fails. Use make magic-pex for parasitic extraction.
3 The Analog Macro: A CMOS Inverter
The inverter macro is the analog example of this workshop. It is a CMOS inverter drawn at transistor level and verified with a complete verification chain: DRC, LVS, and PEX with KLayout and Magic + Netgen.
An inverter can act as a digital circuit, when the input is driven by a digital signal and the output switches between the supply rails. Here we characterize the inverter as an analog circuit. We are interested in its continuous voltage transfer characteristic and its small-signal voltage gain around the operating point.
Figure 1 shows the circuit. The NMOS \(M_1\) and the PMOS \(M_2\) form the complementary inverter. The transistors \(M_\mathrm{dummy1}\) and \(M_\mathrm{dummy2}\) are dummy devices. They do not contribute to the circuit function but improve matching in the layout, because the active transistors then see identical neighboring structures on both sides.
3.1 Transistor Sizing
The inverter is sized with the \(g_\mathrm{m}/I_\mathrm{D}\) method (see Transistor Sizing Using \(g_\mathrm{m}/I_\mathrm{D}\) Methodology for the theoretical background). Instead of relying on textbook square-law equations, the method looks up the actual device behavior from pre-characterized SPICE simulations. The full sizing flow is implemented in the Jupyter notebook scripts/sizing/sizing_inverter.ipynb, which uses pygmid to query the pre-characterized ihp-sg13cmos5l lookup tables in scripts/sizing/data/. An overview of the techsweeps is available in doc/sizing/.
3.1.1 Design Tradeoffs: Choosing \(g_\mathrm{m}/I_\mathrm{D}\) and \(L\)
Before sizing anything, two numbers have to be picked: the transconductance efficiency \(g_\mathrm{m}/I_\mathrm{D}\) and the channel length \(L\). Everything else follows from them, so it is worth understanding what each one buys and what it costs.
\(g_\mathrm{m}/I_\mathrm{D}\) says how much transconductance you get per ampere of bias current, in \(\mathrm{S/A}\). It is a normalized, technology-independent measure of the inversion level of the device.
Figure 2 shows the tradeoffs in one picture. The arrows point from the center towards the corner where each design parameter reaches its best value, so the same parameter is at its worst in the opposite direction.
Read it corner by corner:
- Strong inversion, short \(L\) (bottom left): \(f_\mathrm{T}\) and input impedance \(Z_\mathrm{in}\) are maximal, gate capacitance \(C_\mathrm{gg}\) and area are minimal. This is the high-speed corner.
- Strong inversion, medium \(L\) (left): minimal thermal noise PSD \(S_\mathrm{TH}\) and maximal current density \(I_\mathrm{D}/W\).
- Moderate inversion, short \(L\) (bottom): the speed-efficiency product \(f_\mathrm{T} \cdot g_\mathrm{m}/I_\mathrm{D}\) is maximal, the best overall compromise between bandwidth and current.
- Moderate inversion, long \(L\) (top): the output resistance \(r_\mathrm{ds} = 1/g_\mathrm{ds}\) is maximal. This is where current sources belong.
- Weak inversion, long \(L\) (top right): intrinsic gain \(g_\mathrm{m}/g_\mathrm{ds}\) is maximal, while flicker corner frequency \(f_\mathrm{co}\), noise factor \(\gamma\), flicker noise \(S_\mathrm{FL}\), offset \(V_\mathrm{OS}\), and power \(P\) are minimal. This is the low-power, low-noise, high-gain corner.
- Weak inversion, medium \(L\) (right): overdrive \(V_\mathrm{od}\) and \(V_\mathrm{ds,sat}\) are minimal, so this corner gives the most voltage headroom.
Two corners stay empty on purpose. Strong inversion with a long channel and weak inversion with a short channel optimize nothing, they combine the drawbacks of both settings, so in practice they make little sense.
Fast circuits use strong inversion with short channels. Low-power and low-noise circuits use weak inversion with long channels. Current sources use moderate inversion with longer channels, to maximize their output resistance. Everything else starts in moderate inversion and is moved from there.
The sizing below lands at \(g_\mathrm{m}/I_\mathrm{D} = 3.76\,\mathrm{S/A}\) with \(L = 1\,\mathrm{\mu m}\), which is deep strong inversion with a comparatively long channel, that is the corner the map leaves empty. This is a deliberate compromise for this example, not an oversight: the stage has to drive a \(10\,\mathrm{pF}\) load, which demands a lot of \(g_\mathrm{m}\) and therefore a lot of current, while the long channel keeps the intrinsic gain and the matching of this very simple circuit acceptable. It also explains the modest measured gain of about \(31\,\mathrm{dB}\). Keep the map in mind when you size your own blocks, where you are free to choose the operating point rather than inheriting it.
3.1.2 Sizing the Inverter
The notebook runs through the following steps:
- Define the design targets: \(V_\mathrm{DD} = 1.5\,\mathrm{V}\), \(V_\mathrm{cm,in} = V_\mathrm{cm,out} = V_\mathrm{DD}/2 = 0.75\,\mathrm{V}\), \(I_\mathrm{out} = 0.75\,\mathrm{mA}\), \(C_\mathrm{load} = 10\,\mathrm{pF}\), and \(L = 1\,\mathrm{\mu m}\). \(L\) is chosen longer than the minimum to increase intrinsic gain and reduce mismatch, at the cost of area and bandwidth.
- Look up \(g_\mathrm{m}/I_\mathrm{D}\), \(g_\mathrm{m}/g_\mathrm{ds}\), \(g_\mathrm{m}/C_\mathrm{gg}\), and \(I_\mathrm{D}/W\) for the chosen \(L\) and operating-point voltages.
- Compute the required widths \(W = I_\mathrm{D} / (I_\mathrm{D}/W)\) for NMOS and PMOS.
- Round the continuous widths to a practical finger configuration with equal finger counts for NMOS and PMOS (for better layout matching), and recompute the small-signal parameters with the final widths.
- Estimate the open-loop gain \(A_\mathrm{ol} = -g_\mathrm{m,tot} / g_\mathrm{ds,tot}\) and the output resistance \(R_\mathrm{out} = 1/g_\mathrm{ds,tot}\).
The resulting device geometry is summarized in Table 5. These are exactly the values you will enter in the schematic in Section 4.
| Device | \(L\) (\(\mathrm{\mu m}\)) | Fingers (\(\mathrm{ng}\)) | \(W\) / Finger (\(\mathrm{\mu m}\)) | Total \(W\) (\(\mathrm{\mu m}\)) |
|---|---|---|---|---|
| NMOS \(M_1\) | 1.0 | 20 | 1.0 | 20.0 |
| PMOS \(M_2\) | 1.0 | 20 | 6.0 | 120.0 |
| NMOS \(M_\mathrm{dummy1}\) | 1.0 | 2 | 1.0 | 2.0 |
| PMOS \(M_\mathrm{dummy2}\) | 1.0 | 2 | 6.0 | 12.0 |
With the final widths, the notebook computes \(g_\mathrm{m,tot} = g_\mathrm{m,NMOS} + g_\mathrm{m,PMOS} = 3.35\,\mathrm{mS} + 2.92\,\mathrm{mS} = 6.28\,\mathrm{mS}\) and \(g_\mathrm{ds,tot} = 145\,\mathrm{\mu S}\). From these values the key metrics can be estimated:
\[ |A_\mathrm{ol}| = \frac{g_\mathrm{m,tot}}{g_\mathrm{ds,tot}} = \frac{6.28\,\mathrm{mS}}{145\,\mathrm{\mu S}} = 43.2 = 32.72\,\mathrm{dB}, \]
\[ R_\mathrm{out} = \frac{1}{g_\mathrm{ds,tot}} = \frac{1}{145\,\mathrm{\mu S}} = 6.89\,\mathrm{k\Omega}, \]
\[ f_\mathrm{cu} = \frac{1}{2\pi\,R_\mathrm{out}\,C_\mathrm{load}} = \frac{1}{2\pi \cdot 6.89\,\mathrm{k\Omega} \cdot 10\,\mathrm{pF}} = 2.31\,\mathrm{MHz}, \]
\[ f_\mathrm{T} = \frac{g_\mathrm{m,tot}}{2\pi\,C_\mathrm{load}} = \frac{6.28\,\mathrm{mS}}{2\pi \cdot 10\,\mathrm{pF}} = 100\,\mathrm{MHz}. \]
Table 6 compares these hand calculations against the ac simulation results from the testbench in Section 4.4.1. The agreement is very good, which confirms the lookup-table approach.
| Quantity | \(g_\mathrm{m}/I_\mathrm{D}\) calculation | ac open-loop simulation |
|---|---|---|
| Open-loop dc gain \(A_\mathrm{ol}\) | \(32.72\,\mathrm{dB}\) | \(31.33\,\mathrm{dB}\) |
| Open-loop cut-off frequency \(f_\mathrm{cu}\) | \(2.31\,\mathrm{MHz}\) | \(2.89\,\mathrm{MHz}\) |
| Unity-gain frequency \(f_\mathrm{T}\) | \(100\,\mathrm{MHz}\) | \(107\,\mathrm{MHz}\) |
The notebook is fully parametric. Changing \(L\), the target \(g_\mathrm{m}/I_\mathrm{D}\), or \(I_\mathrm{out}\) and re-running the cells immediately produces a new sizing point. Try it yourself: decrease the transistor length to \(L = 0.5\,\mathrm{\mu m}\) and observe how the small-signal parameters and the resulting gain and cut-off frequency change. What would you expect beforehand?
3.2 Batch Simulation with the Makefile
Every testbench can be run non-interactively from the inverter/ folder. This is useful for reproducible results and scripting.
cd inverter
make sim-xschem TB=inverter_tb_dc_vout
make sim-xschem TB=inverter_tb_ac_ol
make sim-xschem TB=inverter_tb_tran
make sim-all # all three in sequence
make sim-view-xschem SCRIPT=plot_inverter # plot the exported data with Pythonsim-xschem netlists the testbench with xschem netlist and then calls ngspice -b directly, so make blocks until the run finishes and sees its exit status. Because the run is headless, the plot commands in the testbench are a no-op. Every testbench instead exports its results with wrdata to testbenches/xschem/plot_simulations/data/, from where the Python script plots them. Figure 3 and Figure 4 show the plotted results.
4 Hands-On: Schematic Entry
In this session we draw the inverter schematic, create its symbol, and complete the testbenches together in Xschem.
4.1 Getting Started with Xschem
Xschem is a schematic capture tool where circuits are built from symbols, wires, and net labels. Everything is stored in plain text files (.sch for schematics, .sym for symbols), which makes them friendly for version control and search-and-replace in a text editor.
Start Xschem from the schematic folder of the macro so that the local xschemrc is picked up (it configures the PDK libraries and search paths):
cd inverter/schematic/xschem
xschem # empty session
xschem inverter.sch # or load the schematic directlyThe most important shortcuts are listed in Table 7. A complete list is available in Xschem under Help -> Keys and in the Xschem Cheatsheet.
| Shortcut | Action |
|---|---|
Ins |
Insert a symbol from the component library |
w |
Draw a wire |
m |
Move the selected objects |
c |
Copy the selected objects |
Del |
Delete the selected objects |
q |
Edit the properties of the selected object |
Shift + R |
Rotate the selection |
Shift + F / Shift + V |
Mirror the selection horizontally / vertically |
u / Shift + U |
Undo / redo |
f |
Zoom full |
Ctrl + s |
Save |
e |
Descend into the schematic of the selected symbol |
i |
Descend into the symbol view of the selected symbol |
Ctrl + e |
Return to the parent schematic |
Shift + T |
Toggle the ignore flag on an instance (excluded from netlist) |
n |
Generate a netlist |
Ctrl + o |
Open a schematic or symbol |
Since Xschem is a text-based schematic editor, you can also open .sch and .sym files in a text editor and modify them there. This is especially useful for search-and-replace operations.
4.2 Drawing the Inverter Schematic
We now draw the schematic shown in Figure 5 step by step. The reference is inverter.sch.
inverter schematic in Xschem, with dummy transistors and operating-point annotations.
- Place the transistors. Press
Ins, and picksg13_lv_nmos.symandsg13_lv_pmos.symfrom the PDK library. Place the NMOS \(M_1\) at the bottom and the PMOS \(M_2\) at the top. - Set the device parameters. Select a transistor and press
q. Enter the values from Table 5, for example for \(M_1\):l=1.0u,w=20.0u,ng=20,m=1. Note thatwis the total width andngis the number of fingers. Keepspiceprefix=X, because the PSP transistor models of the PDK are SPICE subcircuits and must be instantiated with anXprefix. - Place the dummy transistors. Copy each device with
cand change the properties to the dummy sizing (w=2.0u,ng=2for the NMOS andw=12.0u,ng=2for the PMOS). Wire all four dummy terminals to the respective supply rail (gate, source, drain, and bulk of \(M_\mathrm{dummy1}\) toVSS, those of \(M_\mathrm{dummy2}\) toVDD), so the dummies are off and only their geometry matters. - Wire the circuit. Press
wand connect the gates of \(M_1\) and \(M_2\) to the input net, the drains to the output net, the NMOS source and bulk toVSS, and the PMOS source and bulk toVDD. - Add the pins. Press
Insand placeipin.symfor the inputvin,iopin.symfor the outputvout, andiopin.symforVDDandVSS. The pin names define the port names of the subcircuit later used for LVS, so name them exactlyvin,vout,VDD, andVSS. - Annotate (optional). Place
annotate_fet_params.symnext to \(M_1\) and \(M_2\) and setref=M1andref=M2. After a simulation, these display the operating-point parameters of the devices directly in the schematic (as visible in Figure 5). - Save with
Ctrl + sasinverter.sch.
4.3 Drawing the Inverter Symbol
To use the inverter in a testbench, it needs a symbol. Figure 6 shows the finished symbol, the reference is inverter.sym.
- Auto-generate the symbol. With
inverter.schopen, pressA(Shift + a). Xschem createsinverter.symwith one pin per schematic pin, placed on a rectangular outline. - Open the symbol. Load it with
Ctrl + o(or select a placed instance and pressi). - Redraw the shape. Delete the auto-generated rectangle and draw the triangle with the output bubble using the line (
l) and arc tools. Keep the pin boxes, they carry the electrical information (nameanddirattributes). - Arrange the pins. Place
vinon the left,vouton the right,VDDon top, andVSSat the bottom. Add the small text labels witht. - Check the global properties. Press
qwith nothing selected. The symbol must havetype=subcircuitandformat="@name @pinlist @symname". Withtype=subcircuit, Xschem descends intoinverter.schduring netlisting and emits the full transistor-level subcircuit. - Save the symbol.
You can now place the symbol in any schematic with Ins. Select it and press e to descend into the underlying circuit, and Ctrl + e to go back up.
4.4 Completing the Testbenches
Three testbenches characterize the inverter. Skeletons are already prepared in testbenches/xschem/, and we finish them together:
inverter_tb_dc_vout.schsweeps the input from \(0\) to \(V_\mathrm{DD}\) and records the voltage transfer characteristic (see Figure 7).inverter_tb_ac_ol.schmeasures the open-loop frequency response (see Figure 8).inverter_tb_tran.schapplies a small sine around the operating point and simulates in the time domain (see Figure 9).
Each testbench is operated with the three launcher buttons in the schematic. Hold Ctrl and click Simulate to netlist and start Ngspice, Load waves to load the results into the integrated waveform viewer (the embedded graphs), and Annotate OP to back-annotate the operating point into the schematic.
4.4.1 Anatomy of the AC Testbench
We look at the ac testbench in detail, because it contains every ingredient a complete analog testbench needs. Open inverter_tb_ac_ol.sch and identify the following parts:
1. The device under test. An instance x1 of inverter.sym. Two spare instances x2 (inverter.sym) and x3 (inverter_pex.sym) are also placed, with the attribute spice_ignore=true so they are excluded from the netlist. They are used later for the post-layout comparison in Section 5.5.
2. Supplies and bias. A voltage source VDD with value={VDD} powers the circuit between the VDD rail and ground. The input source vin has the value
dc {Vcm} ac 1
The dc {Vcm} part biases the input at the switching threshold \(V_\mathrm{cm} = V_\mathrm{DD}/2\), where the inverter has its highest gain. The ac 1 part sets the small-signal stimulus amplitude to \(1\,\mathrm{V}\). This does not overdrive the circuit, because the ac analysis linearizes the circuit around the operating point. With an input of exactly 1, the output magnitude directly equals the voltage gain, so v(vout)/v(vin) is a normalized transfer function.
3. The load. A capacitor C1 with value={Cload} (\(10\,\mathrm{pF}\)) loads the output. A load resistor R1 is placed as well but disabled with spice_ignore=true. Toggle it with Shift + T if you want to study resistive loading.
4. The model includes. A code_shown.sym block named MODEL pulls in the PDK device models with the typical corner:
.lib cornerMOSlv.lib mos_tt
.lib cornerMOShv.lib mos_tt
.lib cornerRES.lib res_typ
.lib cornerDIO.lib dio_tt
The bare file names work because the shared Ngspice configuration config/.spiceinit adds the PDK model directories to the Ngspice source path and loads the compiled PSP OSDI models. The Nix shell points Ngspice at this file automatically. To simulate a different process corner, replace mos_tt with, for example, mos_ss or mos_ff.
5. The control block. A second code_shown.sym block named NGSPICE contains the parameters, simulator options, and the .control section:
.include ../../../netlist/pex/inverter_magic_pex_3.spice
.param VDD=1.5
.param Vcm=VDD/2
.param temp=27
.param Cload=10p
.options savecurrents klu method=gear reltol=1e-4 abstol=1e-15 gmin=1e-15
.control
save all
* User Constants
let f_min = 0.1
let f_max = 10G
let fdc = 1
* Operating Point Analysis
op
remzerovec
write @schname.raw
set appendwrite
* AC Analysis
ac dec 101 $&const.f_min $&const.f_max
remzerovec
write @schname.raw
...
.endc
Read it top to bottom:
- The
.includeline loads the extracted PEX netlist, which defines the subcircuitinverter_pex. It is not used whilex1is active, but it must be present for the post-layout swap in Section 5.5. - The
.paramlines define the supply, the input bias, the temperature, and the load as parameters, so one edit changes the whole testbench. - The
.optionsline selects the KLU sparse solver and tightens the tolerances for a clean ac result. - Inside
.control, first an operating point analysis (op) runs and its result is written to the.rawfile. This is what Annotate OP reads. - Then the ac analysis sweeps 101 points per decade from \(0.1\,\mathrm{Hz}\) to \(10\,\mathrm{GHz}\) and appends the result to the same
.rawfile.
6. The measurements. After the analysis, the control block computes the open-loop gain and extracts the key numbers:
let Aol = v(vout)/v(vin)
let Aol_dB = vdb(Aol)
let Aol_arg = 180/PI*cphase(Aol)
meas ac Adc_ol_dB find Aol_dB when frequency = fdc * dc gain at 1 Hz
let Aol_fc = Adc_ol_dB - 3
meas ac fc find frequency when Aol_dB = Aol_fc * -3 dB cut-off frequency
meas ac UGB when Aol_dB=0 fall=1 * unity-gain bandwidth
meas ac arg_0dB find Aol_arg when Aol_dB=0
let PM = 180-abs(arg_0dB) * phase margin
The phase starts at \(180°\) because the inverter is an inverting amplifier. With the sizing from Section 3.1 you should measure approximately \(A_\mathrm{ol} = 31.3\,\mathrm{dB}\), \(f_\mathrm{c} = 2.9\,\mathrm{MHz}\), and \(\mathrm{UGB} = 107\,\mathrm{MHz}\).
7. The data export. The final wrdata command writes the traces to plot_simulations/data/, so the Python plotting script and the batch flow from Section 3.2 can use them.
8. Graphs and the save file. The embedded graph boxes display magnitude and phase after Load waves. A small SAVE code block includes <testbench>.save, which lists the nodes to store.
The dc and transient testbenches follow exactly the same pattern. Only the analysis command changes: dc Vgsp 0 $&VDD 1m sweeps the input source in \(1\,\mathrm{mV}\) steps, and tran 1u 5m simulates \(5\,\mathrm{ms}\) with a sine input sin({Vcm} 10m 1k), a \(10\,\mathrm{mV}\) amplitude at \(1\,\mathrm{kHz}\). Complete these two testbenches yourself. In the dc sweep, at which input voltage does vout cross \(V_\mathrm{cm} = 0.75\,\mathrm{V}\)? The simulation reports approximately \(0.745\,\mathrm{V}\), slightly below \(V_\mathrm{DD}/2\).
4.5 Generating the PEX Symbol
For the post-layout simulation in Section 5.5 we prepare a second symbol now, so that the extraction flow can reorder its netlist pins to match it. The reference is inverter_pex.sym.
- Copy the symbol file:
cp inverter.sym inverter_pex.sym. - Open
inverter_pex.symin Xschem and pressqwith nothing selected to edit the global properties. - Change the type from
subcircuittoprimitive.
The two settings behave differently during netlisting:
type=subcircuit: Xschem descends into the matching schematic (inverter.sch) and writes the full transistor-level subcircuit into the netlist. The instance line references that generated subcircuit.type=primitive: Xschem does not descend and only writes the instance linex1 vin vout VDD VSS inverter_pex. The subcircuit definition must come from somewhere else, in our case from the extracted PEX netlist pulled in with the.includeline of the testbench.
Since the symbol file is named inverter_pex.sym, the @symname in the instance line resolves to inverter_pex, which matches the subcircuit name that the PEX flow generates. The pin order of this symbol is also the reference that make magic-pex uses to reorder the pins of the extracted netlist, so schematic symbol and extracted subcircuit always stay compatible.
5 Hands-On: Layout
In this session we draw the layout of the inverter in KLayout and verify it with DRC, LVS, and PEX.
5.1 Getting Started with KLayout
Open KLayout in edit mode with the sg13cmos5l technology preloaded from the repository root:
make klayoutSome basics before drawing:
- Data base unit. When creating a new layout (
File > New Layout), set the database unit (DBU) to \(0.001\,\mathrm{\mu m}\). This is important for the IHP Open-PDK. - Layers. Each drawing layer has three roles:
MetalX.drawingcarries the actual geometry,MetalX.pinmarks pin shapes, andMetalX.textcarries the pin labels used by LVS. - PCells. The PDK provides parametric cells (PCells) in the
SG13_devlibrary, for examplenmos,pmos, resistors (rppd,rsil,rhigh), capacitors (cap_mfringe,moscap_n,moscap_p), taps (ntap1,ptap1), theguard_ring, andvia_stack.
The most important shortcuts are listed in Table 8, taken from the KLayout cheatsheet in this repository.
| Shortcut | Action |
|---|---|
Q |
Properties of the selected object / PCell |
M |
Move quickly |
A |
Align tool |
C |
Copy object |
P |
Path / drawing tool (double-click to finish) |
R |
Draw rectangle |
T |
Place text |
Shift + P |
Set pin (input, output, …) for LVS |
O |
Place vias during routing (path mode) |
E / Ctrl + E |
Descend into / ascend from an instance |
Shift + R |
Rotate instance by 90° |
Shift + H / Shift + V |
Flip instance horizontally / vertically |
F |
Zoom full |
U / Shift + U |
Undo / redo |
K / Shift + K |
Ruler+ / clear all rulers |
0 … 9 |
Layer focus (see below) |
5.2 The KLayout Productivity Suite
The KLayout Productivity Suite is a collection of plugins developed at JKU that make manual layout much faster. They were installed with make klayout-setup in Section 1, or are already present from an earlier installation, since they live in your user directory ~/.klayout/salt/. The full documentation with animations is here. A short summary:
| Plugin | What it does |
|---|---|
Move Quickly Tool (M) |
Rapid repositioning of shapes and instances with mouse or keyboard, including diagonal moves and multi-object selection |
Align Tool (A) |
Align objects to each other by clicking reference features (middle, corner, or edge markers) |
Pin Tool (Shift + P) |
Place LVS-ready pins (pin shape plus text label) with automatic layer detection from the current layer selection |
Layer Shortcuts (0…9) |
Fast layer visibility switching: 1…7 focus on one metal with its vias, 8 gate poly, 9 diffusion, 0 shows the default layers, , hides them, Shift + 1…Shift + 9 extend the current focus by one more layer group |
Netlist Import (File > Import > Netlist) |
Import a SPICE netlist and place the cell instances with technology-specific mapping. Great for starting a layout from the schematic netlist |
Library Manager (File > Cell Library Manager) |
Manage hierarchical layouts and cell libraries, and export a clean layout for tapeout |
Auto Backup (File > Automatic Backups) |
Periodic layout backups with configurable interval and rotation |
Vector File Export (File > Export Vector File) |
Export the layout view as PDF or SVG |
5.3 Drawing the Inverter Layout
We now draw the layout of the inverter cell. The finished result is shown in Figure 10.
- Create the cell.
File > New Layout, top cell nameinverter, DBU \(0.001\,\mathrm{\mu m}\). - Place the transistor PCells. Drag
nmosandpmosfrom theSG13_devlibrary into the layout. Place the PMOS row above the NMOS row. - Match the schematic. Select each PCell and press
Q. Set the samew,l, andngvalues as in the schematic (Table 5): the NMOS with \(20 \times 1.0\,\mathrm{\mu m}\) fingers and the PMOS with \(20 \times 6.0\,\mathrm{\mu m}\) fingers, both with \(L = 1.0\,\mathrm{\mu m}\). Add the two dummy devices next to the active devices in the same way. - Substrate and well contacts. Place
ptap1/ntap1contacts (or aguard_ring) to tie the substrate toVSSand the n-well of the PMOS toVDD. - Route. Connect the gates (input), the drains (output), and the source rails with
Metal1andMetal2. Use the path toolP, place vias withOor withvia_stackPCells. Keep signal routing on the lower metals within a macro. Use the layer shortcuts1,2,8,9to focus on the layer you are working on. - Add the pins. Select the target metal layer and press
Shift + Pto place the pinsvin,vout,VDD, andVSS. The pin tool places the pin shape onMetalX.pinand the label onMetalX.text, which is exactly what LVS expects. - Save as
layout/inverter.gds.
The layout drawing order follows the matching rules of analog design: active devices in the center with common orientation, dummies at the edges, and taps close to the devices. Also check the layout tips in the KLayout cheatsheet.
5.4 DRC, LVS, and PEX
The layout is verified with the Makefile targets from Section 2.3, executed in the inverter/ folder. Both KLayout and Magic + Netgen flows are available. The sak-* scripts driving them are vendored from IIC-OSIC-TOOLS in scripts/.
5.4.1 Design Rule Check
make klayout-drc # KLayout DRC, default DRC_LEVEL=macro
make magic-drc # Magic DRC (full rule set)The DRC_LEVEL variable selects the KLayout rule set: precheck runs only the core manufacturing rules for fast iteration, macro (the default) adds off-grid, zero-area, and pin checks for block-level sign-off, and regular adds density and antenna checks for full-chip sign-off. Reports are written to verification/drc/. A successful run ends with:
INFO | =====================================================================================
INFO | KLayout DRC Check Passed: No DRC violations detected in the layout.
INFO | =====================================================================================
In the KLayout GUI, a DRC run can be launched with F9, and the DRC options are behind F10. Violations are then browsed interactively in the marker database.
5.4.2 Layout Versus Schematic
make klayout-lvs # CDL netlist exported from Xschem, compared by KLayout
make magic-lvs # SPICE netlist exported from Xschem, compared by Magic + NetgenBoth targets first export the schematic netlist from Xschem automatically, then extract the layout netlist and compare the two. Reports are written to verification/lvs/. The ntap and ptap substrate contacts are ignored during LVS in both flows, so the taps in the layout do not need matching schematic devices. A successful KLayout run prints:
INFO | | Status | PASS |
INFO | | Mode | COMPARE |
INFO | | Outcome | Comparison mode: PASS (netlists match). |
INFO | | Warnings | 0 |
INFO | | Errors | 0 |
The Magic + Netgen flow reports:
Cell pin lists are equivalent.
Device classes inverter and inverter are equivalent.
Final result: Circuits match uniquely.
The Netgen report additionally lists property warnings about a mm_ok property that exists only on the layout side. This warning is expected with the current PDK and can be ignored as long as the final result reads Circuits match uniquely.
In the KLayout GUI, an LVS run can be launched with F11, and the LVS options are behind F12, where the exported schematic netlist (netlist/schematic/inverter_klayout.cdl) has to be selected manually. Set deep as the run mode.
5.4.3 Parasitic Extraction
make magic-pex # full-RC extraction (EXT_MODE=3, default)
make magic-pex EXT_MODE=2 # C-coupled capacitances only
make magic-pex EXT_MODE=1 # C-decoupled capacitances onlyPEX extracts the parasitic capacitances and resistances of the drawn wires into a SPICE netlist in netlist/pex/, for example inverter_magic_pex_3.spice. The subcircuit inside is named inverter_pex, and its pin order is automatically rearranged to match the PEX symbol from Section 4.5. A port check runs at the end and fails the target if any pin of the extracted subcircuit is left floating.
For the full-RC mode, three tuning parameters control how much of the resistor network is kept (THRESHOLD, MINRES, MINDELAY). The defaults are fine for this workshop, details are in the inverter README.
5.5 Post-Layout Simulation
Now we close the loop and simulate the extracted layout in the same testbenches:
- Open a testbench, for example
inverter_tb_ac_ol.sch. - Select the active instance
x1(inverter.sym) and pressShift + Tto set itsspice_ignoreflag. It is now excluded from the netlist. - Select the spare instance
x3(inverter_pex.sym) and pressShift + Tto activate it. Wire positions are identical, so nothing else changes. - The
.include ../../../netlist/pex/inverter_magic_pex_3.spiceline at the top of the control block now supplies the subcircuit definition. If you extracted with a differentEXT_MODE, adapt the file name. - Re-run the simulation with
Ctrl+ click on Simulate.
Compare the post-layout results against the schematic-level results. What differences do you see, and why?
Think about it before simulating. The extracted netlist adds the wiring resistance and the capacitance of every drawn wire to the circuit. The dc transfer characteristic should barely change, because almost no current flows into the parasitics at dc. In the ac response, the added output and internal capacitances and the interconnect resistance shift the poles, so expect a slightly reduced bandwidth and small changes in the measured gain. If your post-layout results deviate strongly, that is usually a sign of a layout problem, for example a too narrow or too long wire on a critical net.
6 Exercises
Now it is your turn. Pick one of the two exercises (or do both if you are fast). They reuse the complete flow of Section 4 and Section 5 on a modified circuit. Work on your own, and ask us whenever you get stuck.
A reference implementation of the exercises will be provided in a separate branch of the repository after the workshop.
6.1 Exercise 1: Self-Biased Single-Ended Inverter Amplifier
The bare CMOS inverter has poor PVT robustness as an amplifier, because nothing pins the input bias to the high-gain point at \(V_\mathrm{DD}/2\). A feedback resistor \(R_\mathrm{f}\) between output and input closes a dc loop that forces the operating point to the crossover (\(V_\mathrm{in} \approx V_\mathrm{out} \approx V_\mathrm{DD}/2\)) regardless of the process corner. This also linearizes the transfer characteristic and turns the inverter into a usable single-ended amplifier. The price is a reduced input resistance of the resulting amplifier.
Recommended steps:
Copy the macro folder and delete the generated files:
cp -r inverter amp cd amp make cleanRename
TOPin theMakefiletoTOP = amp. All targets derive their file paths fromTOP(andCELL, which defaults toTOP), so the design files must carry the same name.Rename the Xschem schematic, symbol, and testbench files:
for f in schematic/xschem/inverter* testbenches/xschem/inverter*; do mv "$f" "$(echo "$f" | sed 's/inverter/amp/')" doneSince Xschem files are plain text, update the remaining
inverterreferences inside the renamed files with search-and-replace, for example theinverter.syminstances and the.includeof the PEX netlist in the testbenches.Rename the KLayout layout file and the top cell:
mv layout/inverter.gds layout/amp.gdsAlso rename the top cell inside the GDS from
invertertoamp: open the layout in KLayout, rename the cell, and save. The verification targets require matching file and top-cell names.Open
amp.schin Xschem and add the feedback resistor between thevinandvoutnets, as shown in Figure 11. Use therhighdevice of the PDK (sg13cmos5l_pr/rhigh.sym), the high-sheet poly resistor, and set \(W = 0.5\,\mathrm{\mu m}\) and \(L = 25\,\mathrm{\mu m}\) withq. Keepspiceprefix=Xand connect thebodyterminal toVSS. The symbol computes its value from the geometry, which gives \(R_\mathrm{f} \approx 74\,\mathrm{k\Omega}\) for these dimensions.Think first, then simulate. What do you expect to happen to the dc transfer characteristic, the ac response, and the measured gain? Then re-run the three testbenches and check whether the results match your expectations. Note that the ac open-loop testbench drives
vindirectly with a source, so to observe the self-biasing effect, also try disconnecting the input bias and let the feedback set the operating point.Vary \(R_\mathrm{f}\) and observe the trade-off. Too small, and the resistor loads the amplifier output and reduces the gain. Too large, and the time constant with the input capacitance limits the low end of the signal band. The resistance scales with the length, roughly \(3\,\mathrm{k\Omega}\) per \(\mathrm{\mu m}\) at \(W = 0.5\,\mathrm{\mu m}\), so \(L = 10\,\mathrm{\mu m}\) gives about \(30\,\mathrm{k\Omega}\) and \(L = 100\,\mathrm{\mu m}\) about \(300\,\mathrm{k\Omega}\). Keep \(W\) at \(0.5\,\mathrm{\mu m}\) and sweep \(L\).
Propagate the resistor to the layout (
layout/amp.gds) with therhighPCell, using the same \(W = 0.5\,\mathrm{\mu m}\) and \(L = 25\,\mathrm{\mu m}\) as in the schematic (also place dummy resistors of minimum length above and below it), and re-run the verification chain withmake klayout-verifyandmake magic-verify.
6.2 Exercise 2: Three-Stage Ring Oscillator with Output Buffer
An odd number of inverters in a loop has no stable operating point and oscillates. Build a three-stage ring oscillator from the inverter cell and use a fourth inverter as an output buffer that decouples the ring from the load, as shown in Figure 12.
Recommended steps:
Follow the same setup recipe as in Section 6.1 with
ringoscinstead ofamp: copy the folder, runmake clean, renameTOPin theMakefiletoTOP = ringosc, rename the Xschem schematic, symbol, and testbench files, and rename the KLayout layout file tolayout/ringosc.gds. One difference: do not rename the top cell inside the GDS yet, theinvertercell stays in use as the unit cell (see the last step).Restore the unit cell from the original macro, you instantiate it four times:
cp ../inverter/schematic/xschem/inverter.sch ../inverter/schematic/xschem/inverter.sym schematic/xschem/Open
ringosc.sch(it still contains the copied transistor-level inverter), delete its content, and place four instances ofinverter.sym. Connect three of them in a ring, and tap the ring node with the fourth inverter as the buffer. Add the pinsvout,VDD, andVSS.Update the symbol
ringosc.symand the PEX symbolringosc_pex.sym: remove thevinpin, the ring oscillator has no input. Alternatively, regenerate the symbol from the schematic (pressA), following Section 4.3 and Section 4.5.Adapt the renamed transient testbench
ringosc_tb_tran.sch: remove the input source, and measure the oscillation frequency. Since the delay per stage is small, simulate a few hundred nanoseconds with a fine step. Measure the frequency with twomeas trancommands that time consecutive rising crossings of \(V_\mathrm{cm}\), and compute \(f_\mathrm{osc} = 1/T_\mathrm{osc}\) from them:meas tran t_rise1 when v(vout)=Vcm rise=1 meas tran t_rise2 when v(vout)=Vcm rise=2 let T_osc = t_rise2 - t_rise1 let f_osc = 1/T_osc print f_oscAdd
.csparam Vcm=Vcmnext to the.param Vcm=VDD/2line, otherwiseVcmis not visible inside the.controlblock. Make sure the stored transient window contains at least two rising edges. The expected relation is \(f_\mathrm{osc} = 1/(2 \cdot N \cdot t_\mathrm{d})\) with \(N = 3\) stages and \(t_\mathrm{d}\) the effective stage delay. Delete the testbenches that are no longer relevant.Think about the startup. An ideal simulation can rest on the metastable point where all stages sit at \(V_\mathrm{cm}\). If the ring does not start, kick it with an initial condition, for example
.ic v(osc)=0.Resize the inverter and watch the frequency. Open
inverter.schand vary \(W\), \(L\), and the finger countngof \(M_1\) and \(M_2\), then re-run the transient simulation for each variant. Predict the outcome before you simulate, and use Section 3.1.1 as your guide.Draw the top-level layout in
layout/ringosc.gds, following Section 5.3: create a new top cellringosc, place four instances of the existinginvertercell, and route the ring. Re-run DRC and LVS, extract the parasitics withmake magic-pex, and compare the post-layout oscillation frequency against the schematic level. The parasitics add delay per stage, so the frequency drops.
7 Acknowledgements
This workshop was created for HeiChips 2026, organized by the Novel Computing Technologies group at Heidelberg University. It is based on the ihp-sg13g2 AMS chip design tutorial by Simon Dorrer and Harald Pretl, Institute for Integrated Circuits and Quantum Computing, Johannes Kepler University Linz. Many thanks to the maintainers of the open-source tools and the IHP Open-PDK that make this flow possible.
