Static Partition Manager and XIP Variant

Hi, I'm using VS Code and v2.5.0 with a custom board based on an nRF52820. I'm using a Segger J-Link Base to flash the board.

I've managed to set up the DFU example with mcuBoot and a static partition map, can flash that onto our board successfully, and dfu-util can find the board and load code into it over USB, so that's all good.

I've also managed to write our actual application, which I can also configure using a static partition map, with mcuBoot, and I can flash that onto the board and it runs, and I can communicate with that over Bluetooth, so that's all good too!

What I need to do, though, is build our application to go into slot 1, so I can load that onto the board using dfu-util, and be able to update it using DFU, and it's that secondary image bit that I'm struggling with.

Because the DFU app is small and the application itself takes up over half the remaining flash, I need to use XIP, as mcuboot won't be able to copy the image, and I need to use the 

CONFIG_BOOT_BUILD_DIRECT_XIP_VARIANT=y
 

in my prj.conf as DFU will write out application to slot 1.

My static_pm.yml file from the DFU project is:

app:
  address: 0x8200
  end_address: 0x16000
  region: flash_primary
  size: 0xde00
mcuboot:
  address: 0x0
  end_address: 0x8000
  placement:
    before:
    - mcuboot_primary
  region: flash_primary
  size: 0x8000
mcuboot_pad:
  address: 0x8000
  end_address: 0x8200
  placement:
    align:
      start: 0x1000
    before:
    - mcuboot_primary_app
  region: flash_primary
  size: 0x200
mcuboot_primary:
  address: 0x8000
  end_address: 0x16000
  orig_span: &id001
  - app
  - mcuboot_pad
  region: flash_primary
  sharers: 0x1
  size: 0xe000
  span: *id001
mcuboot_primary_app:
  address: 0x8200
  end_address: 0x16000
  orig_span: &id002
  - app
  region: flash_primary
  size: 0xde00
  span: *id002
mcuboot_secondary:
  address: 0x16000
  end_address: 0x40000
  placement:
    after:
    - mcuboot_primary
    align:
      start: 0x1000
    align_next: 0x1000
  region: flash_primary
  size: 0x2a000
sram_primary:
  address: 0x20000000
  end_address: 0x20008000
  region: sram_primary
  size: 0x8000

I guess I was hopeful I could use that same file in my application project, and it would magically build into slot 1, but I knew it wouldn't really be that straightforward.
 
The error I get is
Traceback (most recent call last):
  File "/opt/nordic/ncs/v2.5.0/nrf/scripts/partition_manager_output.py", line 251, in <module>
    main()
  File "/opt/nordic/ncs/v2.5.0/nrf/scripts/partition_manager_output.py", line 247, in main
    write_gpm_config(gpm_config, greg_config, name, header_file)
  File "/opt/nordic/ncs/v2.5.0/nrf/scripts/partition_manager_output.py", line 142, in write_gpm_config
    if any('span' in x for x in gpm_config[domain][image]):
KeyError: 'mcuboot_secondary_app'
CMake Error at /opt/nordic/ncs/v2.5.0/nrf/cmake/partition_manager.cmake:665 (message):
  Partition Manager GLOBAL output generation failed,
I've tried quite a lot of editing of that file and usually then get an error about it not fitting into the space available.
I feel like i'm close to getting this working, but could really do with some help configuring my static_pm file to get the application to build into that second slot.
Alex
Parents Reply
  • Hi Sigurd, I can build them both separately without using the static PM, and they seem to work individually.

    However, following your excellent suggestion, I've removed the stain PM and tried using the DFU application to load a new version of the DFU application into the second slot, and that hasn't worked.

    I made sure I updated the version, using:

    CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="1.0.0+1"

    But when it resets after the DFU update, it still runs the initial version in slow 0.

    Alex

