Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ports/raspberrypi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,12 @@ SRC_C += \

INC += \
-isystem lib/Pico-PIO-USB/src

# Core1 posts USB events while core0 may hold the TinyUSB queue mutex. The
# contended wait path calls these two flash-resident SDK functions, and core1
# must not execute from flash. Replace them with RAM-resident implementations
# in common-hal/usb_host/Port.c on USB host builds only. See issue #11116.
PICO_LDFLAGS += -Wl,--wrap=best_effort_wfe_or_timeout -Wl,--wrap=time_us_64
endif

ifeq ($(CIRCUITPY_PICODVI),1)
Expand Down
34 changes: 34 additions & 0 deletions ports/raspberrypi/common-hal/usb_host/Port.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,40 @@ usb_host_port_obj_t usb_host_instance;

volatile bool _core1_ready = false;

// Core1 posts TinyUSB events while core0 may hold the queue FIFO mutex. The
// contended wait path calls best_effort_wfe_or_timeout() and time_us_64(),
// which normally live in flash, but core1 must not touch flash (see the MPU
// setup in core1_main below). These RAM-resident replacements are linked in
// with --wrap on USB host builds only (see the port Makefile). Issue #11116.
//
uint64_t __wrap_time_us_64(void);
bool __wrap_best_effort_wfe_or_timeout(absolute_time_t timeout_timestamp);

// The same hi/lo/hi re-read loop as the SDK's timer_time_us_64, to defeat
// rollover between the two 32-bit halves.
uint64_t __not_in_flash_func(__wrap_time_us_64)(void) {
uint32_t hi = timer_hw->timerawh;
uint32_t lo;
do {
lo = timer_hw->timerawl;
uint32_t next_hi = timer_hw->timerawh;
if (hi == next_hi) {
break;
}
hi = next_hi;
} while (true);
return ((uint64_t)hi << 32) | lo;
}

// Mirrors the SDK's own PICO_TIME_DEFAULT_ALARM_POOL_DISABLED fallback
// (a poll of the clock with no __wfe): "best effort" explicitly permits
// returning early, and every SDK caller re-checks in a loop. Polling honors
// the timeout exactly and avoids dragging the alarm pool machinery into RAM.
bool __not_in_flash_func(__wrap_best_effort_wfe_or_timeout)(absolute_time_t timeout_timestamp) {
tight_loop_contents();
return __wrap_time_us_64() >= to_us_since_boot(timeout_timestamp);
}

static void __not_in_flash_func(core1_main)(void) {
// The MPU is reset before this starts.
SysTick->LOAD = (uint32_t)((common_hal_mcu_processor_get_frequency() / 1000) - 1UL);
Expand Down
2 changes: 1 addition & 1 deletion ports/raspberrypi/link-rp2040.ld
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ SECTIONS
*(.property_getset)
__property_getset_end = .;

*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.o *divider.o *tusb_fifo.o *mem_ops_aeabi.o *usbh.o) .text*)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.o *divider.o *pico_int64_ops_aeabi.o *tusb_fifo.o *mem_ops_aeabi.o *usbh.o) .text*)
/* Allow everything in usbh.o except tuh_task_event_ready because we read it from core 1. */
*usbh.o (.text.[_uphc]* .text.tuh_[cmved]* .text.tuh_task_ext*)
*(.fini)
Expand Down
2 changes: 1 addition & 1 deletion ports/raspberrypi/link-rp2350.ld
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ SECTIONS
*(.property_getset)
__property_getset_end = .;

*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.o *divider.o *tusb_fifo.o *mem_ops_aeabi.o *usbh.o *string0.o) .text*)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.o *divider.o *pico_int64_ops_aeabi.o *tusb_fifo.o *mem_ops_aeabi.o *usbh.o *string0.o) .text*)
/* Allow everything in usbh.o except tuh_task_event_ready because we read it from core 1. */
*usbh.o (.text.[_uphc]* .text.tuh_[cmved]* .text.tuh_task_ext*)
*(.fini)
Expand Down
Loading