从闪存启动时的 STM32 HardFault

STM32 HardFault when booted from Flash

我有一个简单的 STM32F103 ARM 芯片裸机程序(我使用 GNU 工具链和 openocd 调试器)。

当我从 RAM 运行 它工作正常。当我使用预装的引导加载程序引导芯片并直接跳转到闪存中的 Reset_Handler 位置时,它也可以工作。但是当我尝试从闪存启动时,处理器进入 HardFault 模式。

处理器复位后,pc 寄存器立即指向 Reset_Handler 位置。当我迈出一步时,如果落入Hardfault_Handler.

为什么会这样?我怀疑这是一个糟糕的内存访问,但一切似乎都是一致的。

汇编代码:

.syntax unified
.cpu cortex-m3
.arch armv7-m
.fpu softvfp
.thumb

.section .text

Default_Handler:
Infinite_Loop:
  b Infinite_Loop
  .align 4

Reset_Handler:
  mov r0, #0
  mov r1, #0
  mov r2, #0
  b Reset_Handler


.section .vector,"a",%progbits
.type Vectors, %object
.size Vectors, .-Vectors

Vectors:
  .word _estack
  .word 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
  .word WWDG_IRQHandler
  .word PVD_IRQHandler
  .word TAMPER_IRQHandler
  .word RTC_IRQHandler
  .word FLASH_IRQHandler
  .word RCC_IRQHandler
  .word EXTI0_IRQHandler
  .word EXTI1_IRQHandler
  .word EXTI2_IRQHandler
  .word EXTI3_IRQHandler
  .word EXTI4_IRQHandler
  .word DMA1_Channel1_IRQHandler
  .word DMA1_Channel2_IRQHandler
  .word DMA1_Channel3_IRQHandler
  .word DMA1_Channel4_IRQHandler
  .word DMA1_Channel5_IRQHandler
  .word DMA1_Channel6_IRQHandler
  .word DMA1_Channel7_IRQHandler
  .word ADC1_2_IRQHandler
  .word USB_HP_CAN1_TX_IRQHandler
  .word USB_LP_CAN1_RX0_IRQHandler
  .word CAN1_RX1_IRQHandler
  .word CAN1_SCE_IRQHandler
  .word EXTI9_5_IRQHandler
  .word TIM1_BRK_IRQHandler
  .word TIM1_UP_IRQHandler
  .word TIM1_TRG_COM_IRQHandler
  .word TIM1_CC_IRQHandler
  .word TIM2_IRQHandler
  .word TIM3_IRQHandler
  .word 0
  .word I2C1_EV_IRQHandler
  .word I2C1_ER_IRQHandler
  .word 0
  .word 0
  .word SPI1_IRQHandler
  .word 0
  .word USART1_IRQHandler
  .word USART2_IRQHandler
  .word 0
  .word EXTI15_10_IRQHandler
  .word RTC_Alarm_IRQHandler
  .word USBWakeUp_IRQHandler
  .word 0
  .word 0
  .word 0
  .word 0
  .word 0
  .word 0
  .word 0
  b Reset_Handler

  .weak NMI_Handler
  .thumb_set NMI_Handler,Default_Handler
  .weak HardFault_Handler
  .thumb_set HardFault_Handler,Default_Handler
  .weak MemManage_Handler
  .thumb_set MemManage_Handler,Default_Handler
  .weak BusFault_Handler
  .thumb_set BusFault_Handler,Default_Handler
  .weak UsageFault_Handler
  .thumb_set UsageFault_Handler,Default_Handler
  .weak SVC_Handler
  .thumb_set SVC_Handler,Default_Handler
  .weak DebugMon_Handler
  .thumb_set DebugMon_Handler,Default_Handler
  .weak PendSV_Handler
  .thumb_set PendSV_Handler,Default_Handler
  .weak SysTick_Handler
  .thumb_set SysTick_Handler,Default_Handler
  .weak WWDG_IRQHandler
  .thumb_set WWDG_IRQHandler,Default_Handler
  .weak PVD_IRQHandler
  .thumb_set PVD_IRQHandler,Default_Handler
  .weak TAMPER_IRQHandler
  .thumb_set TAMPER_IRQHandler,Default_Handler
  .weak RTC_IRQHandler
  .thumb_set RTC_IRQHandler,Default_Handler
  .weak FLASH_IRQHandler
  .thumb_set FLASH_IRQHandler,Default_Handler
  .weak RCC_IRQHandler
  .thumb_set RCC_IRQHandler,Default_Handler
  .weak EXTI0_IRQHandler
  .thumb_set EXTI0_IRQHandler,Default_Handler
  .weak EXTI1_IRQHandler
  .thumb_set EXTI1_IRQHandler,Default_Handler
  .weak EXTI2_IRQHandler
  .thumb_set EXTI2_IRQHandler,Default_Handler
  .weak EXTI3_IRQHandler
  .thumb_set EXTI3_IRQHandler,Default_Handler
  .weak EXTI4_IRQHandler
  .thumb_set EXTI4_IRQHandler,Default_Handler
  .weak DMA1_Channel1_IRQHandler
  .thumb_set DMA1_Channel1_IRQHandler,Default_Handler
  .weak DMA1_Channel2_IRQHandler
  .thumb_set DMA1_Channel2_IRQHandler,Default_Handler
  .weak DMA1_Channel3_IRQHandler
  .thumb_set DMA1_Channel3_IRQHandler,Default_Handler
  .weak DMA1_Channel4_IRQHandler
  .thumb_set DMA1_Channel4_IRQHandler,Default_Handler
  .weak DMA1_Channel5_IRQHandler
  .thumb_set DMA1_Channel5_IRQHandler,Default_Handler
  .weak DMA1_Channel6_IRQHandler
  .thumb_set DMA1_Channel6_IRQHandler,Default_Handler
  .weak DMA1_Channel7_IRQHandler
  .thumb_set DMA1_Channel7_IRQHandler,Default_Handler
  .weak ADC1_2_IRQHandler
  .thumb_set ADC1_2_IRQHandler,Default_Handler
  .weak USB_HP_CAN1_TX_IRQHandler
  .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler
  .weak USB_LP_CAN1_RX0_IRQHandler
  .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler
  .weak CAN1_RX1_IRQHandler
  .thumb_set CAN1_RX1_IRQHandler,Default_Handler
  .weak CAN1_SCE_IRQHandler
  .thumb_set CAN1_SCE_IRQHandler,Default_Handler
  .weak EXTI9_5_IRQHandler
  .thumb_set EXTI9_5_IRQHandler,Default_Handler
  .weak TIM1_BRK_IRQHandler
  .thumb_set TIM1_BRK_IRQHandler,Default_Handler
  .weak TIM1_UP_IRQHandler
  .thumb_set TIM1_UP_IRQHandler,Default_Handler
  .weak TIM1_TRG_COM_IRQHandler
  .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler
  .weak TIM1_CC_IRQHandler
  .thumb_set TIM1_CC_IRQHandler,Default_Handler
  .weak TIM2_IRQHandler
  .thumb_set TIM2_IRQHandler,Default_Handler
  .weak TIM3_IRQHandler
  .thumb_set TIM3_IRQHandler,Default_Handler
  .weak I2C1_EV_IRQHandler
  .thumb_set I2C1_EV_IRQHandler,Default_Handler
  .weak I2C1_ER_IRQHandler
  .thumb_set I2C1_ER_IRQHandler,Default_Handler
  .weak SPI1_IRQHandler
  .thumb_set SPI1_IRQHandler,Default_Handler
  .weak USART1_IRQHandler
  .thumb_set USART1_IRQHandler,Default_Handler
  .weak USART2_IRQHandler
  .thumb_set USART2_IRQHandler,Default_Handler
  .weak EXTI15_10_IRQHandler
  .thumb_set EXTI15_10_IRQHandler,Default_Handler
  .weak RTC_Alarm_IRQHandler
  .thumb_set RTC_Alarm_IRQHandler,Default_Handler
  .weak USBWakeUp_IRQHandler
  .thumb_set USBWakeUp_IRQHandler,Default_Handler

