This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Dual Bank Bootloader Over ESB

I have two nrf52832 boards, both being developed with SDK v11 and the s132 soft device. 

Board one is given commands over UART to communicate over ESB with board two. Board two also has serial communication with an FPGA that can be reprogrammed sucsessfully over ESB. 

The goal is to be able to update the firmware on board two over ESB. Doing this will also reprogram the FPGA that currently boots from a file flashed along with the board two app via the "incbin" library.

Issues I'm having:

1. When trying to read and write to flash I discovered that DFU_BANK_1_REGION_START is in fact 0x918CB001. From my understanding this is the memory location of external RAM which is not attached and am not intending to attach. Also this not a multiple of four and is therefore not a valid boot address? The way the space for banks 0 and 1 are worked out seems to just be (BOOTLOADER_REGION_START - CODE_REGION_1_START)/2, leaves the available space around 1.75GB per bank for me, which is certainly not correct. I know i need to redefine these, but what is the best way to set the start and end position of both banks?  

2. Getting the bootloader to validate the new image and swap it into bank 0 over ESB? I currently can't try this due to not being able to write into the right places in memory but any advice other than swapping over to BLE comms for bootloading or trying to change code for BLE comms with ESB protocols throughout the DFU example would be appreciated.

Thanks in advance 

Jack

