nuttx icon indicating copy to clipboard operation
nuttx copied to clipboard

[FEATURE] raspberrypi pico w/2/2w firmware flashing automation with make flash

Open cederom opened this issue 8 months ago • 9 comments

Is your feature request related to a problem? Please describe.

  • For now in order to flash rPI-Pico(-W/-2/-2W) we need to manually press Boot button on USB attach then copy-paste firmware file.
  • That blocks automation testing.

Describe the solution you'd like

  • Use make flash to have firmware flashed and ready for runtime testing.
  • Enable full automation of firmware flashing.

Describe alternatives you've considered

@keever50 reported that picotool (something like esptool for ESP32) can be used to flash firmware but still requires manual Boot button interaction, and PlatformIO can switch to BootROM by some magic sequence. That may indicate flashing automation should be possible.

Verification

  • [x] I have verified before submitting the report.

cederom avatar Apr 26 '25 20:04 cederom

@linguini1 as you are most familiar with rPI-Pico can we assign you this task to research in a free moment? :-) Having this single usb port flashing just as with esptool would enable automated testing on a real world hardware :-)

cederom avatar Apr 27 '25 20:04 cederom

@linguini1 as you are most familiar with rPI-Pico can we assign you this task to research in a free moment? :-) Having this single usb port flashing just as with esptool would enable automated testing on a real world hardware :-)

I can certainly try to research how this is done!

linguini1 avatar Apr 27 '25 21:04 linguini1

TANK U SIR! :-)

cederom avatar Apr 27 '25 21:04 cederom

After doing more research on this topic I haven't been able to find any mention of automatic flashing of RP2040 chips. All sources I've found said that the chip requires the "bootsel" button to be held to enter boot mode, or people have created custom boot loaders to flash without holding the button.

It was also suggested on this forum to use the SWD pins: https://forums.raspberrypi.com/viewtopic.php?t=328795

The pico SDK is meant to have some utilities for rebooting into bootsel, so it may be possible for the purposes of mass testing to create some bootloader/helper program that reboots the pico into bootsel, uploads the configuration to be tested, tests it, and reboots into bootsel again to repeat.

linguini1 avatar May 05 '25 20:05 linguini1

I can confirm platformio (https://github.com/earlephilhower/arduino-pico) is including a bootloader. Might be a nice addition for NuttX to add it as well.

keever50 avatar May 05 '25 20:05 keever50

@keever50: I can confirm platformio (https://github.com/earlephilhower/arduino-pico) is including a bootloader. Might be a nice addition for NuttX to add it as well.

Hmm this seems to be whole Arduino SDK.. we would prefer to have our own bootloader and stay external SDK independent right? :-P

cederom avatar May 05 '25 21:05 cederom

If we were to write such a bootloader for NuttX, would it be preferable to:

  1. Have a bootloader that puts the RP2040 into BOOTSEL when it detects something like 1200 baud on a UART line
  2. Have an application/NSH command that when run reboots the RP2040 into bootsel
  3. Expect some kind of special bytes over USB to enter BOOTSEL

My thought is that with 2) we could flash the Pico manually once, and then always be able to reboot into bootsel to flash more configurations, which would be suitable for creating test-farm setups. I think 1) will be inconvenient because it requires a UART connection (as opposed to just a USB connection which is used for both powering and interacting with the RP2040 in a lot of these boards) and I think 3) would be complicated to get right/integrate + require some special byte sequence known in advance and sent by the host device.

linguini1 avatar May 09 '25 23:05 linguini1

Just a thought but you can use a raspberry pi debug probe (or another pico) to program the pico over SWD. The probe firmware is open source so you can tweak it to give it an unique identifier. https://github.com/raspberrypi/debugprobe (I think its also possible to get an unique ID of the RP2040 to be flashed over SWD?)

Or create a microcontroller dedicated to reset the pico into bootloader and push firmware onto it via usb, or via SWD. (or mod the debugprobe to do this)

Those are dumb but easier solutions. We dont want to make it too complex of course. Maybe worth a thought.

keever50 avatar May 10 '25 10:05 keever50

