Skip to content

Design spec

Generated from the code

This page is written from wholeboard/ai/agent.py and the modules it includes each time the site is built. To change it, change the code.

The design is one JSON document: the designer AI writes it, you can edit it with Edit design JSON, and every build starts from it. This page is the same text the designer is given, so it is exactly what WholeBoard accepts.

Example

{
  "name": "short_snake_case_name",
  "title": "A short title for the boards list, 2 to 6 words, e.g. Qwiic RGB LED driver",
  "summary": "one paragraph describing the board",
  "board": {"width_mm": 50, "height_mm": 30, "layers": 2, "thickness_mm": 1.6,
            "corner_radius_mm": 1, "mounting_holes": {"diameter_mm": 3.2, "inset_mm": 3.5} or null,
            "ground_pour": "GND", "planes": {}},
  "rules": {"track_mm": 0.25, "power_track_mm": 0.5, "clearance_mm": 0.2,
            "via_diameter_mm": 0.6, "via_drill_mm": 0.3},
  "components": [
    {"ref": "U1", "value": "AMS1117-3.3", "footprint": "Package_TO_SOT_SMD:SOT-223-3_TabPin2",
     "symbol": "Regulator_Linear:AMS1117-3.3", "lcsc": "C6186", "mpn": "AMS1117-3.3",
     "description": "3.3 V LDO", "no_connect": [],
     "placement": {"x_mm": 10, "y_mm": 5, "rotation": 0, "locked": true}}
  ],
  "nets": [
    {"name": "GND", "class": "power", "pins": ["U1.1", "C1.2"]},
    {"name": "+3V3", "class": "power", "pins": ["U1.2", "C1.1"]}
  ]
}

Fields

- "footprint" and "symbol" are KiCad library ids ("Library:Name") that the search tools return. "symbol" is optional; without it the schematic shows a generated box.
- Pins are "REF.PAD". Mounting holes from board.mounting_holes need no component entries.
- "rules" is optional; the defaults suit JLCPCB's standard process. Each board also gets a .kicad_dru with JLCPCB's limits for its layer count, so DRC checks annular rings, hole spacing and silkscreen too. Track heating is checked with IPC-2152 (rules.trace_heating "ipc2221" for the older, stricter charts on inner layers).
- "blocks": building blocks (format below); their parts and nets are added to "components" and "nets".
- board.outline: [[x, y], ...] a polygon in mm for a non-rectangular board (width/height follow from it).
- board.mounting_holes.positions: [[x, y], ...] hole centres, instead of one in each corner.
- board.case: the user's own box, for drilling templates instead of a printed enclosure:
  The design names the case in board.case, using the figures from the box maker's drawing:
- "simulation" and per-component "sim" are described below.

Simulation

Every board is simulated with ngspice after the design check. Models are chosen automatically;
supplies are guessed from connectors, batteries and power net names. Say what you know:

"simulation": {
  "sources": [                       // what powers the board, and test signals
    {"net": "VBUS", "volts": 5, "kind": "usb", "max_ma": 500},
    {"net": "VBAT", "volts": 9, "kind": "battery", "capacity_mah": 500, "ohms": 1.5, "max_ma": 100},
    {"net": "IN", "kind": "signal", "wave": {"type": "sine", "amplitude": 0.5, "freq_hz": 1000, "offset": 1.65}},
    {"net": "CLK", "kind": "signal", "wave": {"type": "pulse", "low": 0, "high": 3.3, "freq_hz": 1000, "duty": 0.5}}
  ],                                 // kind: usb | battery | bench | signal; "ac": 1 makes it the AC sweep input
  "time_ms": 3000,                   // transient length (default: picked from the circuit)
  "ac": {"from_hz": 10, "to_hz": 1000000},   // frequency sweep; needs a source with "ac": 1
  "probes": ["OUT", "+3V3"],         // nets to plot (rails and toggling nets are plotted anyway)
  "expect": [                        // what the circuit should do; failures stop the build
    {"net": "OUT", "measure": "frequency", "min": 0.8, "max": 1.2},       // Hz
    {"net": "OUT", "measure": "duty", "min": 0.4, "max": 0.7},            // 0..1
    {"net": "+3V3", "measure": "voltage", "min": 3.2, "max": 3.4},        // settled average, V
    {"net": "VBUS", "measure": "ripple", "max": 0.05},                    // peak-to-peak, V
    {"ref": "D1", "measure": "current", "min": 2, "max": 15},             // mA (LEDs, resistors, diodes...)
    {"net": "OUT", "measure": "gain_db", "freq_hz": 1000, "min": 19, "max": 21},   // AC sweep
    {"net": "OUT", "measure": "cutoff_hz", "min": 900, "max": 1100}                // -3 dB point
  ],
  "ambient_c": 25,
  "battery_profile": {"active_pct": 5, "sleep_ma": 0.05},   // duty cycle for battery life (optional)
  "blocking": true                   // false: report problems but keep building
}

