bengreen.eu Circle Device

 

Techie Stuff

Projects

Quick Reference

Will be moved to Quick Reference soon

Create Cross Compile Toolchain for ARM LPC/mbed

Create Cross Compile Toolchain for LPC/mbed

Warning: This is a work in progress

I have an mbed LPC evaluation board and I need to be able to write raw binary files to the chip... I realise that the aim of the "online compiler" is to make embedded coding easier and somewhat "cooler" than it is when one has to worry about setting up their environment. I also wanted to do it from scratch, I have had to do this before and it helps you learn so...

Firstly download the source for all the required libraries and tools onto your Linux box:

  • binutils
  • mpfr
  • gmp
  • gcc
  • newlib

Our aim is to create a set of executables that are prefixed with arm-none-eabi-. we also DO NOT wish to use any sources modified from the standard distribution... no CodeSourcery Lite arm-none-eabi toolchain!

Building

You should not build the various utilities in the same directories as their source, I use a directory called <blah>-build. Now, lets build binutils and bootstrap GCC.

export PREFIX=/opt/armcortex
cd binutils-build
../binutils-2.22/configure --prefix=$PREFIX --target=arm-none-eabi \
--enable-interwork --enable-multilib --disable-nls --disable-libssp
sudo make install

In the next section we need to edit some files. I found these edits on http://michaldemin.wordpress.com/2010/02/09/arm-toolchain-newlib-part1/ and had a look at the same files in the Mentor Graphics sources (not much help, they must have their changes in a set of patches). From what I can see, all we have done is enable a few things that are disabled (if I was on the mailing lists I am sure I would have known this already).

Files we need to change before we compile GCC bootstrap

In gcc/config/arm/t-arm-elf find a section that looks like this (it is probably commented out with #)

MULTILIB_OPTIONS      += march=armv7
MULTILIB_DIRNAMES     += thumb2
MULTILIB_EXCEPTIONS   += march=armv7* marm/*march=armv7*
MULTILIB_MATCHES      += march?armv7=march?armv7-a
MULTILIB_MATCHES      += march?armv7=march?armv7-r
MULTILIB_MATCHES      += march?armv7=march?armv7-m
MULTILIB_MATCHES      += march?armv7=mcpu?cortex-a8
MULTILIB_MATCHES      += march?armv7=mcpu?cortex-r4
MULTILIB_MATCHES      += march?armv7=mcpu?cortex-m3

In gcc/config/arm/elf.h find the #define SUBTARGET_ASM_FLOAT_SPEC and and change it to look like this:

#define SUBTARGET_ASM_FLOAT_SPEC "%{mapcs-float:-mfloat} \
%{mhard-float:-mfpu=fpa} \
%{!mhard-float:-mfpu=softfpa}"
#endif

I found this information on http://michaldemin.wordpress.com/2010/02/09/arm-toolchain-newlib-part1/. And continue with compilation...

extract newlib
edit nano gcc/config/arm/t-arm-elf
edit nano gcc/config/arm/elf.h
cd ../gcc-build
../gcc-4.7.0/configure --target=arm-none-eabi \
--prefix=$PREFIX --enable-interwork --enable-multilib \
--enable-languages="c" --with-newlib \
--with-headers=../newlib-1.20.0/newlib/libc/include/ --disable-libssp \
--disable-nls --with-system-zlib --with-float=soft --with-gnu-as \
--with-gnu-ld --with-cpu=cortex-m3 --with-tune=cortex-m3 \
--with-mode=thumb
make all-gcc
sudo make install-gcc

A quick check to see if we are ok. At this point we can check that the compiler is able to generate the code we want it to (thumb2 code). Try the below command, you should see something similar.

$ arm-none-eabi-gcc --print-multi-lib
.;
thumb;@mthumb
thumb/thumb2;@mthumb@march=armv7

If you don't, or maybe you see something like this:

$ arm-none-eabi-gcc --print-multi-lib
.;
thumb;@mthumb
fpu;@mfloat-abi=hard

... you have missed something in the files we were modifying earlier. Please have a look at gcc/config/arm/t-arm-elf and gcc/config/arm/elf.h again.

cd newlib-build
export PATH=$PATH:$PREFIX/bin
../newlib-1.20.0/configure --target=arm-none-eabi --prefix=$PREFIX \
--enable-interwork --enable-multilib --disable-libssp --disable-nls \
--with-float=soft --with-gnu-as --with-gnu-ld \
--disable-newlib-supplied-syscalls
make CFLAGS_FOR_TARGET="-ffunction-sections \
                        -fdata-sections \
                        -DPREFER_SIZE_OVER_SPEED \
                        -D__OPTIMIZE_SIZE__ \
                        -Os \
                        -fomit-frame-pointer \
                        -mcpu=cortex-m3 \
                        -mthumb \
                        -mfix-cortex-m3-ldrd \
                        -mfloat-abi=softfp \
                        -D__thumb2__ \
                        -D__BUFSIZ__=256" \
               CCASFLAGS="-mcpu=cortex-m3 \
                          -mthumb \
                          -mfix-cortex-m3-ldrd \
                          -D__thumb2__"
sudo su
export PATH=$PATH:/opt/armcortex/bin
make install
/* we have some stuff here: newlib-build/arm-none-eabi/thumb/thumb2/libgloss/arm  :-)*/

/* go back and build the rest of GCC (libgcc.a) */
cd gcc-build
make all
sudo make install

cd gdb-build
../gdb-7.4/configure --prefix=$PREFIX --target=arm-none-eabi
make
sudo make install

That is all folks, we are done creating our cross compile toolchain.

A useful thing to have is QEMU so you can simulate code you write for the Cortex-M3 hardware (mbed/LPC1768 in my case). For the below I cloned the

cd qemu-build
../qemu/configure --prefix=$PREFIX --target-list=arm-softmmu
make

Writing a Test Programme

Possible Problem: QEMU is not working properly for me when it handles exceptions. I compiled the code from FreeRTOS demo for the LM3S811 and then tried to execute it on the emulator, this worked fine until a task switch causes QEMU to call the USAGE_FAULT interrupt handler. For more details on this please see Felice Tufo's page on the subject.

It is important to have the right memory locations for the stack and all that stuff in your linker script. At the moment QEMU cannot emulate the NXP LPC17xx IC so we wil use another Cortex-M3 chip for our tests, lm3s6965evb. This chip has the UART located at 0x4000c000 and 64kB of SRAM at 0x20000000. In the linker script you will notice that I have put the stack at 0x20008000 there is no special reason for this location except that it is located in main memory, difficult to have your stack anywhere else really ;-).