Parents
  • Hi Jack, 

     

    Leave the FPGA aside, I assume that you just need to flash a particular binary image to board #2 and the FPGA board will automatically be reprogrammed with the new image located on board #2 ? 

    Could you check and find why DFU_BANK_1_REGION_START = 0x918CB001 in your case ? It doesnt seem correct. Could you trace back and find what cause it to be >2GB ? 

    DFU_BANK_1_REGION_START by default equals (DFU_BANK_0_REGION_START + DFU_IMAGE_MAX_SIZE_BANKED)  

     

    How did you modify the bootloader to receive the image via ESB ? If you can manage to add a new dfu transport layer using ESB, then the bootloader should do its job to validate new image and swap the new image. 

  • Hi Hung, 

    Correct, the FPGA image is sorted out within the app on board #2. The plan is to use existing code for sending new FPGA images to board #2 from board #1. It works over some ESB commands controlled using matlab with realterm as a serial communication to board #1.  I've included the library that is being used for writing to flash for that. Once i've got a valid bank 1 to write to i plan to just use the bootloader library to validate the image and boot. 

    These are the relevant defines from the dfu_types.h file in the bootloader_dfu library and the respective values i get returned for them.

    #define NRF_UICR_BOOT_START_ADDRESS         (NRF_UICR_BASE + 0x14)      
    
    #if defined(NRF52)
    #define NRF_UICR_MBR_PARAMS_PAGE_ADDRESS    (NRF_UICR_BASE + 0x18)      
    #endif
    
    #define CODE_REGION_1_START                 SD_SIZE_GET(MBR_SIZE)   //0x2311D001    
    #define SOFTDEVICE_REGION_START             MBR_SIZE                //0X00001000    
    
    //nrf51 definitions
    
    #elif NRF52
    
    #define BOOTLOADER_REGION_START             0x0007A000                 
    #define BOOTLOADER_SETTINGS_ADDRESS         0x0007F000                  
    #define BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS  0x0007E000                  
    
    #define CODE_PAGE_SIZE                      0x1000                      
    
    #else
    
    #error No target defined
    
    #endif
    
    #define DFU_REGION_TOTAL_SIZE           (BOOTLOADER_REGION_START - CODE_REGION_1_START)             //0XDCF5CFFF            
    
    #ifndef DFU_APP_DATA_RESERVED
    #define DFU_APP_DATA_RESERVED           CODE_PAGE_SIZE * 0                                          //0X00000000   
    #endif
    
    #define DFU_IMAGE_MAX_SIZE_FULL         (DFU_REGION_TOTAL_SIZE - DFU_APP_DATA_RESERVED)             //0XDCF5CFFF    
    
    #define DFU_IMAGE_MAX_SIZE_BANKED       (((DFU_IMAGE_MAX_SIZE_FULL) - \
                                            (DFU_IMAGE_MAX_SIZE_FULL % (2 * CODE_PAGE_SIZE)))/2)        //0X6E7AE000    
                                            
    #define DFU_BL_IMAGE_MAX_SIZE           (BOOTLOADER_SETTINGS_ADDRESS - BOOTLOADER_REGION_START)        
    
    #define DFU_BANK_0_REGION_START         CODE_REGION_1_START                                         //0x2311D001                    
    #define DFU_BANK_1_REGION_START         (DFU_BANK_0_REGION_START + DFU_IMAGE_MAX_SIZE_BANKED)       //0X918CB001

    The issue seems to be the BOOTLOADER_REGION_START value when calculating DFU_REGION_TOTAL_SIZE, as it is less than CODE_REGION_1_START. How do you suggest changing this?

    
    #include "bsp.h"
    
    /******************************************************
    * FUNCTION DECLARATIONS
    *******************************************************/
    uint32_t * start_addr;
    
    /**
     * Function for erasing a page in flash.
     * @param page_address Address of the first word in the page to be erased.
     */
    void flash_page_erase(uint32_t * page_address)
    {
        // Turn on flash erase enable and wait until the NVMC is ready:
        NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos);
    
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
        {
            // Do nothing.
        }
    
        // Erase page:
        NRF_NVMC->ERASEPAGE = (uint32_t)page_address;
    
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
        {
            // Do nothing.
        }
    
        // Turn off flash erase enable and wait until the NVMC is ready:
        NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
    
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
        {
            // Do nothing.
        }
    }
    
    
    /**
     * Function for filling a page in flash with a value.
     * @param[in] address Address of the first word in the page to be filled.
     * @param[in] value Value to be written to flash.
     * Note 32 bit words written! To write less than 32 bit words set the bits that should
     * remain unchanged in the word to '1'
     */
    void flash_word_write(uint32_t * address, uint32_t value)
    {
        // Turn on flash write enable and wait until the NVMC is ready:
        NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
    
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
        {
            // Do nothing.
        }
    
        *address = value;
    
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
        {
            // Do nothing.
        }
    
        // Turn off flash write enable and wait until the NVMC is ready:
        NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
    
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
        {
            // Do nothing.
        }
    }
    
    
    
    void init_flash(int erase)
    {
    	int j;
    	uint32_t * addr;
    	uint32_t  pg_size = NRF_FICR->CODEPAGESIZE;
    	uint32_t  start_pg_num = NRF_FICR->CODESIZE - (71339/pg_size + 1);  // Use last pages in flash
    	start_addr = (uint32_t *) (start_pg_num * pg_size);
    
    	//Erase the pages
    	if (erase == 1) {
    		for (j=0; j<(71339/pg_size + 1);j++)
    		{
    			// Set address:
    			addr = (uint32_t *)(start_addr + j*pg_size/4); //Divide by 4 because address is a count of 32 bit words and pg_size is a count of bytes
    			// Erase page:
    			flash_page_erase(addr);
    		}
    	}
    }
    
    
    
    
    
    
    /***********************************************
    * Function to write an array - slightly dodgy casting?
    ************************************************/
    bool flash_array_write(uint32_t * address, uint8_t dataIn[], uint32_t dataLength, uint32_t * checksum)
    {
    	uint32_t  * data;
    	int j;
    
    	if ((dataLength*4)%4 !=0) return false; //Had to make temporary change while I figure out how to pass actual number of bytes instead of number of ints
    	data = (uint32_t *) dataIn;  //Is this ok?
    
    	for (j=0;j<dataLength;j++)
    	{
    		flash_word_write(address+j, *(data+j)); //data is 4 bytes
    		*checksum += *(data+j);
    	}
    	return true;
    }
    
    
    uint32_t flash_word_read(uint32_t * address)
    {
    	//ensure read only is Enabled
    	NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
    
    	while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
    	{
    			// Do nothing.
    	}
    
    	return *address;
    
    	while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
    	{
    			// Do nothing.
    	}
    }
    

    Thanks again :) 

  • The start address of the my app will be CODE_REGION_1_START right? I've not flashed the bootloader yet as I was trying to resolve this issue first. 

    I'm not really sure how to locate where my app ends in flash but I know that my hex file is around 240KB. 

    I'm using the .ld file form the blinky example which may be the issue, this is what it looks like currently. 

    SEARCH_DIR(.)
    GROUP(-lgcc -lc -lnosys)
    
    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x80000
      UICR (rx) : ORIGIN = 0x10001000, LENGTH = 0x0FFFF000
      RAM (rwx) :  ORIGIN = 0x20000000, LENGTH = 0x10000
    }
    
    SECTIONS
    {
      .uicr 0x1000120C :
      {
    	KEEP(*(.uicr))
      } > UICR
      .fs_data :
      {
        PROVIDE(__start_fs_data = .);
        KEEP(*(.fs_data))
        PROVIDE(__stop_fs_data = .);
      } > RAM
    } INSERT AFTER .data;
    
    INCLUDE "nrf5x_common.ld"

    the origin of flash should maybe be moved to after the softdevice?  

  • Hi Jack, 

     

    No, not really CODE_REGION_1_START is used in the bootloader code to detect where it should swap the old image with the new image. 

    But do you need softdevice in your application ? As far as I can see you are using ESB no BLE ? 

    If you use softdevice, you application start address has to adjust to be located after the softdevice. Please have a look at any example inside ble_peripheral or in the blinky example adjusted for softdevice:  \examples\peripheral\blinky\pca10040\s132\armgcc 

     

    How do you plan to swap image on the board 2 ? You would need to use a bootloader to do the task. 

  • I see, in which case how do I check the start and end points of my application?

    If the soft device is only required for the BLE communication then you're right i wont be needing it, thanks.  I assumed that it would be required to work with the bootloader but I guess not. The plan is to write the new image to the address immediately after the current application and get the bootloader to validate it from there and swap them over. Writing the new image can be done the same way that I am currently receiving the FPGA image, just writing it to an address in flash rather than the FPGA. I'm just not sure which address to write it to. 

  • You can remove the softdevice if you don't plan to use BLE. But you still need the MBR to do the image replacing. What you are planing to do require good understanding on how the MBR works and assessment if a bootloader needed or not. I would suggest you to get familiar with how our bootloader work, how we replace application image and also replace the bootloader itself. How the bootloader is executed and the vector table is forwarded by the MBR. 

    The start point of your application is defined in the .ld file, as in the blinky example :  FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x80000 , start at address 0x00.

    The size of your application shows by the compiler after you build it. Or you can find the size using .map file, or convert to binary file. 

     

     

  • Hello again, sorry for the late response I've been working on this among other things. So currently I've managed to get the boot commands to work over ESB as far as validating an image and booting from an address, in this case still just 0x00000000. 

    I have code which will sucsessfully send a binary file and write it into a memory location of my choosing however the binary file produced by GCC is far too large. 

    From what i understand and from looking at the information on the file in a binary/hex file interpreter, all of the information past  0x00004510 are just zeros. untill you hit the final address in my currently 256MB file 0x10001209, which simply contains 0xFFFFFFFF, which i assume is just storing the flash mask. 

    My question now is, can i chop my binary file down to 0x00004510 and run it? 

    Again, thanks in advance 

    Jack

