nRF52840 with MCUBOOT over SMP using SPI Flash - Fails to boot

Hello,

I'm trying to use MCUBOOT with external flash on a nRF52840 board that has 4MB SPI flash. I'm using nRF Connect SDK v2.6.0.

I've verified that the SPI flash works fine using the "spi_flash" sample.

With CONFIG_BOOTLOADER_MCUBOOT=n, SMP with SPI flash runs fine. I'm able to connect to the "Zephyr" BLE device from nRF Connect for mobile.

However, with CONFIG_BOOTLOADER_MCUBOOT=y, the build is successful but the nRF52840 board fails to boot.

As the nRF52840 board (particle_xenon) has SPI flash instead of QSPI, I've set CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU=n and added the required Kconfig dependencies.

Here is the prj.conf:

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_MCUMGR=y
CONFIG_ZCBOR=y
CONFIG_CRC=y
CONFIG_MCUMGR_TRANSPORT_BT=y
CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL=y
CONFIG_IMG_MANAGER=y
CONFIG_STREAM_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_MCUMGR_GRP_IMG=y
CONFIG_MCUMGR_GRP_OS=y
CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO=y
CONFIG_MCUMGR_TRANSPORT_BT_REASSEMBLY=y
CONFIG_BASE64=y
CONFIG_MCUMGR_TRANSPORT_SHELL=y

CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU=n
CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU_SPEEDUP=y
CONFIG_BOOTLOADER_MCUBOOT=y

CONFIG_NORDIC_QSPI_NOR=n
CONFIG_FLASH=y
CONFIG_SPI=y
CONFIG_SPI_NOR=y
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096

CONFIG_LOG=y
CONFIG_PRINTK=y
CONFIG_CONSOLE=y
CONFIG_SERIAL=y

# Preferred SHELL options
CONFIG_SHELL=y
CONFIG_BOOT_BANNER=y
CONFIG_DATE_SHELL=y
CONFIG_POSIX_CLOCK=y
CONFIG_DEVICE_SHELL=n
CONFIG_INIT_STACKS=y
CONFIG_KERNEL_SHELL=y
CONFIG_SHELL_STATS=n
The board configuration "particle_xenon.conf" is set to use SPI flash:
CONFIG_FLASH=y
CONFIG_SPI=y
CONFIG_SPI_NOR=y


The board "particle_xenon.overlay" is configured to use the external SPI flash (
&mx25l32 is defined in "particle_xenon.dts" board):
/ {
	chosen {
		nordic,pm-ext-flash = &mx25l32;
	};
};
For MCUBOOT, folder "child_image > mcuboot > boards" has the board configuration & overlay files as,

particle_xenon.conf:

CONFIG_PM=n

CONFIG_MAIN_STACK_SIZE=10240
CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h"

CONFIG_BOOT_SWAP_SAVE_ENCTLV=n
CONFIG_BOOT_ENCRYPT_IMAGE=n

CONFIG_BOOT_UPGRADE_ONLY=n
CONFIG_BOOT_BOOTSTRAP=n

### mbedTLS has its own heap
# CONFIG_HEAP_MEM_POOL_SIZE is not set

### We never want Zephyr's copy of tinycrypt.  If tinycrypt is needed,
### MCUboot has its own copy in tree.
# CONFIG_TINYCRYPT is not set
# CONFIG_TINYCRYPT_ECC_DSA is not set
# CONFIG_TINYCRYPT_SHA256 is not set

CONFIG_FLASH=y
CONFIG_FPROTECT=y

### Various Zephyr boards enable features that we don't want.
# CONFIG_BT is not set
# CONFIG_BT_CTLR is not set
# CONFIG_I2C is not set

CONFIG_LOG=y
CONFIG_LOG_MODE_MINIMAL=y # former CONFIG_MODE_MINIMAL
### Ensure Zephyr logging changes don't use more resources
CONFIG_LOG_DEFAULT_LEVEL=0
### Use info log level by default
CONFIG_MCUBOOT_LOG_LEVEL_INF=y
### Decrease footprint by ~4 KB in comparison to CBPRINTF_COMPLETE=y
CONFIG_CBPRINTF_NANO=y
CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT=0
### Use the minimal C library to reduce flash usage
CONFIG_MINIMAL_LIBC=y

### Enable serial recovery
CONFIG_MCUBOOT_SERIAL=y
CONFIG_BOOT_SERIAL_CDC_ACM=y
CONFIG_BOOT_SERIAL_NO_APPLICATION=y
CONFIG_UART_CONSOLE=n

