Something isn't correct with the code I'm generating. The CPU quickly runs into a HardFault. Here's how I debugged the problem with OpenOCD.
> soft_reset_halt
requesting target halt and executing a soft reset
target state: halted
target halted due to single-step, current mode: Thread
xPSR: 00000000 pc: 0x0000010c msp: 0x20000800
> stm32x mass_erase 0
stm32x mass erase complete
> flash write_bank 0 main.bin 0
wrote 23564 byte from file main.bin to flash bank 0 at offset 0x00000000 in 1.313711s (17.516576 kb/s)
> soft_reset_halt
requesting target halt and executing a soft reset
target state: halted
target halted due to breakpoint, current mode: Thread
xPSR: 00000000 pc: 0x0000010c msp: 0x20000800
> cortex_m3 disassemble 0x0010c 1
0x0000010c 0x2100 MOVS r1, #00
> step
target state: halted
target halted due to single-step, current mode: Handler HardFault
xPSR: 0x01000003 pc: 0x00000138 msp: 0x200007e0
Hmmm. crashed and ended up in the hard fault handler. Notice the msp (main stack pointer) has moved. The CPU dumps a load of state
on the stack before entering the handler. Let's view and decode from msp at 0x200007e0
> mdw 0x200007c0 24
0x200007c0: 25755485 a37a32e8 2d1af580 e55f3d49 6545d91c 0231a83a ae2e2af8 ca03fd92
0x200007e0: ffff3d6d f6ffdff4 21a13c08 0007c589 ffffcfbf ffffffff 0000010c 00000000
0x20000800: b7a2a2ba 317e1c50 2c4c5a0d 6cf67a8b 2988a1a5 6bc1f1dc 99a6d10d 98ef9b9e
This forum post http://www.st.com/mcu/modules.php?mop=modload&name=Splatt_Forums&file=viewtopic&topic=6778&forum=23 gives the following ordering of data on the stack:
r0 ffff3d6d
r1 f6ffdff4
r2 21a13c08
r3 0007c589
r12 ffffcfbf
LR ffffffff
PC 0000010c
xPSR 00000000
We can also have a look at the Hard Fault Status register at 0xE000ED2C. This has the 30th bit set (0b0100 in the top nibble) indicating "hard fault
taken because of bus fault/memory management fault/usage fault" :
> mdw 0xE000ED2C 1
0xe000ed2c: 40000000
See page 180 of the Cortex-M3 Technical Reference Manual http://infocenter.arm.com/help/topic/com.arm.doc.ddi0337e/DDI0337E_cortex_m3_r1p1_trm.pdf
We can check the Memory Management Fault Status Register 0xE000ED28, Bus Fault Status Register 0xE000ED29, and Usage Fault Status Register
0xE000ED2A for more information on the cause. Dumping 4 bytes from the base (together known as CSFR):
> mdb 0xE000ED28 1
0xe000ed28: 00020000
0x00 Memory Management Fault Status Register
0x00 Bus fault Status Register
0x0002 Usage Fault Status Register
Value 02 in the usage fault status register corresponds to bit 1 being set, ie.
[2] INVPC Attempt to load EXC_RETURN into PC illegally. Invalid instruction, invalid context, invalid
value. The return PC points to the instruction that tried to set the PC.
[1] INVSTATE Invalid combination of EPSR and instruction, for reasons other than UNDEFINED
instruction. Return PC points to faulting instruction, with the invalid state.
[0] UNDEFINSTR The UNDEFINSTR flag is set when the processor attempts to execute an undefined
instruction. This is an instruction that the processor cannot decode. The return PC points to
the undefined instruction.
How did the CPU end up in INVSTATE?
Looks like this is a common occurence! http://infocenter.arm.com/help/topic/com.arm.doc.faqs/ka12545.html
"Why does my Cortex-M3 Lock Up with a Hard Fault three cycles after reset?"
The vector table in Cortex-M3 (unlike older ARM processors) is a list of addresses (vectors). These vectors are defined
as interworking-capable, therefore all populated entries in the vector table must have bit[0] SET.
If the Reset vector is an even number, the
processor will immediately call a Usage Fault of type INVSTATE due to
the attempt to invoke the ARM instruction set for the Reset Handler.
Because the Usage Fault is not enabled at Reset, the fault will
escalate to Hard Fault. The Hard Fault vector itself will cause another
Usage Fault due to its bit[0] being CLEAR. The occurrence of a fault
inside the Hard Fault handler will result in the processor
discontinuing fetching and execution, and asserting its LOCKUP output
signal.
Vector table entries which are code entry point
labels will be taken care of by the compilation toolset. For vector
table entries which are written as literal constants in the code, the
programmer must ensure that bit[0] is set to ‘1’.
Ah, that makes sense!
Determined this occurred because I'm missing a .thumb_func modifier for the assembly main function. Here's the correction to startup_stm32f10x_ld.s
.section .text
.weak Reset_Handler
.type Reset_Handler, %function
.thumb_func
Reset_Handler:
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
The listing file now shows the vector table, with the reset vector pointing to the code at 0000010c, adjusted to 10d with the LSB set:
main.out: file format elf32-littlearm
Disassembly of section .text:
00000000 <g_pfnVectors>:
0: 00 08 00 20 0d 01 00 00 39 01 00 00 39 01 00 00 ... ....9...9...
10: 39 01 00 00 39 01 00 00 39 01 00 00 00 00 00 00 9...9...9.......
...
2c: 39 01 00 00 39 01 00 00 00 00 00 00 39 01 00 00 9...9.......9...
It would help a lot if the
.thumb_func modifier were set automatically when
-mcpu=cortex_m3 is passed to
arm-elf-as/arm-elf-ld (maybe I could patch it?).