I am trying to create a bootloader that jumps to my application code on a MKE02Z32VFM4 (KEO2 Series from Freescale). I am working with the Keil IDE 5 and the Armv6 Compiler v6.16.
After Issuing the Jump Instruction to the application start address, the code Jumps to "a" reset handler. And when the instruction to jump to __main is reached, it jumps to the main of the bootloader. The Flash Memory is defined by the linker file as followed:
#define m_interrupts_start 0x00000000
#define m_interrupts_size 0x00000200
#define m_flash_config_start 0x00000400
#define m_flash_config_size 0x00000010
#define bootloader_start 0x00000410
#define bootloader_size 0x00000800 //2kb size 0x410+0x800=0xC10 ==> 256 byte aligned => 0xE00
#define ota_part_0_start 0x00000E00 //Vector Table interrupt must be 256 byte aligned
#define ota_part_0_size 0x00003800 //14KB (14336 Byte) 0xE00+0x3800 => 0x4600
#define ota_part_1_start 0x00004600
#define ota_part_1_size 0x00003800 //14KB (14336 Byte) 0x4600+0x3800 = 0x7E00 || flash_end == 0x0000 7FFF => 0x100(256) byte frei
#define m_data_start 0x1FFFFC00 //ram start
#define m_data_size 0x00001000 //4kb
The application linker file (scatter file) is working with these defines:
#define m_interrupts_start 0x00000E00 //Address of the application reset handler
#define m_interrupts_size 0x00000200
#define m_flash_config_start 0x00001000 //some config bytes, defined by manufacturer
#define m_flash_config_size 0x00000010
#define m_text_start 0x00001010 // start address of application code
#define m_text_size 0x000035F0
#define m_data_start 0x1FFFFC00 //ram start
#define m_data_size 0x00001000 //4kb
The reset handler is written in assembler, i tried to comment the instructions:
Reset_Handler:
cpsid i /* Mask interrupts */
.equ VTOR, 0xE000ED08 //.equ is like #define in C. VTOR = predefined ARMv6 label. 0xE000ED08 VectorTableOffsetRegister.
ldr r0, =VTOR // load word from memory. load value from word at VTOR address to r0. R0 now contains the offset for the vector table.
ldr r1, =__Vectors // load word from memory. load value of word at __Vectors address to r1. --> the first word at __Vectors is the initial stack pointer
str r1, [r0] //store Register to memory. content of r1 is stored to memory adress in r0(==VTOR) --> initial stack pointer is stored to the first word of the Vector table
ldr r2, [r1] //load word from memory. r2 is set to the value of the word in memory at address in r1. --> r2 is set to the address of the initial stack pointer
msr msp, r2 //move to special register. move value of r2 to special register msp (main stack pointer) --> main stack pointer is set to the valjue of the initial stack pointer
ldr r0,=SystemInit //set register 0 to address of SystemInit function. (
blx r0 // branch with link ( to address of r0)
cpsie i /* Unmask interrupts */
ldr r0,=__main
bx r0
.pool
.size Reset_Handler, . - Reset_Handler
The bootloader code is as followed: Address in this first test is the value 0x00000E00 (start of user app)
__attribute__( ( naked, noreturn ) ) void BootJumpASM( uint32_t SP, uint32_t RH )
{
__asm("MSR MSP,r0");
__asm("BX r1");
}
static void BootJump( uint32_t *Address )
{
if( CONTROL_nPRIV_Msk & __get_CONTROL( ) ) //THIS is from the arm doku, but it is always false in our implementation and skipped.
{ /* not in privileged mode */
EnablePrivilegedMode( ) ;
}
NVIC->ICER[0] = 0xFFFFFFFF ;
NVIC->ICPR[0] = 0xFFFFFFFF ;
SysTick->CTRL = 0 ;
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk ;
if( CONTROL_SPSEL_Msk & __get_CONTROL( ) ) //THIS is from the arm doku, but it is always false in our implementation and skipped. (only 1 stack pointer used)
{ /* MSP is not active */
__set_MSP( __get_PSP( ) ) ;
__set_CONTROL( __get_CONTROL( ) & ~CONTROL_SPSEL_Msk ) ;
}
SCB->VTOR = ( uint32_t )Address ; //Setting the Vector Table Offset Register to the start of the user app.
BootJumpASM( Address[ 0 ], Address[ 1 ] ) ; //This function is taken from the Arm Documentation
}
After
SCB->VTOR = (uint32_t)Address; // Set VTOR to 0xE00
The VTOR register IS updated to 0xE00. However after executing the function:
__attribute__( ( naked, noreturn ) ) void BootJumpASM( uint32_t SP, uint32_t RH )
{
__asm("MSR MSP,r0");
__asm("BX r1"); //<-- This is the Point where VTOR changes it value to 0x00 again
}
VTOR is 0x00 again and im in the resethandler. This resethandler connects to the bootloader main. So i assume im in the reset handler at 0x00 and not the one at 0xE00. I checked the flash memory and am positive that a Vector Table is located at 0x000 AND 0xE00. I am positive that the firmware of the application is also at the right place in the flash.
I am assuming that I either:
Defined the Memory space wrong.
The BootJumpASM function jumps to a illegal location and the MCU restarts over at 0x00 with a reset VTOR Register.
I am not sure, why the BootJumpASM function uses r0 and r1 and what it does with the arguments of the function. I am simply new at assembler and all the specific compiler attributes. The function like described above is directly copied from:
https://developer.arm.com/documentation/ka002218/latest
And while i do not understand how the compiler manages to put the Function arguments to register r0 and r1 I am sure that the mistake is at my side and not in the official arm docs.
Can someone explain to me, why after the second instruction of the "BootJumpASM" function "VTOR" is reset to 0x00? and why the resethandler ,the debugger is in right after, connects to the bootloader main and not the application main. And how do i manage to jump to the right location in memory.
Thanks for your time. I hope this explanation is not too confusing.
The problem was not the jump instruction, but the Debugger of the Keil IDE. I set up the debug environment according to arm and Keil documentation but after the jump out of the code environment of the bootloader into the application memory area, the Debugger triggered a reset. (Bootloader is a seperate Keil project.)
Starting the debugger within the application project, no such reset is triggered after the jump instruction and following the dissasembly view the bootloader executes as expected and the jump instruction works.
Thanks to all for taking time to try and find the error with me.