【问题标题】:STM32 HardFault when booted from Flash从 Flash 启动时的 STM32 HardFault
【发布时间】:2018-11-01 17:50:09
【问题描述】:

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

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

【问题讨论】:

  • 向量表的反汇编或其他转储显示什么?第一个单词应该是您的堆栈指针预设值,第二个单词应该是 0x08000121 如果它是 0x080000120 然后在 Reset_Handler 标签之前的行上尝试 .thumb_func
  • @PeterJ_01 我正在查看pcxPSR 寄存器。 pc 指向 0x8000120 (Reset_Handler),然后指向 0x8000110 (Default_Handler)。 xPST 重置后是 0,然后是 0x010000033 是硬故障代码。 MSPPSPSP0x20004FFC,它比栈尾低 4 个字节。
  • @old_timer 成功了!但这是怎么回事? Reset_Handler函数地址是08000120,向量表中指向它的指针是08000121。我认为它应该是指向处理程序的指针。为什么少了 1 个字节?
  • @Sergey PC 的最低位表示实际指令集,0 是 4 字节 ARM,1 是 Thumb。由于 Cortex-M 仅支持 Thumb,因此最低位必须始终设置为 1,但仍会从偶数地址 (PC &amp; 0xFFFFFFFE) 获取指令。
  • 拇指指令不在奇数地址上。一些指令使用 lsbit 来确定切换到 arm 或 thumb 的模式。也许出于未来的原因,向量表不是一个全尺寸的手臂,它使用 lsbit 来指示在该地址的代码中启动什么模式。通过实验很容易看出 lsbit 被剥离,因为它被启动hander 或 bx 或 blx 或 pop 或其他的逻辑所消耗。指令在偶数地址上对齐,因为否则不会使用 lsbit,因此通过 ... 的指令使用该位是有意义的

标签: assembly arm stm32 cortex-m3 stm32f1


【解决方案1】:

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

_estack = 0x20004FFF;

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

第一次使用堆栈时奇数值触发故障。

修复很简单:

_estack = 0x20005000;

编辑:

向量表错误较多:

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

8000004 的“重置处理程序”应该是 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 ..*/

【讨论】:

  • 你是对的。但在这个特定的例子中,Reset_vector 指针地址是给出Hard Fault 错误的那个(参见问题的 cmets)。堆栈根本没有使用。但是在为 C 设置堆栈时,我会提醒您更正。谢谢!
猜你喜欢
  • 2021-07-29
  • 2017-06-13
  • 1970-01-01
  • 2019-01-28
  • 2012-02-16
  • 2014-02-04
  • 2016-07-11
  • 2019-06-07
  • 1970-01-01
相关资源
最近更新 更多