I am trying to learn the startup process of an ARM processor by writing my own startup code and linker script. The chip I use is an LPC810, and I followed the examples from http://midibel.com/blink0.html, and both examples workes on my board, so the circuit is OK. Then, I try to implement a complete list of Exception and IRQ handler, but then the code does not start. I only have a st-link broken off from a NUCLEO board but I don't know how to debug it. Plus, the LPC chip is just plugged on a breadboard so I would try to avoid connecting more wires since many holes are already used or blocked by the ISP programming wires.
This is the linker script lpc810.ld
:
ENTRY(Reset_Handler);
MEMORY {
FLASH(rx) : ORIGIN = 0x0, LENGTH = 0x1000 /* 4K */
RAM(rwx) : ORIGIN = 0x10000000, LENGTH = 0x400 /* 1K */
}
SECTIONS {
. = ORIGIN(FLASH);
.text : {
KEEP(* (.isr_vectors));
. = ALIGN(4);
__vec_end__ = .;
/*. = 0xC4;*/ /* End of vectors */
* (.text);
. = ALIGN(4);
__end_text__ = .;
} > FLASH
__flash_sdata__ = .;
.data : AT(__flash_sdata__){
__data_start__ = .;
* (.data);
. = ALIGN(4);
__data_end__ = .;
} > RAM
.bss : {
__bss_start__ = .;
* (.bss);
. = ALIGN(4);
_bss_end__ = .;
}
__stack_top = ORIGIN(RAM) + LENGTH(RAM);
}
_end = .;
PROVIDE(end = .);
This is the startup code crt0.S
:
/*
This is the startup file for LPC81x. The C definition of the vector tables are defined in vector.c
*/
.section .isr_vectors
.align 2
.long __stack_top
.long Reset_Handler
.long NMI_Handler
.long HardFault_Handler
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long SVCall_Handler
.long 0
.long 0
.long PendSV_Handler
.long SysTick_Handler
.long 0
/* IRQ 0 */
.long SPI0_IRQ
.long SPI1_IRQ
.long 0
.long UART0_IRQ
.long UART1_IRQ
.long UART2_IRQ
.long 0
.long 0
/* IRQ 8 */
.long I2C0_IRQ
.long SCT_IRQ
.long MRT_IRQ
.long CMP_IRQ
.long WDT_IRQ
.long BOD_IRQ
.long 0
.long WKT_IRQ
/* IRQ 16 */
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
/* IRQ 24 */
.long PININT0_IRQ
.long PININT1_IRQ
.long PININT2_IRQ
.long PININT3_IRQ
.long PININT4_IRQ
.long PININT5_IRQ
.long PININT6_IRQ
.long PININT7_IRQ
.text
.align
.global Reset_Handler
Reset_Handler:
b main
I know I am supposed to initialize .data
.bss
and other stuff, but since the minimal example doesn't initialize them, the problem might not lie there. The vector.c
which contains the symbols:
extern void Reset_Handler(void);
extern void NMI_Handler(void);
extern void HardFault_Handler(void);
extern void SVCall_Handler(void);
extern void PendSV_Handler(void);
extern void SysTick_Handler(void);
extern void SPI0_IRQ(void);
extern void SPI1_IRQ(void);
extern void UART0_IRQ(void);
extern void UART1_IRQ(void);
extern void UART2_IRQ(void);
extern void I2C0_IRQ(void);
extern void SCT_IRQ(void);
extern void MRT_IRQ(void);
extern void CMP_IRQ(void);
extern void WDT_IRQ(void);
extern void BOD_IRQ(void);
extern void WKT_IRQ(void);
extern void PININT0_IRQ(void);
extern void PININT1_IRQ(void);
extern void PININT2_IRQ(void);
extern void PININT3_IRQ(void);
extern void PININT4_IRQ(void);
extern void PININT5_IRQ(void);
extern void PININT6_IRQ(void);
extern void PININT7_IRQ(void);
__attribute__((naked))
void _unhandled_exception(void) {
for(;;);
}
void NMI_Handler(void) __attribute__((weak, alias("_unhandled_exception")));
void HardFault_Handler(void) __attribute__((weak, alias("_unhandled_exception")));
void SVCall_Handler(void) __attribute__((weak, alias("_unhandled_exception")));
void PendSV_Handler(void) __attribute__((weak, alias("_unhandled_exception")));
void SysTick_Handler(void) __attribute__((weak, alias("_unhandled_exception")));
void SPI0_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void SPI1_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void UART0_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void UART1_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void UART2_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void I2C0_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void SCT_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void MRT_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void CMP_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void WDT_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void BOD_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void WKT_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void PININT0_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void PININT1_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void PININT2_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void PININT3_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void PININT4_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void PININT5_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void PININT6_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
void PININT7_IRQ(void) __attribute__((weak, alias("_unhandled_exception")));
The main.c
which is basically unmodified from the tutorial:
/*
BLINK0:
A minimal "Blinky" for NXP-LPC810 miniboard
A single-file example using only out-of-the-box arm-gcc compiler and no crt0
Using default reset state values for everything
Pressing the ISP button will change blink rate
WARN: due to missing crt0 - no init of data/bss (see blink1 ;) )
*/
typedef unsigned int volatile * vp;
void main()
{
*(vp)0x4000c1c0 = 0xffffffbfUL; // PINENABLE0 (disable SWclk/dio)
*(vp)0xa0002000 |= (1 << 2); // DIR0 (set pinio-2 to output)
for (;;)
{
*(vp)0xa0002100 ^= (1 << 2); // PIN0 - toggle LED
// Some busy loop waiting...
volatile long vaste = *(vp)0xa0002100 & (1 << 1) ? 250000 : 50000; // PIN0 (fast blink when ISP pressed)
while (vaste > 0) --vaste;
}
}
The CFLAGS
I am using:
CFLAGS=-Os -mthumb -mcpu=cortex-m0plus -nostartfiles -Wl,-T$(LDS),-nostdlib,-Map=$(TGT).map
Update
This is the disassembly code generated by arm-none-eabi-objdump -d
:
main.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_unhandled_exception-0xc4>:
0: 10000400 .word 0x10000400
4: 000000c8 .word 0x000000c8
8: 000000c5 .word 0x000000c5
c: 000000c5 .word 0x000000c5
...
2c: 000000c5 .word 0x000000c5
...
38: 000000c5 .word 0x000000c5
3c: 000000c5 .word 0x000000c5
40: 00000000 .word 0x00000000
44: 000000c5 .word 0x000000c5
48: 000000c5 .word 0x000000c5
4c: 00000000 .word 0x00000000
50: 000000c5 .word 0x000000c5
54: 000000c5 .word 0x000000c5
58: 000000c5 .word 0x000000c5
...
64: 000000c5 .word 0x000000c5
68: 000000c5 .word 0x000000c5
6c: 000000c5 .word 0x000000c5
70: 000000c5 .word 0x000000c5
74: 000000c5 .word 0x000000c5
78: 000000c5 .word 0x000000c5
7c: 00000000 .word 0x00000000
80: 000000c5 .word 0x000000c5
...
a4: 000000c5 .word 0x000000c5
a8: 000000c5 .word 0x000000c5
ac: 000000c5 .word 0x000000c5
b0: 000000c5 .word 0x000000c5
b4: 000000c5 .word 0x000000c5
b8: 000000c5 .word 0x000000c5
bc: 000000c5 .word 0x000000c5
c0: 000000c5 .word 0x000000c5
000000c4 <_unhandled_exception>:
c4: e7fe b.n c4 <_unhandled_exception>
...
000000c8 <Reset_Handler>:
c8: e000 b.n cc <__end_text__>
ca: 46c0 nop ; (mov r8, r8)
Disassembly of section .text.startup:
000000cc <main>:
cc: 4b0d ldr r3, [pc, #52] ; (104 <__end_text__+0x38>)
ce: 2241 movs r2, #65 ; 0x41
d0: 4252 negs r2, r2
d2: 601a str r2, [r3, #0]
d4: 4b0c ldr r3, [pc, #48] ; (108 <__end_text__+0x3c>)
d6: 2104 movs r1, #4
d8: 681a ldr r2, [r3, #0]
da: b082 sub sp, #8
dc: 430a orrs r2, r1
de: 601a str r2, [r3, #0]
e0: 4b0a ldr r3, [pc, #40] ; (10c <__end_text__+0x40>)
e2: 2104 movs r1, #4
e4: 681a ldr r2, [r3, #0]
e6: 404a eors r2, r1
e8: 601a str r2, [r3, #0]
ea: 681b ldr r3, [r3, #0]
ec: 079a lsls r2, r3, #30
ee: d501 bpl.n f4 <main+0x28>
f0: 4b07 ldr r3, [pc, #28] ; (110 <__end_text__+0x44>)
f2: e000 b.n f6 <main+0x2a>
f4: 4b07 ldr r3, [pc, #28] ; (114 <__end_text__+0x48>)
f6: 9301 str r3, [sp, #4]
f8: 9b01 ldr r3, [sp, #4]
fa: 2b00 cmp r3, #0
fc: ddf0 ble.n e0 <main+0x14>
fe: 9b01 ldr r3, [sp, #4]
100: 3b01 subs r3, #1
102: e7f8 b.n f6 <main+0x2a>
104: 4000c1c0 .word 0x4000c1c0
108: a0002000 .word 0xa0002000
10c: a0002100 .word 0xa0002100
110: 0003d090 .word 0x0003d090
114: 0000c350 .word 0x0000c350
The symbol table generated by arm-none-eabi-nm -n
:
000000c4 W BOD_IRQ
000000c4 W CMP_IRQ
000000c4 W HardFault_Handler
000000c4 W I2C0_IRQ
000000c4 W MRT_IRQ
000000c4 W NMI_Handler
000000c4 W PendSV_Handler
000000c4 W PININT0_IRQ
000000c4 W PININT1_IRQ
000000c4 W PININT2_IRQ
000000c4 W PININT3_IRQ
000000c4 W PININT4_IRQ
000000c4 W PININT5_IRQ
000000c4 W PININT6_IRQ
000000c4 W PININT7_IRQ
000000c4 W SCT_IRQ
000000c4 W SPI0_IRQ
000000c4 W SPI1_IRQ
000000c4 W SVCall_Handler
000000c4 W SysTick_Handler
000000c4 W UART0_IRQ
000000c4 W UART1_IRQ
000000c4 W UART2_IRQ
000000c4 T _unhandled_exception
000000c4 T __vec_end__
000000c4 W WDT_IRQ
000000c4 W WKT_IRQ
000000c8 T Reset_Handler
000000cc T __end_text__
000000cc T __flash_sdata__
000000cc T main
10000000 T _bss_end__
10000000 T __bss_start__
10000000 T __data_end__
10000000 T __data_start__
10000000 T _end
10000400 A __stack_top
The main.map
:
Memory Configuration
Name Origin Length Attributes
FLASH 0x0000000000000000 0x0000000000001000 xr
RAM 0x0000000010000000 0x0000000000000400 xrw
*default* 0x0000000000000000 0xffffffffffffffff
Linker script and memory map
0x0000000000000000 . = ORIGIN (FLASH)
.text 0x0000000000000000 0xcc
*(.isr_vectors)
.isr_vectors 0x0000000000000000 0xc4 crt0.o
0x00000000000000c4 . = ALIGN (0x4)
0x00000000000000c4 __vec_end__ = .
*(.text)
.text 0x00000000000000c4 0x0 main.o
.text 0x00000000000000c4 0x2 vectors.o
0x00000000000000c4 SVCall_Handler
0x00000000000000c4 PININT5_IRQ
0x00000000000000c4 HardFault_Handler
0x00000000000000c4 SysTick_Handler
0x00000000000000c4 SPI1_IRQ
0x00000000000000c4 PendSV_Handler
0x00000000000000c4 NMI_Handler
0x00000000000000c4 CMP_IRQ
0x00000000000000c4 SPI0_IRQ
0x00000000000000c4 WKT_IRQ
0x00000000000000c4 PININT0_IRQ
0x00000000000000c4 PININT2_IRQ
0x00000000000000c4 PININT6_IRQ
0x00000000000000c4 PININT1_IRQ
0x00000000000000c4 MRT_IRQ
0x00000000000000c4 WDT_IRQ
0x00000000000000c4 UART2_IRQ
0x00000000000000c4 PININT7_IRQ
0x00000000000000c4 SCT_IRQ
0x00000000000000c4 I2C0_IRQ
0x00000000000000c4 PININT4_IRQ
0x00000000000000c4 BOD_IRQ
0x00000000000000c4 UART0_IRQ
0x00000000000000c4 _unhandled_exception
0x00000000000000c4 PININT3_IRQ
0x00000000000000c4 UART1_IRQ
*fill* 0x00000000000000c6 0x2
.text 0x00000000000000c8 0x4 crt0.o
0x00000000000000c8 Reset_Handler
0x00000000000000cc . = ALIGN (0x4)
0x00000000000000cc __end_text__ = .
0x00000000000000cc __flash_sdata__ = .
.glue_7 0x00000000000000cc 0x0
.glue_7 0x0000000000000000 0x0 linker stubs
.glue_7t 0x00000000000000cc 0x0
.glue_7t 0x0000000000000000 0x0 linker stubs
.vfp11_veneer 0x00000000000000cc 0x0
.vfp11_veneer 0x0000000000000000 0x0 linker stubs
.v4_bx 0x00000000000000cc 0x0
.v4_bx 0x0000000000000000 0x0 linker stubs
.text.startup 0x00000000000000cc 0x4c
.text.startup 0x00000000000000cc 0x4c main.o
0x00000000000000cc main
.iplt 0x0000000000000118 0x0
.iplt 0x0000000000000000 0x0 crt0.o
.rel.dyn 0x0000000000000118 0x0
.rel.iplt 0x0000000000000000 0x0 crt0.o
.data 0x0000000010000000 0x0 load address 0x00000000000000cc
0x0000000010000000 __data_start__ = .
*(.data)
.data 0x0000000010000000 0x0 main.o
.data 0x0000000010000000 0x0 vectors.o
.data 0x0000000010000000 0x0 crt0.o
0x0000000010000000 . = ALIGN (0x4)
0x0000000010000000 __data_end__ = .
.igot.plt 0x0000000010000000 0x0 load address 0x00000000000000cc
.igot.plt 0x0000000000000000 0x0 crt0.o
.bss 0x0000000010000000 0x0 load address 0x00000000000000cc
0x0000000010000000 __bss_start__ = .
*(.bss)
.bss 0x0000000010000000 0x0 main.o
.bss 0x0000000010000000 0x0 vectors.o
.bss 0x0000000010000000 0x0 crt0.o
0x0000000010000000 . = ALIGN (0x4)
0x0000000010000000 _bss_end__ = .
0x0000000010000400 __stack_top = (ORIGIN (RAM) + 0x400)
0x0000000010000000 _end = .
0x0000000010000000 PROVIDE (end, .)
LOAD main.o
LOAD vectors.o
LOAD crt0.o
START GROUP
LOAD /usr/lib/gcc/arm-none-eabi/4.8.2/armv6-m/libgcc.a
LOAD /usr/lib/gcc/arm-none-eabi/4.8.2/../../../arm-none-eabi/lib/armv6-m/libc.a
END GROUP
OUTPUT(main.elf elf32-littlearm)
.comment 0x0000000000000000 0x1f
.comment 0x0000000000000000 0x1f main.o
0x20 (size before relaxing)
.comment 0x0000000000000000 0x20 vectors.o
.ARM.attributes
0x0000000000000000 0x30
.ARM.attributes
0x0000000000000000 0x32 main.o
.ARM.attributes
0x0000000000000032 0x32 vectors.o
.ARM.attributes
0x0000000000000064 0x22 crt0.o
The startup code provides the reset vector, initial stack pointer value, and a symbol for each of the interrupt vectors. When the processor starts, it will initialize the MSP by loading the value stored in the first 4 bytes of the vector table. Then it will jump to the reset handler.
Startup code is a small block of assembly language code that prepares the way for the execution of software written in a high-level language. Each high-level language has its own set of expectations about the run-time environment.
A startup file is a piece of code written in assembly or C language that executes before the main() function of our embedded application. It performs various initialization steps by setting up the hardware of the microcontroller so that the user application can run.
I solved it. After carefully reading http://community.arm.com/docs/DOC-8769, I found that Reset_Handler
should be a function, or .thumb_func
. Apparently a subroutine won't work. All I did was change my
.global Reset_Handler
Reset_Handler:
b main
to:
.func Reset_Handler, Reset_Handler
.type Reset_Handler, %function
.thumb_func
.align
Reset_Handler:
b main
.size Reset_Handler, . - Reset_Handler
.pool
.endfunc
I don't understand the underlying reason yet, but this works.
Update
Apparently dwelch is right. In my original code, if I place .thumb_func
before the .global
, it works. It seems that that directive is needed to generate correct code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With