Specification
Overview
Zener is a domain-specific language built on top of Starlark for describing PCB schematics. It provides primitives for defining components, symbols, nets, interfaces, and modules in a type-safe, composable manner. This specification describes the language extensions and primitives added on top of Starlark. For the base Starlark language features, please refer to the Starlark specification and the starlark-rust types extension.Table of Contents
- Modules and Imports
- Nets and Interfaces
- Components and Symbols
- Modules
- Utilities
- Schematic Position Comments
Modules and Imports
Each.zen file is a Starlark module that can be used in two ways:
-
Symbol imports with
load()bring functions and types into scope: -
Schematic modules with
Module()create instantiable subcircuits:
Import Paths
Import paths support local files, stdlib, and remote packages:@stdlib alias is special and virtual—its content is controlled by the toolchain. You don’t need to declare it in [dependencies].
Remote package URLs don’t include version information. Versions are declared separately in pcb.toml, so import statements remain stable across upgrades:
Dependency Resolution
Dependencies are automatically resolved when you import remote packages. The toolchain discovers dependencies from import paths, resolves versions, downloads packages, and updatespcb.toml.
@stdlib is toolchain-managed and implicit.
KiCad symbol/footprint/model linkage is configured by workspace-level [[workspace.kicad_library]] entries.
Dependencies are still declared in [dependencies], and auto-deps can add them from imports.
If [[workspace.kicad_library]] is omitted, the toolchain uses a built-in KiCad 9 default:
version = "9.0.3"symbols = "gitlab.com/kicad/libraries/kicad-symbols"footprints = "gitlab.com/kicad/libraries/kicad-footprints"models.KICAD9_3DMODEL_DIR = "gitlab.com/kicad/libraries/kicad-packages3D"parts = "https://kicad-mirror.api.diode.computer/kicad-parts-{version}.toml"http_mirror = "https://kicad-mirror.api.diode.computer/{repo_name}-{version}.tar.zst"
parts and http_mirror are optional and support template placeholders:
{repo}{repo_name}{version}{major}
pcb.sum) records exact versions and cryptographic hashes. Commit it to version control for reproducible builds across machines.
See Packages for complete details on version resolution and dependency commands.
Project Structure
A Zener project is a git repository containing a workspace with boards, modules, and components. Create a new project withpcb new workspace:
pcb new board <name> to add boards and pcb new package <path> to add modules or components.
Workspace manifest (root pcb.toml):
repository: Git remote URL (used to derive package URLs for publishing)pcb-version: Minimum compatiblepcbtoolchain release series (e.g.,"0.3"). Required for workspaces.members: Glob patterns matching subdirectories that contain packages
pcb migrate to upgrade manifests and import paths.
Only the workspace root pcb.toml should contain a [workspace] section.
Board manifest (e.g., boards/MainBoard/pcb.toml):
[board]: Defines a buildable board withnameand entrypath[dependencies]: Version constraints for this board’s dependencies
modules/PowerSupply/pcb.toml):
[board] section—they’re libraries meant to be instantiated by boards or other modules.
[dependencies]: Version constraints for packages imported by this packageparts: Optional default sourcing metadata keyed by symbol file. Each entry providesmpn,manufacturer, optionalqualifications, a package-relative.kicad_symsymbolpath, and optionalsymbol_nameto target a specific symbol within a multi-symbol library file. Ifsymbol_nameis omitted, the referenced.kicad_symfile must contain exactly one symbol; otherwise resolution fails andsymbol_nameis required.Component()sourcing precedence is described below.
Prelude
These stdlib symbols are available in every user.zen file without load():
Net,Power,Ground,NotConnected— from@stdlib/interfaces.zenBoard— from@stdlib/board_config.zenLayout,Part— from@stdlib/properties.zen
Nets and Interfaces
Nets
ANet represents an electrical connection between component pins. Net is the base net type; specialized types like Power, Ground, and NotConnected add metadata (schematic symbols, voltage) while remaining fundamentally nets.
Net(name, voltage=None, impedance=None) — all parameters optional. Power and Ground additionally accept a voltage parameter. Additional net types (Analog, Pwm, Gpio) are available from @stdlib/interfaces.zen.
Net types follow automatic conversion rules across io() boundaries: NotConnected promotes to any type (universal donor), any specialized type demotes to Net, but Net cannot automatically promote to specialized types. For explicit casting, call the target constructor with an existing net: Power(net, voltage=Voltage("3.3V")) or Net(power_net).
Interfaces
Interfaces define reusable connection patterns — groups of related nets. Define custom interfaces withinterface():
field() specs. When instantiated, the first positional argument is an optional name; named arguments override defaults.
The standard library provides common interfaces (Spi, I2c, Uart, Usb2, DiffPair, Pcie, Jtag, Swd, etc.) in @stdlib/interfaces.zen. Helper functions UartPair(a, b) and UsartPair(a, b) create cross-connected pairs for point-to-point links.
Components and Symbols
Component
Components represent physical electronic parts with pins, a schematic symbol, and a PCB footprint.Component(**kwargs)
| Parameter | Required | Description |
|---|---|---|
name | yes | Instance name |
symbol | yes | Symbol object defining the schematic representation |
pins | yes | Dict mapping pin names to nets |
part | no | Part object specifying manufacturer sourcing (preferred) |
prefix | no | Reference designator prefix (default: "U") |
manufacturer | no | Manufacturer name (legacy — prefer part) |
mpn | no | Manufacturer part number (legacy — prefer part) |
footprint | no | PCB footprint path (usually omit — inferred from symbol) |
type | no | Component type string |
properties | no | Additional properties dict |
dnp | no | Do Not Populate flag |
skip_bom | no | Exclude from BOM |
footprint is omitted, it is inferred from the symbol’s metadata. When part is provided, its mpn and manufacturer override any scalar mpn/manufacturer parameters or symbol metadata. If no part is set and no explicit/scalar mpn is set, Component() can inherit a default part from the package manifest’s parts entries matching the symbol path. manufacturer without mpn is treated as incomplete legacy scalar sourcing and does not prevent manifest fallback; prefer part = Part(...) as the simplest and most robust way to specify sourcing.
Part
Part specifies manufacturer sourcing for a component. It is a prelude symbol — available in all .zen files without load().
Constructor: Part(mpn, manufacturer, qualifications=[])
| Parameter | Required | Description |
|---|---|---|
mpn | yes | Manufacturer part number (non-empty string) |
manufacturer | yes | Manufacturer name (non-empty string) |
qualifications | no | List of qualification strings (e.g. ["AEC-Q200"]) |
.mpn, .manufacturer, .qualifications
Use Part with the part parameter on Component() for primary sourcing, and in properties["alternatives"] for alternate parts:
pcb build, reference designators are automatically allocated per-prefix (e.g. R1, R2, C1).
Symbol
ASymbol represents a schematic symbol loaded from a KiCad symbol library.
Symbol(library, name=None)
library: Path to a.kicad_symfile. Supports local paths,@kicad-symbols/alias, and package paths.name: Symbol name within the library. Required for multi-symbol libraries; omit for single-symbol files.
Physical Quantities
Physical quantities represent electrical values with a nominal value, optional min/max bounds, and a unit. Unit-specific constructors are provided by@stdlib/units.zen (e.g. Voltage, Current, Resistance, Capacitance, Inductance, Impedance, Frequency, Temperature):
@stdlib/units.zen for the complete list of unit constructors.
Properties: .value (alias for .nominal), .nominal, .min, .max, .tolerance, .unit
Methods: .with_tolerance(t), .with_value(v), .with_unit(u), .abs(), .diff(other), .within(other)
Operators: +, -, *, / (with unit tracking), <, >, <=, >=, == (compare nominal), unary -
String formatting: Point values → "3.3V". Symmetric tolerances → "10k 5%". Ranges → "11–26V (16V nom.)".
Generic Components
Prefer generic components over rawComponent() where possible. Generics come with standard symbols, footprints, and automatic BOM matching to house parts.
@stdlib/generics/ for the full list of available generics and their accepted parameters.
Modules
Modules are reusable subcircuits —.zen files that declare their electrical interface and configuration, then build a circuit from them. They are the primary mechanism for hierarchical design.
Module()
Module() loads a .zen file and returns a callable that instantiates it as a subcircuit:
name and passes remaining arguments as inputs to the module’s io() and config() declarations:
properties: Dict of property overrides for the module instance.dnp: Bool — mark as Do Not Populate.schematic:"collapse"or"embed"— controls schematic rendering of the subcircuit.
io()
Declare a net or interface input for a module. This defines the module’s electrical interface — the nets that a parent must (or may) connect when instantiating it. Signature:io(name, typ, checks=None, default=None, optional=False, help=None)
name: Input name (conventionally UPPERCASE).typ: A net type (Net,Power,Ground, etc.) or an interface factory (Spi,Uart, etc.).checks: Optional check function or list of checks applied to the resolved value.default: Explicit default value.optional: IfTrue, a generated net/interface is used when the parent doesn’t provide one. DefaultFalse.help: Help text for documentation and signatures.
config()
Declare a typed configuration input for a module. This defines parameters that control the module’s behavior — values (not nets) provided by the parent. Signature:config(name, typ, checks=None, default=None, optional=None, help=None)
name: Input name (conventionally lowercase).typ: Expected type — primitives (str,int,float,bool),enum,record, or physical quantity constructors.checks: Optional check function or list of checks.default: Default value. When provided,optionaldefaults toTrue.optional: Explicit override. WhenTruewith no default, returnsNone.help: Help text.
"10k" → Resistance("10k")), enum variants ("0603" → Package("0603")), etc. This is why Resistor(name="R1", value="10k", package="0603", ...) works even though value expects Resistance and package expects Package.
Writing a Module
A module is a.zen file that declares its interface with io() and config(), then uses those values to build its circuit:
Utilities
Board and Layout
Board() configures PCB manufacturing parameters — stackup, design rules, and layout path. It is a prelude symbol.
name, layout_path, layers (2/4/6/8/10), config (explicit BoardConfig), outer_copper_weight ("1oz" or "2oz"), copper_finish (default "ENIG").
When layers is provided, Board() selects an appropriate default stackup, netclasses, and design rules. An explicit config is merged on top. See @stdlib/board_config.zen for BoardConfig, Stackup, DesignRules, NetClass, and preset stackups.
Layout() defines reusable layout blocks for modules. When writing a module, use Layout(name, path) to associate a PCB layout with the subcircuit. See @stdlib/properties.zen.
File and Path
File(path) — Resolve a path relative to the current .zen file. Raises an error if it doesn’t exist.
Path(path, allow_not_exist=False) — Like File() but supports package paths and optional non-existence.
Assertions
Three global functions for validation and diagnostics:check(condition, message)— Assert a condition. Raises an error withmessageifconditionis false.error(message)— Raise an error unconditionally.warn(message)— Emit a warning diagnostic.
Electrical Checks
@stdlib/checks.zen provides reusable check functions for io() boundaries. For example, voltage_within(range) validates that a Power net’s voltage falls within a specified range:
E-Series
@stdlib/utils.zen provides functions to snap values to standard resistor/capacitor E-series: e3(), e6(), e12(), e24(), e48(), e96(), e192().
Schematic Position Comments
Zener supports persisted schematic placement metadata in trailing comment blocks. These comments are consumed by tooling and surfaced in netlist output. Do not edit these comments directly. Canonical line format:id: Position key (component or net symbol key in comment form, e.g.R1,VCC.1)x,y: Schematic coordinatesrot: Rotation in degreesmirror(optional): Mirror axis (xory)