【问题标题】:Bootloader for STM32F405 Not Jumping to ApplicationSTM32F405 的引导加载程序未跳转到应用程序
【发布时间】:2017-11-03 02:14:13
【问题描述】:

我有一个非常小的引导加载程序,它位于基于 STM32F405VGT 芯片的定制设计板上运行的主固件前面。它对两个应用程序都有相当少的修改 startup.s 和链接器文件。主应用程序在加载到闪存的根目录时运行良好,但不会从引导加载程序启动。

当单步执行代码时,一旦它尝试启动应用程序,程序就会以 WWDG_IRQHandler 结束,它是 Default_Handler 的别名,只是在无限循环中坐下来旋转(WWDG 对引导加载程序禁用) .

引导加载程序代码:

    uint32_t addr = 0x08010000;

    /* Get the application stack pointer (First entry in the application vector table) */
    uint32_t appStack = (uint32_t) *((__IO uint32_t*) addr);

    /* Get the application entry point (Second entry in the application vector table) */
    ApplicationEntryPoint entryPoint = (ApplicationEntryPoint)*((__IO uint32_t*)(addr + sizeof(uint32_t)));

    /* would expect the value of entryPoint to be 0x802bc9c based on the values in the .map file as well as the actual values downloaded from the image using openocd.  Instead, it comes back as 0x802bc9d, not sure if this is related to THUMB code */

    /* Reconfigure vector table offset register to match the application location */
    SCB->VTOR = addr;

    /* Set the application stack pointer */
    __set_MSP(appStack);

    /* Start the application */
    entryPoint();

这是应用程序的 .ld 文件:

/* Include memory map */
/* Uncomment this section to use the real memory map */
MEMORY
{
    BOOTLOADER   (rx)    : ORIGIN = 0x08000000, LENGTH =  32K
    USER_PROPS   (rw)    : ORIGIN = 0x08008000, LENGTH =  16K
    SYS_PROPS    (r)     : ORIGIN = 0x0800C000, LENGTH =  16K
    APP_CODE     (rx)    : ORIGIN = 0x08010000, LENGTH = 448K
    SWAP         (rx)    : ORIGIN = 0x08070000, LENGTH = 384K

    RAM          (xrw)   : ORIGIN = 0x20000000, LENGTH = 128K
    BOOT_RAM     (xrw)   : ORIGIN = 0x2001E000, LENGTH =   8K
    CCMRAM       (rw)    : ORIGIN = 0x10000000, LENGTH =  64K
}

/* Uncomment this section to load directly into root memory */
/*
MEMORY
{
  APP_CODE (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
  CCMRAM (rw)     : ORIGIN = 0x10000000, LENGTH = 64K
}
*/

/* Highest address of the user mode stack */
_estack = 0x20020000;    /* end of 128K RAM */

/* Entry Point */
ENTRY(Reset_Handler)



/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size  = 0x000;      /* required amount of heap (none)  */
_Min_Stack_Size = 0x400;      /* required amount of stack */

