Repository navigation
One AVR emulator on every platform, I2C reads, and eight sensor parts - #5
Merged
Merged
Conversation
Every read on the bus used to return 0xFF, so no I2C sensor could be modelled: a part could only receive what the sketch wrote. A read has to be answered mid-frame, while the CPU clocks it, and part logic only runs between frames. So a part now publishes registers and the bus answers from them, which is how I2C sensors are built: - context.i2c.serve(address, size:, pointerBytes:) declares a bank and claims the address. The first pointerBytes of a write set the register pointer (MSB first); reads return the register under it and advance, wrapping. pointerBytes: 0 is for devices with nothing to select, like a PCF8574, whose reads start at 0. - context.i2c.setRegisters(address, offset, bytes) publishes readings. - Bytes the sketch writes after the pointer are stored, so a register reads back what the sketch set, and still arrive through drain(). - Serving the same shape again keeps registers and pointer, so a part can declare itself every frame. The register logic lives in I2cRecorder (native) and is mirrored in web/avr_bridge.js, so a read never crosses into Dart on either platform. An address nothing serves still reads 0xFF and is NACKed. Tested at three levels: I2cRecorder unit tests for each rule; the same scenarios run against the JS copy in Node (not checked in); and a real Wire sketch (test/fixtures/i2c_read), compiled with arduino-cli, that reads one register, six in a row after a repeated start, reads back a write, and is refused at an absent address. PDL.md's context.i2c section documents the API. Also drops the empty `()` the Dart 3.13 migration left on PartI2cApi and I2cRecorder, the last two. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AVRBridge.tick(cycles) looped `cycles` times over avrInstruction, so it ran that many instructions. Most AVR instructions take one cycle but branches, calls and memory access take two to four, so a frame asked for 256,000 cycles ran about 345,000 and sketches ran roughly 30% fast on desktop: delay(1000) lasted about 0.77 s. The web bridge already looped until cpu.cycles reached the target; native now does the same. avr_tick_test pins it: each frame advances the requested cycles, give or take the last instruction. It fails on the old loop (frame 0 ran 345,030 cycles). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The web used avr8js from the unpkg CDN, so the desktop and the web ran two different emulators and every feature was written twice: the I2C reads in web/avr_bridge.js had to mirror I2cRecorder by hand. Now web/avr_bridge.js is the native AVRBridge itself, compiled by dart2js and exported as window.AVR8 (web_bridge/avr_bridge_main.dart), so both platforms run the same code and the CDN dependency is gone. dart2js rather than calling the bridge from the app: the app ships as WebAssembly, and dart2wasm runs this loop at about half the speed (2.1x real time against 4.4x on the OLED firmware). To match avr8js's speed, tick() reads its statics into locals (a lazily initialised static costs a check on every access in dart2js) and samples PWM duty from the port registers instead of 14 pinState calls; the masks agree with pinState on every template (4.7M samples, 0 differences). Measured warm in Chrome 154: 2.71x real time against avr8js's 2.56x. The bridge no-ops before a sketch is loaded, as the JS one did, and no longer logs from tick (sim_log pulls in Flutter-side code). tools/build_avr_bridge.sh builds it against the app's committed lockfile; CI checks the committed file is current and runs tools/test_avr_bridge.mjs against it (I2C reads, cycle-accurate ticks, blink timing, real-time speed). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The web called a separate JavaScript build of the emulator (a dart2js build of AVRBridge, before that avr8js) because dart2wasm ran avr8_dart 0.1.0 at half dart2js's speed. That was the package, not the compiler: avr8_dart 0.2.0 removes the late fields and the per-instruction decode chain, and dart2wasm now runs it as fast as dart2js (4.70x against 4.56x real time over five templates, identical final state), steady from the first frame. So the web app, which already ships as WebAssembly, calls AVRBridge directly, like desktop. Gone: web/avr_bridge.js, its build and test scripts, the CI step, the window.AVR8 facade and the conditional export; avr_interop_io.dart becomes avr_interop.dart, the one implementation. The bridge now drops the CR of CRLF itself, which only the web facade did, so both platforms print the same lines. avr8_dart 0.2.0 is not on pub.dev yet, so it is pinned to its commit on GitHub (00d14d2, branch perf/hot-path) in the app and in pinbench_sim. Checked: analysis, 453 app tests and every package suite, a Wasm web build, and Blink running in Chrome with no avr_bridge.js or window.AVR8. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The repository moved from burhankhanzada/avr8_dart to PinBench/avr8_dart. Pinned to db48705, the perf/hot-path head: 0.2.0 plus its metadata pointing at the new home. Still not on pub.dev. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The repository moved from burhankhanzada/ngspice_dart to PinBench/ngspice_dart; same commit (1061e5f, its main). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Three things a sensor part needs from the engine: - Parts ran after the CPU in every frame, so on the first frame setup() probed the bus before any I2C device had served its registers. The address was NACKed and Adafruit_MPU6050.begin() and friends report "chip not found" for good. Parts now get one pass before the first instruction, with the analog model treated as unsolved, since nothing has been solved yet. - The evaluator seeded a .pdl part's STATE from the node's properties, which still hold where the last run ended. A new run resumed from there (a PIR still holding) although PDL.md says STATE is recreated every run. Declared STATE keys are now left out of what seeds it. - The same STATE was written into the saved .cdl, dirtying the file after every run. The writer now skips declared STATE like the runtime flags. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every CP set V, so signed branches after a compare went the wrong way and Serial.print() of any float printed "inf". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- BH1750 light, MPU-6050 accelerometer and gyro, DS1307 real-time clock and AHT20 temperature and humidity: LOGIC parts that serve the registers a sketch reads (logic/i2c_sensors.dart). Each answers only when powered and wired to A4/A5; address jumpers (ADDR, AD0) are read from the wiring. The MPU-6050 boots asleep and clears its own reset bit; the DS1307 keeps simulated time and can start unset; the AHT20 stands in for the DHT, which needs a part that can drive a pin. - TMP36, LM35 and a capacitive soil probe: voltage sources read by analogRead. A 9 V battery. Tested per part against a register bus, and end to end: a sketch built on Adafruit MPU6050, BH1750, RTClib and Adafruit AHTX0, plus two analogReads, runs on the emulator against the placed parts (test/fixtures/sensors). Also points the pinbench_pdl submodule at the doc listing them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Both PRs were squash-merged (merge commits are disabled on those repos), so the branch commits pinned here would be orphaned once their branches were deleted. Same content: avr8_dart 183c19a/ac16ee0 -> 70f4415 (#1), pdl 59951b8 -> 714d715 (#2). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Member
Author
|
PinBench/avr8_dart#1 and PinBench/pdl#2 are merged. Both repos only allow squash merges, so |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Everything on
feat/web-sim-workersincemain, in four groups.Depends on PinBench/avr8_dart#1 (the app pins its
183c19a) and PinBench/pdl#2 (thepinbench_pdlsubmodule points at59951b8). Merge those first, with merge commits.1. I²C devices can answer reads (
43ad684)Every read on the bus used to return
0xFF, so no I²C sensor could be modelled. A part nowserves a register bank and keeps it current withsetRegisters. The bus answers the sketch mid-frame: pointer first, auto-incrementing, and writes are stored so they read back. An address nothing serves is still NACKed.2. The same emulator on desktop and web (
6b915c4,90009d3,b956a37,8cbfeea,9748796)AVRBridge.tick(cycles)counted instructions, not cycles, sodelay(1000)lasted about 0.77 s.avr_tick_testpins the fix.AVRBridgedirectly, like desktop.web/avr_bridge.js, its build and test scripts, the CI step andwindow.AVR8are gone.avr8_dartandngspice_dartare fetched from their new home in the PinBench org.3. Engine fixes the sensors needed (
2851d5c,6286fad)setup(). Parts used to run after the CPU in every frame. So on the first framesetup()probed the bus before any device had served its registers, andAdafruit_MPU6050.begin()and friends reported "chip not found" for good. Parts now get one pass before the first instruction. That pass treats the analog model as unsolved, since nothing has been solved yet..pdlSTATE starts fresh every run, as PDL.md promises. It used to resume from the last run's final values left on the canvas; a PIR, for example, was still holding. Those values are also no longer written into saved.cdlfiles.CP, soSerial.print(1.5)printedinfin every sketch. Fixed there (0.2.0: decode opcodes once, move to the PinBench org, fix CP's overflow flag avr8_dart#1); this pins the fix.4. Eight new parts (
a196059,a75cf6b)LOGIC. ADDR selects 0x23/0x5CLOGIC. Boots asleep, clears its own reset bit, scales to the sketch's range. AD0 selects 0x68/0x69LOGIC. Keeps simulated time; starts at the host's time or unset. Needs 5 VLOGIC. Stands in for the DHT, which needs a part that can drive a pinanalogReadAn I²C sensor answers only when its VCC and GND reach the board and SDA/SCL reach A4/A5. So a missing supply or swapped SDA/SCL fails the way it does on a desk.
How it was verified
dart analyze --fatal-infos: cleandart format: no changes in the files this PR touchesflutter test: 453 app tests pass (7 skipped);pinbench_parts137,pinbench_sim80New tests:
i2c_sensors_test.dart: each sensor's registers, frame by frame, against a register bus.sensor_sketches_test.dart: a sketch built on Adafruit MPU6050, BH1750, RTClib and Adafruit AHTX0, plus twoanalogReads (test/fixtures/sensors/), runs on the emulator against the placed parts wired to the Uno. Every library initialises and every reading comes back exact..cdlwriter. Each fails without its fix.If you changed a painter
No painters changed. The new parts are
.pdl+ SVG.pdl_bundled_parts_testchecks that every pin is on the lattice and that every part loads with zero diagnostics.Notes for the reviewer
These engine gaps, found while doing this and recorded in the handbook's architecture README, kept some parts out of this PR:
5V/3.3Vpins aren't SPICE sources. So resistive sensors fed from 5V (FSR, tilt, reed) wouldn't respond; runtimealteron resistors is also rejected, as already known.The roadmap's next parts (HC-SR04, DHT, RGB LED, relay, transistor) need multi-element parts or parts that drive a pin first.
🤖 Generated with Claude Code