I am writing a bootloader for an STM32, where I need to jump from bootloader to the real app.
In C this works, because I can cast an address to a void pointer and assign that to a function pointer, and call the function pointer as follows:
void jump_to_firmware(uint32_t address)
{
uint32_t reset_handler_add = *((volatile uint32_t *)(address + 4));
void (*app_reset_handler)(void) = (void *)reset_handler_add;
SCB->VTOR = address;
uint32_t msp_value = *((volatile uint32_t *)address);
__set_MSP(msp_value);
app_reset_handler();
}
If I use the same implementation in a C++ the gnu compiler will give an error on the cast to void pointer.
include/bootloader.hpp:58:39: error: invalid conversion from 'void*' to 'void (*)()' [-fpermissive]
After googling I found this SO page, which I tried and came to the following implementation in my class:
void JumpToApp()
{
// Quick test if C style cast does work
//jump_to_firmware(_appStartAddress);
uint32_t mspAdress = *((volatile uint32_t *)_appStartAddress);
uint32_t resetHandlerAddress = *((volatile uint32_t *)(_appStartAddress + sizeof(uint32_t)));
// https://stackoverflow.com/questions/1096341/function-pointers-casting-in-c
typedef void (*functionPointer)();
functionPointer resetHandler = 0;
reinterpret_cast<void*&>(resetHandler) = (void*)resetHandlerAddress;
SCB->VTOR = _appStartAddress;
__set_MSP(mspAdress);
resetHandler();
}
In the C++ implementation:
functionPointer resetHandler
is assigned with 0x8035065
SCB->VTOR
is assigned with 0x08020000
mspAddress
is assigned with `0x20020000resetHandler
is calledIn the C implementation:
app_reset_handler
is assigned with 0x8035065
SCB->VTOR
is assigned with 0x08020000
mspAddress
is assigned with `0x20020000app_reset_handler
is calledThe C implementation works, it jumps to my app, the app runs without issues.
The C++ implementation ends up nowhere. It hangs/crashes on the following (to me meaningless) address:
I am trying to keep the amount of source files to a minimum, so I would like to keep the logic in the single class definition.
My questions:
PS: The bootloader code is the same in both cases. The only difference I made to test either implementation is to comment out the code in Bootloader::JumpToApp
and call the C function jump_to_firmware(uint32_t)
instead.
PPS: all peripherals are deinitialized properly. Again, it works fine, the problem only appears when I use this C++ implementation.
The same code will compile in C and C++. You simple has to cast to the correct cast (in C++ you cant assign a void *
to non void *
pointer. It is much more strict than in C.
void jump_to_firmware(uint32_t address)
{
uint32_t reset_handler_add = *((volatile uint32_t *)(address + 4));
void (*app_reset_handler)(void) = (void (*)(void))reset_handler_add;
/* ... */
}
If you do not like those weird casts you can typedef the function.
typedef void handler(void);
void jump_to_firmware(uint32_t address)
{
uint32_t reset_handler_add = *((volatile uint32_t *)(address + 4));
handler *app_reset_handler = (handler *)reset_handler_add;
/* ... */
}