5
votes

I am trying to use LLVM to generate code for baremetal ARM Cortex M4 development. The creation of the IR is going well and LLVM is generating (in my oppinion) correct ARM Thumb ASM.

I have purchased an Arm dev kit to do some testing: an Atmel SAM4L-EK http://www.atmel.com/tools/SAM4L-EK.aspx

I created an application that turns on the yellow led on the dev board. (I just want to get something out of the board) I ran an included app with Atmel Studio and the LED works fine. But my application seems to do nothing...

Acording to the manual the LED is connected to PC10 In the datasheet of the ATSAM4LC4C MCU it says that address of the GPIO ports is 0x400E1000, one port takes 0x0200 byes of address space so port C is on 0x400E1000 + 0x0400.

So this is the program output I have (LLVM Output):

.syntax unified
.eabi_attribute 6, 10
.eabi_attribute 9, 2
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 44, 1
.file   ""
.text
.globl  main
.align  2
.type   main,%function
.code   16
.thumb_func
main:
movw    r0, #5120
movw    r2, #5184
movw    r3, #5200
mov.w   r1, #1024
movt    r0, #16398
movt    r2, #16398
movt    r3, #16398
.LBB0_1:
str r1, [r0]
str r1, [r2]
str r1, [r3]
b   .LBB0_1
.Ltmp0:
.size   main, .Ltmp0-main

This code sets bit 10 in GPIOEnableRegister to 1

then bit 10 in OutputDriverEnableRegister to 1

then bit 10 in OutputValueRegister to 1

At that point the LED should turn on...

This is the startup code I used:

.section INTERRUPT_VECTOR, "x"
.global _Reset
_Reset:
B Reset_Handler /* Reset */
B . /* Undefined */
B . /* SWI */
B . /* Prefetch Abort */
B . /* Data Abort */
B . /* reserved */
B . /* IRQ */
B . /* FIQ */

Reset_Handler:
#mov    r0, stack_top
MOV sp,r0
BL main
B .

Both assembler files were compiled to object files like this:

as -mcpu=cortex-m4 -g startup.s -o startup.o

Using the GNU ARM assembler

This is the linker script I used:

ENTRY(_Reset)
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SEARCH_DIR(.)

/* Memory Spaces Definitions */
MEMORY
{
     rom (rx)  : ORIGIN = 0x00000000, LENGTH = 0x00040000 /* flash, 256K */
     ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 /* sram, 32K */
}

SECTIONS
{
 . = 0x0;
 .text : {
 startup.o (INTERRUPT_VECTOR)
 *(.text)
}
.data : { *(.data) }
.bss : { *(.bss COMMON) }
. = ALIGN(8);0
. = . + 0x1000; /* 4kB of stack memory */
stack_top = .;
}

Then the object files were linked to an ELF binary like this:

ld -T linkerscript.ld armtest.o startup.o -o armtest.elf

using the GNU Arm Linker

I then uploaded the ELF binary to the board using Atmel Studio

Bot nothing happens (also not after a reset)

Any insights would greatly appreciated!

1
Whoever voted to close this as "too localized" please note the family-wide applicability of the issue pointed out in the accepted answer!Chris Stratton

1 Answers

5
votes

the cortex-m series does not boot like a non-cortex-m series arm. the cortex-m has a (non arm) traditional interrupt vector table approach rather than a table of instructions like the full sized arms.

/* vectors.s */
.thumb

.word   0x20020000  /* stack top address */
.word   _start      /* 1 Reset */
.word   hang        /* 2 NMI */
.word   hang        /* 3 HardFault */
.word   hang        /* 4 MemManage */
.word   hang        /* 5 BusFault */
.word   hang        /* 6 UsageFault */
.word   hang        /* 7 RESERVED */
.word   hang        /* 8 RESERVED */
.word   hang        /* 9 RESERVED*/
.word   hang        /* 10 RESERVED */
.word   hang        /* 11 SVCall */
.word   hang        /* 12 Debug Monitor */
.word   hang        /* 13 RESERVED */
.word   hang        /* 14 PendSV */
.word   hang        /* 15 SysTick */
.word   hang        /* 16 External Interrupt(0) */
.word   hang        /* 17 External Interrupt(1) */
.word   hang        /* 18 External Interrupt(2) */
.word   hang        /* 19 ...   */

.thumb_func
.global _start
_start:
    /*ldr r0,stacktop */
    /*mov sp,r0*/
    bl notmain
    b hang

.thumb_func
hang:   b .

You need to set the stack top address as the first item specific to your processor. And using llvm you might need to tweak a few directives here, the above was for gnu.