KiCad schematic review

One of the kicad_skills usage guides for the eda CLI — 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

  1. 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

  1. sch info — get the parts list, the net list and the sheet hierarchy. Note the supply rails, the ICs and anything unfamiliar.
  2. sch review --text — machine findings. Every error must be explained or fixed; every warning must be judged, not blindly reported.
  3. sch render + Read the PNG — the machine cannot see intent. On a multi-sheet schematic start with contact-sheet.png, then the individual sheets; schematic.pdf is 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.
  4. Datasheets — for each IC, get its datasheet (the part number and the Datasheet field are in sch info) and use the datasheet-analysis guide to check the actual part against its ratings: supply range, input common-mode range, required external components, pins that must not float.
  5. 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)

Notes