> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pcb.new/llms.txt
> Use this file to discover all available pages before exploring further.

# Zener language

> Language reference for Zener PCB designs

# Specification

## Overview

Zener is a domain-specific language built on
[Starlark](https://github.com/bazelbuild/starlark/blob/master/spec.md) for
describing PCB schematics. It adds components, symbols, nets, interfaces,
physical quantities, and hierarchical circuit modules.

This specification defines the Zener extensions. The
[Starlark specification](https://github.com/bazelbuild/starlark/blob/master/spec.md) and the
[starlark-rust type extensions](https://github.com/facebook/starlark-rust/blob/main/docs/types.md)
define the underlying language.

## Modules and imports

Each `.zen` file is a Starlark module that can be used in two ways:

1. **Symbol imports** with `load()` bring functions and types into scope:
   ```python theme={null}
   load("./utils.zen", "helper")
   load("@stdlib/units.zen", "Voltage", "Resistance")
   ```

2. **Schematic modules** with `Module()` create instantiable subcircuits:
   ```python theme={null}
   Resistor = Module("@stdlib/generics/Resistor.zen")
   Resistor(name="R1", value="10k", P1=vcc, P2=gnd)
   ```

### Import paths

Import paths support local files, stdlib, and remote packages:

```python theme={null}
# Local file (relative to current file)
load("./utils.zen", "helper")

# Stdlib (version controlled by toolchain)
load("@stdlib/units.zen", "Voltage", "Resistance")
load("@stdlib/interfaces.zen", "Spi")

# Remote packages (version declared in pcb.toml)
Resistor = Module("@stdlib/generics/Resistor.zen")
TPS54331 = Module("github.com/diodeinc/registry/components/TPS54331/TPS54331.zen")
```

The toolchain supplies the virtual `@stdlib` package. Do not declare it in
`[dependencies]`.

Remote package URLs omit versions. Declare each version in `pcb.toml` so import
statements remain stable across upgrades:

```toml theme={null}
[dependencies]
"github.com/diodeinc/registry/components/TPS54331" = "1.0"
```

### Dependency resolution

Remote package URLs do not include versions. Declare versions in `pcb.toml`, then
run `pcb sync` to reconcile imports and hydrate dependency manifests. `@stdlib`
is toolchain-managed and implicit.

See [Packages](/pages/packages) for version resolution, package manifests, and
dependency commands.

### Repository layouts

Zener projects use one of two repository shapes: a board repository or a registry repository.

#### Board repository

A board repository contains one board plus any local modules and components it owns. Create one with `pcb new board <name> <repo-url>`:

```
MainBoard/
├── pcb.toml              # Workspace and board manifest
├── MainBoard.zen
├── modules/
│   └── PowerSupply/
│       ├── pcb.toml      # Package manifest
│       └── PowerSupply.zen
└── components/
    └── TPS54331/
        ├── pcb.toml
        └── TPS54331.zen
```

Use `pcb new package <path>` to add modules or components inside the board repository.

**Manifest** (root `pcb.toml`):

```toml theme={null}
[workspace]
repository = "github.com/myorg/MainBoard"
pcb-version = "0.4"
endpoint = "diode.computer"

[board]
name = "MainBoard"
path = "MainBoard.zen"
description = "Replace with concise board description."
```

* `name`: Optional Diode workspace name override for board release uploads. When omitted, publish uses the first path segment of `repository`, such as `myorg` from `github.com/myorg/MainBoard`.
* `repository`: Git remote URL (used to derive package URLs for publishing)
* `pcb-version`: Required `pcbc` toolchain lane, such as `"0.3"`, `"0.4"`, or `"nightly"`. For semver lanes, `pcb` selects the newest installed patch in that lane and installs one if needed. Ordinary commands never auto-upgrade across lanes; use `pcb migrate` to run the latest stable toolchain migrations and update this field after they succeed.
* `endpoint`: Optional Diode host suffix used for workspace-scoped app/API URLs. `"diode.computer"` resolves to `app.diode.computer` and `api.diode.computer`.
* `[board]`: Defines the repository's buildable board with `name`, entry `path`, and a short `description`

Older workspace formats are no longer supported. Update legacy manifests and
imports before building with the current toolchain.
Only the workspace root `pcb.toml` may contain a `[workspace]` section.
Board dependencies are also declared in the root manifest's `[dependencies]` table.

#### Registry repository

A registry repository contains reusable packages but no board at the root. It uses top-level `components/` and `modules/` directories:

```
registry/
├── pcb.toml              # Workspace manifest
├── components/
│   └── TPS54331/
│       ├── pcb.toml      # Package manifest
│       ├── TPS54331.zen
│       ├── TPS54331.kicad_sym
│       └── TPS54331.kicad_mod
└── modules/
    └── UsbCSink/
        ├── pcb.toml      # Package manifest
        └── UsbCSink.zen
```

Registry manifests include `[workspace]` but do not include `[board]`:

```toml theme={null}
[workspace]
repository = "github.com/myorg/registry"
pcb-version = "0.4"
```

**Package manifest** (e.g., `modules/PowerSupply/pcb.toml`):

```toml theme={null}
[dependencies]
"github.com/diodeinc/registry/components/TPS54331" = "1.0"

parts = [
  { mpn = "TPS54331DR", symbol = "TPS54331.kicad_sym", symbol_name = "TPS54331", manufacturer = "Texas Instruments", qualifications = ["Preferred"] },
]
```

Module and component packages omit `[board]` because boards and other modules
instantiate them as libraries.

* `[dependencies]`: Version constraints for packages imported by this package
* `parts`: Optional default sourcing metadata keyed by symbol file. Each entry provides `mpn`, `manufacturer`, optional `qualifications`, optional `datasheet`, a package-relative `.kicad_sym` `symbol` path, and optional `symbol_name` to target a specific symbol within a multi-symbol library file. If `symbol_name` is omitted, the referenced `.kicad_sym` file must contain exactly one symbol; otherwise resolution fails and `symbol_name` is required. `Component()` sourcing precedence is described below.

See [Packages](/pages/packages) for the complete manifest reference.

### Prelude

These stdlib symbols are available in every user `.zen` file without `load()`:

* `io`, `input`, `output` — from `@stdlib/io.zen`
* `Net`, `Power`, `Ground`, and the `NotConnected()` open-net constructor — from `@stdlib/interfaces.zen`
* `Board` — from `@stdlib/board_config.zen`
* `Layout`, `Part` — from `@stdlib/properties.zen`

Local definitions shadow prelude symbols. The prelude does not apply to stdlib modules themselves.

## Nets and interfaces

### Nets

A `Net` represents an electrical connection between component pins. `Net` is the base net type; specialized types like `Power` and `Ground` add metadata (schematic symbols, voltage) while remaining fundamentally nets. `NotConnected` is a constructor for an intentionally open net, not a net type.

```python theme={null}
load("@stdlib/units.zen", "Impedance")

# Basic nets
CLK = Net()
DATA = Net(impedance=Impedance(50))  # controlled impedance
VREF = Net()  # inferred from assignment

# Power and ground (prelude — no load needed)
VCC = Power("VCC_3V3", voltage="3.3V")
GND = Ground()  # inferred from assignment; voltage defaults to 0V

# Intentionally open
nc = NotConnected()
```

`Net(name_or_net=None, voltage=None, impedance=None)` accepts a positional-only
name string or existing net to cast. `Power` and `Ground` also accept `voltage`.
Additional connected net types (`Analog`, `Pwm`, `Gpio`) are available from
`@stdlib/interfaces.zen`. `NotConnected` is reserved for the open-net
constructor; `builtin.net_type("NotConnected")` is invalid.

If a net constructor omits `name`, the assigned variable name is used when available:

```python theme={null}
CLK = Net()       # equivalent to Net("CLK")
VDD = Power()     # equivalent to Power("VDD")
```

If the explicit name duplicates the inferred assignment name, Zener reports
style advice because the explicit name is redundant.

Explicit constructor names still win. Regular nets must either have a name or be
assigned where a name can be inferred:

```python theme={null}
alias = Net("CLK")  # name is "CLK", not "alias"
Net()               # error: Net is unnamed
```

Regular net names must be unique. Duplicate regular net names are rejected; open `NotConnected()` nets are exempt.
`NotConnected()` has no source-level name. A supplied name is ignored with a
warning. Downstream tools assign connection-derived names where required.

Typed net fields validate against their declared field type and use the same
string coercions as module inputs, so `Power("VCC", voltage="3.3V")` is
equivalent to `Power("VCC", voltage=Voltage("3.3V"))`.

Net type annotations on `io()` boundaries check electrical compatibility; they
do not change whether a provided value is connected or intentionally open.
`NotConnected()` can satisfy any net-shaped `io()` because it is an open net
value, and it remains `NotConnected` in the emitted netlist. Specialized
connected types can be viewed as `Net`; plain connected `Net` values do not
automatically promote to specialized types.

Explicit constructors assert the resulting connected net type. For example,
`Net(NotConnected())` constructs a regular connected `Net`, while passing
`NotConnected()` directly to `io(Net)` keeps it open. `NotConnected` itself is
not valid where a type is expected, such as `io(NotConnected)`.

### Interfaces

Interfaces define reusable connection patterns — groups of related nets. Define custom interfaces with `interface()`:

```python theme={null}
MyBus = interface(
    clk = Net("CLK"),
    data = Net("DATA"),
    enable = field(bool, True),
)

bus = MyBus("BUS1", enable=False)
debug_bus = MyBus()
```

Interface fields can be net instances, interface instances (for hierarchical composition), or `field()` specs. When instantiated, the first positional argument is an optional name; named arguments override defaults.

If an interface instance omits its explicit name, the assigned variable name becomes the root used for generated child nets:

```python theme={null}
PowerIf = interface(
    vcc = Net(),
    gnd = Net("GND"),
)

power = PowerIf()
# generated child nets become power_vcc and power_GND
```

Only values generated by the interface definition are renamed this way. Caller-provided nets or interface instances keep their existing names:

```python theme={null}
ext = Net("EXT")
power = PowerIf(vcc = ext)
# ext stays "EXT"
```

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.

```python theme={null}
Component(
    name = "U1",
    symbol = my_symbol,
    pins = {
        "VCC": vcc,
        "GND": gnd,
        "OUT": output_net,
    },
    prefix = "U",
    part = Part(mpn="LM358", manufacturer="TI"),
)
```

**Constructor**: `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; omit KiCad `no_connect` pins                                                                                                                                                                              |
| `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 (default: inferred from symbol `Footprint` property)                                                                                                                                                                   |
| `type`         | no       | Component type string                                                                                                                                                                                                                     |
| `properties`   | no       | Additional properties dict                                                                                                                                                                                                                |
| `spice_model`  | no       | Explicit `SpiceModel`; default: inferred from symbol `Sim.*` properties when present                                                                                                                                                      |
| `dnp`          | no       | Do Not Populate flag                                                                                                                                                                                                                      |
| `skip_bom`     | no       | Exclude from BOM (default: inverse of symbol `in_bom` flag)                                                                                                                                                                               |
| `datasheet`    | no       | Datasheet URL or path (default: `part.datasheet`, then this component value, then symbol `Datasheet` property; local component paths resolved relative to the `.zen` file, symbol-local paths resolved relative to the `.kicad_sym` file) |

When KiCad symbol pin metadata is available:

* omitted `no_connect` pins are auto-wired to `NotConnected()`
* explicit `no_connect` entries warn
* `power_in` and `power_out` pins warn if connected to plain `Net` instead of `Power` or `Ground`
* if `spice_model` is omitted and the symbol provides `Sim.Library`, `Sim.Name`, `Sim.Device=SUBCKT`, `Sim.Pins`, and optional `Sim.Params`, `Component()` derives the SPICE model from those symbol properties

### 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=[], datasheet=None)`

| 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"]`) |
| `datasheet`      | no       | Datasheet URL or path for this manufacturer part    |

**Attributes**: `.mpn`, `.manufacturer`, `.qualifications`, `.datasheet`

Use `Part` with the `part` parameter on `Component()` for primary sourcing, and in `properties["alternatives"]` for alternate parts:

```python theme={null}
Component(
    name = "R1",
    symbol = Symbol(library="Device.kicad_sym", name="R"),
    pins = {"P1": vcc, "P2": gnd},
    part = Part(
        mpn = "RC0603FR-0710KL",
        manufacturer = "Yageo",
        qualifications = ["AEC-Q200"],
        datasheet = "https://www.yageo.com/upload/media/product/products/datasheet/rchip/RC_L_51_RoHS_L_6.pdf",
    ),
    properties = {
        "alternatives": [
            Part(mpn="ERJ-3EKF1001V", manufacturer="Panasonic"),
        ],
    },
)
```

During `pcb build`, reference designators are automatically allocated per-prefix (e.g. `R1`, `R2`, `C1`).

### Symbol

A `Symbol` represents a schematic symbol loaded from a KiCad symbol library.

```python theme={null}
ic_symbol = Symbol(library="TCA9554DBR.kicad_sym")
connector = Symbol(library="Connector_Generic.kicad_sym", name="Conn_01x14")
```

**Constructor**: `Symbol(library, name=None)`

* `library`: Path to a `.kicad_sym` file. Supports local paths and package paths.
* `name`: Symbol name within the library. Required for multi-symbol libraries; omit for single-symbol files.

### Physical quantities

Physical quantities have a nominal value, optional min/max bounds, and dimensional units. Zener exposes five SI bases as `builtin.Mass`, `builtin.Length`, `builtin.Time`, `builtin.Current`, and `builtin.Temperature`. `@stdlib/units.zen` re-exports them and derives conventional electrical types such as `Voltage`, `Resistance`, `Capacitance`, `Inductance`, `Impedance`, and `Frequency` using multiplication and division. For example, `Voltage` is `Mass * Length * Length / (Current * Time * Time * Time)`.

```python theme={null}
load("@stdlib/units.zen", "Voltage", "Current", "Time", "Resistance", "Capacitance")

# Point values
supply = Voltage("3.3V")
resistor = Resistance("4k7")     # 4.7kΩ using resistor notation
cap = Capacitance("100nF")

# Ranges
input_range = Voltage("1.1–3.6V")
explicit_nominal = Voltage("11–26V (12V)")

# Keyword bounds
operating = Voltage(min=11, max=26)

# Arithmetic with automatic unit tracking
power = Voltage("3.3V") * Current("0.5A")   # → 1.65W
r = Voltage("5V") / Current("100mA")        # → 50Ω

# The same algebra constructs declarable physical types
SlewRate = Voltage / Time
slew_rate = config(SlewRate, default="5V/us")
```

Derived types are dimensional rather than nominal: independently constructed types with the same dimensions accept the same values. For example, `Voltage / Current` and stdlib `Resistance` are the same physical type.

SI base symbols are resolved in the context of the declared type, so `Length("1m")` is one metre. Untyped `"1m"` parsing retains its historical meaning of one milliohm for compatibility.

**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)`, `.matches(other)`, `.spice()`

**Operators**: `+`, `-`, `*`, `/` (with unit tracking), `<`, `>`, `<=`, `>=`, `==` (strict equality against another `PhysicalValue`), unary `-`

Use `.matches(other)` for coercive comparisons against strings or scalars, such
as `Voltage("5V").matches("5V")`.

**String formatting**: Point values → `"3.3V"`. Symmetric tolerances → `"10k 5%"`. Ranges → `"11–26V (16V nom.)"`.

**SPICE formatting**: Pass a `PhysicalValue` directly or use `.spice()` to emit an ngspice-safe string (`meg` for mega, no unit suffix, e.g. `Resistance("2MOhm")` → `2meg`).

### Generic components

Prefer generic components over raw `Component()` where possible. Generics come with standard symbols, footprints, and automatic BOM matching to house parts.

```python theme={null}
Resistor = Module("@stdlib/generics/Resistor.zen")
Capacitor = Module("@stdlib/generics/Capacitor.zen")

Resistor(name="R1", value="10k", package="0603", P1=vcc, P2=gnd)
Capacitor(name="C1", value="100nF", package="0402", voltage="16V", P1=vcc, P2=gnd)
```

See `@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:

```python theme={null}
PowerSupply = Module("./modules/PowerSupply.zen")
TPS54331 = Module("github.com/diodeinc/registry/components/TPS54331/TPS54331.zen")
```

Instantiation takes a required `name` and passes remaining arguments as inputs to the module's `io()` and `config()` declarations:

```python theme={null}
PowerSupply(name="PSU1", VIN=vin, VOUT=vout, GND=gnd, output_voltage="3.3V")
```

Additional instantiation parameters:

* `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.

`io`, `input`, and `output` are prelude symbols re-exported from `@stdlib/io.zen`. The low-level builtin is `builtin.io(...)`.

**Signature:** `io(name, typ_or_template, checks=None, optional=False, help=None, direction=None)` or `io(typ_or_template, checks=None, optional=False, help=None, direction=None)`

* `name`: Optional explicit input name (conventionally UPPERCASE). If omitted, `io()` must be assigned to a top-level variable and that variable name is used.
* `typ_or_template`: A net type (`Net`, `Power`, `Ground`, etc.), an interface factory (`Spi`, `Uart`, etc.), a net template value, or an interface template value.
* `checks`: Optional check function or list of checks applied to the resolved value.
* `optional`: If `True`, use a generated net or interface when the parent omits
  the input. The default is `False`.
* `help`: Help text for documentation and signatures.
* `direction`: Optional signature metadata. Must be `"input"` or `"output"` when provided.

```python theme={null}
VCC = io(Power)
GND = io(Ground)
VDD_3V3 = io(Power(voltage="3.3V"))
SPI_BUS = io(Spi("SPI"), optional=True)
SPI = io(Spi, optional=True)
DATA = io(Net)
CS = io(Net)
VIN = io(Power, direction="input")
VOUT = io(Power, direction="output")
```

When `typ_or_template` is a template value, `io()` derives all three of these from it:

* the placeholder type
* the default/template metadata
* implicit checks that constrain any provided input

For example, `io(Power(voltage="1.8V - 3.6V"))` requires any supplied `Power` net to stay within that voltage range.

Use `direction` only for one-way signal or power flow. Leave shared rails such
as `GND` as plain `io()` declarations.

When the explicit `name` matches the assigned variable name, omitting it is the preferred style and emits no redundancy advice.

`input(name, typ_or_template, ...)` and `output(name, typ_or_template, ...)` are equivalent to `io(...)` with `direction="input"` or `direction="output"` respectively, and they also support omitted explicit names when assigned to top-level variables.

```python theme={null}
VIN = input(Power)
VDD = input(Power(voltage="3.3V"))
VOUT = output(Power)
CS = input(Net)
```

### 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, allowed=None, optional=None, help=None)` or `config(typ, checks=None, default=None, allowed=None, optional=None, help=None)`

* `name`: Optional explicit input name (conventionally lowercase). If omitted, `config()` must be assigned to a top-level variable and that variable name is used.
* `typ`: Expected type — primitives (`str`, `int`, `float`, `bool`), `enum`, or physical quantity constructors. `record()` types are not supported as module `config()` inputs.
* `checks`: Optional check function or list of checks.
* `default`: Default value. When provided, `optional` defaults to `True`.
* `allowed`: Optional finite set of allowed values. Accepts a `list`, `tuple`, or `dict` (using only the keys). Supported for `str`, `int`, `float`, `bool`, `enum`, and physical quantity types.
* `optional`: Explicit override. When `True` with no default, returns `None`.
* `help`: Help text.

```python theme={null}
value = config(Resistance)
package = config(Package, default=Package("0603"))
voltage = config(Voltage, optional=True)
manufacturer = config(str, default="Acme")
output_voltage = config(
    "output_voltage",
    Voltage,
    allowed=["0.8V", "0.9V", "1.0V", "1.1V"],
    default="1.0V",
)
```

Values passed by the parent are automatically converted to the declared type when possible. String inputs can coerce to primitives (`"true"` → `True`, `"42"` → `42`, `"3.3"` → `3.3`), physical quantities (`"10k"` → `Resistance("10k")`), and enum variants (`"0603"` → `Package("0603")`). This is why `Resistor(name="R1", value="10k", package="0603", ...)` works even though `value` expects `Resistance` and `package` expects `Package`. When `allowed` is present, both the allowed set and the provided value are normalized through that same coercion path before membership is checked, and physical values are surfaced using their canonical formatting.

As with `io()`, repeating the assigned variable name as an explicit `config()` name is redundant and triggers a style advice.

### Write a module

A module is a `.zen` file that declares its interface with `io()` and `config()`, then uses those values to build its circuit:

```python theme={null}
# modules/LedIndicator.zen
Resistor = Module("@stdlib/generics/Resistor.zen")
Led = Module("@stdlib/generics/Led.zen")

# Configuration
color = config(str, default="red")
r_value = config(str, default="330ohms")

# Electrical interface
VCC = io(Power)
GND = io(Ground)

# Internal net
led_anode = Net("LED_ANODE")

# Circuit
Resistor(name="R1", value=r_value, package="0603", P1=VCC, P2=led_anode)
Led(name="D1", color=color, package="0603", A=led_anode, K=GND)

# Layout
Layout(name="LedIndicator", path="layout/LedIndicator")
```

Instantiated by a parent board:

```python theme={null}
# MainBoard.zen
LedIndicator = Module("./modules/LedIndicator.zen")

vcc = Power("VCC_3V3", voltage="3.3V")
gnd = Ground("GND")

LedIndicator(name="LED1", VCC=vcc, GND=gnd, color="green")
LedIndicator(name="LED2", VCC=vcc, GND=gnd, color="red")

Board(name="MainBoard", layers=4, layout_path="layout/MainBoard")
```

## Utilities

### Board and layout

`Board()` configures PCB manufacturing parameters — stackup, design rules, and layout path. It is a prelude symbol.

```python theme={null}
Board(name="my_board", layers=4, layout_path="layout/my_board")
```

Key parameters: `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`.

**`Simulation(name, setup=None, modifiers=None, bom_profile=...)`** — Attach inline simulation setup and component modifiers to the current module.

`Simulation()` uses the same BOM-profile hook as `Layout()`: by default it registers the standard house-part matcher, `modifiers` run before `bom_profile`, and `bom_profile=None` disables automatic house matching for simulation-only evals.

### File and path

**`File(path)`** — Resolve an existing path relative to the current `.zen` file.
The call fails if the path does not exist.

```python theme={null}
datasheet = File("TPS54331.pdf")
footprint = File("Resistor_SMD.pretty/R_0603_1608Metric.kicad_mod")
```

**`Path(path, allow_not_exist=False)`** — Like `File()` but supports package paths and optional non-existence.

```python theme={null}
layout_dir = Path("layout/my_board", allow_not_exist=True)
```

### Assertions

Three global functions for validation and diagnostics:

* **`check(condition, message)`** — Assert a condition. Raises an error with `message` if `condition` is false.
* **`error(message)`** — Raise an error unconditionally.
* **`warn(message)`** — Emit a warning diagnostic.

```python theme={null}
check(voltage <= Voltage("3.6V"), "Voltage exceeds maximum rating")
warn("Using deprecated parameter")
```

### Electrical checks

`@stdlib/checks.zen` provides reusable check functions for typed inputs. For example, `voltage_within(range)` validates that a net with voltage metadata, or a direct `Voltage` value, falls within a specified range:

```python theme={null}
load("@stdlib/checks.zen", "voltage_within")
vref = config(Voltage, checks=voltage_within("1.1–3.6V"))
```

Template-first `io()` can also contribute implicit checks. A typed net template with a meaningful `voltage` property enforces the same containment rule automatically:

```python theme={null}
VCC = io(Power(voltage="1.1–3.6V"))
```

Explicit `checks=` still run as well, after type validation and any template-derived implicit checks.

### E-series

`@stdlib/utils.zen` provides functions to snap values to standard resistor/capacitor E-series: `e3()`, `e6()`, `e12()`, `e24()`, `e48()`, `e96()`, `e192()`.

```python theme={null}
load("@stdlib/utils.zen", "e96")
r = e96(Resistance("4.8k"))  # → 4.87kΩ (nearest E96 value)
```

## 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:

```text theme={null}
# pcb:sch <id> x=<f64> y=<f64> rot=<f64> [mirror=<x|y>]
```

* `id`: Position key (component or net symbol key in comment form, e.g. `R1`, `VCC.1`)
* `x`, `y`: Schematic coordinates
* `rot`: Rotation in degrees
* `mirror` (optional): Mirror axis (`x` or `y`)

Examples:

```text theme={null}
# pcb:sch R1 x=100.0000 y=200.0000 rot=0
# pcb:sch U1 x=150.0000 y=200.0000 rot=90 mirror=x
```