### Increase receive buffer to improve transfer speed in serial recovery mode
CONFIG_BOOT_MAX_LINE_INPUT_LEN=8192
CONFIG_BOOT_SERIAL_MAX_RECEIVE_SIZE=4096

### Enable SPI flash
CONFIG_NORDIC_QSPI_NOR=n
CONFIG_SPI=y
CONFIG_SPI_NOR=y
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096

### Partition size allocated to bootloader - can be reduced if logging is disabled
CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x12000


particle_xenon.overlay:

/ {
	chosen {
		nordic,pm-ext-flash = &mx25l32;
	};

	aliases {
		mcuboot-button0 = &mode_button;
	};
};



MCUBOOT > zephyr > zephyr.dts does include the "nordic,pm-ext-flash". 
Here's a snippet from the file:

	model = "Particle Xenon";
	compatible = "particle,xenon", "particle,feather";
	chosen {
		zephyr,entropy = &cryptocell;
		zephyr,flash-controller = &flash_controller;
		zephyr,console = &uart0;
		zephyr,uart-mcumgr = &uart0;
		zephyr,shell-uart = &uart0;
		zephyr,sram = &sram0;
		zephyr,flash = &flash0;
		zephyr,code-partition = &slot0_partition;
		zephyr,ieee802154 = &ieee802154;
		nordic,pm-ext-flash = &mx25l32;
	};



What could be the reason for the application not running?

