Automating the path from datasheet → schematic → PCB — one deterministic, verifiable step at a time.
Named for its first solid module (the E-series value solver); the project's scope is the whole datasheet→PCB chain below.
The goal of this project is to take a board from a list of parts to a fab-ready design with as little human last-mile as possible. The method is the opposite of "ask an LLM to draw a schematic": every step that can be made deterministic and checkable is, and the LLM is used only for judgment (which topology, which part, which formula) — never to produce a number or a connection that a tool could compute and verify instead.
Why: when an LLM generates a schematic it picks topology reasonably but guesses values and support circuitry — the measured failure is overwhelmingly wrong/missing support components (decoupling, pull-ups, feedback dividers) and wrong component values, not wrong wiring. Connectivity is checkable; a guessed value is not. So we move every guessable thing behind a solver or a check.
This repo is built in modules, shipped as each one becomes solid. Today the value solver is done and tested; the datasheet pipeline is early; PCB layout is roadmap.
Give it a formula, the knowns, and one unknown to solve for. It solves symbolically (SymPy), snaps the answer to a real E-series value, recomputes your target with that value, and checks tolerance + datasheet limits. The number comes from a solver, never a guess. 11 tests, CLI + Python API.
eseries-solve solve --spec examples/feedback-divider.json
# ideal R1 88,733Ω -> snapped E96 88.7kΩ -> Vout 5.0V, within 2%, under abs-maxSee docs below.
Turns a datasheet PDF into a structured per-MPN record: the abs-max envelope, pin electrical types, and the required support circuit the part needs. Deterministic text + table harvest, LLM extraction, optional page-image vision for the reference circuit. Records carry the formula for design-point-dependent values, which the value solver then computes.
read_datasheet.py— deterministic section harvest + page rasterizeparse_table.py— parses abs-max / recommended-operating tables straight from the PDF textvalidate_record.py— JSON-Schema + domain-sanity gateschema/part-record.schema.json— the record contract
Status: works end-to-end on initial parts; not yet validated across a broad part set. Table parser handles 2-/3-column layouts; known over-capture on some multi-page tables. Use with review.
datasheet-reader (drives the datasheet pipeline) and value-solver (drives the solver). They
encode the discipline: deterministic tools do the maths and checks, the agent does judgment and
flags what it can't resolve. Optional — every tool also runs standalone from the CLI.
The aim is to close the chain below, each link deterministic-and-verified before the next:
parts list
└─ datasheet → PartRecord (extract: abs-max, pins, required support) [WIP]
└─ value solving (compute support values, never guess) [done: closed-form]
└─ netlist assembly (compose vetted sub-circuits → netlist) [planned]
└─ rule checks (power-tree, decoupling-present, ERC++) [planned]
└─ schematic (emit to KiCad / SKiDL / atopile) [planned]
└─ PCB layout (placement + routing handoff) [roadmap]
Closed-form value solving is solved. Constraint solving (interval/SMT over coupled values) and optimization (loop compensation, magnetics, analog sizing) are future engines. PCB layout is a deliberate horizon, not a present claim.
pip install -e . # or: pip install sympy jsonschemaeseries-solve solve --spec examples/feedback-divider.json # solve one unknown
eseries-solve snap 88733 --series E96 # snap a value to E-seriesfrom eseries_solver import solve, snap
solve({"formula": "Vout = Vref*(1 + R1/R2)", "solve_for": "R1",
"knowns": {"Vout": 5.0, "Vref": 0.6, "R2": 12100}, "series": "E96",
"verify": {"target": "Vout", "ideal": 5.0, "tol_pct": 2.0}})
snap(9700, "E24") # -> (10000.0, 0.0309) nearest part + the +3.09% it costsstatus: OK · OUT_OF_TOL · LIMIT_VIOLATION · FAIL (under-determined / no real solution).
- Solves for exactly one unknown; an under-determined problem fails loudly.
- Always snaps to a real E-series value and reports the snapping error.
- Always re-verifies the target with the snapped value, and checks datasheet limits.
Judgment is yours, arithmetic is the tool's.
pip install -e ".[test]" && pytest # 11 passingSee CLAUDE.md for the architecture, the discipline, and how the pieces compose.
MIT.
{ "formula": "Vout = Vref*(1 + R1/R2)", // any algebraic equation with '=' "solve_for": "R1", // exactly one unknown "knowns": { "Vout": 5.0, "Vref": 0.6, "R2": 12100 }, "series": "E96", // E6 | E12 | E24 | E48 | E96 "verify": { "target": "Vout", "ideal": 5.0, "tol_pct": 2.0 }, "limits": [ { "expr": "Vout", "min": 0.0, "max": 5.5 } ] }