PluginBench
Skill
Pass
Audit score 90

idiomatic-zener

diodeinc/pcb

How to install idiomatic-zener

npx skills add null --skill idiomatic-zener
Claude Code
Cursor
Windsurf
Cline
Full instructions (SKILL.md)

Source of truth, from diodeinc/pcb.


name: idiomatic-zener description: Style rules and idioms for writing high-quality Zener HDL code. Use when writing, reviewing, or refactoring .zen files — modules, reference designs, or board files. Covers DNP patterns, typed configs, voltage checks, component naming, computation style, and common gotchas.

Idiomatic Zener

Prescriptive style rules for .zen code. These apply to all modules, reference designs, and board files.

No Conditional Instantiation

Never use if to create or remove components. Always instantiate every component. Use dnp= to control whether it is populated. This applies to modules, boards, and reference designs, including optional feature blocks.

# BAD
if add_decoupling:
    Capacitor(name="C_VDD", value="100nF", package="0402", P1=VCC, P2=GND)

# GOOD
Capacitor(name="C_VDD", value="100nF", package="0402", P1=VCC, P2=GND)

DNP via Record Pattern

For optional subcircuits controlled by a config, use a record to pair values with DNP state. A zero/disabled config value means dnp=True with placeholder values.

Passive = record(value=typing.Any, dnp=bool)

input_filter = config(Frequency, default="0Hz", optional=True,
    help="Input lowpass cutoff. 0Hz disables the filter.")

def input_rc(f):
    dnp = f <= Frequency("0Hz")
    r = e96(Resistance("100ohm") if not dnp else Resistance("0ohm"))
    c = e24(1 / (2 * PI * r * f) if not dnp else Capacitance("100pF"))
    return Passive(value=r, dnp=False), Passive(value=c, dnp=dnp)

input_r, input_c = input_rc(input_filter)
Resistor(name="R_IN", value=input_r.value, dnp=input_r.dnp, package="0402", P1=A, P2=B)
Capacitor(name="C_IN", value=input_c.value, dnp=input_c.dnp, package="0402", P1=B, P2=GND)

Minimize Component Count

Fewer parts = simpler BOM, easier assembly, lower cost.

Prefer value-switching over duplicate components. When a config selects between discrete options, use a single component with a computed value — don't instantiate multiple components with opposing DNP conditions.

# BAD: two resistors, one always DNP
Resistor(name="R_STRAP_HI", value="10kohm", P1=STRAP, P2=VCC, dnp=mode != "HIGH")
Resistor(name="R_STRAP_LO", value="100kohm", P1=STRAP, P2=VCC, dnp=mode != "LOW")

# GOOD: one resistor, value changes with config
_strap_value = { Mode("HIGH"): "10kohm", Mode("LOW"): "100kohm", Mode("FLOAT"): "10kohm" }[mode]
Resistor(name="R_STRAP", value=_strap_value, P1=STRAP, P2=VCC, dnp=mode == Mode("FLOAT"))

Leverage internal pull-ups/pull-downs. Many ICs have internal bias on strap pins. If the default state uses the internal pull, don't add an external resistor — just DNP the single resistor for that case.

Typed Unit Configs

Use physical types from @stdlib/units.zen for configs. Expose one meaningful parameter (e.g. cutoff frequency), not raw R/C values. Use enum() only for discrete design choices.

# BAD
config("filter_r", str, default="10ohms")

# GOOD
input_filter = config(Frequency, default="0Hz", optional=True,
    help="Input lowpass cutoff. 0Hz disables the filter.")

Computation in Named Functions

Put calculations in named functions with datasheet references. Snap to E-series with e96() / e24().

def load_r(v_out, v_sense):
    """Datasheet §8.1.1 / Eq 4: V_OUT = V_SENSE × gm × R_L"""
    GM = Current("200uA") / Voltage("1V")
    return e96(v_out / (v_sense * GM))

Voltage on Power IOs

Every Power io declares its voltage range via the template.

VCC = io(Power(voltage="2.7V to 36V"))

Help Strings

Use help= when it adds integrator-visible meaning that is not already obvious from the name, type, or default. Omit it when it would just restate those fields.

VDD = io(Power(voltage="3.0V to 5.5V"))
GND = io(Ground)
EN = io(Net, help="High to enable the regulator")
input_filter = config(Frequency, default="0Hz", optional=True,
    help="Input lowpass cutoff. 0Hz disables the filter.")

No .NET Accessor

Use Power/Ground ios directly as pin connections. Never use .NET.

# BAD
Capacitor(name="C_VDD", value="100nF", P1=VCC.NET, P2=GND.NET)

# GOOD
Capacitor(name="C_VDD", value="100nF", P1=VCC, P2=GND)

Naming

Beyond the standard conventions (UPPERCASE io, lowercase config):

ElementConventionExample
Internal nets_ prefix_VREF, _XI, _RBIAS
Component namesUppercase functional prefixR_LOAD, C_VDD, U_LDO
Differential pairs_P / _N suffixesIN_P, IN_N (not _PLUS / _MINUS)

Opinionated Defaults

Don't expose configs for implementation details integrators shouldn't tune: decoupling cap values, passive package sizes, test point style.

Do expose configs for things integrators legitimately need to change: filter cutoffs, output voltage, gain settings, enable/disable optional subcircuits.

Checklist

  1. No if guards on instantiation — use dnp=
  2. No .NET accessor — use ios directly
  3. No str configs for physical values — use typed units
  4. Calculations in named functions with e96() / e24()
  5. Voltage range on all Power ios via template
  6. help= only when it adds non-obvious integrator-facing meaning
  7. Diff pairs use _P / _N
  8. Internal nets prefixed with _
  9. Minimize component count — value-switch, leverage internal bias
  10. When renaming components or nets, keep # pcb:sch comments in sync
  11. Omit no_connect pins from Component() pins

Related skills

More from diodeinc/pcb and the wider catalog.

DA

datasheet-reader

diodeinc/pcb

Read datasheets and technical PDF documents with `pcb scan`. Use when the user gives a local PDF path or an `http(s)` datasheet/document URL, when a task requires reading, summarizing, extracting information from, or answering questions about a datasheet or technical PDF, or when a KiCad symbol / `.kicad_sym` provides a `Datasheet` property to resolve. Run `pcb scan <input>` in bash, treat stdout as the generated `.md` path, then read that markdown file.

1.4k installs
ZE

zener-language

diodeinc/pcb

Canonical Zener HDL semantics and workflow. Use before reading or modifying `.zen` files. Covers module loading and instantiation, `io()`/`config()` API design, nets/interfaces/power domains, components and sourcing, `pcb.toml` manifests, stdlib/package discovery with `pcb doc`, physical units, generics, checks, DNP patterns, naming, and validation.

1.3k installs
SP

spice-sim

diodeinc/pcb

Adds an ngspice-backed simulation testbench to a Zener `.zen` design. Use when the user asks to simulate a circuit, validate behavior in SPICE, or wire a `spice_model=SpiceModel(...)` into a leaf component. Covers `pcb sim`, `Simulation` property, and ngspice `.control` blocks with `tran`/`PULSE`/`PWL` sources and SVG `hardcopy` output.

810 installs
RE

reference-design

diodeinc/pcb

Grow a component package into a high-quality, sourceable reusable design in Zener. Use when translating a datasheet, application note, or eval design into circuitry that should live with the component package itself — including checking for existing reusable packages first, extracting evidence, choosing sourceable passives, documenting the design in the `.zen` docstring, and validating with `pcb build`.

799 installs
CO

component-search

diodeinc/pcb

Search for and add electronic components and reusable subcircuits to a Zener PCB project. Use when designing a board, module, or subsystem and you need a part package, a reusable module, or a component package that may already include application circuitry. Covers `pcb search` (registry:modules, registry:components, web:components) and `pcb new component` for importing web components into a workspace.

787 installs