Electronic – bootloader application on arm controller

armbootloader

I am using a micro-controller with ARM Cortex M4 processor and developing a bootloader application on it.

  1. Compiler : GCC- GNU ARM compiler
  2. Code flash size : 1MB
  3. Interface for application code download : USB CDC ACM class
  4. One application software at PC to transfer the application code binary file to MCU o USB
  5. I have two codes : a. main code(boot code), b. Application code
  6. In application code, run_my_app() function is written.
  7. Flow of main code is as follow :

        reset->
        initialize MCU
        initialize USB
        boot status = CLEAR;
        while(1)
        {
                wait for command from USB : write/ erase
                if(boot status == BOOT_COMPLETE)
                {
                    call a function name run_my_app() from application code;
                }
        }
    
        if erase command received : 
        erase the application code at a flash location 0x0020000
    
        if write command received : 
        Write the application code to flash location 0x0020000
        after completion successful write,
                 boot status == BOOT_COMPLETE;
    

My question is how should i map the run_my_app() function from application code to boot code?
How can i get the address of run_my_code() function so that using function pointer i can call that function?

Hello everyone, I have to modify the entry point of application code to location 0x0020000 in flash from location 0x0000000 using linker script. Currently generated linker script file is :

MEMORY
{
   FLASH (rx)         : ORIGIN = 0x00000000, LENGTH = 0x0100000  /*   1M */
   RAM (rwx)          : ORIGIN = 0x20000000, LENGTH = 0x0030000  /* 192K */
   DATA_FLASH (rx)    : ORIGIN = 0x40100000, LENGTH = 0x0004000  /*  16K */
   QSPI_FLASH (rx)    : ORIGIN = 0x60000000, LENGTH = 0x4000000  /*  64M, Change in QSPI section below also */
}

ENTRY(Reset_Handler)