Yeah but the goal (and need) is to have rpi using only single usb connection to flash and run.. to be honest its quite funny it cannot by default :D

cederom avatar May 10 '25 17:05 cederom

Status update:

  • @shtirlic added NuttX RISC-V core support for rPI Pico2(W) in https://github.com/apache/nuttx/pull/16424, yeah!
  • picotool grows with functions, now load (firmware flashing) and reboot (reset to bootrom, arm core, riscv core) commands are now possible, we are getting closer to full automation, as discussed in https://github.com/raspberrypi/picotool/issues/270, this however requires api stub in firmware to perform controlled reset over usb protocol, hardware reset/bootom is not possible as in ESP family :-)
  • Here is initial implementation of usb-protocol-reset-vector for rPI Pico2 https://github.com/apache/nuttx/pull/16848.
  • So far we are working on RP2350, but maybe this will be also possibe for RP2040.

cederom avatar Aug 14 '25 14:08 cederom

@cederom I must clarify that this is NOT usb-protocol-reset-vector it's calling internal pico bootrom functions to reboot normally or reboot to bootsel mode, so you can just type/send the reboot bootloader command to nsh and it goes to reboot. The only problem I see if the NuttX crashed or not booting at all so the board will be stuck.

shtirlic avatar Aug 14 '25 21:08 shtirlic

Do we have make flash target implemented @shtirlic ? :-)

Today I have some free moment will test the reset vector and flashing :-) Thanks again :-)

cederom avatar Aug 16 '25 12:08 cederom

Not yet implemented. make flash only produces nuttx.uf2 while the goal is to automate flashing of the physical chip like in other targets. Reopening :-)

cederom avatar Aug 19 '25 23:08 cederom

@linguini1 Have a bootloader that puts the RP2040 into BOOTSEL when it detects something like 1200 baud on a UART line

In my RP2040 board I have implemented this over USB CDC-ACM instead of UART. This comes at the cost of having a background demon that listens for LINE_CODING events (let me improperly call them "baud rate changes"), though.

Example code, ran as part of bringup

Note: I have removed all error handling, comments, and mangled the control structure of this code for maximum brevity. Consider it pseudocode at this point :) The BOARDIOC_RESET_BOOTSEL ioctl does what the name suggests and is backed by a ROM lookup like in pico-sdk.

static sem_t g_board_bootsel_on_baudrate_change_daemon_sem;

static void cdcacm_daemon_callback(enum cdcacm_event_e event) {
    if (event == CDCACM_EVENT_LINECODING) {
        sem_post(&g_board_bootsel_on_baudrate_change_daemon_sem);
    }
}

static int cdcacm_daemon(int argc, char *argv[]) {
    while (true) {
        sem_wait(&g_board_bootsel_on_baudrate_change_daemon_sem);

        int fd = open("/dev/ttyACM0", O_RDONLY);
        struct cdc_linecoding_s linecoding;
        int ret = ioctl(fd, CAIOC_GETLINECODING, &linecoding);
        unsigned long baud = (linecoding.baud[3] << 24 | linecoding.baud[2] << 16 | linecoding.baud[1] << 8 | linecoding.baud[0]);
        close(fd);

        if (baud == 1200) {
            boardctl(BOARDIOC_RESET_BOOTSEL, (uintptr_t)NULL);
        }
    }
}

static int board_cdcacm_daemon_setup(int argc, char *argv[]) {
    uint8_t retry = 0;
    uint8_t retry_delay_lut[] = { 1, 2, 3, 5, 10, 15, 30, 60 };

    int fd;
    while (true) {
        fd = open("/dev/ttyACM0", O_RDONLY);
        if (fd >= 0) break;
        if (retry < ARRAY_SIZE(retry_delay_lut)) {
            uint8_t delay = retry_delay_lut[retry++];
            usleep(delay * USEC_PER_SEC);
        } else {
            return EXIT_FAILURE;
        }
    }
    ioctl(fd, CAIOC_REGISTERCB, &cdcacm_daemon_callback);
    close(fd);

    sem_init(&g_board_bootsel_on_baudrate_change_daemon_sem, 0, 0);
    sem_setprotocol(&g_board_bootsel_on_baudrate_change_daemon_sem, SEM_PRIO_NONE);

    task_create("cdcacm_daemon", sched_get_priority_min(SCHED_FIFO), 512, cdcacm_daemon, NULL);
    return EXIT_SUCCESS;
}