arm-none-eabi-as -mcpu=cortex-m3  -o startup_LPC17xx.o startup_LPC17xx.s
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -O0 -nostartfiles -Wl,-Map=mbed_barebones_test.map -TLPC17xx.ld startup_LPC17xx.o main.o  -o mbed_barebones_test.elf
arm-none-eabi-objcopy -R .stack -O ihex mbed_barebones_test.elf mbed_barebones_test.hex
arm-none-eabi-objcopy -O binary -j .text -j .data mbed_barebones_test.elf mbed_barebones_test.bin

Makefile and stuff available here, once compiled, kick it off in the simulator:

qemu-system-arm -M lm3s6965evb -cpu cortex-m3 -nographic -monitor \
null -serial stdio -kernel mbed_barebones_test.elf

You can also use the .bin that you could load onto the IC if the UART was located at 0x4000c000 and the function to output to the UART was actually written properly ;-).

Connect to QEMU with GDB:

qemu-system-arm -M lm3s6965evb -m 128 -cpu cortex-m3 -nographic -monitor \
null -serial stdio -kernel mbed_barebones_test.elf -gdb tcp::5022 -S

Then run arm-none-eabi-gdb from another terminal

(gdb) target remote 127.0.0.1:5022
Remote debugging using 127.0.0.1:5022
0x000000c4 in ?? ()
(gdb) x/10i $pc
=> 0xc4:	ldr	r0, [pc, #8]	; (0xd0)
   0xc6:	blx	r0
   0xc8:	ldr	r0, [pc, #8]	; (0xd4)
   0xca:	blx	r0
   0xcc:	b.n	0xcc
   0xce:	movs	r0, r0
   0xd0:	lsls	r5, r1, #4
   0xd2:	movs	r0, r0
   0xd4:	lsls	r1, r3, #4
   0xd6:	movs	r0, r0
(gdb) si

Quick things I found useful when in GDB, go find a tutoial and spend some time with the ARM reference manual, I promise it will pay off :-)

  • si step instruction
  • x/10i $pc decode 10 instructions from $pc
  • x/10wx $sp dump 10 words (32 bit values) from $sp
  • x/10hx 0x0 dump 10 halfwords (16 bit values) from 0x0
  • x/10bx $sp dump 10 bytes (8 bit values) from $sp
  • set $pc = 0x50 set $pc to 0x50
  • print varname if varname is in current context print it's value.
  • info reg show registers
  • br *0x4c set breakpoint at 0x4c
  • list *$pc show the code (C source) of the current $pc address.
  • monitor reset reset the target
  • flushregs ignore the current values of regs, next time you query them they will be reloaded (this is useful after monitor reset)

CMSIS

http://www.onarm.com/cmsis/download/

Interesting Pages of ARMv7M Technical Reference Manual

Stack and exception entering: 647. Stack Pointer manipulation 154.

Doing Stuff with Actual Hardware

Given the problems I had (see above) I thought I should get some hardware and see if it is just a problem with QEMU. I chose an LM3S6965 (nearly the hardware QEMU is emulating, which is LM3S811) and I needed some way of getting my code onto the device (OpenOCD).

More to come...

A link to a page that was quite useful, Debian had all I needed in the repo... don't worry I will be using Gentoo for my main development ;-). http://e2e.ti.com/support/microcontrollers/stellaris_arm_cortex-m3_microcontroller/f/471/t/65137.aspx.

Little Update - 2nd December 2012

I have been using this toolchain for actual work... you know important things that MUST work on the LPC/mbed. I have noticed that my linker script/startup code does not honour the requirement to copy the initialised data into the correct position in RAM at runtime. As you might expect this does not lead to working software, I recommend that you have a look at this page on const pointers and when I get chance I will update this page.

References

Quick Links: Techie Stuff | General | Personal | Quick Reference