Skip to content

x4prosim: X3 CPU instruction costs calibrated to the ESP32-C3 - #6

Merged
prokrypt merged 3 commits into
prokrypt:x4prosimfrom
serialx:serialx/cpu-timing
Oct 9, 2026
Merged

prokrypt merged 3 commits into
prokrypt:x4prosimfrom
serialx:serialx/cpu-timing

Conversation

@serialx

@serialx serialx commented Oct 9, 2026 •

Copy link
Copy Markdown

Stacked on the panel timing branch; the three new commits are the CPU ones.

Under -icount shift=2 every guest instruction cost 4 ns, a 250 MIPS CPU with free memory. A probe firmware (tight SRAM loop, cached and cache-thrashing flash code, loads/stores, memcpy, mul/div, soft-float, calls, and the firmware's own text layout and glyph rendering) measured the real ESP32-C3 at 160 MHz against that: SRAM code runs 2.0x slower than the emulator, ROM library code 1.7x, cached flash code 2.2x, multiply 2.6x, divide 10x, and code that misses the 16 KB instruction cache 41x. Page rendering in CrossPoint sat at 2.7x.

Model

  • accel/tcg/translator.c, include/exec/translator.h: an optional per-target insn_cost hook. tb->icount and the CF_COUNT_MASK budget still count instructions; a separate icount_cost is what the icount decrementer charges, with per-instruction costs in the unwind table so exceptions and MMIO re-translation refund unexecuted ticks. Targets bound the TB length so a block's cost fits the 16-bit counter.
  • target/riscv/translate.c, cpu.c: costs as CPU properties, in nanoseconds, by the instruction's region (ROM 0x40000000, SRAM, the flash cache window 0x42000000) plus surcharges for loads, stores, mul, div and branches, times a clock factor.
  • hw/riscv/esp32c3_clk.c: the SYSTEM clock registers now drive that factor (160 MHz = 1x, 80 MHz = 2x, 10 MHz = 16x) and flush translations on a change, so the firmware's low-power mode after 3 s idle really slows it 16x, and getCpuFrequencyMhz() reads 160 instead of 80. The pre-app boot phase at 80 MHz is kept faithful (about 130 ms longer than before).
  • The X3 machine moves to -icount shift=0 (1 ns ticks) in run.sh/drive.py; defaults ROM/SRAM/flash 6/8/10 ns, store +3, div +160, branch +2. The SD bus setup terms were re-fitted on top (GPSPI 700 + 300 ns), all 30 SD probe rows still within 5 % / 15 %.

Calibration

Probe rows device vs emulator after calibration: sram_loop +3 %, memcpy +6 %, memset 0 %, mul +2 %, div -8 %, fmul -6 %, fdiv +9 %, dmul -6 %, call_ret +10 %. The flash base is set for page rendering rather than tiny cached loops, so the small cached loop, the load/store loops and the probe's layout/glyph paths read 15 to 33 % slow. Code that thrashes the cache is still 16x too fast: a static penalty cannot model that; an instruction-cache model is the next step if it ever matters. Full tables: x4prosim/sdcal/cpu.md.

Reading workload, device vs emulator (ms, median of 6 page turns)

book side prewarm bw_render display gray_lsb gray_msb gray_display cleanup total
Aesop device 32 23 588 88 86 228 102 1150
Aesop emulator, all three models 30 22 581 83 83 229 96 1126
Demian device 114 35 587 108 108 228 102 1282
Demian emulator, all three models 114 34 581 101 102 228 96 1256

Cold-open first render: Aesop 3495 vs 3575 ms, Demian 3437 vs 3486 ms. Before any of the three timing branches the emulator reported 683 and 704 ms per turn.

Error of the emulator against the device, same runs (negative = emulator faster; medians of 6 turns on one boot per side, and the device's own turn-to-turn spread is about 5 %, so differences under about 3 % are noise):

workload prewarm bw_render display gray_lsb gray_msb gray_display cleanup total
Aesop page turn -6.2% -4.3% -1.2% -6.2% -4.0% +0.4% -5.9% -2.1%
Demian page turn +0.4% -2.8% -1.0% -6.5% -5.5% +0.2% -5.9% -2.1%
Aesop cold open -14.3% -0.8% -2.0% -9.8% -9.7% +0.4% -6.9% -2.2%
Demian cold open +2.9% -3.3% -1.2% -7.9% -6.0% +0.9% -5.9% -1.4%

The residual is systematic: the emulator is a few percent fast everywhere except the panel refresh, which is exact. The grayscale passes read SD-font glyphs with a tail-heavy latency the card model represents by its median, and cleanup is a 52 KB plane write whose driver cost is still about 6 % under the device; both are property changes if they ever need closing. Aesop's cold-open prewarm is a 21 ms stage, so its -14 % is 3 ms.

Limits

  • Effective costs, not a pipeline: no cache state, branch prediction, operand-dependent divide, fetch alignment.
  • X4 Pro (xtensa) keeps plain instruction counting at shift=2.
  • Host wall time per page turn is about 2.5 s (it was 1 s untimed and 4 to 5 s with the SD model at shift=2).

🤖 Generated with Claude Code

https://claude.ai/code/session_01T57W7AZf7tgmkuDMMSaWwz

serialx and others added 3 commits October 9, 2026 17:07
Keep translation block instruction counts separate from virtual-time costs, including timer budget conversion and exception refunds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01T57W7AZf7tgmkuDMMSaWwz
Retain clock-register writes, derive the CPU cost multiplier, and flush translations when it changes; preserve the existing 80 MHz reset state and record boot timing and register evidence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01T57W7AZf7tgmkuDMMSaWwz
Use nanosecond region costs for X3 and fit instruction surcharges against the
CPU probe and page-rendering measurements. Reduce SPI setup charges and refit
SD stop busy time after accounting for the slower guest driver. Record CPU,
SD, reading, panel, and clock validation with the remaining residuals.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T57W7AZf7tgmkuDMMSaWwz
@prokrypt
prokrypt merged commit f933915 into prokrypt:x4prosim Oct 9, 2026
12 checks passed
@serialx
serialx deleted the serialx/cpu-timing branch October 9, 2026 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants