cancel
Showing results for 
Search instead for 
Did you mean: 

M0 in-line assembler moving from bootloader to main app

DiBosco
Senior II

Folks,

I have a bootloader for an M0 which works without using interrupts. It works in that it loads the new app, the CRC in the new app matches and I then try and jump to 0x8004000.

In the past I have used a couple of different methods with M3/4 to boot to the app from bootloader dependent on what compiler/IDE I was using, but the M0 is slightly different due to how the vector table is not relocatable. So we've come up with a method to get round that and "all" I need to do is jump to 0x8004000.

I *think I need something like this:

void jump_to_normal_application(void)
{

    u32 jump_address;

    jump_address = *(volatile u32*) (MAIN_PROG_START_ADDRESS + 4);
    jump_to_application = (pFunction) jump_address;

    asm("ldr sp, =0x20002000");

    jump_to_application();

}


I'm getting the error:

Error: lo register required -- `ldr sp,=0x20002000'

I've searched this and although I can find many hits  I can't see anything vaguely relevant to my situation.

Also, I would like to make 0x20002000 on line 9 top be a #define or loading from a C variable


Can anyone help me with this please? __Set_MSP is not available to me due to the, errr, "individual" setup I am forced to use.

What is the way to do this and are any of my assumptions above even vaguely correct? Am using gcc.

22 REPLIES 22

>>I can conform, that I had already tried

Then STOP trying to solve the "Problem" this way..

The normative way of addressing multiple firmware images on the F0 / CM0 is to copy an instance of the correctly built vector table (ie address appropriate, bound / fixed by the linker) into the base of RAM 0x20000000 and using SYSCFG to REMAP FLASH vs ROM vs RAM to address 0x00000000 where the MCU is loading the vectors from. This way interrupts all work properly.

The startup.s code for the application portion starts with Reset_Handler doing the equivalent of

Reset_Handler:
                 ldr r0, =__initial_sp ; 0x20002000
                 movs r0, sp
...
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0

STM32Cube_FW_F0_V1.10.1\Projects\STM32091C_EVAL\Applications\IAP

  /* Relocate by software the vector table to the internal SRAM at 0x20000000 ***/

  /* Copy the vector table from the Flash (mapped at the base of the application
     load address 0x08004000) to the base address of the SRAM at 0x20000000. */
  for(i = 0; i < 48; i++)
  {
    VectorTable[i] = *(__IO uint32_t*)(APPLICATION_ADDRESS + (i<<2));
  }

  /* Enable the SYSCFG peripheral clock*/
  __HAL_RCC_SYSCFG_CLK_ENABLE();
  /* Remap SRAM at 0x00000000 */
  __HAL_SYSCFG_REMAPMEMORY_SRAM();

 

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
DiBosco
Senior II

Well, it's a way to do it.

As I say, we're not using interrupts on this bootloader. At all.

 

@DiBosco 

 

void jump_to_normal_application(void)
{

    u32 jump_address;

    jump_address = *(volatile u32*) (MAIN_PROG_START_ADDRESS + 4);
    jump_to_application = (pFunction) jump_address;

    asm("ldr r0, =0x20002000");
    asm("movs sp, r0");
    asm("ldr r1, =jump_address");
    asm("ldr r0, [r1]");
    push r0
    pop pc



//    jump_to_application();
}
DiBosco
Senior II

@wegi01 

Using

asm("ldr r0, =0x20002000");

asm("mov sp, r0");

asm("ldr r1, =0x8004004");

asm("ldr r0, [r1]");

asm("push {r0}");
asm("pop {pc}");


Has it compiling and you can see it then jumps to above 0x8004000. I need to get the second elf file loaded into the debugger to check what happens then, but that looks promising, thank you.

It isn't happy with using "jump_address" in the assembly language, but I can live with that/use a .s file method you suggested. Many thanks.

If you load immediatelly this value 0x08004004 you should to do like before with stack - so:

asm("ldr r0, =0x20002000");
asm("mov sp, r0");

//asm("ldr r1, =0x8004004");
// asm("ldr r0, [r1]"); not in this case !!

asm("ldr r0, =0x8004004");
asm("bx  R0");  // without push/pop

 

Kudos :D

@DiBosco 

If 0x08004004 is the vector table (pointer) you should do this like that:

asm("ldr r0, =0x20002000");
asm("mov sp, r0");

asm("ldr r1, =0x8004004");
asm("ldr r0, [r1]"); 
asm("bx  R0");  // without push/pop

 

If you want exactly run code from 0x080040004 you nedd to do:

asm("ldr r0, =0x20002000");
asm("mov sp, r0");

asm("ldr r0, =0x8004004");
asm("bx  R0");  // without push/pop

 

If you want this working wit jump_address variable:

void jump_to_normal_application(void)
{
    u32 jump_address;

    jump_address = *(volatile u32*) (MAIN_PROG_START_ADDRESS + 4);
    jump_to_application = (pFunction) jump_address;

    asm("ldr r0, =0x20002000");
    asm("movs sp, r0");
    asm("ldr r1, =jump_address");
    asm("ldr r0, [r1]");
    asm("bx r0");
}

 

You need the jump_address variable make as GLOBAL variable not local. 

 

 

 

 

 

asm("ldr r0, =0x8004004");
asm("bx R0"); // without push/pop

That will Hard Fault for sure, even if there code there rather than a jump table.

EVEN addresses are assumed to be 32-bit ARM code, which the CMx MCU's cannot execute. The CM0 will also fault if you read unaligned data.

Entry points deeper into the image are probably best done in a consistent tabular form, so you're not making the loader aware of symbols in an unrelated image. And that you can add others along the way.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

>>Well, it's a way to do it.  As I say, we're not using interrupts on this bootloader. At all.

Indeed, but I'm unclear as to why doing a simple control transfer is so complicated to implement and debug.

Even SysTick? What's the firmware actually doing?

Create less dependencies, let the application code set/reset it's own stack when it enters, ie EntryPoint() if that's supposedly at 0x08004005

And also consider what you're doing to initialize the C runtime environment, this is partially done in GNU's startup.s / Reset_Handler() code, ie clearing BSS, and moving initialization constants from FLASH into their home in RAM.

You can Uphill Ski this as much as you want, but you'll be fighting gravity all the way to the summit.

The IAP examples show a way to do this without fighting the tools, and allowing for the application to make it's own choices about how the system is working, should that change, or need to change, down the road.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

@Tesla DeLorean  Yeah true! I forgot !

 

@DiBosco If you want run code from exactly 0x08004004 - you need get to R0 value bigger than 1 so:

asm("ldr r0, =0x20002000");
asm("mov sp, r0");

asm("ldr r0, =0x8004005"); //!!! make ODD for thumb code
asm("bx  R0");  // without push/pop

 but if this is vector table you do without ODD bcos od is given in vector table automatically by compiler so:

 

asm("ldr r0, =0x20002000");
asm("mov sp, r0");

asm("ldr r1, =0x8004004");
asm("ldr r0, [r1]"); 
asm("bx  R0");  // without push/pop

 

@DiBosco  - don't forgot about interrupts which you have running should you disable interrupt by asm opcode

__asm ("cpsid i");

or peripherials/gpios which you running could be do deinit...

 

 

 

 

DiBosco
Senior II

Again, I'll say that I am forced to use a very unusual setup, with a custom from-scratch makefile, custom headers, custom linker file, all created using emacs. I have to import the make file project into Cube so I can use a debugger with breakpoints and single stepping.

I simply cannot use standard tools, I cannot follow the IAP examples because they use a number of things I just don't have access to.

And yes, absolutely no interrupts. None, nada, zip, zero, zilch, rien; not even systick. All interrupts are off. 100% unused, dormant, unnecessary.