embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.15k stars 713 forks source link

Bootloader problem for RP Pico W #1767

Open exor2008 opened 1 year ago

exor2008 commented 1 year ago

Hi, I'am trying to implement firmware update for my RP Pi Pico W via Wi-Fi and I ran into a problem. So I tried to run the boot example from https://github.com/embassy-rs/embassy/tree/main/examples/boot/bootloader/rp and https://github.com/embassy-rs/embassy/tree/main/examples/boot/application/rp

This example compiling and flashing just fine, but I can't see the result, because https://github.com/embassy-rs/embassy/blob/main/examples/boot/application/rp/src/bin/b.rs is designed for RP Pico, not RP Pico W.

Commands I'am using to flash bootloader example:

# Flash bootloader
cargo flash --manifest-path ../../bootloader/rp/Cargo.toml --release --chip RP2040

# Build 'b'
cargo build --release --bin b

# Generate binary for 'b'
cargo objcopy --release --bin b -- -O binary b.bin

# Flash `a` (which includes b.bin)
cargo flash --release --bin a --chip RP2040

So I built https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/wifi_blinky.rs to binary and used it as b.bin for bootloading example. I used the same memory layout as in examples:

MEMORY
{
  /* NOTE 1 K = 1 KiBi = 1024 bytes */
  BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
  BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K
  FLASH : ORIGIN = 0x10007000, LENGTH = 512K
  DFU : ORIGIN = 0x10087000, LENGTH = 516K
  RAM : ORIGIN = 0x20000000, LENGTH = 256K
}

__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2);
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOT2);

__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2);
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2);

After cargo flash --release --bin a --chip RP2040 I start to getting errors:

Erasing sectors ✔ [00:00:06] [#####################################################################################] 312.00 KiB/312.00 KiB @ 51.35 KiB/s (eta 0s ) Programming pages ✔ [00:00:00] [#########----------------------------------------------------------------------------] 32.00 KiB/312.00 KiB @ 331.17 KiB/s (eta 0s )
Error Something during the interaction with the core went wrong

or

Erasing sectors ✔ [00:00:03] [############################################################-------------------------] 220.00 KiB/312.00 KiB @ 82.59 KiB/s (eta 0s ) Programming pages ✔ [00:00:03] [---------------------------------------------------------------------------------------] 0B/312.00 KiB @ 81.89 KiB/s (eta 0s )
Error Failed to erase flash sector at address 0x1003e000.

Athorus commented 11 months ago

Hi, I had the same issue, it is caused by the watchdog, if you flash many data, as you have to do with wifi-blinky sample, the watchdog will reboot the pico as it takes too much time. So remove watchdog code from all the samples and all your flashes with probe-run will work fine !

Use Flash instead of WatchdogFlash in the bootloader and remove Watchdog in a.rs

It is a real issue as probe-run does not disable Watchdog.

CBJamo commented 11 months ago

The watchdog is also not enabled when in USB bootloader mode, so if your application needs the watchdog and is too big to flash before it resets, this is a viable, if annoying, workaround.

lulf commented 11 months ago

I'm wondering: would it be better if we disable the watchdog and instead make it some opt-in compile option? Although it's generally a 'good thing to do', I've seen multiple users hitting this.

Athorus commented 11 months ago

Watchdog is only useful when the pico is not plugged to a debug probe. With a debug probe, I can restart anytime I want.

If you know nothing about the watchdog, you think it is a random bug while flashing, as I did. This stucks many beginners.

exor2008 commented 11 months ago

@Athorus @lulf @CBJamo thanks for your responses.

But I thought I figured it out with watchdog. I made it 8 sec and was feeding him during the flashing. And according to my observations an execution was never stuck for at least a single second. But I still getting those errors.

pub async fn update_firmware<'a>(
    firmware: &'static Channel<ThreadModeRawMutex, Vec<u8, 4096>, 1>,
    watchdog: WATCHDOG,
    flash: FLASH,
) {
    //Override bootloader watchdog
    let mut watchdog = Watchdog::new(watchdog);
    watchdog.start(Duration::from_secs(8));

    let flash: Flash<_, FLASH_SIZE> = Flash::new(flash);
    let flash = Mutex::new(RefCell::new(flash));

    let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
    let mut updater = BlockingFirmwareUpdater::new(config);

    watchdog.feed();

    let mut offset = 0;
    let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]);
    defmt::info!("preparing update");

    let state = updater
        .get_state(&mut buf.0[..1])
        .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e)))
        .unwrap();

    let writer = updater
        .prepare_update(&mut buf.0[..1])
        .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e)))
        .unwrap();
    defmt::info!("writer created, starting write");

    let mut chunk = firmware.recv().await;

    while !chunk.is_empty() {
        buf.0[..chunk.len()].copy_from_slice(&chunk.as_slice());
        defmt::info!("writing block at offset {}", offset);
        writer.write(offset, &buf.0[..chunk.len()]).unwrap();
        offset += chunk.len() as u32;
        watchdog.feed();
    }

    watchdog.feed();
    defmt::info!("firmware written ({} bytes), marking update", offset);

    updater.mark_updated(&mut buf.0[..1]).unwrap();
    Timer::after(Duration::from_secs(2)).await;
    defmt::info!("update marked, resetting");
    cortex_m::peripheral::SCB::sys_reset();
    loop {}
}

What confuses me is that sometimes an error occurs during the erasure of memory, and sometimes during the writing of a new one.

Athorus commented 11 months ago

I’m not sure of what I’m saying, but I think during flashing your soft is stopped but not the watchdog, that is was why you don’t know when the reset will happen. As I said, without watchdog in all the code (bootloader + soft), no issue while flashing.