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 instructionx/10i $pc
decode 10 instructions from $pcx/10wx $sp
dump 10 words (32 bit values) from $spx/10hx 0x0
dump 10 halfwords (16 bit values) from 0x0x/10bx $sp
dump 10 bytes (8 bit values) from $spset $pc = 0x50
set $pc to 0x50print varname
if varname is in current context print it's value.info reg
show registersbr *0x4c
set breakpoint at 0x4clist *$pc
show the code (C source) of the current$pc
address.monitor reset
reset the targetflushregs
ignore the current values of regs, next time you query them they will be reloaded (this is useful aftermonitor 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
- http://www.esden.net/blog/2009/02/26/how-to-build-arm-gnu-gcc-toolchain-for-mac-os-x/ Building the compiler on MacOS X, it is like Linux
- http://fun-tech.se/stm32/gcc/index.php eLua
- GDB
- more GDB