M0 in-line assembler moving from bootloader to main app
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-12 4:38 AM - edited ‎2025-05-20 7:33 AM
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.
- Labels:
-
STM32F0 Series
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-20 3:21 AM
Hi @DiBosco ,
In the article How to share an API between a bootloader and an application, STM32G0 is used.
I assume that you can follow the same steps as both F0 & G0 are based on Cortex-M0.
Try it and let us know if it was helpful, or may be it is missing something.
-Amel
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-20 3:28 AM - edited ‎2025-05-20 3:28 AM
You can't load SP directly due to the reduced ISA of the CM0
You must load the value to R0, then MOV SP, R0
On the F0 you need to copy the vector table to the base of RAM and remap that memory to the zero address space
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-20 3:33 AM
Look at the IAP examples
You can set the SP in the receiving end Reset_Handler as it's not coming back.
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-20 4:03 AM - edited ‎2025-05-20 4:33 AM
Myguess one line more could be enought:
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");
jump_to_application();
}
And much easier can be done from the assembler than C make jump to any address like for example start addres any procedure/function. Also you can do it a bit easier when you create any file with ".S" extension where you can write wthout quote marks and asm directives - just look at the startupxxxx.s file in the generated project. I'm not sure how ldr r0, =0x20002000 can work in inline injection, maybe beter could be done own procedure for this like:
// any file with ".S" extension in folder with sources
.syntax unified
.cpu cortex-m0 // or which one you used
.fpu softvfp
.thumb
.thumb_func
.global set_stack
.type set_stack , %function
.text
//----------------
//======================================================
// set stack pointer procedure
//====================================
// INPUT:
// R0, = ; new stack value with AACP standard
// output: SP reinitialized
//=======================================================
//=========================
set_stack:
// or LDR R0, = 0x20002000
MOV SP, R0 // set the stack
//---
BX LR // go back to invoked place
//=============================================================
This procedure should be invoked from C like:
extern void set_stack( uint32_t address);
set_stack(0x20002000);
// now we can continue C run code like JumToApplication(); and so on
What I want notice - some things from assembler are much much easier to do than in C...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-20 5:41 AM
Thanks for all the sudden replies! :)
We don't need to do any vector table remapping. We're doing a funky thing whereby we're using the bootloader vector table to simply jump to address + bootloader offset and not using interrupts in the bootloader.
That example was not helpful as is uses __Set_MSP function which, as I said, I do not have access to.
@wegi01 I think I tried this:
asm("ldr r0, =0x20002000"); asm("movs sp, r0");
But I shall try that again, just in case. Not sure I agree that having an extra .s file is easier, but at least that would make it easy to have a macro for the address rather than a magic number.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-20 5:51 AM
Changing the SP mid-function will break auto/local variables on the stack.
You won't get assembler errors/warnings about using lower order registers if you do it correctly.
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-20 6:11 AM - edited ‎2025-05-20 6:13 AM
From CMSIS\Include\cmsis_gcc.h
/**
\brief Set Main Stack Pointer
\details Assigns the given value to the Main Stack Pointer (MSP).
\param [in] topOfMainStack Main Stack Pointer value to set
*/
__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack)
{
__ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : );
}
where __STATIC_FORCEINLINE is __attribute__((always_inline)) static inline
But the jump_to_application variable may be stored on the stack. What do you think will happen to it after change of the SP?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-20 6:43 AM - edited ‎2025-05-20 6:46 AM
"Changing the SP mid-function will break auto/local variables on the stack."
Definatelly it's a true. But I assume the user know what hi doing and I see what hi want to doing:
Hi want running the other program and before this hi want reinitialize the stack pointer what in this case is absolutly uderstandable. All variables from stack going to the hell when hi running other program.
I'd check how it look with injection asm directive - probably it work correctly - the C code looks like:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
/* USER CODE BEGIN 2 */
asm("ldr r0, =0x20002000");
asm("mov sp, r0");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
After translation to assembler looks OK:
08000284 <main>:
8000284: b510 push {r4, lr}
8000286: f000 f881 bl 800038c <HAL_Init>
800028a: f7ff ffc9 bl 8000220 <SystemClock_Config>
800028e: 4801 ldr r0, [pc, #4] @ (8000294 <main+0x10>) take into R0 new stack pointer value from the en of function area
8000290: 4685 mov sp, r0 // transfer R0 to SP
8000292: e7fe b.n 8000292 <main+0xe> while(1) - neverending loop
8000294: 20002000 .word 0x20002000 // our new stack pointer value
Notice the 0x08000294 word whats incomming from ARM architecture...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-05-20 7:04 AM - edited ‎2025-05-20 7:11 AM
I can confirm, that I had already tried
static u32 jump_address;
typedef void (*pFunction)(void);
pFunction jump_to_application;
void jump_to_normal_application(void)
{
jump_address = *(volatile u32*) (MAIN_PROG_START_ADDRESS + 4);
jump_to_application = (pFunction) jump_address;
asm("ldr r0, =0x20002000");
asm("mov sp, r0");
jump_to_application();
}
This does compile happily, but I just get a hard fault error as soon as I hit
jump_to_application();
I should have posted that I had tried a whole raft of other things after my original post, but simply forgot to come back here to post findings. At this point I'd just given up.
