Analog Transformer · Circuit Validation Report

Doing a neural network's math with resistors — the physics checks out to 0.0011%

PASS · max error 0.0011%

We took a tiny neural network, rebuilt its math as a grid of resistors, and simulated the real circuit. The currents it produces match the math it's meant to do to within 0.0011% — so the idea is physically sound, before spending a cent on a chip.

Stack PyTorch · ngspice Simulator ngspice 45.2 Test cases 3 (W_V×digit7, W_Q×digit3, W_K×digit9)

The idea, in one minute

A neural network's core move is boring: multiply two numbers, then add up the results. Do that billions of times and you get things like ChatGPT.

A resistor multiplies for free. Push a voltage through it and Ohm's law hands you a current — voltage divided by resistance. Wire up a grid of resistors and those currents add themselves up. So a grid of resistors can do a neural network's math directly, as physics — no processor crunching numbers.

This page checks one thing: in a full circuit simulation, do the resistor currents actually match the math the network intended? They do — to within 0.0011%.

The catch: it's still a simulation of a tiny model, not a real chip. But the physics holds, and that's the part worth proving first.

01

Train the toy model

In plain words: first, teach a tiny neural network to add single digits — so we have some real trained numbers to turn into a circuit.

A single self-attention block (16-dim, one head, residual around attention) is trained on all 100 ordered digit pairs (0–9 + 0–9), predicting the sum as one of 19 classes. This is a memorization task by design — the physical circuit will only ever be asked about digits 0–9, so there is no "unseen input" to generalize to.

100%final accuracy
100epoch to 100%
0.00152final loss
100 / 100pairs correct

Training loss

Cross-entropy, log scale, 1500 epochs

310.30.10.030.010.003 0.00152 030060090012001499

Accuracy

Full 100-pair set, reaches 100% by epoch 100

0%25%50%75%100% 100% @ epoch 100 030060090012001499
02

Quantize to resistance

In plain words: turn each of those trained numbers into a resistor value. A resistor can't be negative, so every number is stored as a pair — one for the positive part, one for the negative — and the readout subtracts them.

Each trained weight is clipped to [−1, 1] and rounded to one of 15 signed levels (−7…+7, a 4-bit range). A resistor can only pass more or less current — never subtract — so every weight becomes a differential pair: a strong positive weight is a low-resistance path on the "positive" row and a near-open path on the "negative" row; a strong negative weight is the reverse. Resistance runs 10k–100kΩ, with levels mapped linearly onto conductance (1/R) — the domain the crossbar actually computes in. (The obvious linear-in-ohms mapping distorts every mid-range weight; see the Lab write-up.)

W_V — trained (float)