Per component "sim" (only when the automatic model is wrong):
  {"skip": true}                                   leave the part out
  {"vf": 2.0, "max_ma": 20}                        LED forward voltage and current limit
  {"vout": 3.3, "dropout": 0.25, "max_ma": 300}    regulator; "vref": 0.8 for adjustable/switching ones
  {"load_ma": 25, "peak_ma": 120}                  IC supply current (typical, peak)
  {"pins": {"PA5": "high", "PB2": "low", "7": 1.8}} IC pins driven high (its supply), low, or to a voltage
  {"closed": [["1", "2"]]}                         switch contacts that are closed (pushbuttons are open
                                                   by default, slide/toggle switches take the first throw)
  {"vth": 1.0}                                     MOSFET threshold
  {"spice": "R{ref} {1} {2} 4.7k"}                 your own SPICE lines: {PAD} is the node of that pad,
                                                   {ref} the reference, {gnd} ground; several lines with \n
MCU pins that drive LEDs through a resistor are driven high (anode side) or low (cathode side)
automatically, so LED currents are checked. Results list every assumption the models made.

Building blocks

A design spec may list blocks next to its own components:

  "blocks": [
    {"block": "usb_c_power", "name": "USB", "connect": {"VBUS": "VBUS", "GND": "GND"},
     "placement": {"J1": {"x_mm": 4, "y_mm": 12, "rotation": 270, "locked": true}}},
    {"block": "esp32c3_wroom02", "name": "MCU", "connect": {"IO4": "SDA", "IO5": "SCL"}}
  ]

`connect` maps the block's ports to nets of the design (a port left out keeps its default net,
or is left unconnected when it has none). `options` turns optional parts on or off, `parts`
overrides fields of a block part ({"D1": {"lcsc": "C2297", "value": "Green"}}) and `placement`
places block parts by their block reference. Expansion gives every block part a free reference
in the design and names the block's inner nets "<name>_<net>".

Pins in a block are "REF.PAD" or "REF:PIN_NAME" (every pad of that symbol pin name).
More blocks can be added as JSON files in ~/.wholeboard/blocks/.

Controlled impedance

A design lists its controlled nets in "impedance": [{"nets": ["USB_DP", "USB_DN"], "ohms": 90, "kind": "diff", "max_skew_mm": 1.25}, {"nets": ["RF_ANT"], "ohms": 50, "kind": "single"}, {"nets": ["QSPI_SCLK", "QSPI_SD0"], "kind": "bus", "max_length_mm": 30, "max_match_mm": 5}] An entry may name a routing profile ("profile": "usb2_hs") for an interface's limits; its own fields override the profile's. Limits: max_length_mm (each net), max_skew_mm (within a pair), max_match_mm (between the nets of a bus), max_vias (each net), max_stub_mm (a branch off the main run, e.g. to an ESD diode), and match with max_skew_between_mm (pairs that share a "match" name stay within that length of each other). Profiles: usb2_hs (USB 2.0 high speed (480 Mbit/s)); usb2_fs (USB 2.0 full speed (12 Mbit/s)); ethernet_100 (100BASE-TX pair); rmii (RMII (50 MHz)); qspi_flash (QSPI flash); rf_50 (50 Ω RF feed). Building blocks carry the profile of the interfaces they provide, so the checks apply wherever they are used. Widths use the IPC-2141 microstrip formulas for an outer-layer trace over the nearest plane. They are approximate (the coupled-pair formula most of all): order 4 and 6 layer boards with impedance control and the stack-up named here (free at JLCPCB), and check the figures in JLCPCB's impedance calculator.

Mains

A design that connects to mains declares it in "mains": {"nets": ["AC_L", "AC_N"], "volts": 230, "isolated": true} volts is the working voltage (rms for AC; default 250, which covers 230 and 240 V mains). isolated true (the default) means the low-voltage side is meant to be safe to touch (SELV): it must be separated from everything on the mains side by reinforced insulation, through an isolation barrier part: an isolated supply module (HLK-PM01, IRM-02), a transformer, an optocoupler or digital isolator, or a relay; mark any other part that crosses the barrier (a Y capacitor) with "barrier": true. isolated false means the whole board is live (a capacitive dropper, a non-isolated buck): it must never be touched while plugged in, and only the mains nets' own spacing is checked. The mains side is every net reached from the mains nets through parts that are not barrier parts. Requirements, for pollution degree 2 and FR-4 (material group IIIb), overvoltage category II (overvoltage_category 3 for fixed wiring): - between the listed mains nets (live and neutral, before and after the fuse, the rectified bus): IPC-2221 table 6-1 B2 (outer layers) and B1 (inner) for the peak voltage (functional insulation). List every net that carries the full mains voltage; other mains-side nets (an LED feed, a controller's supply) are not held to it; - from the mains side to the low-voltage side, mounting holes and anything else: IEC 62368-1 / IEC 60664-1 reinforced insulation: clearance for the next mains transient step up (3.0 mm at 230 V), creepage twice the basic value (5.0 mm at 250 V). Creepage runs along the board surface, so a milled slot (at least 1 mm wide) makes it longer: WholeBoard cuts one under each barrier part whose pins are closer than that, unless the part says "slot": false. WholeBoard refuses to finish a mains design with no fuse on the mains input, no isolation barrier (when isolated) or a spacing that fails, and says why. These checks are an aid, not a certification: have a mains design reviewed, and build it into an enclosure that keeps fingers out.

Defaults for rules

{
  "track_mm": 0.25,
  "power_track_mm": 0.5,
  "clearance_mm": 0.2,
  "via_diameter_mm": 0.6,
  "via_drill_mm": 0.3,
  "edge_clearance_mm": 0.3
}