.end

RAM 链接描述文件:

OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)

MEMORY {
  RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 20k
  FLASH (rx)  : ORIGIN = 0x8000000,  LENGTH = 64k
}

_estack = 0x20004FFF;

SECTIONS {
  .isr_vector : {
    . = ALIGN(4);
    KEEP(*(.vector))
    . = ALIGN(8);
  } > RAM
  .text : {
    . = ALIGN(4);
    *(.text)
  } > RAM
}

Flash 链接描述文件:

OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)

MEMORY {
  RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 20k
  FLASH (rx)  : ORIGIN = 0x8000000,  LENGTH = 64k
}

_estack = 0x20004FFF;

SECTIONS {
  .isr_vector : {
    . = ALIGN(4);
    KEEP(*(.vector))
    . = ALIGN(8);
  } > FLASH
  .text : {
    . = ALIGN(4);
    *(.text)
  } > FLASH
}

.text 部分的反汇编:

08000110 <ADC1_2_IRQHandler>:
 8000110:       e7fe            b.n     8000110 <ADC1_2_IRQHandler>
 8000112:       bf00            nop
 8000114:       f3af 8000       nop.w
 8000118:       f3af 8000       nop.w
 800011c:       f3af 8000       nop.w

08000120 <Reset_Handler>:
 8000120:       f04f 0000       mov.w   r0, #0
 8000124:       f04f 0100       mov.w   r1, #0
 8000128:       f04f 0200       mov.w   r2, #0
 800012c:       e7f8            b.n     8000120 <Reset_Handler>
 800012e:       bf00            nop

.isr_vector 部分的反汇编(部分):

08000000 <Vectors>:
 8000000:       20004fff        strdcs  r4, [r0], -pc   ; <UNPREDICTABLE>
 8000004:       08000120        stmdaeq r0, {r5, r8}
 8000008:       08000111        stmdaeq r0, {r0, r4, r8}
 800000c:       08000111        stmdaeq r0, {r0, r4, r8}
 8000010:       08000111        stmdaeq r0, {r0, r4, r8}
 ...
 8000108:       08000111        stmdaeq r0, {r0, r4, r8}
 800010c:       00000000        andeq   r0, r0, r0

您的错误在链接描述文件中:

_estack = 0x20004FFF;

由于该值在向量 table 中使用不变,因此 SP 将被初始化为一个奇数值。但是 ARM 规范实际上指定 SP 可以被 8 整除(对齐到 8 字节)。

奇数在第一次使用栈时触发错误

修复很简单:

_estack = 0x20005000;

编辑:

向量中有更多错误table:

8000004:       08000120        stmdaeq r0, {r5, r8}
8000008:       08000111        stmdaeq r0, {r0, r4, r8}

8000004处的"reset handler"应该是08000121,但实际上是08000120。这是无效的 - 最后一位 必须 设置为 thumb 指令集。

一旦将此值加载到 PC 中,这将在执行第一条指令之前触发复位故障。

原因是缺少声明: .type Reset_Handler, %function

在我使用的汇编程序启动文件中还有一些声明:

    .text
    .thumb
    .thumb_func
    .align 1
    .globl Reset_Handler
    .type Reset_Handler, %function
Reset_Handler:
/* code here ..*/