row 0, col 0: weight=-0.019row 0, col 1: weight=0.339row 0, col 2: weight=0.241row 0, col 3: weight=1.085row 0, col 4: weight=0.232row 0, col 5: weight=0.294row 0, col 6: weight=0.779row 0, col 7: weight=0.611row 0, col 8: weight=0.403row 0, col 9: weight=-0.726row 0, col 10: weight=-0.131row 0, col 11: weight=0.093row 0, col 12: weight=-0.540row 0, col 13: weight=-0.119row 0, col 14: weight=-0.675row 0, col 15: weight=0.136row 1, col 0: weight=0.458row 1, col 1: weight=0.517row 1, col 2: weight=0.052row 1, col 3: weight=-0.551row 1, col 4: weight=0.446row 1, col 5: weight=0.128row 1, col 6: weight=-0.450row 1, col 7: weight=-0.088row 1, col 8: weight=0.871row 1, col 9: weight=-0.697row 1, col 10: weight=0.580row 1, col 11: weight=0.365row 1, col 12: weight=0.116row 1, col 13: weight=0.434row 1, col 14: weight=-0.976row 1, col 15: weight=-0.387row 2, col 0: weight=-0.469row 2, col 1: weight=0.390row 2, col 2: weight=0.543row 2, col 3: weight=-0.235row 2, col 4: weight=-0.018row 2, col 5: weight=0.560row 2, col 6: weight=-0.510row 2, col 7: weight=-0.047row 2, col 8: weight=-0.095row 2, col 9: weight=-0.669row 2, col 10: weight=0.361row 2, col 11: weight=0.150row 2, col 12: weight=-0.290row 2, col 13: weight=0.038row 2, col 14: weight=0.526row 2, col 15: weight=0.301row 3, col 0: weight=0.724row 3, col 1: weight=-0.708row 3, col 2: weight=-0.535row 3, col 3: weight=0.427row 3, col 4: weight=-0.259row 3, col 5: weight=-0.073row 3, col 6: weight=0.542row 3, col 7: weight=-0.302row 3, col 8: weight=-0.037row 3, col 9: weight=-0.542row 3, col 10: weight=0.276row 3, col 11: weight=-0.077row 3, col 12: weight=-0.908row 3, col 13: weight=-0.388row 3, col 14: weight=-0.570row 3, col 15: weight=0.779row 4, col 0: weight=0.482row 4, col 1: weight=-0.693row 4, col 2: weight=-0.452row 4, col 3: weight=-0.577row 4, col 4: weight=-0.077row 4, col 5: weight=-0.291row 4, col 6: weight=-0.144row 4, col 7: weight=-0.042row 4, col 8: weight=0.769row 4, col 9: weight=-0.428row 4, col 10: weight=0.116row 4, col 11: weight=-0.795row 4, col 12: weight=0.563row 4, col 13: weight=-0.429row 4, col 14: weight=-0.663row 4, col 15: weight=0.754row 5, col 0: weight=0.028row 5, col 1: weight=0.811row 5, col 2: weight=0.327row 5, col 3: weight=-0.386row 5, col 4: weight=-0.233row 5, col 5: weight=0.604row 5, col 6: weight=-0.397row 5, col 7: weight=-0.316row 5, col 8: weight=0.083row 5, col 9: weight=-0.169row 5, col 10: weight=-0.509row 5, col 11: weight=-0.121row 5, col 12: weight=0.031row 5, col 13: weight=-0.325row 5, col 14: weight=-0.114row 5, col 15: weight=0.508row 6, col 0: weight=-0.532row 6, col 1: weight=0.245row 6, col 2: weight=-0.206row 6, col 3: weight=-0.718row 6, col 4: weight=0.707row 6, col 5: weight=0.009row 6, col 6: weight=-0.479row 6, col 7: weight=0.003row 6, col 8: weight=0.838row 6, col 9: weight=-0.541row 6, col 10: weight=0.594row 6, col 11: weight=-0.186row 6, col 12: weight=0.317row 6, col 13: weight=0.710row 6, col 14: weight=-0.458row 6, col 15: weight=-0.244row 7, col 0: weight=0.431row 7, col 1: weight=-0.055row 7, col 2: weight=-0.275row 7, col 3: weight=-0.796row 7, col 4: weight=0.345row 7, col 5: weight=-0.165row 7, col 6: weight=-0.519row 7, col 7: weight=0.276row 7, col 8: weight=0.860row 7, col 9: weight=-0.445row 7, col 10: weight=-0.044row 7, col 11: weight=-0.393row 7, col 12: weight=-0.205row 7, col 13: weight=-0.727row 7, col 14: weight=-0.652row 7, col 15: weight=0.362row 8, col 0: weight=0.685row 8, col 1: weight=-0.639row 8, col 2: weight=-0.438row 8, col 3: weight=0.321row 8, col 4: weight=-0.004row 8, col 5: weight=0.824row 8, col 6: weight=0.698row 8, col 7: weight=-0.532row 8, col 8: weight=-0.660row 8, col 9: weight=0.333row 8, col 10: weight=-0.086row 8, col 11: weight=-0.385row 8, col 12: weight=-0.656row 8, col 13: weight=-0.627row 8, col 14: weight=0.232row 8, col 15: weight=0.787row 9, col 0: weight=-0.609row 9, col 1: weight=0.774row 9, col 2: weight=0.703row 9, col 3: weight=-0.490row 9, col 4: weight=0.059row 9, col 5: weight=-0.412row 9, col 6: weight=-0.092row 9, col 7: weight=-0.474row 9, col 8: weight=0.423row 9, col 9: weight=0.369row 9, col 10: weight=0.247row 9, col 11: weight=0.221row 9, col 12: weight=0.829row 9, col 13: weight=0.585row 9, col 14: weight=0.064row 9, col 15: weight=-0.753row 10, col 0: weight=0.714row 10, col 1: weight=-0.562row 10, col 2: weight=-0.924row 10, col 3: weight=0.755row 10, col 4: weight=0.391row 10, col 5: weight=0.365row 10, col 6: weight=-0.567row 10, col 7: weight=0.226row 10, col 8: weight=-0.638row 10, col 9: weight=-0.245row 10, col 10: weight=0.180row 10, col 11: weight=-0.134row 10, col 12: weight=-0.652row 10, col 13: weight=-0.498row 10, col 14: weight=0.127row 10, col 15: weight=0.941row 11, col 0: weight=-0.512row 11, col 1: weight=-0.308row 11, col 2: weight=0.206row 11, col 3: weight=0.646row 11, col 4: weight=-0.341row 11, col 5: weight=-0.299row 11, col 6: weight=0.554row 11, col 7: weight=-0.542row 11, col 8: weight=-0.657row 11, col 9: weight=0.345row 11, col 10: weight=0.574row 11, col 11: weight=0.246row 11, col 12: weight=0.237row 11, col 13: weight=0.778row 11, col 14: weight=0.677row 11, col 15: weight=-0.450row 12, col 0: weight=-0.279row 12, col 1: weight=0.423row 12, col 2: weight=0.660row 12, col 3: weight=-0.695row 12, col 4: weight=-0.170row 12, col 5: weight=-0.365row 12, col 6: weight=-0.110row 12, col 7: weight=-0.446row 12, col 8: weight=0.090row 12, col 9: weight=0.520row 12, col 10: weight=0.025row 12, col 11: weight=0.412row 12, col 12: weight=0.990row 12, col 13: weight=0.669row 12, col 14: weight=0.178row 12, col 15: weight=-0.644row 13, col 0: weight=-0.576row 13, col 1: weight=-0.804row 13, col 2: weight=-0.748row 13, col 3: weight=0.354row 13, col 4: weight=0.651row 13, col 5: weight=0.510row 13, col 6: weight=0.451row 13, col 7: weight=-0.388row 13, col 8: weight=-0.666row 13, col 9: weight=0.811row 13, col 10: weight=0.148row 13, col 11: weight=0.401row 13, col 12: weight=-0.004row 13, col 13: weight=0.844row 13, col 14: weight=-0.165row 13, col 15: weight=0.073row 14, col 0: weight=-0.042row 14, col 1: weight=-0.401row 14, col 2: weight=0.147row 14, col 3: weight=0.577row 14, col 4: weight=0.130row 14, col 5: weight=0.710row 14, col 6: weight=0.433row 14, col 7: weight=0.364row 14, col 8: weight=-0.355row 14, col 9: weight=-0.070row 14, col 10: weight=-0.447row 14, col 11: weight=-0.547row 14, col 12: weight=-0.560row 14, col 13: weight=-0.490row 14, col 14: weight=0.678row 14, col 15: weight=0.711row 15, col 0: weight=-0.294row 15, col 1: weight=-0.472row 15, col 2: weight=0.500row 15, col 3: weight=0.770row 15, col 4: weight=-0.540row 15, col 5: weight=-0.361row 15, col 6: weight=0.611row 15, col 7: weight=0.106row 15, col 8: weight=-0.445row 15, col 9: weight=0.008row 15, col 10: weight=0.008row 15, col 11: weight=0.704row 15, col 12: weight=-0.476row 15, col 13: weight=-0.270row 15, col 14: weight=0.786row 15, col 15: weight=-0.163

W_V — quantized (4-bit)

row 0, col 0: level=0.000row 0, col 1: level=2.000row 0, col 2: level=2.000row 0, col 3: level=7.000row 0, col 4: level=1.000row 0, col 5: level=2.000row 0, col 6: level=5.000row 0, col 7: level=4.000row 0, col 8: level=3.000row 0, col 9: level=-5.000row 0, col 10: level=-1.000row 0, col 11: level=1.000row 0, col 12: level=-3.000row 0, col 13: level=-1.000row 0, col 14: level=-4.000row 0, col 15: level=1.000row 1, col 0: level=3.000row 1, col 1: level=3.000row 1, col 2: level=0.000row 1, col 3: level=-4.000row 1, col 4: level=3.000row 1, col 5: level=1.000row 1, col 6: level=-3.000row 1, col 7: level=-1.000row 1, col 8: level=6.000row 1, col 9: level=-4.000row 1, col 10: level=4.000row 1, col 11: level=2.000row 1, col 12: level=1.000row 1, col 13: level=3.000row 1, col 14: level=-6.000row 1, col 15: level=-2.000row 2, col 0: level=-3.000row 2, col 1: level=3.000row 2, col 2: level=4.000row 2, col 3: level=-2.000row 2, col 4: level=0.000row 2, col 5: level=4.000row 2, col 6: level=-3.000row 2, col 7: level=0.000row 2, col 8: level=-1.000row 2, col 9: level=-4.000row 2, col 10: level=2.000row 2, col 11: level=1.000row 2, col 12: level=-2.000row 2, col 13: level=0.000row 2, col 14: level=3.000row 2, col 15: level=2.000row 3, col 0: level=5.000row 3, col 1: level=-5.000row 3, col 2: level=-3.000row 3, col 3: level=3.000row 3, col 4: level=-2.000row 3, col 5: level=0.000row 3, col 6: level=3.000row 3, col 7: level=-2.000row 3, col 8: level=0.000row 3, col 9: level=-3.000row 3, col 10: level=2.000row 3, col 11: level=0.000row 3, col 12: level=-6.000row 3, col 13: level=-3.000row 3, col 14: level=-4.000row 3, col 15: level=5.000row 4, col 0: level=3.000row 4, col 1: level=-4.000row 4, col 2: level=-3.000row 4, col 3: level=-4.000row 4, col 4: level=0.000row 4, col 5: level=-2.000row 4, col 6: level=-1.000row 4, col 7: level=0.000row 4, col 8: level=5.000row 4, col 9: level=-3.000row 4, col 10: level=1.000row 4, col 11: level=-5.000row 4, col 12: level=4.000row 4, col 13: level=-3.000row 4, col 14: level=-4.000row 4, col 15: level=5.000row 5, col 0: level=0.000row 5, col 1: level=5.000row 5, col 2: level=2.000row 5, col 3: level=-2.000row 5, col 4: level=-2.000row 5, col 5: level=4.000row 5, col 6: level=-3.000row 5, col 7: level=-2.000row 5, col 8: level=1.000row 5, col 9: level=-1.000row 5, col 10: level=-3.000row 5, col 11: level=-1.000row 5, col 12: level=0.000row 5, col 13: level=-2.000row 5, col 14: level=-1.000row 5, col 15: level=3.000row 6, col 0: level=-3.000row 6, col 1: level=2.000row 6, col 2: level=-1.000row 6, col 3: level=-5.000row 6, col 4: level=5.000row 6, col 5: level=0.000row 6, col 6: level=-3.000row 6, col 7: level=0.000row 6, col 8: level=5.000row 6, col 9: level=-3.000row 6, col 10: level=4.000row 6, col 11: level=-1.000row 6, col 12: level=2.000row 6, col 13: level=5.000row 6, col 14: level=-3.000row 6, col 15: level=-2.000row 7, col 0: level=3.000row 7, col 1: level=0.000row 7, col 2: level=-2.000row 7, col 3: level=-5.000row 7, col 4: level=2.000row 7, col 5: level=-1.000row 7, col 6: level=-3.000row 7, col 7: level=2.000row 7, col 8: level=6.000row 7, col 9: level=-3.000row 7, col 10: level=0.000row 7, col 11: level=-3.000row 7, col 12: level=-1.000row 7, col 13: level=-5.000row 7, col 14: level=-4.000row 7, col 15: level=2.000row 8, col 0: level=4.000row 8, col 1: level=-4.000row 8, col 2: level=-3.000row 8, col 3: level=2.000row 8, col 4: level=0.000row 8, col 5: level=5.000row 8, col 6: level=5.000row 8, col 7: level=-3.000row 8, col 8: level=-4.000row 8, col 9: level=2.000row 8, col 10: level=-1.000row 8, col 11: level=-2.000row 8, col 12: level=-4.000row 8, col 13: level=-4.000row 8, col 14: level=1.000row 8, col 15: level=5.000row 9, col 0: level=-4.000row 9, col 1: level=5.000row 9, col 2: level=5.000row 9, col 3: level=-3.000row 9, col 4: level=0.000row 9, col 5: level=-3.000row 9, col 6: level=-1.000row 9, col 7: level=-3.000row 9, col 8: level=3.000row 9, col 9: level=2.000row 9, col 10: level=2.000row 9, col 11: level=1.000row 9, col 12: level=5.000row 9, col 13: level=4.000row 9, col 14: level=0.000row 9, col 15: level=-5.000row 10, col 0: level=5.000row 10, col 1: level=-4.000row 10, col 2: level=-6.000row 10, col 3: level=5.000row 10, col 4: level=3.000row 10, col 5: level=2.000row 10, col 6: level=-4.000row 10, col 7: level=1.000row 10, col 8: level=-4.000row 10, col 9: level=-2.000row 10, col 10: level=1.000row 10, col 11: level=-1.000row 10, col 12: level=-4.000row 10, col 13: level=-3.000row 10, col 14: level=1.000row 10, col 15: level=6.000row 11, col 0: level=-3.000row 11, col 1: level=-2.000row 11, col 2: level=1.000row 11, col 3: level=4.000row 11, col 4: level=-2.000row 11, col 5: level=-2.000row 11, col 6: level=4.000row 11, col 7: level=-3.000row 11, col 8: level=-4.000row 11, col 9: level=2.000row 11, col 10: level=4.000row 11, col 11: level=2.000row 11, col 12: level=2.000row 11, col 13: level=5.000row 11, col 14: level=4.000row 11, col 15: level=-3.000row 12, col 0: level=-2.000row 12, col 1: level=3.000row 12, col 2: level=4.000row 12, col 3: level=-4.000row 12, col 4: level=-1.000row 12, col 5: level=-2.000row 12, col 6: level=-1.000row 12, col 7: level=-3.000row 12, col 8: level=1.000row 12, col 9: level=3.000row 12, col 10: level=0.000row 12, col 11: level=3.000row 12, col 12: level=6.000row 12, col 13: level=4.000row 12, col 14: level=1.000row 12, col 15: level=-4.000row 13, col 0: level=-4.000row 13, col 1: level=-5.000row 13, col 2: level=-5.000row 13, col 3: level=2.000row 13, col 4: level=4.000row 13, col 5: level=3.000row 13, col 6: level=3.000row 13, col 7: level=-2.000row 13, col 8: level=-4.000row 13, col 9: level=5.000row 13, col 10: level=1.000row 13, col 11: level=3.000row 13, col 12: level=0.000row 13, col 13: level=5.000row 13, col 14: level=-1.000row 13, col 15: level=0.000row 14, col 0: level=0.000row 14, col 1: level=-3.000row 14, col 2: level=1.000row 14, col 3: level=4.000row 14, col 4: level=1.000row 14, col 5: level=5.000row 14, col 6: level=3.000row 14, col 7: level=2.000row 14, col 8: level=-2.000row 14, col 9: level=0.000row 14, col 10: level=-3.000row 14, col 11: level=-4.000row 14, col 12: level=-4.000row 14, col 13: level=-3.000row 14, col 14: level=4.000row 14, col 15: level=5.000row 15, col 0: level=-2.000row 15, col 1: level=-3.000row 15, col 2: level=3.000row 15, col 3: level=5.000row 15, col 4: level=-3.000row 15, col 5: level=-2.000row 15, col 6: level=4.000row 15, col 7: level=1.000row 15, col 8: level=-3.000row 15, col 9: level=0.000row 15, col 10: level=0.000row 15, col 11: level=5.000row 15, col 12: level=-3.000row 15, col 13: level=-2.000row 15, col 14: level=5.000row 15, col 15: level=-1.000
−max+max — hover any cell for its exact value
03

