5
\$\begingroup\$

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?

\$\endgroup\$
11
  • \$\begingroup\$ Since you presently have a vector table at the start of your payload firmware, you can just read the entry address from the second word of that. Do put some thought into if/how the hardware will get pointed to this to make those vectors active, and if you want your bootloader to initialize the stack pointer to the value given with vector table as the hardware would have if this were booted, or if you want the payload firmware to inherit the bootloader's stack which may not match the assumptions it was linked with. \$\endgroup\$ Sep 16, 2016 at 15:02
  • \$\begingroup\$ @ChrisStratton : Thank you for your comment. I am unable to understand the statement put by you : "you can just read the entry address from the second word of that." Why second word? \$\endgroup\$
    – Nama
    Sep 22, 2016 at 6:55
  • \$\begingroup\$ Hello, I am still stuck here only. From boot code, how can i call a function written in application code? Can anyone provide any code snippet or simple example? Any suggestions or comments are welcome. :) \$\endgroup\$
    – Nama
    Sep 22, 2016 at 6:58
  • \$\begingroup\$ You would have to have something like a data structure at an expected address you can read to find the function address. You should have as few such distinct calls as possible, ideally just one non-returning one. As previously mentioned you may be able to use the vector table. SO does not encourage code example requests, ultimately that is a close reason. \$\endgroup\$ Sep 22, 2016 at 14:15
  • \$\begingroup\$ The ARM vector table is at the beginning of the binary flash image for your application. You need to switch the Vector Table over to the new application flash address (VTOR). You can load the Stack Pointer from the first address, and the PC from the second address there. This was not a simple thing to make work. As you have already found, you need to change the origin address for your application, but you also may need to change the origin address in some startup header files in your application. I unpleasantly discovered my vector table getting overwritten by GCC/ARM startup code! \$\endgroup\$
    – Daniel
    Sep 24, 2016 at 4:33

3 Answers 3

2
\$\begingroup\$

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.

\$\endgroup\$
5
  • \$\begingroup\$ Hello Arsenal, thanks for your valued suggestions. It is fine that the compiler and programming language u have used is different. I just wanted the logic. I will use it for my application. :) \$\endgroup\$
    – Nama
    Sep 16, 2016 at 5:03
  • \$\begingroup\$ How to change the start point address of application code to a specific location? \$\endgroup\$
    – Nama
    Sep 16, 2016 at 5:50
  • \$\begingroup\$ @Nama with the approach above, you don't have to fix the start address to a specific location. When you initialize the structure, you put in the address of the function, the linker will resolve that address and everytime you compile your project the address will be correctly stored in the structure. Otherwise, you can usually just use a #pragma location=xyz in front of your startup function, then it will be placed there. \$\endgroup\$
    – Arsenal
    Sep 16, 2016 at 8:11
  • \$\begingroup\$ Assigning fixed locations is going to be different with the asker's GCC toolchain, typically done via sections in the linker map. \$\endgroup\$ Sep 16, 2016 at 14:59
  • \$\begingroup\$ @ChrisStratton I never used GCC, so I can't say how to do it there, I'd guess there would be a similar mechanism to place it at the beginning of a section or a defined address. \$\endgroup\$
    – Arsenal
    Sep 16, 2016 at 17:13
1
\$\begingroup\$

Thank you for your efforts...

As I have stated in my question that I have created a instance of a structure at a fixed location 0x0020100. by using following method.

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

But, i have observed that the structure is not stored at this particular location. So, accessing the required address of function pointer which was stored in this structure at this location was wrong. The logic of accessing the application code from the boot code works perfect.

Correct way to write the structure(any data/variable) at specific location is as follow:

struct CodeInfoStruct __attribute__((section (".ArrSectioninflash"))) CodeInformation =  
{
    &my_run_code     
};

And add section "ArrSectioninflash" in linker file as follow:

.myBufBlock 0x00021000 :    
{
            KEEP(*(.ArrSectioninflash))     
} > FLASH
\$\endgroup\$
0
\$\begingroup\$

Two possible approaches:

  • Make the file downloaded an ELF binary, and parse its headers to determine either the "entry point" or a specific symbol.

  • Just make the bootloader jump to a specific fixed point in the file downloaded, usually the start. Write a linker script to put the entry point in the right place. (Usually this ends up as a "bin" file).

\$\endgroup\$
2
  • \$\begingroup\$ Hello pjc50, thanks for your reply. I am not getting the first point u have mentioned . Can u elaborate it? \$\endgroup\$
    – Nama
    Sep 16, 2016 at 5:07
  • \$\begingroup\$ How to change the start point address of application code to a specific location? \$\endgroup\$
    – Nama
    Sep 16, 2016 at 5:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.