Skip to content

A11b: pico2_cyccnt times bridge and rational, from SRAM - #95

Merged
tap merged 2 commits into
mainfrom
claude/sample-rate-solutions-comparison-mqc190
Oct 15, 2026
Merged

tap merged 2 commits into
mainfrom
claude/sample-rate-solutions-comparison-mqc190

Conversation

@tap

@tap tap commented Oct 15, 2026

Copy link
Copy Markdown
Owner

What this changes

This is the second of the three PRs for the roadmap's A11. The RP2350 cycle-measurement firmware (async/examples/pico2_cyccnt/) now measures the family's two synchronous engines as well as async, and runs from SRAM by default.

  • A second table. It times these cases, all Q15 at the economy profile, in stereo:

    • bridge, both directions: 48 → 44.1 and 44.1 → 48 kHz;
    • rational, up 2, down 2 and 2/3.

    Each process() call of 32 input frames is timed with DWT.CYCCNT. Each row reports mean, p99 and max cycles per call, cycles per output frame, and one stream's share of the core at that engine's output rate.

  • PICO2_COPY_TO_RAM (default ON). This uses the SDK's copy_to_ram binary type, so the whole image runs from SRAM.

    • ON matches the ratchet: QEMU's M33 has no wait states.
    • OFF measures running in place from flash, which is what a deployment ships.
    • The difference between the two runs is what the XIP cache costs.
  • Release by default. This is how the ratchet's workloads are built.

  • CI: the existing no-exceptions pico2_cyccnt build now also uses PICO2_COPY_TO_RAM=OFF, so both layouts link in CI.

  • Done marker fixed. The firmware's error path printed SRT_PICO2_DONE; it now prints TAP_SR_PICO2_DONE, like the success path and the README.

  • Docs: the firmware README, async/docs/HARDWARE_TESTING.md, the book's part5/hardware.md, and the ROADMAP A11 note.

Why

Block B calibrates the ratchet against real silicon: one constant, "1 QEMU instruction ≈ N RP2350 cycles". Until now the firmware measured only async. Timing bridge and rational as well checks whether one constant serves every engine. Running from SRAM keeps flash-cache misses out of that constant.

bridge's M33 steady states give the second datapath's figures. They come from bridge/bench/baselines.json, taking each streaming workload less its construction-only leg (2 s of stereo):

  • down: (173,755,176 − 136,279,322) / 88,200 = 424.9 instructions per output frame;
  • up: (133,914,472 − 104,011,630) / 96,000 = 311.5 instructions per output frame.

rational's single stages can't be split the same way: its construction-only leg (construct_q15_eco) builds the down2∘down2 chain, not a single stage. For rational, the firmware's own cycles are the measurement.

Verification

  • Firmware builds. I built it with the Pico SDK 2.1.1 and arm-none-eabi-gcc 13.2.1 in both of CI's configurations:

    Configuration Memory map .text .bss
    Exceptions on, COPY_TO_RAM on memmap_copy_to_ram.ld 140,712 B 12,224 B
    No exceptions, COPY_TO_RAM off memmap_default.ld 131,908 B 12,224 B

    Both builds report CMAKE_BUILD_TYPE=Release (-O3). The only link warning is newlib's _getentropy note, which comes from the SDK's libc.

  • Host run. I ran the whole firmware on the host against stub Pico headers, under clang with ASan and UBSan. It was clean, and every case constructed, streamed and printed its row. The stubs' cycle counts are zero, so this checks the code paths only, not the numbers.

  • Compile checks. clang -Wall -Wextra -Wconversion -Werror passes with and without -fno-exceptions.

  • Style. clang-format and scripts/check_retired_words.py are clean. The firmware is outside the clang-tidy gate's compile database, as before.

  • Not run: nothing has run on a real Pico 2. The cycle figures come from Block B's bench.

Notes for the reviewer

  • Float ABI. The SDK compiles -mfloat-abi=softfp; the ratchet's toolchain file uses hard. The Q15 cases pass no floating-point arguments in their hot loops, so this moves only the float rows. The README says so.
  • README correction. The README previously said the workload "runs from SRAM", which was not true of the SDK's default layout. It now is by default, and the sentence is corrected.
  • Next: A11c, the UAC2 asynchronous-mode USB audio firmware, follows as its own PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_016GKzq1VSdcMYCRMhddiMaf


Generated by Claude Code

The firmware gains a second table: bridge in both directions (48 -> 44.1,
44.1 -> 48) and rational up 2, down 2 and 2/3, all Q15 economy stereo,
each process() of 32 input frames timed with DWT.CYCCNT and stated per
output frame and as one stream's share of the core at its output rate.
bridge's M33 steady states (baselines less the construct-only legs:
424.9 and 311.5 instructions per output frame) give the calibration
ratio off a second datapath.

PICO2_COPY_TO_RAM (default ON) runs the image from SRAM, as the
ratchet's QEMU M33 runs from zero-wait-state memory; OFF measures the
flash (XIP) layout a deployment ships. The build type defaults to
Release, as the workloads are built. CI's no-exceptions build takes the
XIP layout so both settings link. The error path's done marker now
matches the success path's (TAP_SR_PICO2_DONE).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016GKzq1VSdcMYCRMhddiMaf
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016GKzq1VSdcMYCRMhddiMaf
@tap
tap merged commit 5768dcf into main Oct 15, 2026
32 checks passed
@tap
tap deleted the claude/sample-rate-solutions-comparison-mqc190 branch October 15, 2026 18:54
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