Wire the crossbar

In plain words: lay the resistors out in a grid. Voltages go in along the rows; the currents add themselves up down the columns — and that sum is the answer.

Simplified to 4 inputs × 1 differential output pair below (the real circuit is 16×16×2 = 512 resistors per matrix). Row voltages are the token's embedding; column buses are held at a virtual ground by an ideal 0V "ammeter" source — exactly what a real transimpedance readout amplifier does. That virtual ground is what makes each resistor see a clean Vrow − 0, so Ohm's law applies with no cross-talk between rows.

Vrow₀+0.18 VVrow₁-3.25 VVrow₂-0.90 VVrow₃+0.68 VAcol_pos (0 V)Acol_neg (0 V)
04

Simulate & compare

In plain words: run the actual circuit in a physics simulator and check its currents against the intended math. If they match, the design is wired right — this is the whole point of the report.

Three independent test cases — different weight matrix, different digit, different voltage scale — run through ngspice. Each column's current is compared against the same computation done in pure Python (Ohm's law over the identical quantized resistor values). Perfect agreement here means the wiring and sign convention are correct; any real gap would show up as points off the diagonal.

-150-150-75-75007575150150225225 W_V, digit 7, output 0: software -176.12 µA · SPICE -176.12 µA · rel. error 0.00005%W_V, digit 7, output 1: software -170.05 µA · SPICE -170.05 µA · rel. error 0.00001%W_V, digit 7, output 2: software 136.25 µA · SPICE 136.25 µA · rel. error 0.00003%W_V, digit 7, output 3: software 382.66 µA · SPICE 382.66 µA · rel. error 0.00014%W_V, digit 7, output 4: software 182.79 µA · SPICE 182.79 µA · rel. error 0.00020%W_V, digit 7, output 5: software -233.43 µA · SPICE -233.43 µA · rel. error 0.00001%W_V, digit 7, output 6: software -20.85 µA · SPICE -20.85 µA · rel. error 0.00018%W_V, digit 7, output 7: software -120.97 µA · SPICE -120.97 µA · rel. error 0.00003%W_V, digit 7, output 8: software 457.26 µA · SPICE 457.26 µA · rel. error 0.00004%W_V, digit 7, output 9: software -456.57 µA · SPICE -456.57 µA · rel. error 0.00007%W_V, digit 7, output 10: software 702.71 µA · SPICE 702.71 µA · rel. error 0.00003%W_V, digit 7, output 11: software 245.28 µA · SPICE 245.28 µA · rel. error 0.00002%W_V, digit 7, output 12: software -378.16 µA · SPICE -378.16 µA · rel. error 0.00001%W_V, digit 7, output 13: software 426.58 µA · SPICE 426.58 µA · rel. error 0.00005%W_V, digit 7, output 14: software 376.13 µA · SPICE 376.13 µA · rel. error 0.00001%W_V, digit 7, output 15: software 81.96 µA · SPICE 81.96 µA · rel. error 0.00006%W_Q, digit 3, output 0: software -260.35 µA · SPICE -260.35 µA · rel. error 0.00002%W_Q, digit 3, output 1: software 399.30 µA · SPICE 399.30 µA · rel. error 0.00001%W_Q, digit 3, output 2: software -554.25 µA · SPICE -554.25 µA · rel. error 0.00004%W_Q, digit 3, output 3: software -471.63 µA · SPICE -471.63 µA · rel. error 0.00001%W_Q, digit 3, output 4: software 54.25 µA · SPICE 54.25 µA · rel. error 0.00013%W_Q, digit 3, output 5: software 75.45 µA · SPICE 75.45 µA · rel. error 0.00001%W_Q, digit 3, output 6: software -301.15 µA · SPICE -301.15 µA · rel. error 0.00002%W_Q, digit 3, output 7: software -228.25 µA · SPICE -228.25 µA · rel. error 0.00001%W_Q, digit 3, output 8: software 31.74 µA · SPICE 31.74 µA · rel. error 0.00015%W_Q, digit 3, output 9: software 39.56 µA · SPICE 39.56 µA · rel. error 0.00009%W_Q, digit 3, output 10: software -426.66 µA · SPICE -426.66 µA · rel. error 0.00000%W_Q, digit 3, output 11: software -286.52 µA · SPICE -286.52 µA · rel. error 0.00002%W_Q, digit 3, output 12: software 242.73 µA · SPICE 242.73 µA · rel. error 0.00003%W_Q, digit 3, output 13: software -186.73 µA · SPICE -186.73 µA · rel. error 0.00003%W_Q, digit 3, output 14: software -326.58 µA · SPICE -326.58 µA · rel. error 0.00000%W_Q, digit 3, output 15: software -427.51 µA · SPICE -427.50 µA · rel. error 0.00006%W_K, digit 9, output 0: software 170.41 µA · SPICE 170.41 µA · rel. error 0.00003%W_K, digit 9, output 1: software 56.37 µA · SPICE 56.37 µA · rel. error 0.00008%W_K, digit 9, output 2: software 132.51 µA · SPICE 132.51 µA · rel. error 0.00003%W_K, digit 9, output 3: software 28.59 µA · SPICE 28.59 µA · rel. error 0.00012%W_K, digit 9, output 4: software 48.76 µA · SPICE 48.76 µA · rel. error 0.00023%W_K, digit 9, output 5: software 198.94 µA · SPICE 198.94 µA · rel. error 0.00004%W_K, digit 9, output 6: software 56.51 µA · SPICE 56.51 µA · rel. error 0.00008%W_K, digit 9, output 7: software 130.47 µA · SPICE 130.47 µA · rel. error 0.00007%W_K, digit 9, output 8: software 212.86 µA · SPICE 212.86 µA · rel. error 0.00020%W_K, digit 9, output 9: software -88.99 µA · SPICE -88.99 µA · rel. error 0.00002%W_K, digit 9, output 10: software 48.20 µA · SPICE 48.20 µA · rel. error 0.00009%W_K, digit 9, output 11: software 104.05 µA · SPICE 104.05 µA · rel. error 0.00016%W_K, digit 9, output 12: software -226.20 µA · SPICE -226.20 µA · rel. error 0.00013%W_K, digit 9, output 13: software -112.69 µA · SPICE -112.69 µA · rel. error 0.00020%W_K, digit 9, output 14: software 27.05 µA · SPICE 27.05 µA · rel. error 0.00002%W_K, digit 9, output 15: software -2.19 µA · SPICE -2.19 µA · rel. error 0.00105%
W_V, digit 7 W_Q, digit 3 W_K, digit 9 x = software (µA) · y = SPICE (µA) · dashed line = perfect agreement
Show all 48 measurements
Test caseOutputSoftware (µA)SPICE (µA)Rel. error
W_V, digit 70-176.1247-176.12460.000046%
W_V, digit 71-170.0525-170.05250.000011%
W_V, digit 72136.2471136.24710.000029%
W_V, digit 73382.6580382.65850.000139%
W_V, digit 74182.7893182.78970.000198%
W_V, digit 75-233.4327-233.43270.000015%
W_V, digit 76-20.8471-20.84710.000176%
W_V, digit 77-120.9748-120.97490.000027%
W_V, digit 78457.2581457.25790.000040%
W_V, digit 79-456.5711-456.57140.000074%
W_V, digit 710702.7062702.70600.000029%
W_V, digit 711245.2785245.27850.000019%
W_V, digit 712-378.1610-378.16110.000014%
W_V, digit 713426.5824426.58220.000052%
W_V, digit 714376.1321376.13210.000008%
W_V, digit 71581.963481.96340.000062%
W_Q, digit 30-260.3474-260.34740.000017%
W_Q, digit 31399.3021399.30210.000010%
W_Q, digit 32-554.2485-554.24870.000041%
W_Q, digit 33-471.6265-471.62660.000011%
W_Q, digit 3454.248954.24880.000134%
W_Q, digit 3575.447875.44780.000013%
W_Q, digit 36-301.1542-301.15430.000018%
W_Q, digit 37-228.2508-228.25080.000005%
W_Q, digit 3831.737231.73720.000146%
W_Q, digit 3939.560239.56020.000087%
W_Q, digit 310-426.6597-426.65970.000003%
W_Q, digit 311-286.5242-286.52430.000019%
W_Q, digit 312242.7325242.73240.000034%
W_Q, digit 313-186.7335-186.73350.000032%
W_Q, digit 314-326.5810-326.58100.000003%
W_Q, digit 315-427.5053-427.50500.000059%
W_K, digit 90170.4072170.40720.000029%
W_K, digit 9156.365756.36570.000078%
W_K, digit 92132.5102132.51020.000033%
W_K, digit 9328.591028.59100.000121%
W_K, digit 9448.760448.76030.000233%
W_K, digit 95198.9439198.94400.000035%
W_K, digit 9656.514256.51420.000080%
W_K, digit 97130.4698130.46990.000073%
W_K, digit 98212.8567212.85630.000199%
W_K, digit 99-88.9881-88.98810.000015%
W_K, digit 91048.203348.20340.000090%
W_K, digit 911104.0495104.04930.000156%
W_K, digit 912-226.1987-226.19840.000128%
W_K, digit 913-112.6887-112.68850.000198%
W_K, digit 91427.047527.04750.000018%
W_K, digit 915-2.1891-2.18910.001054%

How it runs

The whole pipeline is a handful of scripts — train, quantize to resistor values, emit a raw SPICE netlist, simulate, and diff the currents against a pure-software Ohm's-law model. The core of the recipe:

# emit a SPICE netlist for a weight matrix + input digit, then simulate python3 gen_netlist.py W_V 7 1.0 # → W_V_digit7.cir (512 resistors) ngspice -b W_V_digit7.cir # solve; print each column's current # or open the same netlist in xschem to inspect/redraw the schematic xschem W_V_digit7.cir
05

Tiny Tapeout

Not started · parked

Real silicon, real money

Today's resistors are ideal SPICE elements — proving the math, not yet fabrication-ready. A real shuttle submission needs Sky130 physical resistor primitives (their achievable range, sizing, and matching tolerance all differ from an ideal 10k–100kΩ behavioral model), a GDSII export, and a paid submission (~$150–300) on a months-long shuttle schedule. That leg stays parked until the design clearly earns a fabrication slot.

See it working

This report is the physics check behind a live demo: run the calculator at the Lab — the same architecture and resistor physics, retrained for all four operations. Re-running this report's ngspice check on that calculator's own WV crossbar gives 0.0004% worst-case error across seven token inputs.