Reply
  • Hello again, sorry for the late response I've been working on this among other things. So currently I've managed to get the boot commands to work over ESB as far as validating an image and booting from an address, in this case still just 0x00000000. 

    I have code which will sucsessfully send a binary file and write it into a memory location of my choosing however the binary file produced by GCC is far too large. 

    From what i understand and from looking at the information on the file in a binary/hex file interpreter, all of the information past  0x00004510 are just zeros. untill you hit the final address in my currently 256MB file 0x10001209, which simply contains 0xFFFFFFFF, which i assume is just storing the flash mask. 

    My question now is, can i chop my binary file down to 0x00004510 and run it? 

    Again, thanks in advance 

    Jack

Children
  • Hi Jack, 

     

    I guess in your code you have some data to be written to UICR (0x10001209) ? If you do , would suggest you to split the hex file, so you have separate hex for the main application and separate hex for the UICR. Then you convert them to binary. 

    If you keep them in one hex file, gcc has no other way of generating binary other than making that 256MB bin file (binary file has no address, to get to the UICR it has to add the 0xFFFFFFFF)

    In your DFU application, you then receive 2 files one to be written as application and one to be written to UICR

  • I'm currently trying to change the linker file to write the UCIR data right after my application then on start up have the app write the UCIR values to the correct address, is this a good approach? I'm thinking that this would streamline the updating process a bit more. My linker file and the start up code currently looks like this.

     

    /* Linker script to configure memory regions. */
    
    SEARCH_DIR(.)
    GROUP(-lgcc -lc -lnosys)
    
    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x00000, LENGTH = 0x80000
      UICR (rx) : ORIGIN = 0x10001000, LENGTH = 0x0FFFF000
      RAM (rwx) :  ORIGIN = 0x20000000, LENGTH = 0x10000
    }
    
    SECTIONS
    {
      .app 0x000000000 : { *(.app) _eapp = . ;}
      .muicr 0x1000120C :
      AT( ADDR (.app) + SIZEOF (.app) )
      { _uicr = . ;  *(.uicr);  _eucir = . ;  }
      .fs_data :
      {
        PROVIDE(__start_fs_data = .);
        KEEP(*(.fs_data))
        PROVIDE(__stop_fs_data = .);
      } > RAM
    } INSERT AFTER .data;
    
    INCLUDE "nrf5x_common.ld"
    
      //save the UCIR address manully to reduce the size of the bin fin for each applcation
        extern char _eapp, _ucir, _eucir; //beginning of UCIR LMA, beginning of UCIR VMA, end of UCIR VMA
                    // __start_fs_data, //data start address
                    // __stop_fs_data;  //data end address
        //define pointer to the start of both the load and virtual memory addresses fo the UCIR data
        char *src = &_eapp;
        char *dst = &_ucir;
    
        //write the UCIR data across on boot, this should be indpendant of the start address of the application
        while(dst < &_eucir){
          *dst = *src;
          *src = 0xFF; //write flash mask to the temp storage address
          dst++;
          src++;
        }

  • If you have the code to read the UICR and don't try to write to it on every boot then it's fine. We also do that with some of our code to enable errata fix or configure Reset pin, NFC pins. 

    But I don't think you can write to flash by just assigning the value to the pointer. Please look for this line in system_nrf52.c to see how to do it

    #if defined (CONFIG_NFCT_PINS_AS_GPIOS)

     

    I'm not sure why you don't want to edit the hex file. The intel hex definition can be found here. You just need to edit the hex to split it into two. 

    Another option if you don't want to edit the hex is to flash the hex to a chip, then try to read it out using nrfjprog, you can read the application part and the UICR part separately to have 2 hex files. Then you convert the hexes to binary files. 

Related