KiCad schematic review
One of the kicad_skills usage guides for the
edaCLI — all of them. Plain Markdown: read it directly, or hand it to whatever assistant you use.
Reads .kicad_sch files and reviews them. Runs in the container
(see the eda-environment guide); pass paths relative to the repository root.
The commands
./bin/eda.sh sch info hardware/ # structure: components, nets, hierarchy
./bin/eda.sh sch review hardware/ --text # ERC + design heuristics
./bin/eda.sh gate hardware/ --policy ai-generated --text # one pass/fail verdict
./bin/eda.sh sch render hardware/ -o /tmp/sch # PDF + a PNG per sheet + contact sheet
./bin/eda.sh sch pdf hardware/ -o /tmp/sch.pdf
./bin/eda.sh report hardware/ -o /tmp/report # review + images + BOM on one page
./bin/eda.sh diff old/ hardware/ -o /tmp/diff # what changed between revisions
The target may be a .kicad_sch, a .kicad_pro, or the project directory
(the root sheet is found automatically, sub-sheets are followed).
eda diff compares connectivity by which pins each net joins, so a net that
gained or lost a pin shows up and one that was only moved on the sheet does not.
A net whose connections survived under a new name is reported as a rename rather
than as one deletion and one addition. Components are compared by reference, with
value, footprint, DNP and library changes named.
It also renders both revisions of every sheet and compares them: red is what
the old revision had and the new one does not, green is the other way round.
A symbol that moved is red where it was and green where it is now, over a faded
copy of the sheet for context. Because a moved part is a speck on an A4 page, a
zoomed crop of the changed region is written next to the full sheet - read that
one first. --no-images skips the rendering when only the connectivity matters.
The drawing diff answers “what moved, what appeared, what went away”. It is not the channel for “what was re-labelled”. Suppressing anti-aliasing means ink that lands within a pixel of where ink already was is treated as the renderer wobbling rather than as a change — and text redrawn in place is largely that. Retagging a resistor 10k → 4k7 registers zero changed pixels at 100 dpi, 13 at 150 and 130 at
- Read the Components table for that: it compares the field rather than the picture of it, so it catches the change at any dpi.
How to actually review a schematic
sch info— get the parts list, the net list and the sheet hierarchy. Note the supply rails, the ICs and anything unfamiliar.sch review --text— machine findings. Everyerrormust be explained or fixed; everywarningmust be judged, not blindly reported.sch render+ Read the PNG — the machine cannot see intent. On a multi-sheet schematic start withcontact-sheet.png, then the individual sheets;schematic.pdfis the thing to hand to a human. Look at the drawing to check signal flow, that the topology is what the user described, and that nothing important is drawn but disconnected.- Datasheets — for each IC, get its datasheet (the part number and the
Datasheetfield are insch info) and use thedatasheet-analysisguide to check the actual part against its ratings: supply range, input common-mode range, required external components, pins that must not float. - Report findings grouped by severity, each with the reference designator or net name, why it matters, and the concrete fix.
What sch review checks
From KiCad’s own ERC (erc.* rules — authoritative): unconnected pins and
wire endpoints, conflicting drivers, power pins not driven, duplicate
references, library symbol mismatches, off-grid endpoints, bus errors.
Design heuristics on top of the netlist:
| Rule | Meaning |
|---|---|
net.single_pin |
net reaches exactly one pin — usually a wiring mistake |
net.no_driver |
a net with only input pins, nothing drives it |
analog.missing_decoupling |
an IC supply net with no capacitor to ground — judged from pin electrical types when the netlist has them, and never asked of a net an output pin drives |
analog.no_dc_path |
a net whose every pin is a capacitor or connector: nothing sets its DC level |
analog.i2c_pullup |
a net named SDA/SCL with no resistor on it |
analog.led_no_series_resistor |
LED with no current limiting on either terminal |
analog.unprotected_power_input |
a power connector (a ground, a supply, nothing else, four pins or fewer) whose supply reaches some load with no fuse in that path, or no diode in it or across it the right way round — walked inward through series fuses, diodes, inductors and beads, branch by branch; a rail the board itself drives is not judged |
analog.clock_no_series_resistor |
an oscillator module’s output net carrying a load directly — nothing but resistors and test points may sit on it; a pull on the net is not a series resistor |
power.no_ground / power.no_supply / power.many_supplies |
rail sanity |
schematic.duplicate_reference / schematic.unannotated |
annotation problems |
schematic.missing_footprint / missing_value / missing_datasheet |
field completeness |
schematic.dnp |
DNP parts, listed so they are not forgotten in a BOM |
Drawing readability — none of these change the netlist, which is why ERC has nothing to say about them, and why a generated sheet fails them so reliably:
| Rule | Meaning |
|---|---|
readability.off_grid_pin / _wire / _junction / _label |
geometry off the 1.27 mm grid; KiCad connects on exact coordinates, so this draws as a connection that is not one |
readability.missing_junction |
a wire ending on another wire with no junction dot — KiCad treats that as crossing |
readability.power_symbol_orientation |
a rotated power symbol — rails point up, grounds hang down |
readability.wire_through_junction |
a junction in a wire’s middle instead of at a break — KiCad 9 connects only one side of the wire |
readability.overlapping_wires |
two wires drawn along the same stretch of line — plots as one, edits as two |
readability.dangling_wire |
a wire end reaching no pin, label, junction or other wire |
readability.facing_away |
a single-row symbol whose connected pins point away from their signals — the symbol wants mirroring |
readability.margin_intrusion |
pins or notes on the page frame strip or the title block. A note is measured by the box it covers, not by its anchor — the sentence that ran into the Pico carrier’s title block started 12 mm clear of it and was 67 mm long |
readability.text_over_symbol |
a design note whose estimated extent prints over a symbol — over the shape the library draws, which is read out of the schematic’s own lib_symbols, not over the box the part’s pins span |
readability.text_over_text |
two printed strings whose estimated extents overlap — a designator, a value, a rating, a design note or a net label drawn through another one, or through a symbol body. The body is the shape KiCad draws: an LED’s two pins span 2.54 mm and its emission arrows reach 4.6 mm the other way, so a value cleared of the pins still prints through the part. A field’s box also follows the rotation KiCad gives it — a symbol’s own angle is added to the field’s, and at half a turn KiCad keeps the glyphs upright by swapping the justification, so a value written justify left on a part standing at 90° prints to the left of its anchor. Graded info: it fires on 15 of KiCad’s 18 demo sheets, so on human work it is measuring the character-count estimate as much as the drawing. ai-generated promotes it to an error |
readability.text_over_wire |
a symbol’s own designator, value or rating printed across a net — a value with a wire drawn through it is a value nobody can read off the plot. Info, for the same reason and with the same promotion |
readability.overlapping_symbols |
symbols drawn on top of each other |
readability.outside_page |
items past the page border, missing from the plot and the PDF |
readability.diagonal_wire |
wires that do not run orthogonally |
readability.unnamed_nets |
most multi-pin nets still carry generated names |
readability.sheet_density |
one sheet holding more than a reader can follow |
readability.title_block |
no title, revision, date or company |
readability.label_only |
most connections are a stub ending in a label, not a drawn wire — a valid netlist that reads as a name table |
Part specification — a value is not a specification. C3 = 100n is the same
line for the 16 V part that fails on a 24 V rail and the 50 V part that does not:
| Rule | Meaning |
|---|---|
spec.missing_rating |
R without tolerance/power, C without voltage/tolerance, L without current |
spec.voltage_derating |
capacitor rating against the rail it sits on: below the rail is an error, under 1.5x headroom a warning |
spec.missing_part_number |
an active part with no MPN or manufacturer |
spec.no_design_notes |
nothing on any sheet records why the design is the way it is |
spec.missing_esr |
a polarised capacitor on a net an inductor also reaches — a switching regulator’s output — with no ESR field; the regulator’s loop is designed around that ESR |
spec.voltage_derating only judges rails whose name states a voltage (+3V3,
-12V, VDD_1V8, VBUS); derating against a number nobody wrote down would be
inventing the requirement.
Thresholds are adjustable with --threshold key=value — grid_mm,
symbol_margin_mm, max_symbols_per_sheet, min_named_net_ratio,
capacitor_derating_factor.
net.single_pin is graded: an auto-named net (unconnected-(U1-Pad3)) is a
dangling wire and warns, a net the designer named is reported as info because
it is usually a deliberate spare. A rule that fires more than six times is
folded into one finding with the count and the first examples; --collapse N
changes the limit, --collapse 0 prints every occurrence.
Exit code is 2 when there is at least one error, 0 otherwise — usable in CI.
eda gate turns all of this into a single verdict against a policy, which is
what to use when the schematic is being generated rather than drawn: see the
kicad-design-gate guide.
Things the tool cannot check (do these by hand)
- Whether the topology implements the intended function.
- Component values: gain, cut-off frequency, current limits, divider ratios,
time constants. Compute them, or verify with the
spice-simulationguide. - Power budget and thermal dissipation.
- Whether a part’s operating conditions are respected (needs the datasheet).
- Reset/boot strapping, ESD protection on signal connectors, connector pinout
against the mating part. (Whether a power connector has a fuse and a
diode between it and the circuit is checked —
analog.unprotected_power_input— but not whether their ratings suit the supply.)
Notes
- With
--no-clithe review runs without KiCad, using a pure-python connectivity extractor (netlist_source: geometry-fallback). It agrees with KiCad on ordinary sheets but resolves cross-sheet connections only through power symbols and global labels, and it cannot run ERC. Prefer the container. ./bin/eda.sh sch netlist <target> --format kicadxml -o out.netexports the netlist for other tools;--format jsongives the normalised structure the review uses../bin/eda.sh sch erc <target>returns KiCad’s raw ERC JSON when the details of a violation are needed../bin/eda.sh sch bom <target> -o bom.csvexports a grouped bill of materials (see thekicad-fabrication-outputguide).