1

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
artless noise
  • 21,212
  • 6
  • 68
  • 105
Sergey
  • 1,166
  • 14
  • 27
  • 2
    what does the disassembly or otherwise a dump of the vector table show? first word should be your stack pointer preset value and the second should be 0x08000121 if it is 0x080000120 then try a .thumb_func on the line before the Reset_Handler label – old_timer May 23 '18 at 01:13
  • @PeterJ_01 I was looking at `pc` and `xPSR` registers. `pc` points to `0x8000120` (`Reset_Handler`) and then to `0x8000110` (`Default_Handler`). `xPST` is `0` after reset and then `0x01000003`. `3` is a code for hardfault. `MSP`, `PSP` and `SP` are `0x20004FFC` which is 4 bytes below end of stack. – Sergey May 23 '18 at 06:20
  • @old_timer it worked! But what is going on? `Reset_Handler` function address is `08000120` and pointer to it in the vector table is `08000121`. I thought it was supposed to be a pointer to the handler. Why is it 1 byte off? – Sergey May 23 '18 at 06:36
  • 1
    @Sergey the lowest bit of `PC` indicates the actual instruction set, `0` is 4-byte ARM, `1` is Thumb. Because Cortex-M is Thumb-only, the lowest bit must always be set to `1`, but the instructions are still fetched from even addresses (`PC & 0xFFFFFFFE`). – followed Monica to Codidact May 23 '18 at 08:29
  • @berendi And it worked when booted from RAM because it didn't look for `Reset_Handler` but looked for command in `0x20000108`? Is it a static address for booting from RAM or there's something else to it? – Sergey May 23 '18 at 08:58
  • 1
    thumb instructions are NOT on odd addresses. The lsbit is used by some instructions to determine which mode to swtich to arm or thumb. Perhaps for future reasons the vector table which was not a full sized arm thing, uses the lsbit to indicate what mode to launch into the code at that address. it is easy to see through experiments that that lsbit is stripped off as it is consumed by the logic that launches the hander or bx or blx or pop or other. the instructions are aligned on even addresses, since the lsbit is otherwise not used it made sense to use that bit by the instructions that ... – old_timer May 23 '18 at 11:42
  • ...can change modes. – old_timer May 23 '18 at 11:42

1 Answers1

3

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 ..*/
Turbo J
  • 7,563
  • 1
  • 23
  • 43
  • You are right. But in this particular example `Reset_vector` pointer address was the one giving the `Hard Fault` error (see comments to the question). Stack was not used at all. But I will keep you correction in mind when setting up stack for C. Thanks! – Sergey May 27 '18 at 11:57