I have a simple bare metal program for STM32F103 ARM chip (I use GNU toolchain and openocd debugger).
When I run it from RAM it works fine. When I boot chip using preinstalled bootloader and jump straight to Reset_Handler
location in flash, it also works. But when I try to boot from Flash, processor goes into HardFault mode.
Immediately after processor reset, pc
register points to the Reset_Handler
location. When I make a step, if falls into Hardfault_Handler
.
Why is this happening? I suspect that it is a bad memory access but everything seems to be aligned.
Assembly code:
.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 linker script:
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 linker script:
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
}
Disassembly of section .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
Disassembly of section .isr_vector
(partial):
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
Your error is in the linker script:
_estack = 0x20004FFF;
Since that value is used unchanged in the vector table, the SP will be initialized with an odd value. But the ARM spec actually specifies SP to be dividable by 8 (aligned to 8 bytes).
The odd value triggers a fault as soon as the stack is used for the first time.
Fix is simply:
_estack = 0x20005000;
EDIT:
There are more errors in the vector table:
8000004: 08000120 stmdaeq r0, {r5, r8}
8000008: 08000111 stmdaeq r0, {r0, r4, r8}
The "reset handler" at 8000004
should be 08000121
, but is actually 08000120
. This is invalid - the last bit must be set for the thumb instruction set.
This will trigger a fault on reset as soon as this value is loaded into the PC - before the first instruction can be executed.
The reason is a missing declaration:
.type Reset_Handler, %function
In the assembler startup file that I use are some more declarations:
.text
.thumb
.thumb_func
.align 1
.globl Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* code here ..*/