diff --git a/frontend/src/simulation/AVRSimulator.ts b/frontend/src/simulation/AVRSimulator.ts index d08c39c4..97c22084 100644 --- a/frontend/src/simulation/AVRSimulator.ts +++ b/frontend/src/simulation/AVRSimulator.ts @@ -295,15 +295,6 @@ const MEGA_PORT_CONFIGS = [ ]; export class AVRSimulator { - // Digital input pins are driven from the SPICE solve - // (connectDigitalInputsToMcu) for nets backed by a real source/element, so - // `digitalRead()` reflects the real wiring (a pin wired to 5V reads HIGH, a - // button to 5V reads HIGH when pressed) instead of a hardcoded part seed. - // The connector skips floating nets, so event-driven parts with no SPICE - // model (rotary encoder, keypad, dialer, dip-switch, stepper) keep driving - // their pins via the part layer. Input-control parts (button / slide-switch) - // check this flag and skip their direct seed — see BasicParts.spiceDriven(). - readonly spiceDrivenInputs = true; private cpu: CPU | null = null; /** Peripherals kept alive by reference so GC doesn't collect their CPU hooks */ private peripherals: unknown[] = []; diff --git a/frontend/src/simulation/RP2040Simulator.ts b/frontend/src/simulation/RP2040Simulator.ts index dd84a0c2..8a666b84 100644 --- a/frontend/src/simulation/RP2040Simulator.ts +++ b/frontend/src/simulation/RP2040Simulator.ts @@ -145,12 +145,6 @@ export class IdleSpinDetector { export type RP2040I2CDevice = I2CDevice; export class RP2040Simulator { - // Digital inputs are driven from the SPICE solve (connectDigitalInputsToMcu) - // for source-backed nets, so digitalRead reflects real wiring (a GP pin wired - // to a rail / button reads the right level). Floating nets are left to the - // part layer, so event-driven parts (encoder/keypad/…) keep working. Mirrors - // the AVR fix; the connector + sourcedNets gating are board-agnostic. - readonly spiceDrivenInputs = true; private rp2040: RP2040 | null = null; private running = false; private animationFrame: number | null = null; diff --git a/frontend/src/simulation/spice/CircuitSimulationService.ts b/frontend/src/simulation/spice/CircuitSimulationService.ts index 3988e51b..1fb3e8d7 100644 --- a/frontend/src/simulation/spice/CircuitSimulationService.ts +++ b/frontend/src/simulation/spice/CircuitSimulationService.ts @@ -67,9 +67,6 @@ export interface ElectricalSnapshot { timeWaveforms?: TimeWaveforms; /** Convergence warnings from the solver. */ warnings: string[]; - /** Nets backed by a real source/element (see NetlistBuilder). Gates which - * MCU input pins connectDigitalInputsToMcu may drive from the solve. */ - sourcedNets: Set; } /** What the service needs from the scheduler. */ @@ -133,7 +130,6 @@ export class CircuitSimulationService { nets: string[]; voltageSources: string[]; analysisKind: 'op' | 'tran' | 'ac'; - sourcedNets: Set; } | null = null; /** Set by `stop()`. Once true, `tick()` and `handleMcuEdge()` @@ -306,7 +302,7 @@ export class CircuitSimulationService { })), }; const input = buildInputFromStore(snap as Parameters[0]); - const { netlist, pinNetMap, nets, voltageSources, sourcedNets } = buildNetlist(input); + const { netlist, pinNetMap, nets, voltageSources } = buildNetlist(input); // Tell the scheduler exactly which vectors we want — every net // voltage + every branch current. @@ -330,7 +326,6 @@ export class CircuitSimulationService { nets, voltageSources, analysisKind: input.analysis.kind, - sourcedNets, }; this.publishFromLastResult(); } @@ -385,7 +380,6 @@ export class CircuitSimulationService { analysisMode: ctx.analysisKind, timeWaveforms, warnings: result.warnings, - sourcedNets: ctx.sourcedNets, }); } diff --git a/frontend/src/simulation/spice/NetlistBuilder.ts b/frontend/src/simulation/spice/NetlistBuilder.ts index 3e632c0b..be20a913 100644 --- a/frontend/src/simulation/spice/NetlistBuilder.ts +++ b/frontend/src/simulation/spice/NetlistBuilder.ts @@ -67,18 +67,6 @@ export interface BuildNetlistResult { * request branch currents (`i(v_)`). */ voltageSources: string[]; - /** - * Nets that are backed by a real electrical source/element — a power rail - * ('0' / 'vcc_rail'), a board GPIO V-source, an internal pull resistor, or - * any net a componentToSpice mapper emitted a card for (resistor, button - * switch, divider, etc.). Excludes purely-floating nets (which only get the - * step-7 auto-pull-down). `connectDigitalInputsToMcu` drives an MCU input pin - * from the solve ONLY when its net is in here, so event-driven parts with no - * SPICE model (rotary encoder, keypad, dialer, dip-switch, stepper) keep - * driving their own pins via the part layer instead of being forced LOW by a - * floating-net read. - */ - sourcedNets: Set; } export function buildNetlist(input: BuildNetlistInput): BuildNetlistResult { @@ -150,11 +138,6 @@ export function buildNetlist(input: BuildNetlistInput): BuildNetlistResult { const cards: string[] = []; const modelLines = new Set(); const dominantVcc = boards[0]?.vcc ?? 5; - // Nets backed by a real source/element (see BuildNetlistResult.sourcedNets). - // Rails are always sourced; component + GPIO-source + pull nets are added - // below. Deliberately NOT populated from the step-7 auto-pull-down cards, - // since those mark FLOATING nets. - const sourcedNets = new Set(['0', 'vcc_rail']); for (const comp of components) { const localLookup = (pinName: string) => netLookup(comp.id, pinName); @@ -162,11 +145,6 @@ export function buildNetlist(input: BuildNetlistInput): BuildNetlistResult { if (!emission) continue; cards.push(...emission.cards); for (const m of emission.modelsUsed) modelLines.add(m); - // Every net this component connects to now has a real SPICE element on it. - for (const pinName of pinsReferencedByWires(comp.id, wires)) { - const n = netLookup(comp.id, pinName); - if (n) sourcedNets.add(n); - } } // ── 5. Board GPIO sources ───────────────────────────────────────────────── @@ -196,7 +174,6 @@ export function buildNetlist(input: BuildNetlistInput): BuildNetlistResult { cards.push( `R_pull_${sanitizeSpiceId(board.id)}_${sanitizeSpiceId(pinName)} ${net} ${rail} 45000`, ); - sourcedNets.add(net); // weak pull to a rail → determinate idle level } continue; } @@ -204,7 +181,6 @@ export function buildNetlist(input: BuildNetlistInput): BuildNetlistResult { if (net === '0' || net === 'vcc_rail') continue; // already served const v = state.type === 'digital' ? state.v : state.duty * board.vcc; cards.push(`V_${sanitizeSpiceId(board.id)}_${sanitizeSpiceId(pinName)} ${net} 0 DC ${v}`); - sourcedNets.add(net); // board GPIO V-source drives this net (e.g. cross-board input) } } @@ -227,8 +203,6 @@ export function buildNetlist(input: BuildNetlistInput): BuildNetlistResult { const b = netLookup(w.end.componentId, w.end.pinName); if (!a || !b) continue; cards.push(`R_wire_${w.id} ${a} ${b} ${ohms}`); - sourcedNets.add(a); - sourcedNets.add(b); } // ── 7. Auto pull-downs for floating nets ───────────────────────────────── @@ -305,7 +279,6 @@ export function buildNetlist(input: BuildNetlistInput): BuildNetlistResult { pinNetMap, nets, voltageSources, - sourcedNets, }; } diff --git a/frontend/src/simulation/spice/connectDigitalInputsToMcu.ts b/frontend/src/simulation/spice/connectDigitalInputsToMcu.ts index 843a02e2..fb2effee 100644 --- a/frontend/src/simulation/spice/connectDigitalInputsToMcu.ts +++ b/frontend/src/simulation/spice/connectDigitalInputsToMcu.ts @@ -21,8 +21,6 @@ */ import { useSimulatorStore, getBoardSimulator, getBoardPinManager } from '../../store/useSimulatorStore'; import { useElectricalStore } from '../../store/useElectricalStore'; -import { isStm32BoardKind } from '../../types/board'; -import { stm32PinNameToLinear } from '../Stm32Bridge'; // 3.3 V LVCMOS thresholds with a hysteresis band so a node hovering near the // midpoint doesn't chatter. A pulled-up idle input sits at ~3.3 V and a @@ -45,7 +43,7 @@ export function connectDigitalInputsToMcu(): () => void { const lastLevel = new Map(); function injectDigitalInputs() { - const { nodeVoltages, pinNetMap, sourcedNets } = useElectricalStore.getState(); + const { nodeVoltages, pinNetMap } = useElectricalStore.getState(); const { boards } = useSimulatorStore.getState(); for (const board of boards) { const sim = getBoardSimulator(board.id) as @@ -55,23 +53,11 @@ export function connectDigitalInputsToMcu(): () => void { const pm = getBoardPinManager(board.id); const driven = pm ? pm.getOutputPins() : new Set(); const prefix = `${board.id}:`; - // STM32 names pins PA0/PC13/… and its PinManager + setPinState key on the - // linear pin (port*16+pin); every other board uses plain GPIO numbers. - const isStm32 = isStm32BoardKind(board.boardKind); for (const [key, net] of pinNetMap) { if (!key.startsWith(prefix)) continue; - const pinName = key.slice(prefix.length); - const gpio = isStm32 ? stm32PinNameToLinear(pinName) : gpioFromPinName(pinName); + const gpio = gpioFromPinName(key.slice(prefix.length)); if (gpio < 0) continue; if (driven.has(gpio)) continue; // the MCU drives this pin (digitalWrite) - // Only drive pins whose net is backed by a real source/element (rail, - // pull, button switch, divider, cross-board output, …). A net that is - // only floating (an event-driven part like a rotary encoder / keypad - // that has no SPICE model) is left to the part layer, which seeds the - // pin directly — otherwise its ~0 V floating read would force it LOW - // and fight the part. This is what makes it safe to enable - // spiceDrivenInputs on the AVR (which has many such part-driven pins). - if (!sourcedNets.has(net)) continue; const v = nodeVoltages[net]; if (v == null) continue; const stateKey = `${board.id}:${gpio}`; diff --git a/frontend/src/simulation/spice/start.ts b/frontend/src/simulation/spice/start.ts index f583bc55..0979c4ad 100644 --- a/frontend/src/simulation/spice/start.ts +++ b/frontend/src/simulation/spice/start.ts @@ -50,7 +50,6 @@ function createElectricalStorePort(): ElectricalStorePort { error: snapshot.warnings[0] ?? null, lastSolveMs: 0, submittedNetlist: '', - sourcedNets: snapshot.sourcedNets, }); }, }; diff --git a/frontend/src/store/useElectricalStore.ts b/frontend/src/store/useElectricalStore.ts index d2a7a46b..b8e47f6b 100644 --- a/frontend/src/store/useElectricalStore.ts +++ b/frontend/src/store/useElectricalStore.ts @@ -25,10 +25,6 @@ export interface ElectricalSnapshot { error: string | null; lastSolveMs: number; submittedNetlist: string; - /** Nets backed by a real source/element (rail, GPIO V-source, pull, or any - * component card). connectDigitalInputsToMcu only drives MCU input pins - * whose net is here, so floating event-part pins aren't forced LOW. */ - sourcedNets: Set; } interface ElectricalState extends ElectricalSnapshot { @@ -55,7 +51,6 @@ const EMPTY: ElectricalSnapshot = { error: null, lastSolveMs: 0, submittedNetlist: '', - sourcedNets: new Set(), }; export const useElectricalStore = create((set) => ({ diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts index 3d9be4d1..ff31dba4 100644 --- a/frontend/src/store/useSimulatorStore.ts +++ b/frontend/src/store/useSimulatorStore.ts @@ -600,12 +600,6 @@ function makePinPullHandler(boardId: string) { // minus the ESP32-only WiFi / proxy-resync machinery. ────────────────────── class Stm32BridgeShim { pinManager: PinManager; - // Digital inputs are driven from the SPICE solve (connectDigitalInputsToMcu) - // for source-backed nets — digitalRead reflects the real wiring instead of a - // part seed. The connector maps STM32 pin names (PA0, PC13) to the linear pin - // setPinState expects via stm32PinNameToLinear. Floating nets stay with the - // part layer. Mirrors the AVR / RP2040 fix. - readonly spiceDrivenInputs = true; onSerialData: ((ch: string) => void) | null = null; onPinChangeWithTime: ((pin: number, state: boolean, timeMs: number) => void) | null = null; onBaudRateChange: ((baud: number) => void) | null = null;