int board_app_finalinitialize(uintptr_t arg) {
    int pid = task_create("board_cdcacm_daemon_setup", sched_get_priority_min(SCHED_FIFO) + 1, 1024, board_cdcacm_daemon_setup, NULL);
    return EXIT_SUCCESS;
}
nsh> ps
  PID GROUP PRI POLICY   TYPE    NPX STATE    EVENT       STACK    USED FILLED COMMAND
    0     0   0 FIFO     Kthread   - Ready              0001008 0000560  55.5%  Idle_Task
    1     1 100 RR       Task      - Running            0003032 0001664  54.8%  nsh_main
    3     3   1 RR       Task      - Waiting  Semaphore 0000472 0000408  86.4%! cdcacm_daemon

This way I can run stty -F /dev/serial/by-id/usb-<your_device_id> 1200 on the host and have the board reboot to bootloader, and then it's just a matter of a few shell commands to wait for the USB drive to appear and copy the new UF2 file to it. I've wrapped all these actions in a couple of Just recipes for ease of use, so I can get away with things like just build && just bootsel-flash :)

@cederom What do you think of this approach? Would it be interesting to upstream it or is it too niche? I typically use a debug probe while developing, but this alternative way of flashing still comes in handy at times.

nmaggioni avatar Sep 07 '25 11:09 nmaggioni

@linguini1 Have a bootloader that puts the RP2040 into BOOTSEL when it detects something like 1200 baud on a UART line

In my RP2040 board I have implemented this over USB CDC-ACM instead of UART. This comes at the cost of having a background demon that listens for LINE_CODING events (let me improperly call them "baud rate changes"), though.

This way I can run stty -F /dev/serial/by-id/usb-<your_device_id> 1200 on the host and have the board reboot to bootloader, and then it's just a matter of a few shell commands to wait for the USB drive to appear and copy the new UF2 file to it. I've wrapped all these actions in a couple of Just recipes for ease of use, so I can get away with things like just build && just bootsel-flash :)

I really like this approach! If it can be Kconfig selected as whether to run at boot or not for the RP2040s, I think it's great. That way the Pico can have a single connection (USB shell) for testing and booting in a hardware test setup. Thanks for sharing that!

linguini1 avatar Sep 07 '25 15:09 linguini1

Thanks @nmaggioni :-) Yes I saw that 1200 baudrate magic on the picotool but did not use that yet myself. So it needs some in-firmware stub anyways? Would it reset board under segfault?

We also have reset to bootloader recently merged but this 1200 baudrate seems simpler, true :-)

Lets keep make flash command to flash as with the other boards that may use different underlying mechanisms too :-)

cederom avatar Sep 08 '25 13:09 cederom

The pico boards will not reset with the magic baud when its stuck, such as in a seg fault. Usually for development, i put code in the start of my code that Waits for a serial connection before continuing. This way baud resets are still possible because bootloops are way less likely. Assuming you have a watchdog.

On Mon, 8 Sept 2025, 15:05 CeDeROM, @.***> wrote:

cederom left a comment (apache/nuttx#16271) https://github.com/apache/nuttx/issues/16271#issuecomment-3266222254

Thanks @nmaggioni https://github.com/nmaggioni :-) Yes I saw that 1200 baudrate magic on the picotool but did not use that yet myself. So it needs some in-firmware stub anyways? Would it reset board under segfault?

— Reply to this email directly, view it on GitHub https://github.com/apache/nuttx/issues/16271#issuecomment-3266222254, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACJ3DKDCRRPH43XUQVXVKND3RV5JRAVCNFSM6AAAAAB35Y6UQKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTENRWGIZDEMRVGQ . You are receiving this because you were mentioned.Message ID: @.***>

keever50 avatar Sep 08 '25 13:09 keever50