ESP32-S3 reset-held batching
The original halted-secondary optimization widened a machine window without preserving per-instruction peripheral and scheduler visibility. Real Arduino firmware stopped booting, so that implementation was retired.
The replacement distinguishes three secondary-core states with one hot-path query: active, WAITI-parked, and reset-held. While APP CPU is reset-held, PRO CPU instructions share one host-side planning/commit window, but each retired instruction still:
- publishes the live cycle;
- ticks peripherals;
- drains scheduler events;
- checks reset requests; and
- checks for APP CPU release, steps APP on that exact boundary, and ends the coalesced window immediately.
The loop uses step_batch(..., 1) and the returned retired count, so zero
progress neither consumes fuel nor charges simulated time. CoreProgress
explicitly records internally committed cycles, avoiding the double/lost cycle
accounting that invalidated the earlier generalized interval-one attempt.
Callgrind A/B
Measured with scripts/perf/board_perf.py and the ESP32-S3 spin fixture:
| path | before (Ir/step) | after (Ir/step) | steps/batch |
|---|---|---|---|
| ESP32-S3 batch | 815.7 | 364.8 | 1023.9 |
| ESP32-S3-Zero batch | 815.7 | 364.8 | 1023.9 |
That is 55.3% less host work per simulated instruction, or 2.24x throughput at the same instruction mix. The reference step path remains separately gated.
Correctness gates
- all
machine_advanceunit tests, including explicit reset-held zero-progress and elapsed-cycle cases; e2e_esp32s3_flash_boot_no_elfbare-IDF flash boot; and- the same test's dual-core Arduino/FreeRTOS wide-batch boot, which detects the scheduler corruption caused by the retired implementation.