Children
  • Right, the DFU partition is smaller than the main application of course.

    One possible limitation here is that MCUboot assumes that both slots are the same size, which may make partitioning harder.

    In that case I agree that you need static partitioning to fix this yes.

    I will have a look and see if I can figure something out.

  • Here are some pointers:

    First, I would like to say that this approach is not officially supported by us, so best I can do is to give you help to do it yourself. And we can not guarantee anything related to this, as it is not properly tested.

    If you do not want to take all the effort to craft a new pm_static.yml for testing every time you change the partition size, you can temporarily make changes to pm.yml for mcuboot to set mcuboot_secondary. In that case, you can change this line to "size: 0x20000" (or any other size), and the partition manager will automatically size the rest to fit with that.

    Next up, if you build your project with CONFIG_BOOT_BUILD_DIRECT_XIP_VARIANT, the build will create a secondary image file build/zephyr/mcuboot_secondary_app_update.bin which you should be able to use for the secondary slot.

    Further more, I think you need to do two different projects, and make sure that mcuboot signing keys match up (If you do not set the key manually it will be the same test key).
    Then as long as both projects have the same pm_static.yml, I think you should be able to switch between the two.

    Lastly, you need a way to go from the application to the DFU image when you want to do DFU.
    By default, direct_XIP boots the image with the newest version. So we need some trick to make it go back to the previous image. I think the easiest way here might be to set CONFIG_BOOT_DIRECT_XIP_REVERT for the child image. Then find a way to revert the image when you need to go to the DFU image.
    Testing this, I see that MUCboot can no longer find the image if I enable this configuration. I asked our MCUboot developers about this. Maybe you can find another way to switch between the images?

  • Thanks for the pointers Sigurd, sounds like a bit of a challenge but I'll give it a go and report back how I get on!

    Alex

  • Ok, so I've got it working, and here's what I did in case others find it helpful!

    I didn't quite end up following your suggestion above Sigurd, but I did get a lot of help from reading through all that, as well as this.

    I've ended up making the mcuboot wait for 15 seconds when it starts for a DFU connection, and using that to update the application. This allows me to have a single image that's big enough for my application to fit in, and will enable the devices to be updated in the field.

    I set the prj.conf for mcuboot as follows:

    CONFIG_MAIN_STACK_SIZE=10240
    CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h"
    
    CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
    
    CONFIG_PM_PARTITION_SIZE_MCUBOOT=0xc000
    
    CONFIG_SIZE_OPTIMIZATIONS=y
    CONFIG_SINGLE_APPLICATION_SLOT=y
    
    # Enable wait for DFU functionality
    CONFIG_BOOT_USB_DFU_WAIT=y
    CONFIG_BOOT_USB_DFU_WAIT_DELAY_MS=15000
    
    CONFIG_FLASH=y
    CONFIG_FLASH_PAGE_LAYOUT=y
    CONFIG_FLASH_MAP=y
    CONFIG_FCB=y
    CONFIG_SETTINGS=y
    CONFIG_SETTINGS_FCB=y
    CONFIG_MPU_ALLOW_FLASH_WRITE=y

    (There's other pointers on here how to do that in the child_image folder, but that's for another day!)

    In my board.dts file I had the following set up:

    &flash0 {
    	partitions {
    		compatible = "fixed-partitions";
    		#address-cells = <1>;
    		#size-cells = <1>;
    
    		boot_partition: partition@0 {
    			label = "mcuboot";
    			reg = <0x0 0x10000>;
    		};
    		slot0_partition: partition@10000 {
    			label = "image-0";
    			reg = <0x10000 0x30000>;
    		};
    	};
    };

    In my application's prj.conf file I added:

    CONFIG_BOOTLOADER_MCUBOOT=y
    
    # Build
    CONFIG_SIZE_OPTIMIZATIONS=y
    
    CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE=y

    Once it was all up and working, I went back and added the pm_statc.yml file to my project (taken directly from the generated partitions.yml file in the build folder.

    app:
      address: 0xc200
      end_address: 0x3e000
      region: flash_primary
      size: 0x31e00
    mcuboot:
      address: 0x0
      end_address: 0xc000
      placement:
        before:
        - mcuboot_primary
      region: flash_primary
      size: 0xc000
    mcuboot_pad:
      address: 0xc000
      end_address: 0xc200
      placement:
        before:
        - mcuboot_primary_app
      region: flash_primary
      size: 0x200
    mcuboot_primary:
      address: 0xc000
      end_address: 0x3e000
      orig_span: &id001
      - app
      - mcuboot_pad
      region: flash_primary
      size: 0x32000
      span: *id001
    mcuboot_primary_app:
      address: 0xc200
      end_address: 0x3e000
      orig_span: &id002
      - app
      region: flash_primary
      size: 0x31e00
      span: *id002
    settings_storage:
      address: 0x3e000
      end_address: 0x40000
      placement:
        align:
          start: 0x1000
        before:
        - end
      region: flash_primary
      size: 0x2000
    sram_primary:
      address: 0x20000000
      end_address: 0x20008000
      region: sram_primary
      size: 0x8000
    

    For completeness, when I boot the board up now, I can run the following line from where I downloaded dfu-util:

    dfu-util --alt 0 --download ../nRFprojects/xxxx/build_Xxxx/zephyr/app_update.bin

    My plan is to build a reset into my application, which can be triggered over the Bluetooth connection, so that in the field we can plug in the USB (which will also charge the device), trigger the reset from our app, then use df-util to update the app.

    What could go wrong! Slight smile

    Hopefully the above helps someone else out too, I've learnt a lot in doing it though, and thanks ever so much to Sigurd and the rest of the Nordic support team too!

    Alex

  • Good catch! If you use serial DFU, you only need one slot, getting a lot more space to work with.

    Thanks a lot for the kind words, and especially for sharing the solution! I am sure that future users will find this helpful.

Related