When something breaks
The failures that actually happen, and what they mean.
Nothing runs at all
magic: command not found, or the PDK is missing. PDK_ROOT and PDK are not set. See bash/bashrc, and check ls $PDK_ROOT/$PDK/libs.tech/magic/sky130A.magicrc returns something.
can't read "env(PDK_ROOT)" from magic. Same cause: the rc file is sourced before the variable exists.
Magic starts but the helpers are missing. magic/.magicrc sources ../tech/magic/cic.tcl with a relative path, so magic has to be started from work/. Use make lview.
LVS
Hundreds of port errors, or “failed pin matching” on a cell you know is right. Hierarchical extraction promoted every dangling net to a port: dummy poly, unrouted bulk straps of tap-less cells. Switch that IP to the flat netlist flow in work/Makefile:
LVSTCL=lvsflat.tcl
See lvsflat.tcl for why, and Verification for the trade-off.
“Property errors” but every itemized property matches. Symmetric gates get resolved by property in a flat run and netgen calls that an error. The flat flow already passes --noprop (checklvs); keep the hierarchical per-cell runs for real property checking.
An empty subcircuit appears in the source netlist. The block is defined only in gate level verilog. Set VERILOG_FILE in work/Makefile so netgen reads it.
Circuits match, but the two netlists are painful to diff. The pin order differs, which LVS does not care about because netgen matches by name. Fix it with make matchports MPOPT=--apply (matchports.py) and re-extract.
Layout
A cell overhangs its parent, or cicarray steps by the wrong pitch. A stale FIXED_BBOX. make fixbbox to see, make fixbbox BBOPT=--apply to write. See fixbbox; it never touches symlinked cells.
Magic loads the wrong child cell, or none, after moving cells between libraries. The use lines still name the old directory. make fixmag (fixmag.py) re-resolves them. It writes in place with no dry run, so commit first.
Magic keeps showing the old layout after regenerating it. The session still has the cells in memory. Source magic/flush_all.tcl to reload every cell from disk.
You cannot see the routing for the implants. Source magic/see_metal.tcl, and magic/see_all.tcl to put everything back.
DRC is clean in magic and fails in KLayout. Believe KLayout: it is closer to the foundry deck. make kdrc never fails the build precisely so this shows up as information rather than a blocked flow.
Netlists and simulation
The testbench cannot find the DUT, or connects the wrong nets. Port order changed. Testbenches include ../xdut.spi, regenerated by genxdut on every make netlist, exactly so this cannot happen. If you instantiated the DUT by hand, stop doing that.
The top level subcircuit is commented out in the netlist. Xschem prefixes it with a double asterisk to emulate a testbench top. make xsch pipes through fixsubckt, which undoes it. Getting a raw netlist some other way skips that step.
The extracted netlist has the wrong ports. Flat extraction derives the port list from whatever is labelled in the flattened layout. fixlpe swaps in the schematic’s port list, and make lpe runs it automatically. The original is left commented out in the file.
A netlist diffs noisily against the previous run for no reason. Instance order. make spi pipes through fixspi, which sorts instances by group and number.
Post-layout simulation is unbearably slow. Use lpeh instead of lpe: it keeps the hierarchy and uses a looser 0.1 fF threshold, at the cost of missing coupling between cells. Or raise cthresh in the extraction template.
make lpe says the extracted netlist does not match the CDL. That check exists to stop exactly this from becoming your simulation result. Something in extraction lost or added a connection; fix it before trusting any post-layout number.
Corners
A corner name is not recognised. The name must exist in the corner: section of cicsim/cicsim.yaml. The four positions are general, process, temperature and supply; see Simulation for the grammar.
Corners are wrong after a PDK update. Regenerate them: cd py && make parse && make process, then cd ngspice && make corners. See genyaml.
Documentation
mkdocsite.py --check fails in CI. A file was added without a .md beside it, a folder has no README.md, or a .md documents a file that was deleted. The message says which. That is the whole point of the check: see mkdocsite.py.
“Get Pages site failed” in the DOCS workflow. The repository has no Pages site yet. The main branch run passes enablement: true to create it; pull requests skip Pages entirely and only prove the site compiles.