Cortex-M0+ Linker Script and Startup Code

5

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
assembly
linker
arm
startup
interrupt
asked on Stack Overflow Nov 25, 2014 by Carl Dong • edited Nov 25, 2014 by Carl Dong

2 Answers

7

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.

answered on Stack Overflow Nov 25, 2014 by Carl Dong • edited Nov 25, 2014 by Carl Dong
0

If you add -mthumb to your assembler options (you currently have it in CFLAGS) you shouldn't need .thumb_func prior to Reset_Handler.

-mthumb instructs the assembler to create Thumb instructions, rather than ARM.

answered on Stack Overflow Jul 16, 2017 by donturner

User contributions licensed under CC BY-SA 3.0