Am I missing some configuration or is part of it incorrect?  Thank you.
Regards,
Ravi
  • Hi Ravi,

    Could you post any device logs and or/track where in the code your application reaches before it does not boot? I'm assuming that your fw hangs in the bootloader if it never gets to the application.

    Kind regards,
    Andreas

  • Hi Andreas,

    Thank you for looking at this issue. It's much appreciated.

    The application I'm building for test purposes is "mcuboot_external_flash" for particle_xenon board. I believe its from Nordic or one of the simple SMP sample to demo use of external flash.

    When RTT is enabled, I don't get any output at the J-Link RTT Viewer and no UART console gets enumerated.

    With CONFIG_BOOTLOADER_MCUBOOT=n, I get the expected output at both RTT and UART console, and I'm able to connect to "zephyr" SMP service via nRF Connect app.

    The problem appears to be somewhere within mcuboot code, perhaps due to possible misconfiguration or something else.

    When debugging using J-Link, debugger hits a breakpoint in opt > nordic > v2.6.0 > bootloader > mcuboot > boot > boot_serial > serial > boot_serial.c at line

        " if (rc <= 0 && !full_line) { " in boot_serial_read_console.

     Stepping from there leads to into the depths of  " ... > core > cortex_m > reset.S". The firmware never gets into main().

    /*
     * Task which waits reading console, expecting to get image over
     * serial port.
     */
    static void
    boot_serial_read_console(const struct boot_uart_funcs *f,int timeout_in_ms)
    {
        int rc;
        int off;
        int dec_off = 0;
        int full_line;
        int max_input;
        int elapsed_in_ms = 0;
    
    #ifndef MCUBOOT_SERIAL_WAIT_FOR_DFU
        bool allow_idle = true;
    #endif
    
        boot_uf = f;
        max_input = sizeof(in_buf);
    
        off = 0;
        while (timeout_in_ms > 0 || bs_entry) {
            /*
             * Don't enter CPU idle state here if timeout based serial recovery is
             * used as otherwise the boot process hangs forever, waiting for input
             * from serial console (if single-thread mode is used).
             */
    #ifndef MCUBOOT_SERIAL_WAIT_FOR_DFU
            if (allow_idle == true) {
                MCUBOOT_CPU_IDLE();
                allow_idle = false;
            }
    #endif
            MCUBOOT_WATCHDOG_FEED();
    #ifdef MCUBOOT_SERIAL_WAIT_FOR_DFU
            uint32_t start = k_uptime_get_32();
    #endif
            rc = f->read(in_buf + off, sizeof(in_buf) - off, &full_line);
            if (rc <= 0 && !full_line) {
    #ifndef MCUBOOT_SERIAL_WAIT_FOR_DFU
                allow_idle = true;
    #endif
                goto check_timeout;
            }
            off += rc;
            if (!full_line) {
                if (off == max_input) {
                    /*
                     * Full line, no newline yet. Reset the input buffer.
                     */
                    off = 0;
                }
                goto check_timeout;
            }
            if (in_buf[0] == SHELL_NLIP_PKT_START1 &&
              in_buf[1] == SHELL_NLIP_PKT_START2) {
                dec_off = 0;
                rc = boot_serial_in_dec(&in_buf[2], off - 2, dec_buf, &dec_off, max_input);
            } else if (in_buf[0] == SHELL_NLIP_DATA_START1 &&
              in_buf[1] == SHELL_NLIP_DATA_START2) {
                rc = boot_serial_in_dec(&in_buf[2], off - 2, dec_buf, &dec_off, max_input);
            }
    
            /* serve errors: out of decode memory, or bad encoding */
            if (rc == 1) {
                boot_serial_input(&dec_buf[2], dec_off - 2);
            }
            off = 0;
    check_timeout:
            /* Subtract elapsed time */
    #ifdef MCUBOOT_SERIAL_WAIT_FOR_DFU
            elapsed_in_ms = (k_uptime_get_32() - start);
    #endif
            timeout_in_ms -= elapsed_in_ms;
        }
    }
    

    Here is the partitions.yml. As you can see, mcuboot_secondary is using the external flash. Its size is 0xee000, same as mcuboot_primary.

    app:
      address: 0x12200
      end_address: 0x100000
      region: flash_primary
      size: 0xede00
    external_flash:
      address: 0xee000
      end_address: 0x400000
      region: external_flash
      size: 0x312000
    mcuboot:
      address: 0x0
      end_address: 0x12000
      placement:
        before:
        - mcuboot_primary
      region: flash_primary
      size: 0x12000
    mcuboot_pad:
      address: 0x12000
      end_address: 0x12200
      placement:
        align:
          start: 0x1000
        before:
        - mcuboot_primary_app
      region: flash_primary
      size: 0x200
    mcuboot_primary:
      address: 0x12000
      end_address: 0x100000
      orig_span: &id001
      - mcuboot_pad
      - app
      region: flash_primary
      size: 0xee000
      span: *id001
    mcuboot_primary_app:
      address: 0x12200
      end_address: 0x100000
      orig_span: &id002
      - app
      region: flash_primary
      size: 0xede00
      span: *id002
    mcuboot_secondary:
      address: 0x0
      device: DT_CHOSEN(nordic_pm_ext_flash)
      end_address: 0xee000
      placement:
        align:
          start: 0x4
      region: external_flash
      share_size:
      - mcuboot_primary
      size: 0xee000
    sram_primary:
      address: 0x20000000
      end_address: 0x20040000
      region: sram_primary
      size: 0x40000
    


    Does the partition map look ok?

    Anything else in the app and mcboot configuration or overlay that may possibly be incorrect?

    Let me know how I can assist further. I can upload a zipped copy the project if needed.  Thank you.

    Regards,
    Ravi

  • Hi Andreas,

    I checked were I got the "mcuboot_external_flash" example from.

    It's from your colleague, Vidar Berg (Case ID: 322102). He provided 8475.hello_world_dfu.zip as an example of Serial recovery mode and FOTA over BLE about few months back.

    The example was for QSPI flash, I've made a minor change to use the external 4MB SPI flash on the particle_xenon board.

     NCS 2.5.1 sample application with MCUBoot updates from external flash 

    With CONFIG_BOOTLOADER_MCUBOOT=y and nRF52840 device connected to a Mac over serial, I do see the MCUBOOT port enumerated. However, there's no device 'zephyr' for FOTA BLE.

    Normally, to enter serial recovery mode, I'd press the RESET button while keeping MODE button on the particle_xenon pressed. As serial recovery mode comes up immediately on device connection to Mac, it confirms that the fw application isn't running, it's still in MCUBOOT.

    I can connect to it using mcumgr to view slot-0 image.

    quark11:~ quark11$ mcumgr --conntype=serial --connstring='dev=/dev/cu.usbmodem14201,baud=115200' image list
    Images:
     image=0 slot=0
        version: 1.2.3.4
        bootable: false
        flags: 
        hash: c8f175ea09650363419dc2449acb06114a72aec0aacbc8d476553bc289889428
    Split status: N/A (0)

    I've also tried to upload a known working fw application image using mcumgr. The image gets uploaded but doesn't run.

  • Hi,

    Thank you for sharing this. I will have a closer look today and get back to you with more info when I have more.

    In the meanwhile, could you try to nrfjprog --qspieraseall -f nrf52 before programming the device and see if the behavior is still the same?

    Kind regards,
    Andreas

  • Hello Andreas,
    I tried your suggestion before programming the device but it made no difference.

    Also, please note that the device has SPI flash and not QSPI.

    Regards,
    Ravi

Related