A tech file is a JSON document that tells Digital_Layout_Auto everything it needs to
know about a PDK. The generators themselves contain no PDK data.
ihp_sg13g2.json is a complete, working example for the open
IHP SG13G2 PDK.
Keep tech files for proprietary PDKs out of version control.
.gitignorealready ignoreslocal/andtech/*.local.json; put them there.
- A std-cell LEF (macros with
SIZE, signal pins drawn on one metal layer — the pin layer) and the matching GDS (one cell per macro, same names). - Three metal layers usable by the generators: the pin layer (usually metal 1), a vertical routing layer above it and a horizontal routing layer above that, plus the two vias between them.
- Cells for the logic roles
inv,nand2,nor2,nor3and one or more fillers whose widths are multiples of the site. Rows abut with alternating R0/MX orientation and share their power rails, which must run along the full cell width on the pin layer. - Libraries that need separate well-tap cells are not supported yet.
Paths may use ${VAR} or ${VAR:-default}; relative paths are relative to the tech file.
If a library's pin names differ from A/B/C/Y, give the mapping under pins.
- Routing grid. Via pads are sized from the via size and the rectangular enclosure, oriented to fit the track pitch. The y pitch is the smallest value for which pads/wires of different nets on neighbouring grid nodes keep the layer spacing, so the router only has to track node ownership.
- Pin access. For every signal pin a VIA1 position on a vertical-layer track is searched in the LEF geometry. If the pin metal is too narrow for the via enclosure, a small pin-layer patch is added when it keeps the pin-layer spacing to everything else in the cell.
- Minimum area. Pin stubs on the vertical layer are lengthened to the layer's
min_area, and their grid nodes are reserved accordingly. - Rails are read from the filler's power pins.
Run python gen.py --tech my_pdk.json decoder --bits 4 to see the derived grid
([tech] line) and check a first block.
Needed only for python gen.py stdcell, which generates the library itself. It needs
no LEF/GDS files: files and cells may be omitted when only the generator is used.
Metal 1 rules come from layers.pin, the pin access pad from layers.via1, the track
grid from grid.
"stdcell": {
"row_height": 3.78, "site_width": 0.48, "site_name": "CoreSite",
"rail_width": 0.44, // metal-1 power rail (centred on the cell edge)
"layers": {"activ": [1, 0], "poly": [5, 0], "cont": [6, 0], "nwell": [31, 0],
"pimp": [14, 0], // p+ implant
"nimp": null, // n+ implant layer if the PDK draws one
"pin": [8, 2], "boundary": [189, 4]},
"devices": {"nmos": "sg13_lv_nmos", "pmos": "sg13_lv_pmos", // SPICE model names
"l": 0.13, "wn": 0.74, "wp": 1.12},
"rules": {
"activ_width": 0.15, "activ_space": 0.21,
"poly_width": 0.13, "poly_space": 0.18, "poly_endcap": 0.18, "poly_activ_space": 0.07,
"cont_size": 0.16, "cont_space": 0.18, "cont_activ_enc": 0.07, "cont_poly_enc": 0.07,
"cont_gate_space": 0.11, "polycont_activ_space": 0.14, "m1_cont_enc": [0.0, 0.05],
"nwell_enc_pdiff": 0.31, "nwell_space_ndiff": 0.31, "nwell_enc_ntap": 0.24, "nwell_space_ptap": 0.24,
"pimp_enc_pdiff": 0.18, "pimp_space_ndiff": 0.18, "pimp_enc_ptap": 0.03, "pimp_space_ntap": 0.03,
"pimp_enc_gate": 0.30, "pimp_space_gate": 0.30, "pimp_cont_space": 0.09
}
}If layers.via1_keepout names the contact layer, the generator keeps every pin access
via that far from contacts, puts the tap contacts halfway between vertical-layer tracks
(so power-strap vias on the rails clear them) and reports all contacts as VIA1
obstructions in the LEF.
The run prints the derived template ([std ] line: band positions, n-well edge,
poly-contact window, gate pitches) and fails early if the row height cannot hold
wn / wp. It writes <lib>.tech.json, a copy of the tech file pointing at the new
LEF/GDS with the inv/nand2/nor2/nor3/fill roles filled in.
Needed only for python gen.py sram. It sits at the top level (or inside stdcell):
"sram": {"min_nmos_width": 0.15, "min_pmos_width": 0.15, "cr": 1.5, "pr": 1.0} // "l" optionalmin_nmos_width/min_pmos_width: smallest legal transistor channel width (defaultstdcell.rules.activ_width). They can be larger than the active width rule: NSPL05 hasactiv_width0.7 but rule 2.ACT.W1 needs a channel width of 1.6.cr(cell ratioW_PD/W_PG, minimum) andpr(pull-up ratioW_PU/W_PG, maximum): defaults for--cr/--pr.loverrides the gate length (defaultstdcell.devices.l).
The sizer finds the minimum legal quantized widths (W_PU, W_PG, W_PD) that satisfy the
CR / PR targets and the single-contact / minimum-width floors; it is not a delay or power
optimum. cr, pr, v_read, v_write and the snm_* / write_margin_factor figures
("model": "heuristic_ratio" in the JSON metrics) come from a fixed-MU_P (0.4) / fixed-VM
(0.5) first-order resistive-divider heuristic ratio model. They are not PDK MOS-model results:
true SNM, write margin, delay and power need SPICE simulation with the PDK device models.
The generator also reads layers.vertical (M2), layers.horizontal (M3), layers.via2,
rules.via_stack, rules.via1_via2_space and layers.via1_keepout (see above) to draw the
bit lines (M2), word lines (M3) and the power straps. The array needs the stdcell section
but no LEF/GDS files.
Every key is optional; without the section generation is unchanged and simulation is reported as unavailable (never faked). It sits at the top level of the tech file:
"spice": {
"includes": [ // device model files, in order
{"file": "${PDK_ROOT}/models/corner.lib", "section": "tt"}, // -> .lib "<file>" tt
"${PDK_ROOT}/models/extra.mod" // -> .include "<file>"
],
"cell_library": ["${PDK_ROOT}/cells/stdcell.cdl"],// SPICE/CDL .SUBCKTs of the LEF cells (structural block netlists)
"vdd": 1.2, // supply voltage in volts (number or SPICE string like "3.3")
"temperature": 27, // deg C, emitted as .temp
"corner": "tt", // free text; recorded in the metadata/drawings
"testbench": { // all optional
"max_vectors": 24, // decoder: input vectors simulated (default 24)
"high_fraction": 0.9, "low_fraction": 0.1, // logic levels as a fraction of VDD (defaults 0.9 / 0.1)
"edge": "0.1n", "load": "10f" // BOTH set -> also measure delay arcs of generated cells
}
}- Paths follow the same rules as
files:${VAR}/${VAR:-default}, relative to the tech file. Do not commit proprietary paths or models: use${PDK_ROOT}-style variables or an ignoredlocal/file. includes+vddare what a simulation needs. Models are never guessed: an unset variable, a missing file or a missingvddis reported (simulation.status = "unavailable"plus the reason).cell_librarysupplies the real sub-circuits for decoder/block netlists.python gen.py stdcellwrites it into the generated<lib>.tech.jsonautomatically (and carries overincludes, re-based relative to the new file), so blocks built from generated cells get structural SPICE without extra setup.testbench.edgeis the full 0-100 % ramp time of the input,loadthe output capacitance (farads); delay is measured 50 % -> 50 %. They are testbench conditions you choose, recorded in the results.- The device model names in
stdcell.devices(nmos,pmos) must exist in the included model files.
| artifact | <name>.spice.json |
schematic SVG | --simulate checks (DC, real models) |
|---|---|---|---|
stdcell |
cells, functions, device counts | <lib>_schematics.svg + schematics/<cell>.svg |
every input vector of every logic cell reaches the logic level (+ delay arcs if edge/load) |
decoder |
block ports, instance count | <name>_schematic.svg |
addressed output high, others low, for up to max_vectors vectors |
sram |
rows/cols, sizing | <name>_schematic.svg, <name>_bitcell_schematic.svg |
6T bit-cell hold 0/1, read 0/1 (no flip, read bump measured), write 0/1 |
Metadata JSON ("schema": "dla-spice-metadata/1") holds provenance (generator version, tech name, SHA-256 of
the tech and model files), the configured includes/corner/VDD/temperature, sub-circuit and device counts, top
pins and supplies, and simulation: requested (off|auto|require), status
(not_requested|unavailable|passed|failed), reason, simulator command, deck/log paths, checks,
measurements (value + unit) and delays. Values only appear when a simulator produced them.
| mode | not configured / no simulator | check fails or simulator errors |
|---|---|---|
off (default) |
nothing runs | – |
auto |
recorded as unavailable with the reason, exit code 0 |
failed, exit code 1 |
require |
unavailable, exit code 1 |
failed, exit code 1 |
The simulator is --spice-cmd, else $DLA_SPICE, else ngspice/ngspice_con on PATH (see dla.spice).
The bit-cell checks are DC state checks (.nodeset selects the stored state; a write succeeds when the old state
is no longer a DC solution); they are not SNM, write-margin, timing or power characterisation.
{ "name": "my_pdk", // used in messages and output headers "files": { "lef": ["${PDK_ROOT}/.../stdcell.lef"], // one or more LEF files (macros) "gds": "${PDK_ROOT}/.../stdcell.gds", "verilog": "${PDK_ROOT}/.../stdcell.v" // optional, for your own simulations }, "manufacturing_grid": 0.005, // um "site": "CoreSite", // LEF SITE name, or [width, height] in um; // default: size of the smallest filler "power": {"vdd": "VDD", "vss": "VSS"}, // power / ground pin names in the LEF "grid": { // all optional "x_pitch": 0.48, // vertical-layer track pitch, default = site width "x_offset": 0.0, // track offset from the cell origin, default: auto "y_pitch": 0.50 // horizontal grid pitch, default: derived from rules }, "layers": { "pin": {"lef": "Metal1", "gds": [8, 0], "text": [8, 25], "width": 0.16, "space": 0.18, "min_area": 0.09, "wide_space": [[0.30, 1.0, 0.22]]}, // [width >=, parallel run >=, space] "vertical": {"lef": "Metal2", "gds": [10, 0], "text": [10, 25], "width": 0.20, "space": 0.21, ...}, "horizontal": {"lef": "Metal3", "gds": [30, 0], "text": [30, 25], "width": 0.20, "space": 0.21, ...}, "via1": {"lef": "Via1", "gds": [19, 0], "size": 0.19, "space": 0.22, "enc_below": [0.01, 0.05], // [side, end] enclosure, either orientation "enc_above": [0.005, 0.05]}, "via2": {"lef": "Via2", "gds": [29, 0], "size": 0.19, "space": 0.22, "enc_below": [0.005, 0.05], "enc_above": [0.005, 0.05]}, "boundary": [189, 4], // optional: placement boundary layer "via1_keepout": {"CONT": {"gds": [6, 0], "space": 0.1}} // optional }, "rules": { "via_stack": true, // false: VIA2 may not sit on VIA1 "via1_via2_space": 0.0, // optional minimum VIA1-VIA2 distance "via1_obs_space": 0.22 // distance from VIA1-layer OBS in the LEF (default: via1 space) }, "cells": { "inv": "INV_CELL", // or {"cell": "...", "pins": {"A": "I", "Y": "ZN"}} "nand2": "NAND2_CELL", // logical pins A, B, Y "nor2": "NOR2_CELL", "nor3": "NOR3_CELL", // logical pins A, B, C, Y "fill": ["FILL1", "FILL2", "FILL4"] } }