SECTIONS
{
  .text :
  {
    __ROM_Start = .;
    /* Even though the vector table is not 256 entries (1KB) long, we still allocate that much
     * space because ROM registers are at address 0x400 and there is very little space
     * in between. */
    KEEP(*(.vectors))
    __Vectors_End = .;
    __Vectors_Size = __Vectors_End - __Vectors;
    __end__ = .;

    /* ROM Registers start at address 0x00000400 */
    . = __ROM_Start + 0x400;
    KEEP(*(.rom_registers*))

    /* Reserving 0x100 bytes of space for ROM registers. */
    . = __ROM_Start + 0x500;

    *(.text*)

    KEEP(*(.init))
    KEEP(*(.fini))

    /* .ctors */
    *crtbegin.o(.ctors)
    *crtbegin?.o(.ctors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
    *(SORT(.ctors.*))
    *(.ctors)

    /* .dtors */
    *crtbegin.o(.dtors)
    *crtbegin?.o(.dtors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
    *(SORT(.dtors.*))
    *(.dtors)

    *(.rodata*)

    KEEP(*(.eh_frame*))

    __ROM_End = .;
  } > FLASH = 0xFF
   .............
   ..........

Adding some details to my implementation:

A structure 'CodeInfoStruct' is defined in both boot and application code.

struct CodeInfoStruct

{

uint32_t RunCodeFunctionAddress;

};

In Application code :

Here, a instance of structure is created at a fixed location '0x0020100' and address of the function 'run_my_app' is stored there.

const struct CodeInfoStruct CodeInformation __attribute__((section(".ARM.__at_0x0020100")));

const struct CodeInfoStruct CodeInformation =

{

(uint32_t)run_my_app

};

Also, in linker file, the ROM start address is modified to 0x0020000:

MEMORY 

{

   FLASH (rx)         : ORIGIN = 0x00020000, LENGTH = 0x0100000  /*   1M */

   RAM (rwx)          : ORIGIN = 0x20000000, LENGTH = 0x0030000  /* 192K */

   DATA_FLASH (rx)    : ORIGIN = 0x40100000, LENGTH = 0x0004000  /*  16K */

   QSPI_FLASH (rx)    : ORIGIN = 0x60000000, LENGTH = 0x4000000  /*  64M, Change in QSPI section below also */

}

In boot code:

the structure pointer of 'COdeInfoStruct' is defined.

struct CodeInfoStruct* pCodeInfo = 0x0020100;

And then, the function 'run_my_code' is called from boot code as follow:

void (*trgt_fnc)(void);

trgt_fnc = (void (*)())(pCodeInfo->RunCodeFunctionAddress);
trgt_fnc(); 

But, i get zero value in this 'trgt_fnc'.
Is it a correct way to share address of function run_my code?
What is wrong here?

Best Answer

The way we do it in one of our products is like this (code is based on IAR compiler, so pragmas might differ for you):

We defined a structure which contains important information of the application part (which is called mainpart in our projects):

#pragma pack(push,1)
    struct softwareRevision_t {
        uint8_t VersionSystem;
        uint8_t VersionFunction;
        uint8_t VersionError;
        uint8_t VersionCustomer;
        uint8_t BuildNumber;
    };
#pragma pack(pop)

#pragma pack(push,1)
    struct mainInfoStruct_t {
        softwareRevision_t softwareRevision;
        uint32_t startFunctionAddress;
        uint8_t reserve[27];
        uint32_t bootControl;
    };
#pragma pack(pop)

This contains among other things the uint32_t startFunctionAddress. Now for this structure to be useful, you have to do several things:

  1. Initialize it with meaningful values in the application
  2. Make it const so it can be placed in flash
  3. Tell the linker to place the structure always on the exact same location every time you compile your project
  4. share the location and structure between application and boot part

In the application part:

To share the location, we created a header-file which is included in both parts with a simple define:

#define MAIN_INFO_MEMORY_POSITION (0x0807FFD4)

In a .cpp file of our application we create an instance of the structure which is placed accordingly:

#pragma location = MAIN_INFO_MEMORY_POSITION
extern __root const mainInfoStruct_t mainInfoStruct =
{
    {
            1U,     // mainInfo.softwareRevision.VersionSystem
            0U,     // mainInfo.softwareRevision.VersionFunction
            0U,     // mainInfo.softwareRevision.VersionError
            0U,     // mainInfo.softwareRevision.VersionCustomer
            0U      // mainInfo.softwareRevision.BuildNumber
    },

    reinterpret_cast<uint32_t>((&_start)),  // mainInfo.startFunctionAddress
    {0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U},     // reserve

    0xF0F0FFFFU     // mainInfo.bootControl
};

Several things happen here: #pragma location = MAIN_INFO_MEMORY_POSITION tells the linker where to place the next structure.

The __root tells our linker that this symbol must be kept, even if it is not referenced by the application code (which it isn't). It has to be const otherwise it would not get placed in flash (if it doesn't end up in flash try adding a static).

So with this we have done everything in the application.


In the boot part:

The boot part needs access to the application part info structure. We wrapped a class around this whole thing, but basically a pointer will be enough:

volatile mainInfoStruct_t* pMainInfo = reinterpret_cast<mainInfoStruct_t*>(MAIN_INFO_MEMORY_POSITION);

We use volatile here to make sure, that the compiler will not optimize the access to the flash away. With this you can now jump into the application code with an ugly cast:

(reinterpret_cast<void (*)(void)>(pMainInfo->startFunctionAddress))();

Notes:

In our application we do a CRC check over the whole application part which includes the mainInfoStruct, so we don't jump into nirvana is something went wrong with the update.

The first thing we do in our application is to set the stackpointer to the value it is expected to start with (you can get that from the linker configuration file or your interrupt vector table). Otherwise you might end up with a stack overflow and corrupting your other data.

This is not a minimal example, but might give some hint on what might be useful in a shared structure as well.

We use C++, sorry for the reinterpret_casts.