SECTIONS
{


  /* The startup code goes first into EEPROM */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >APP_CODE


  /* The program code and other data goes into EEPROM */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >APP_CODE

  /* Constant data goes into EEPROM */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >APP_CODE

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >APP_CODE
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >APP_CODE
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >APP_CODE


  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data : 
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM AT> APP_CODE

  /* Uninitialized data section */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

引导加载程序 .ld 是相同的,除了所有对 APP_CODE 的引用都替换为 BOOTLOADER

这是引导加载程序的 startup.s 文件。应用程序的 startup.s 文件是相同的,除了 Boot_Reset_Handler 被称为 Reset_Handler :

.syntax unified
  .cpu cortex-m4
  .fpu softvfp
  .thumb

.global  g_pfnVectors
.global  Default_Handler

/* start address for the initialization values of the .data section. 
defined in linker script */
.word  _sidata
/* start address for the .data section. defined in linker script */  
.word  _sdata
/* end address for the .data section. defined in linker script */
.word  _edata
/* start address for the .bss section. defined in linker script */
.word  _sbss
/* end address for the .bss section. defined in linker script */
.word  _ebss
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */

/**
 * @brief  This is the code that gets called when the processor first
 *          starts execution following a reset event. Only the absolutely
 *          necessary set is performed, after which the application
 *          supplied main() routine is called. 
 * @param  None
 * @retval : None
*/

    .section  .text.Boot_Reset_Handler
  .weak  Boot_Reset_Handler
  .type  Boot_Reset_Handler, %function
Boot_Reset_Handler:  
  ldr   sp, =_estack     /* set stack pointer */

/* Copy the data segment initializers from flash to SRAM */  
  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4

LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1
  cmp  r2, r3
  bcc  CopyDataInit
  ldr  r2, =_sbss
  b  LoopFillZerobss
/* Zero fill the bss segment. */  
FillZerobss:
  movs  r3, #0
  str  r3, [r2], #4

LoopFillZerobss:
  ldr  r3, = _ebss
  cmp  r2, r3
  bcc  FillZerobss

/* Call the clock system intitialization function.*/
  bl  SystemInit   
/* Call static constructors */
    bl __libc_init_array
/* Call the application's entry point.*/
  bl  main
  bx  lr    
.size  Boot_Reset_Handler, .-Boot_Reset_Handler

/**
 * @brief  This is the code that gets called when the processor receives an 
 *         unexpected interrupt.  This simply enters an infinite loop, preserving
 *         the system state for examination by a debugger.
 * @param  None     
 * @retval None       
*/
    .section  .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
  b  Infinite_Loop
  .size  Default_Handler, .-Default_Handler

   .section  .isr_vector,"a",%progbits
  .type  g_pfnVectors, %object
  .size  g_pfnVectors, .-g_pfnVectors



g_pfnVectors:
  .word  _estack
  .word  Boot_Reset_Handler

  .word  NMI_Handler
  .word  HardFault_Handler
  .word  MemManage_Handler
  .word  BusFault_Handler
  .word  UsageFault_Handler
  .word  0
  .word  0
  .word  0
  .word  0
  .word  SVC_Handler
  .word  DebugMon_Handler
  .word  0
  .word  PendSV_Handler
  .word  SysTick_Handler
  ...

我想指出,这不是Bootloader for Cortex M4 - Jump to loaded Application 的重复,尽管问题看起来很相似,但该帖子的作者没有充分解释问题是如何解决的。

一切都是使用用于嵌入式开发的标准 gcc 工具构建的。

【问题讨论】:

  • 实际跳转/调用周围的反汇编是什么样的?
  • 以及应用程序的向量表的反汇编。
  • 您是否正在链接 CMSIS 和/或类似于 system_stm32f4xx.c 的文件?该文件可能有一个名为SystemInit() 的函数,它将SCB->VTOR 设置为与您期望的值不同的值。您可能需要编辑该文件并设置 VECT_TAB_OFFSET 的值以满足您的期望。
  • @kkrambo 你或多或少是正确的。有一个类似的函数,但不在同一个文件中,它确实重置了向量表。一旦我删除了该调用,它就会按预期工作。谢谢。
  • 那是什么功能?!

标签: c assembly linker arm embedded


【解决方案1】:

我在各种 STM32 Cortex-M3 和 M4 部件上使用了以下方法:

给定以下内联汇编函数:

__asm void boot_jump( uint32_t address )
{
   LDR SP, [R0]       ;Load new stack pointer address
   LDR PC, [R0, #4]   ;Load new program counter address
}

引导加载程序因此切换到应用程序映像:

// Switch off core clock before switching vector table
SysTick->CTRL = 0 ;

// Switch off any other enabled interrupts too
...

// Switch vector table
SCB->VTOR = APPLICATION_START_ADDR ;

//Jump to start address
boot_jump( APPLICATION_START_ADDR ) ;

APPLICATION_START_ADDR 是应用程序区域的基地址(在您的示例中为addr);这个地址是应用程序向量表的开始,它以初始堆栈指针和复位向量开始,boot_jump() 函数将这些加载到 SP 和 PC 寄存器中以启动应用程序,就好像它在复位时启动一样。应用程序的复位向量包含应用程序的执行起始地址。

这与您的解决方案之间的明显区别是在切换向量表之前禁用任何中断生成器。当然,您可能不会在引导加载程序中使用任何中断。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    • 2022-09-29
    • 2014-10-24
    • 2019-12-26
    • 1970-01-01
    • 2013-06-13
    • 2023-01-31
    相关资源
    最近更新 更多