英文:
Bootloader Jump Function. How to Jump to the right Address?
问题
我正在尝试创建一个引导加载程序,它会跳转到MKE02Z32VFM4(来自Freescale的KEO2系列)上的我的应用程序代码。我正在使用Keil IDE 5和Armv6编译器v6.16进行开发。
在发出跳转指令到应用程序起始地址后,代码跳转到“a”复位处理程序。当执行跳转到__main的指令时,它会跳转到引导加载程序的主函数。闪存内存在链接器文件中定义如下:
#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大小 0x410+0x800=0xC10 => 256字节对齐 => 0xE00
#define ota_part_0_start 0x00000E00 //向量表中断必须是256字节对齐
#define ota_part_0_size 0x00003800 //14KB(14336字节)0xE00+0x3800 => 0x4600
#define ota_part_1_start 0x00004600
#define ota_part_1_size 0x00003800 //14KB(14336字节)0x4600+0x3800 = 0x7E00 || flash_end == 0x00007FFF => 0x100(256)字节空闲
#define m_data_start 0x1FFFFC00 //RAM起始地址
#define m_data_size 0x00001000 //4KB
应用程序的链接器文件(分散文件)使用了这些定义:
#define m_interrupts_start 0x00000E00 //应用程序复位处理程序的地址
#define m_interrupts_size 0x00000200
#define m_flash_config_start 0x00001000 //一些由制造商定义的配置字节
#define m_flash_config_size 0x00000010
#define m_text_start 0x00001010 //应用程序代码的起始地址
#define m_text_size 0x000035F0
#define m_data_start 0x1FFFFC00 //RAM起始地址
#define m_data_size 0x00001000 //4KB
复位处理程序是用汇编语言编写的,我尝试解释其中的指令:
Reset_Handler:
cpsid i /* 屏蔽中断 */
.equ VTOR, 0xE000ED08 //.equ 相当于C中的#define。VTOR = ARMv6预定义标签。0xE000ED08是VectorTableOffsetRegister。
ldr r0, =VTOR // 从内存中加载一个字。将VTOR地址处的值加载到r0中。现在,R0包含向量表的偏移量。
ldr r1, =__Vectors // 从内存中加载一个字。将__Vectors地址处的值加载到r1中。-->__Vectors的第一个字是初始堆栈指针
str r1, [r0] // 将寄存器的内容存储到内存。将r1的内容存储到r0(等于VTOR)的内存地址中-->将初始堆栈指针存储到向量表的第一个字中
ldr r2, [r1] // 从内存中加载一个字。将r2设置为地址r1处内存的字的值-->r2设置为初始堆栈指针的地址
msr msp, r2 // 移动到特殊寄存器。将r2的值移动到msp(主堆栈指针)的特殊寄存器中-->主堆栈指针设置为初始堆栈指针的值
ldr r0, =SystemInit // 将寄存器0设置为SystemInit函数的地址
blx r0 // 分支链接(到r0的地址)
cpsie i /* 解除中断屏蔽 */
ldr r0, =__main
bx r0
.pool
.size Reset_Handler, . - Reset_Handler
引导加载程序代码如下,第一个测试中的地址值为0x00000E00(用户应用程序的起始地址):
__attribute__((naked, noreturn)) void BootJumpASM(uint32_t SP, uint32_t RH)
{
__asm("MSR MSP, r0");
__asm("BX r1"); //<--这是VTOR再次更改为0x00的地方
}
static void BootJump(uint32_t *Address)
{
if (CONTROL_nPRIV_Msk & __get_CONTROL()) //这是来自ARM文档的,但在我们的实现中总是为false并被跳过。
{ /* 不在特权模式下 */
EnablePrivilegedMode();
}
NVIC->ICER[0] = 0xFFFFFFFF;
NVIC->ICPR[0] = 0xFFFFFFFF;
SysTick->CTRL = 0;
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;
if (CONTROL_SPSEL_Msk & __get_CONTROL()) //这是来自ARM文档的,但在我们的实现中总是为false并被跳过(只使用1个堆栈指针)
{ /* MSP未激活 */
__set_MSP(__get_PSP());
__set_CONTROL(__get_CONTROL() & ~CONTROL_SPSEL_Msk);
}
SCB->VTOR = (uint32_t)Address; //将向量表偏移寄存器设置为用户应用程序的起始地址。
BootJumpASM(Address[0], Address[1]); //此函数来自ARM文档
}
在执行以下代码后:
SCB->VTOR = (uint32_t)Address; // 将VTOR设置为0xE00
VTOR确实更新为0xE00。但在执行以下函数后:
__attribute__((naked, n
<details>
<summary>英文:</summary>
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.
</details>
# 答案1
**得分**: 0
问题不在跳转指令上,而是在Keil IDE的调试器上。我按照arm和Keil文档设置了调试环境,但在从bootloader代码环境跳转到应用程序存储区之后,调试器触发了复位。(Bootloader是一个独立的Keil项目。)
在应用程序项目中启动调试器,跳转指令后不会触发这样的复位,并且在反汇编视图中,bootloader按预期执行,跳转指令也正常工作。
感谢大家花时间与我一起尝试找到错误。
<details>
<summary>英文:</summary>
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.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论