velxio/docs/wiki/circuit-emulation-appendix.md

329 lines
10 KiB
Markdown
Raw Normal View History

feat: electrical simulation via ngspice-WASM (eecircuit-engine) Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy- loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate with real analog behaviour: correct voltages on wires, real I–V curves on LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM driving RC filters, transistors, op-amps, diodes, MOSFETs, etc. Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false. Frontend additions: - simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with UnionFind over wires, componentToSpice mapping (24 metadataIds incl. real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148, 1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with debounced coalescing, AVRSpiceBridge for quasi-static co-simulation. - store/useElectricalStore: Zustand slice, feature-flag aware. - components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay. - components/components-instruments/: Voltmeter, Ammeter probes. - 62 tests (spice-*, netlist-builder, component-to-spice, instruments). Sandbox (test/test_circuit/): 47-test validation sandbox that proved the approach (hand-rolled MNA baseline + ngspice pipeline) before porting to the app. Kept as reference. Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering architecture, solvers, components, AVR bridge, gotchas, performance, integration plan, API reference, appendix) + electrical-simulation- user-guide.md (end-user facing). Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
# Appendix — Netlists, Opcodes, Model Parameters, Glossary
## A. Reference netlists (one per analysis type)
### A.1 `.op` — DC operating point
```spice
Voltage divider DC op-point
V1 vcc 0 DC 9
R1 vcc out 1k
R2 out 0 2k
.op
.end
```
### A.2 `.tran` — transient analysis
```spice
RC charging
V1 vcc 0 PULSE(0 5 0 1n 1n 10 20)
R1 vcc out 10k
C1 out 0 100u IC=0
.ic v(out)=0
.tran 10m 3
.end
```
### A.3 `.ac` — AC small-signal sweep
```spice
RC low-pass AC sweep
V1 in 0 AC 1
R1 in out 1k
C1 out 0 159.155n
.ac dec 20 10 1Meg
.end
```
### A.4 `.dc` — DC sweep
```spice
Diode I-V curve
V1 a 0 DC 0
D1 a 0 DMOD
.model DMOD D(Is=1e-14 N=1)
.dc V1 0 1 0.01
.end
```
### A.5 Behavioral logic gates (B-source)
```spice
NAND gate
Va a 0 DC 5
Vb b 0 DC 5
Bnand y 0 V = 5 * (1 - u(V(a)-2.5) * u(V(b)-2.5))
Rload y 0 1k
.op
.end
```
### A.6 Schmitt-switch relaxation oscillator (simplified 555)
```spice
Relaxation oscillator
Vcc vcc 0 DC 5
R1 vcc cap 10k
Ccap cap 0 10n IC=0
Sdis cap 0 cap 0 SMOD
.model SMOD SW(Vt=2.5 Vh=0.833 Ron=100 Roff=1G)
Sbuf out vcc cap 0 SOUT
.model SOUT SW(Vt=2.5 Vh=0.833 Ron=10 Roff=1G)
Rpd out 0 100k
.tran 0.5u 2m
.end
```
### A.7 Bridge rectifier with sine source
```spice
Full-wave bridge rectifier
V1 a b SIN(0 6 50)
D1 a p DMOD
D2 b p DMOD
D3 n a DMOD
D4 n b DMOD
R1 p n 1k
.model DMOD D(Is=1e-14 N=1)
.tran 0.1m 40m
.end
```
### A.8 Common-emitter BJT amplifier
```spice
Common-emitter with 2N2222
Vcc vcc 0 DC 12
Vin in 0 SIN(0 0.01 1k)
Cin in b 1u
RB1 vcc b 47k
RB2 b 0 10k
RC vcc c 4.7k
RE e 0 1k
CE e 0 100u
Q1 c b e Q2N2222
Cout c out 1u
Rout out 0 100k
.model Q2N2222 NPN(Is=1e-14 Bf=200 Vaf=75)
.tran 10u 6m
.end
```
### A.9 Ideal op-amp (VCVS) as inverting amplifier
```spice
Op-amp inverting amplifier, gain -10
Vin in 0 DC 0.2
Rin in n 1k
Rf n out 10k
Eopa out 0 0 n 1e6
.op
.end
```
### A.10 N-MOSFET switch
```spice
N-MOS switch
Vcc vcc 0 DC 5
Vgate gate 0 DC 5
RL vcc drain 1k
M1 drain gate 0 0 NMOS_L1 L=1u W=100u
.model NMOS_L1 NMOS(Level=1 Vto=1.0 Kp=50u Lambda=0.01)
.op
.end
```
## B. AVR opcode encodings (the ones we use)
| Instruction | Encoding (bits 15..0) | Bytes | Notes |
|---|---|---|---|
| `LDI Rd, K` | `1110 KKKK dddd KKKK` | 2 | `d = Rd 16`, `Rd ∈ [16..31]` |
| `OUT A, Rr` | `1011 1AAr rrrr AAAA` | 2 | `A ∈ [0..63]` (I/O space) |
| `IN Rd, A` | `1011 0AAd dddd AAAA` | 2 | |
| `STS k, Rr` | `1001 001r rrrr 0000` + `kkkkkkkk kkkkkkkk` | 4 | 32-bit instruction, `k` is 16-bit address |
| `LDS Rd, k` | `1001 000d dddd 0000` + `kkkkkkkk kkkkkkkk` | 4 | 32-bit |
| `RJMP k` | `1100 kkkk kkkk kkkk` | 2 | signed 12-bit word offset from PC+1 |
| `SBRC Rr, b` | `1111 110r rrrr 0bbb` | 2 | skip if bit clear |
| `SBRS Rr, b` | `1111 111r rrrr 0bbb` | 2 | skip if bit set |
| `NOP` | `0000 0000 0000 0000` | 2 | |
| `CALL k` | `1001 010k kkkk 111k` + `kkkkkkkk kkkkkkkk` | 4 | not used here |
| `RET` | `1001 0101 0000 1000` | 2 | not used here |
## C. ATmega328P register addresses (used in our programs)
| Register | I/O addr | SRAM addr | Purpose |
|---|---|---|---|
| DDRB | 0x04 | 0x24 | Port B data direction |
| PORTB | 0x05 | 0x25 | Port B output |
| PINB | 0x03 | 0x23 | Port B input |
| DDRC | 0x07 | 0x27 | Port C data direction |
| PORTC | 0x08 | 0x28 | Port C output |
| DDRD | 0x0A | 0x2A | Port D data direction |
| PORTD | 0x0B | 0x2B | Port D output |
| TCCR0A | 0x24 | 0x44 | Timer0 control A |
| TCCR0B | 0x25 | 0x45 | Timer0 control B |
| TCNT0 | 0x26 | 0x46 | Timer0 counter |
| OCR0A | 0x27 | 0x47 | Timer0 compare A (PWM D6) |
| OCR0B | 0x28 | 0x48 | Timer0 compare B (PWM D5) |
| OCR2A | — | 0xB3 | Timer2 compare A (PWM D11) |
| OCR2B | — | 0xB4 | Timer2 compare B (PWM D3) |
| TCCR1A | — | 0x80 | Timer1 control A |
| TCCR1B | — | 0x81 | Timer1 control B |
| OCR1AL | — | 0x88 | Timer1A compare low (PWM D9) |
| OCR1AH | — | 0x89 | Timer1A compare high |
| OCR1BL | — | 0x8A | Timer1B compare low (PWM D10) |
| ADCL | — | 0x78 | ADC result low byte |
| ADCH | — | 0x79 | ADC result high byte |
| ADCSRA | — | 0x7A | ADC control and status A |
| ADMUX | — | 0x7C | ADC multiplexer |
Registers above 0x5F can only be accessed via `STS` / `LDS` (32-bit instructions), not `OUT` / `IN`.
## D. ADMUX / ADCSRA bit fields
### ADMUX (0x7C)
| Bit | Name | Purpose |
|---|---|---|
| 7 | REFS1 | Voltage reference selection bit 1 |
| 6 | REFS0 | Voltage reference selection bit 0 — `01` = AVCC with cap at AREF |
| 5 | ADLAR | ADC Left Adjust Result — `0` = right, `1` = left |
| 4 | — | reserved |
| 3..0 | MUX3..0 | Channel select — `0000` = ADC0 (A0), `0001` = A1, … |
Common values:
- `0x40` = AVCC ref, right-adjusted, A0
- `0x60` = AVCC ref, **left-adjusted**, A0 (we use this!)
- `0xC0` = Internal 1.1V ref, right-adjusted, A0
### ADCSRA (0x7A)
| Bit | Name | Purpose |
|---|---|---|
| 7 | ADEN | ADC Enable |
| 6 | ADSC | ADC Start Conversion — write 1 to start; auto-clears when done |
| 5 | ADATE | Auto Trigger Enable |
| 4 | ADIF | ADC Interrupt Flag |
| 3 | ADIE | ADC Interrupt Enable |
| 2..0 | ADPS2..0 | Prescaler Select — `111` = /128 (= 125 kHz ADC clock at 16 MHz) |
Common values:
- `0x87` = ADEN + prescaler /128 (initial setup)
- `0xC7` = ADEN + **ADSC** + /128 (start conversion)
### Conversion timing
- First conversion: 25 ADC clock cycles
- Subsequent: 13 ADC clock cycles
- At 125 kHz ADC clock: first ≈ 200 µs, subsequent ≈ 104 µs
## E. TCCR0A / TCCR0B bit fields
### TCCR0A (0x24, I/O)
| Bit | Purpose |
|---|---|
| 7 | COM0A1 — 1 for non-inverting PWM on OC0A |
| 6 | COM0A0 |
| 5 | COM0B1 |
| 4 | COM0B0 |
| 3..2 | — |
| 1 | WGM01 — Waveform Generation Mode bit 1 |
| 0 | WGM00 — bit 0 |
### TCCR0B (0x25, I/O)
| Bit | Purpose |
|---|---|
| 7 | FOC0A / FOC0B — force compare |
| 5..4 | — |
| 3 | WGM02 |
| 2..0 | CS02..0 — clock source |
**Mode 3: Fast PWM 8-bit** — `WGM02:0 = 011`. Counter counts 0..255, TOP = 0xFF.
Settings we use (for the `potToPwmProgram`):
- `TCCR0A = 0x83` = COM0A1=1 + WGM01=1 + WGM00=1 → non-inverting Fast PWM on D6
- `TCCR0B = 0x01` = CS00=1 → no prescaler (16 MHz counter → 62.5 kHz PWM frequency)
## F. LED model parameters (tuned for typical datasheet Vf @ 10 mA)
```javascript
const LED_PARAMS = {
red: { Is: 1e-20, n: 1.7, ratedCurrent: 0.020 }, // V_f ≈ 2.0 V
green: { Is: 1e-22, n: 1.9, ratedCurrent: 0.020 }, // V_f ≈ 2.2 V
yellow: { Is: 1e-21, n: 1.8, ratedCurrent: 0.020 }, // V_f ≈ 2.1 V
blue: { Is: 1e-28, n: 2.0, ratedCurrent: 0.020 }, // V_f ≈ 3.1 V
white: { Is: 1e-28, n: 2.0, ratedCurrent: 0.020 },
};
```
Shockley: `I = Is · (exp(V / (n · Vt)) 1)`, with `Vt = 0.02585 V` at 300 K.
To tune another color: pick a datasheet point `(V_f, I)` and solve for `Is`:
```
Is = I · exp(V / (n · Vt))
```
## G. NTC β-model
```javascript
R(T) = R0 · exp(β · (1/T 1/T0)) // T in Kelvin
```
Common 10 kΩ NTC (β=3950, T₀=25 °C = 298.15 K):
| T (°C) | T (K) | R (kΩ) | V_A0 @ 10k pullup to 5V |
|---|---|---|---|
| 20 | 253.15 | 95.4 | 4.52 V |
| 0 | 273.15 | 33.6 | 3.85 V |
| 25 | 298.15 | 10.0 | 2.50 V |
| 50 | 323.15 | 3.59 | 1.32 V |
| 100 | 373.15 | 0.68 | 0.32 V |
Inverse (recovering T from measured R):
```
T = 1 / (1/T0 + (1/β) · ln(R / R0))
```
This is what `e2e_thermistor.test.js` does to convert ADC readings back to Celsius.
## H. Glossary
- **MNA (Modified Nodal Analysis)** — SPICE's core algorithm. Unknowns are node voltages plus branch currents of voltage sources.
- **Companion model** — linearization of a reactive element (C, L) into an equivalent resistor + source at each timestep.
- **Backward Euler** — simple first-order implicit integration, unconditionally stable but damps high-frequency content.
- **Trapezoidal integration** — second-order, SPICE's default; more accurate but can ring.
- **Newton-Raphson** — iterative method for non-linear equations. Converges quadratically near a solution.
- **`pnjlim`** — SPICE's internal voltage limiter for PN junctions; prevents `exp()` overflow on early iterations.
- **GMIN** — minimum conductance added to every node; prevents singular matrices.
- **Shockley equation** — `I = Is · (exp(V/(nVt)) 1)`. Governs ideal pn junctions.
- **Ebers-Moll** — simplified BJT model; predecessor to the more accurate Gummel-Poon.
- **Schmitt trigger** — a comparator with hysteresis; outputs digital clean levels from noisy analog inputs.
- **XSPICE** — ngspice extension that adds digital primitives (AND, OR, FF, etc.) as first-class elements. Not compiled into the current `eecircuit-engine` WASM.
- **B-source** — ngspice behavioral voltage/current source: `B1 n+ n- V = expression`.
- **S-element** — ngspice voltage-controlled switch with optional hysteresis.
- **VCVS/VCCS/CCVS/CCCS** — voltage-controlled-voltage / voltage-controlled-current / current-controlled-voltage / current-controlled-current sources; implement ideal op-amps, gain blocks, etc.
- **PWL** — piecewise-linear source, `PWL(t0 v0 t1 v1 …)`. Useful for arbitrary-shape stimuli.
- **Intel HEX** — ASCII file format for program memory, used by Arduino, avr-objcopy, etc.
- **PORTB / PORTC / PORTD** — 8-bit I/O registers on ATmega328P. Drive the Arduino pin sets.
- **ADLAR** — ADC Left Adjust Result. When set, the 10-bit ADC result is aligned so that ADCH has the top 8 bits.
- **OCR** — Output Compare Register. Compared against a timer counter to drive PWM.
- **Quasi-static co-simulation** — digital and analog engines advance independently on a coarse time grid, not cycle-locked.
## I. Useful external references
- ngspice manual — <https://ngspice.sourceforge.io/docs.html>
- `eecircuit-engine` on GitHub — <https://github.com/eelab-dev/EEcircuit-engine>
- EEcircuit online — <https://eecircuit.com/>
- AVR instruction set manual — <https://www.microchip.com/content/dam/mchp/documents/OTH/ProductDocuments/DataSheets/AVR-Instruction-Set-Manual.pdf>
- ATmega328P datasheet — <https://www.microchip.com/en-us/product/atmega328p>
- `avr8js` on GitHub — <https://github.com/wokwi/avr8js>
- Wokwi elements — <https://github.com/wokwi/wokwi-elements>
- SPICE model library (Linear Technology) — <https://www.analog.com/en/design-center/design-tools-and-calculators/ltspice-simulator.html>
- Gummel-Poon BJT model explanation — standard SPICE references