Hello everyone,
I'm currently developing a custom bootloader for the Raspberry Pi Pico (RP2040 and RP2350) and I'm encountering issues when jumping from the bootloader to the application. The intended flow is:
Pico Boot → Custom Bootloader → Application
Problem Description
The jump from the bootloader to the application does not work as expected. I have tried two different approaches:
Approach 1: Manual Jump Using Assembly
bootloader.cppapplication.cpp (linked at 0x20000)Observations:
GPIO toggling works.
USB stdio does not work.
PIO works.
UART0 (which uses interrupts) does not work.
On the RP2040, this approach works if I use:
In that case, USB stdio and UART0 (with interrupts) work fine in both bootloader and application.
Approach 2: Using watchdog_reboot
bootloader.cppOutput:
Additional Observation
If I start the bootloader from itself (i.e., APP_OFFSET = 0), the USB output only works the first time. On subsequent runs, USB output fails, but GPIO toggling still works. The second approach still fails.
Question
What am I doing wrong?
Why does USB stdio and UART0 (with interrupts) fail in the application when started via the bootloader?
Is there a better or more reliable way to jump from a custom bootloader to an application on the RP2040/RP2350?
Any help or insights would be greatly appreciated!
I'm currently developing a custom bootloader for the Raspberry Pi Pico (RP2040 and RP2350) and I'm encountering issues when jumping from the bootloader to the application. The intended flow is:
Pico Boot → Custom Bootloader → Application
Problem Description
The jump from the bootloader to the application does not work as expected. I have tried two different approaches:
Approach 1: Manual Jump Using Assembly
bootloader.cpp
Code:
int main(void){ stdio_init_all(); sleep_ms(100); printf("Bootloader\\r\\n"); uint32_t APP_OFFSET = 0x20000; save_and_disable_interrupts(); asm volatile ( "mov r0, %[start]\\n" "ldr r1, =%[vtable]\\n" "str r0, [r1]\\n" "ldmia r0, {r0, r1}\\n" "msr msp, r0\\n" "bx r1\\n" : : [start] "r" (XIP_BASE + APP_OFFSET), [vtable] "X" (PPB_BASE + M33_VTOR_OFFSET) : ); while(1);}Code:
int main(void){ gpio_init(34); gpio_set_dir(34, true); gpio_put(34, true); sleep_ms(2000); gpio_put(34, false); sleep_ms(2000); gpio_put(34, true); sleep_ms(2000); gpio_put(34, false); stdio_init_all(); sleep_ms(100); printf("Application"); ...}GPIO toggling works.
USB stdio does not work.
PIO works.
UART0 (which uses interrupts) does not work.
On the RP2040, this approach works if I use:
Code:
[start] "r" (APP_START + 0x100), [vtable] "X" (PPB_BASE + M0PLUS_VTOR_OFFSET)Approach 2: Using watchdog_reboot
bootloader.cpp
Code:
int main(void){ stdio_init_all(); sleep_ms(100); printf("Bootloader\\r\\n"); uint32_t APP_OFFSET = 0x20000; uint32_t sp = *(uint32_t*) (XIP_BASE + APP_OFFSET); uint32_t pc = *(uint32_t*) (XIP_BASE + APP_OFFSET + 4); printf("rebooting %08x/%08x in 100ms...\\r\\n", pc, sp); watchdog_enable(1000, 1); watchdog_reboot(pc, sp, 1); while(1);}The disassembly confirms that the addresses match the _reset_handler:rebooting 1002015d/20082000 in 100ms...
Result: This approach does not work at all.10020000 <__VECTOR_TABLE>:
10020000: 20082000
...
1002010c: 1002011d
...
1002015c <_reset_handler>:
1002015c: f04f 4050 mov.w r0, #0xd0000000
...
Additional Observation
If I start the bootloader from itself (i.e., APP_OFFSET = 0), the USB output only works the first time. On subsequent runs, USB output fails, but GPIO toggling still works. The second approach still fails.
Question
What am I doing wrong?
Why does USB stdio and UART0 (with interrupts) fail in the application when started via the bootloader?
Is there a better or more reliable way to jump from a custom bootloader to an application on the RP2040/RP2350?
Any help or insights would be greatly appreciated!
Statistics: Posted by derGerd — Fri Jul 25, 2025 8:58 am — Replies 0 — Views 29