Hardware Export

A .pedal file is not just a simulation target. The same description drives KiCad netlists, bills of material, and voltage-safety checks, so you can go from compiled tone to a physical build without re-entering the circuit anywhere.

The pipeline

                      +---> WDF audio engine   (WAV / JACK real-time)
                      |
  .pedal file  ------>+---> KiCad netlist       (PCB layout)
                      |
                      +---> Bill of Materials   (Mouser-ready)

All three outputs are generated from the same parsed PedalDef. Components, values, and nets are preserved exactly — there is no translation layer.

KiCad export

Every component in the DSL maps to a real KiCad symbol. Triodes route to the correct Valve:ECC8x variants, power pentodes to Valve:6L6GC / Valve:EL34 / Valve:6550, JFETs to Device:Q_NJFET_DGS, and transformers to the matching Transformer:* symbol based on winding type. Nets are preserved by name.

use pedalkernel::kicad;

let netlist = kicad::export_kicad_netlist(&def);
std::fs::write("circuit.net", netlist)?;

Open the .net file in KiCad and start laying out copper.

Schematic-level export (positioned symbols rather than just connectivity) lives in a sibling crate, pedalkernel-layout, which handles automatic placement, layering, and routing.

Bill of materials

The Python tool under tools/mouser_bom.py reads a .pedal file directly and emits a CSV ready to upload to Mouser:

python3 tools/mouser_bom.py \
    pedalkernel/examples/pedals/fuzz/big_muff.pedal \
    --qty 5 --csv bom.csv

The mapper resolves each component to a real Mouser part from a curated database:

There is no longer a Rust-side BOM API — the previous pedalkernel::hw module was removed in mid-2026 as verified-dead public surface. If you need to script BOM generation from a Rust toolchain, shell out to mouser_bom.py or open an issue.

Voltage safety (.pedalhw files)

Running a circuit in a simulator tolerates voltages that a real build cannot. A .pedalhw file sits next to a .pedal file and declares the part numbers and voltage ratings that constrain the physical implementation:

# fuzz_face.pedalhw
Q1: vce_max(32) part("AC128")
Q2: vce_max(32) part("AC128")
C2: voltage_rating(16)

The .pedalhw file is consumed by the Python tooling (mouser_bom.py uses it for part-name overrides; schematic.py reads it for annotated drawings). The Rust compiler itself ships a coarser, heuristic voltage check that does not need a .pedalhw file:

use pedalkernel::compiler::{check_voltage_compatibility, WarningSeverity};

for w in check_voltage_compatibility(&def, 18.0) {
    println!("[{:?}] {}: {}", w.severity, w.component_id, w.message);
}
// [Danger] Q1: Germanium transistor exceeds Vce(max) at 18V (typical Ge PNP rated 15-32V)
// [Info]   V1: Tube needs 150-400V plate supply; at 18V the WDF model
//              runs fine but a physical build needs a B+ supply

The heuristics catch common mistakes — germanium transistors in fuzz circuits above 12 V, undersized electrolytic caps, tubes running at pedal voltages — without requiring annotation. For a strict check that uses the declared .pedalhw ratings, the Python tooling is the path today.