Skip to content

tools: add core1 flash-call checker for rp2 USB host builds#11115

Open
mikeysklar wants to merge 2 commits into
adafruit:mainfrom
mikeysklar:core1-flash-checker
Open

tools: add core1 flash-call checker for rp2 USB host builds#11115
mikeysklar wants to merge 2 commits into
adafruit:mainfrom
mikeysklar:core1-flash-checker

Conversation

@mikeysklar

Copy link
Copy Markdown
Collaborator

Follow-up to #10243 / #11114, answering "could we have caught this at build time?" Yes.

On RP2 USB host boards, core1 may only run code that lives in RAM. If it ever steps into flash, it crashes and USB host goes silent. We keep core1's functions in RAM, but nothing ever checked what those functions call. That's how #10243 slipped in.

This script checks the finished firmware: it follows every call core1 can make and fails the build with the guilty call chain if any of them lead into flash. Runs in ~2 seconds using arm-none-eabi-objdump.

Against current builds it catches three real cases:

  1. the USB Host Issues Using Gamepads #10243 bug (fixed by raspberrypi: make tusb_time_millis_api RAM-resident; core1 calls it #11114)
  2. Pico-PIO-USB's calc_usb_crc16 lives in flash. Verified on hardware: writing to a USB drive hard-locks the board. One-word upstream fix coming in a separate PR.
  3. a rare flash call path in TinyUSB's queue mutex. Only triggers under contention; will file separately.

Not wired into CI yet, since 2 and 3 would fail the build until they're fixed.

On raspberrypi boards with USB host, core1 runs the PIO-USB frame loop
behind an MPU region that makes flash inaccessible, so everything core1
reaches must be RAM-resident. The linker script places the entry points
in RAM, but nothing verified their callees — which is how adafruit#10243
happened: an upstream TinyUSB change added a flash-resident call inside
RAM-placed tuh_task_event_ready() and core1 died at first device attach.

check_core1_flash_calls.py disassembles the linked ELF, walks the static
call graph from core1_main (following linker veneers through their
literal-pool targets), and fails if any reachable function lives in
flash, printing the offending call chain.

Run against current builds it catches the adafruit#10243 regression, the
Pico-PIO-USB calc_usb_crc16 flash placement (hard-locks the board on
any multi-packet OUT transfer, e.g. writing to a USB drive), and a
latent flash call chain in the TinyUSB queue mutex contention path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant