When doing small projects, an 8-bit AVR microcontroller or a Microchip PIC will usually suffice. However, as soon as you target larget projects (for example a MP3 decoder in software or advanced Ethernet processing), the capacity of those small devices soon runs out. A processor familiy which is often overlooked by hobbyists is the ARM Cortex-M3. Those are manufactured by TI, NXP and ST and offer a vast arary of internal peripherals while offering the power of a 32-bit processor to quite low cost. Since I have the Stellaris DK-LM3S9B96 development kit from TI, I tried getting the thing running using Linux and tried out some cool stuff with it using entirely open source tools. Here's how it goes.
As a toolchain for an ARM processor, the native ARM GCC comes to mind immediately. We're going to do this step by step, don't worry.
First, choose where you want to install it and set those two variables which will make it easier for you later on. You can also set BLD_PREFIX to somewhere within your home directory, i.e. "${HOME}/bin/${BLD_TARGET}" or something, whichever you like. If you want to install system-wide (root privileges needed, obviously), do it as below:
joequad joe [~/CortexM3]: export BLD_TARGET="arm-elf" joequad joe [~/CortexM3]: export BLD_RAWPREFIX="/usr/local" joequad joe [~/CortexM3]: export BLD_PREFIX="${BLD_RAWPREFIX}/${BLD_TARGET}" joequad joe [~/CortexM3]: export PATH=${PATH}:${BLD_PREFIX}/bin
Or if you want to install locally only do something like:
joequad joe [~/CortexM3]: export BLD_TARGET="arm-elf" joequad joe [~/CortexM3]: export BLD_RAWPREFIX="${HOME}/bin" joequad joe [~/CortexM3]: export BLD_PREFIX="${BLD_RAWPREFIX}/${BLD_TARGET}" joequad joe [~/CortexM3]: export PATH=${PATH}:${BLD_PREFIX}/bin
Which I will use from here on. To test your settings, you can do:
joequad joe [~/CortexM3]: echo "Will install ${BLD_TARGET} into ${BLD_PREFIX}" Will install arm-elf into /home/joe/bin/arm-elf joequad joe [~/CortexM3]: env | grep BLD_ BLD_TARGET=arm-elf BLD_PREFIX=/home/joe/bin/arm-elf BLD_RAWPREFIX=/home/joe/bin
First download the binutils package from here. I chose the latest stable version, 2.20, available at https://ftp.gnu.org/gnu/binutils/binutils-2.20.tar.gz.
joequad joe [~/CortexM3]: wget https://ftp.gnu.org/gnu/binutils/binutils-2.20.tar.gz [...] joequad joe [~/CortexM3]: tar xfz binutils-2.20.tar.gz joequad joe [~/CortexM3]: cd binutils-2.20 joequad joe [~/CortexM3/binutils-2.20]: ./configure --prefix=${BLD_PREFIX} --target=${BLD_TARGET} --disable-werror --disable-interwork [...] joequad joe [~/CortexM3/binutils-2.20]: make -j4 [...] joequad joe [~/CortexM3/binutils-2.20]: make install [...] joequad joe [~/CortexM3/binutils-2.20]: cd ..
You shold then already have a working assembler, which you will need to build a compiler. To check that, do:
joequad joe [~/CortexM3]: ls -lh ${BLD_PREFIX}/bin total 45M -rwxr-xr-x 1 joe users 2,9M 10. Jul 11:39 arm-elf-addr2line -rwxr-xr-x 2 joe users 3,1M 10. Jul 11:39 arm-elf-ar -rwxr-xr-x 2 joe users 4,6M 10. Jul 11:39 arm-elf-as -rwxr-xr-x 1 joe users 2,9M 10. Jul 11:39 arm-elf-c++filt -rwxr-xr-x 1 joe users 3,4M 10. Jul 11:39 arm-elf-gprof -rwxr-xr-x 2 joe users 3,9M 10. Jul 11:40 arm-elf-ld -rwxr-xr-x 2 joe users 3,0M 10. Jul 11:39 arm-elf-nm -rwxr-xr-x 2 joe users 3,7M 10. Jul 11:39 arm-elf-objcopy -rwxr-xr-x 2 joe users 4,1M 10. Jul 11:39 arm-elf-objdump -rwxr-xr-x 2 joe users 3,1M 10. Jul 11:39 arm-elf-ranlib -rwxr-xr-x 1 joe users 811K 10. Jul 11:39 arm-elf-readelf -rwxr-xr-x 1 joe users 3,0M 10. Jul 11:39 arm-elf-size -rwxr-xr-x 1 joe users 2,9M 10. Jul 11:39 arm-elf-strings -rwxr-xr-x 2 joe users 3,7M 10. Jul 11:39 arm-elf-strip
Again, download the gcc package from here. I chose 4.4.3, available at https://ftp.gnu.org/gnu/gcc/gcc-4.4.3/gcc-4.4.3.tar.gz. GCC cannot be built in its own directory, so just follow the steps below and all will be fine.
joequad joe [~/CortexM3]: mkdir gcc joequad joe [~/CortexM3]: cd gcc joequad joe [~/CortexM3/gcc]: tar xfz ../gcc-4.4.3.tar.gz joequad joe [~/CortexM3/gcc]: gcc-4.4.3/configure --prefix=${BLD_PREFIX} --target=${BLD_TARGET} --disable-nls --disable-shared --disable-threads --with-gnu-ld --with-gnu-as --disable-multilib --disable-libssp --disable-libmudflap --disable-libgomp --with-dwarf2 --with-newlib -v --disable-werror --with-cpu=cortex-m3 --with-tune=cortex-m3 --with-mode=thumb --enable-target-optspace --with-float=soft --enable-languages=c --disable-interwork [...] joequad joe [~/CortexM3/gcc]: make -j4 [...] joequad joe [~/CortexM3/gcc]: make install [...] joequad joe [~/CortexM3/gcc]: cd ..
After that last step, you should already have a fully functioning ARM C compiler. However, you still have no libc. This means you can program, but you cannot use printf, strlen, memcpy or anything else. Let's try it out first. Create a file (or download it here) which does not rely on the libc and save it as example.c:
#define PORT_FOO (*((volatile long*)0x11223344)) #define PORT_BAR (*((volatile long*)0xc0ffee)) int main() { int i; for (i = 0; i < 128; i++) { PORT_FOO = i; PORT_BAR = (~i) & 0xc3; } if (PORT_FOO & PORT_BAR) { PORT_FOO = 0x77; } else { PORT_BAR = 0x99; } return 0; }
joequad joe [~/CortexM3/tmp]: ${BLD_PREFIX}/bin/arm-elf-gcc -nostdlib -O2 -Wall -mtune=cortex-m3 -o example example.c /home/joe/bin/arm-elf/lib/gcc/arm-elf/4.4.3/../../../../arm-elf/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000008000 joequad joe [~/CortexM3/tmp]: ${BLD_PREFIX}/arm-elf/bin/objdump -d example example: file format elf32-littlearm Disassembly of section .text: 00008000: 8000: b410 push {r4} 8002: f64f 7cee movw ip, #65518 ; 0xffee 8006: f243 3444 movw r4, #13124 ; 0x3344 800a: 2300 movs r3, #0 800c: f2c1 1422 movt r4, #4386 ; 0x1122 8010: f2c0 0cc0 movt ip, #192 ; 0xc0 8014: 43d8 mvns r0, r3 8016: 6023 str r3, [r4, #0] 8018: 3301 adds r3, #1 801a: f243 3144 movw r1, #13124 ; 0x3344 801e: f000 00c3 and.w r0, r0, #195 ; 0xc3 8022: f64f 72ee movw r2, #65518 ; 0xffee 8026: 2b80 cmp r3, #128 ; 0x80 8028: f2c1 1122 movt r1, #4386 ; 0x1122 802c: f2c0 02c0 movt r2, #192 ; 0xc0 8030: f8cc 0000 str.w r0, [ip] 8034: d1ee bne.n 8014 8036: 680b ldr r3, [r1, #0] 8038: 6810 ldr r0, [r2, #0] 803a: 4218 tst r0, r3 803c: d104 bne.n 8048 803e: 2399 movs r3, #153 ; 0x99 8040: 6013 str r3, [r2, #0] 8042: 2000 movs r0, #0 8044: bc10 pop {r4} 8046: 4770 bx lr 8048: 2377 movs r3, #119 ; 0x77 804a: 600b str r3, [r1, #0] 804c: e7f9 b.n 8042 804e: bf00 nop
So we get a warning that the gcc does not know where to place the code (obviously, it doesn't), but it compiles it just fine. The result already looks great!
For a C library we will be using newlib. Again, retrieve it from here. I chose 1.18.0 available at ftp://sources.redhat.com/pub/newlib/newlib-1.18.0.tar.gz. This one also cannot be built in it's own directory, so be careful here.
joequad joe [~/CortexM3]: mkdir newlib joequad joe [~/CortexM3]: cd newlib joequad joe [~/CortexM3/newlib]: tar xfz ../newlib-1.18.0.tar.gz joequad joe [~/CortexM3/newlib]: newlib-1.18.0/configure --target=${BLD_TARGET} --prefix=${BLD_PREFIX} --disable-multilib --disable-newlib-supplied-syscalls --disable-interwork [...] joequad joe [~/CortexM3/newlib]: make -j4 [...] joequad joe [~/CortexM3/newlib]: make install [...] joequad joe [~/CortexM3/newlib]: cd ..
Everyone who writes code, writes bugs. To minimize the amount of bugs in your code, using a debugger is a good idea - who would've thought that? Retrieve it from here. I used 7.1 available at https://ftp.gnu.org/gnu/gdb/gdb-7.1.tar.gz.
joequad joe [~/CortexM3]: tar xfz gdb-7.1.tar.gz joequad joe [~/CortexM3]: cd gdb-7.1 joequad joe [~/CortexM3/gdb-7.1]: ./configure --target=${BLD_TARGET} --prefix=${BLD_PREFIX} [...] joequad joe [~/CortexM3/gdb-7.1]: make -j4 [...] joequad joe [~/CortexM3/gdb-7.1]: make install [...] joequad joe [~/CortexM3/gdb-7.1]: cd ..
To get a firmware onto the board and for connecting arm-gdb with the board, we will use openocd. In all likelyhood, openocd will be part of your distribution already. However, you should really make sure you get a new version. For this, I will be fetching the GIT version. You should have development files of the libftdi installied (Gentoo: dev-embedded/libftdi) for this. If you do not want to get the GIT version, latest versions can be fetched from http://developer.berlios.de/project/showfiles.php?group_id=4148&release_id=17280.
joequad joe [~/CortexM3]: git clone git://openocd.git.sourceforge.net/gitroot/openocd/openocd Initialized empty Git repository in /home/joe/CortexM3/openocd/.git/ remote: Counting objects: 33584, done. remote: Compressing objects: 100% (13114/13114), done. remote: Total 33584 (delta 27779), reused 24534 (delta 20368) Receiving objects: 100% (33584/33584), 7.25 MiB | 1.04 MiB/s, done. Resolving deltas: 100% (27779/27779), done. joequad joe [~/CortexM3]: cd openocd joequad joe [~/CortexM3/openocd]: ./bootstrap + aclocal + libtoolize --automake --copy + autoconf + autoheader + automake --gnu --add-missing --copy configure.in:18: installing `./compile' configure.in:26: installing `./config.guess' configure.in:26: installing `./config.sub' configure.in:6: installing `./install-sh' configure.in:6: installing `./missing' doc/Makefile.am:1: installing `doc/mdate-sh' doc/Makefile.am:1: installing `doc/texinfo.tex' src/Makefile.am: installing `./depcomp' Makefile.am: installing `./INSTALL' Bootstrap complete; you can './configure --enable-maintainer-mode ....' joequad joe [~/CortexM3/openocd]: ./configure --enable-ft2232_libftdi --prefix=${BLD_PREFIX} joequad joe [~/CortexM3/openocd]: make -j4 [...] joequad joe [~/CortexM3/openocd]: make install [...]
How you get started the fastest is with using TIs Stellaris CD which is provided with the kit. It is available from Texas Instruments (formerly known as Luminary Micro Stellaris, called "Development Kit CD for the DK-LM3S9B96"). Once you downloaded the ZIP file and placed it in ~/CortexM3, do the following:
joequad joe [~/CortexM3]: 7z x DK-LM3S9B96-CD-562.zip 7-Zip 4.65 Copyright (c) 1999-2009 Igor Pavlov 2009-02-03 p7zip Version 4.65 (locale=de_DE.utf8,Utf16=on,HugeFiles=on,4 CPUs) Processing archive: DK-LM3S9B96-CD-562.zip Extracting DK-LM3S9B96-CD-562/AUTORUN.INF Extracting DK-LM3S9B96-CD-562/Acrobat Extracting DK-LM3S9B96-CD-562/Acrobat/AdbeRdr910_en_US.exe Extracting DK-LM3S9B96-CD-562/Documentation Extracting DK-LM3S9B96-CD-562/Documentation/Application_Notes Extracting DK-LM3S9B96-CD-562/Documentation/Application_Notes/AN01237.pdf Extracting DK-LM3S9B96-CD-562/Documentation/Application_Notes/AN01239.pdf Extracting DK-LM3S9B96-CD-562/Documentation/Application_Notes/AN01241.pdf Extracting DK-LM3S9B96-CD-562/Documentation/Application_Notes/AN01243.pdf [...] joequad joe [~/CortexM3]: mkdir stellaris joequad joe [~/CortexM3/stellaris]: cd stellaris joequad joe [~/CortexM3/stellaris]: 7z x ../DK-LM3S9B96-CD-562/Tools/StellarisWare/SW-DK-LM3S9B96-6075.exe 7-Zip 4.65 Copyright (c) 1999-2009 Igor Pavlov 2009-02-03 p7zip Version 4.65 (locale=de_DE.utf8,Utf16=on,HugeFiles=on,4 CPUs) Processing archive: ../DK-LM3S9B96-CD-562/Tools/StellarisWare/SW-DK-LM3S9B96-6075.exe Extracting EULA.txt Extracting license.html Extracting makedefs Extracting Makefile Extracting settings.ini Extracting boards Extracting boards/Makefile Extracting boards/dk-lm3s9b96 Extracting boards/dk-lm3s9b96/cr_workspace.xml Extracting boards/dk-lm3s9b96/dk-lm3s9b96.eww Extracting boards/dk-lm3s9b96/dk-lm3s9b96.mpw Extracting boards/dk-lm3s9b96/dk-lm3s9b96.sgxw Extracting boards/dk-lm3s9b96/dk-lm3s9b96.uvmpw Extracting boards/dk-lm3s9b96/Makefile Extracting boards/dk-lm3s9b96/aes_expanded_key Extracting boards/dk-lm3s9b96/aes_expanded_key/aes_config_opts.h Extracting boards/dk-lm3s9b96/aes_expanded_key/aes_expanded_key.c Extracting boards/dk-lm3s9b96/aes_expanded_key/aes_expanded_key.ewd Extracting boards/dk-lm3s9b96/aes_expanded_key/aes_expanded_key.ewp Extracting boards/dk-lm3s9b96/aes_expanded_key/aes_expanded_key.icf Extracting boards/dk-lm3s9b96/aes_expanded_key/aes_expanded_key.ld [...]
You should then have a working stellaris/ subdirectory. The examples from TI are made so they compile with gcc just like that, no problems at all. So let's try that out at first. Make sure that your newly compiled compiler chain is in the path:
joequad joe [~/CortexM3]: which arm-elf-gcc /home/joe/bin/arm-elf/bin/arm-elf-gcc
If it isn't in the path, check above ("Preparation") and put it in the ${PATH} environment variable.
Now let's adapt the ~/CortexM3/stellaris/makedefs. Around line 54 sould be the prefix for the ARM compiler. Comment out the two lines above and write PREFIX=arm-elf below. Already done!
[...] #****************************************************************************** # # Definitions for using GCC. # #****************************************************************************** ifeq (${COMPILER}, gcc) # # Get the prefix for the tools to use. Use arm-stellaris-eabi if it exists, # otherwise fall back to arm-none-eabi. # #PREFIX=${shell type arm-stellaris-eabi-gcc > /dev/null 2>&1 && \ # echo arm-stellaris-eabi || echo arm-none-eabi} PREFIX=arm-elf [...]
Then we compile the libraries first because the binaries already distributed in the package are incompatible with GCC. Don't worry, though, they are programmed quite well and will compile using GCC without even the slightest warning:
joequad joe [~/CortexM3/stellaris]: cd driverlib joequad joe [~/CortexM3/stellaris/driverlib]: make CC adc.c CC can.c CC comp.c CC cpu.c CC epi.c CC ethernet.c CC flash.c CC gpio.c CC hibernate.c CC i2c.c CC i2s.c CC interrupt.c CC mpu.c CC pwm.c CC qei.c CC ssi.c CC sysctl.c CC systick.c CC timer.c CC uart.c CC udma.c CC usb.c CC watchdog.c AR gcc/libdriver.a joequad joe [~/CortexM3/stellaris/driverlib]: cd ../grlib joequad joe [~/CortexM3/stellaris/grlib]: make [...] CC string.c CC widget.c AR gcc/libgr.a joequad joe [~/CortexM3/stellaris/grlib]: cd .. joequad joe [~/CortexM3/stellaris]: cd boards/dk-lm3s9b96/hello joequad joe [~/CortexM3/stellaris/boards/dk-lm3s9b96/hello]: make CC hello.c CC ../drivers/kitronix320x240x16_ssd2119_8bit.c CC ../drivers/set_pinout.c CC startup_gcc.c LD gcc/hello.axf joequad joe [~/CortexM3/stellaris/boards/dk-lm3s9b96/hello]: ls -lh gcc/ total 84K -rwx------ 1 joe users 69K 10. Jul 18:47 hello.axf -rwx------ 1 joe users 15K 10. Jul 18:47 hello.bin -rw------- 1 joe users 171 10. Jul 18:47 hello.d -rw------- 1 joe users 1,6K 10. Jul 18:47 hello.o -rw------- 1 joe users 453 10. Jul 18:47 kitronix320x240x16_ssd2119_8bit.d -rw------- 1 joe users 6,3K 10. Jul 18:47 kitronix320x240x16_ssd2119_8bit.o -rw------- 1 joe users 308 10. Jul 18:47 set_pinout.d -rw------- 1 joe users 4,1K 10. Jul 18:47 set_pinout.o -rw------- 1 joe users 33 10. Jul 18:47 startup_gcc.d -rw------- 1 joe users 2,5K 10. Jul 18:47 startup_gcc.o
So we just compiled the "Hello World" example for the Devboard in just a few seconds!
To use OpenOCD with your devboard, you need to create a configuration file in ${BLD_PREFIX}/share/openocd/scripts/board. I called mine dk-3s9b96.cfg which is pretty much a copy of the already existent ek-lm3s9b9x.cfg. It contains the following:
# Luminary Micro Stellaris LM3S9B9x Development Kit source [find interface/luminary.cfg] source [find target/lm3s9b9x.cfg] jtag_nsrst_delay 100 # LM3S9B9x Evaluation Board has only srst reset_config srst_onlyYou can then fire up OpenOCD for the first time:
joequad joe [~/CortexM3]: openocd -f ${BLD_PREFIX}/share/openocd/scripts/board/dk-3s9b96.cfg Open On-Chip Debugger 0.4.0 (2010-07-10-13:59) Licensed under GNU GPL v2 For bug reports, read http://openocd.berlios.de/doc/doxygen/bugs.html jtag_nsrst_delay: 100 srst_only separate srst_gates_jtag srst_open_drain Info : clock speed 6000 kHz Info : JTAG tap: lm3s9b9x.cpu tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4) Info : lm3s9b9x.cpu: hardware has 6 breakpoints, 4 watchpoints
OpenOCD is a excellent and really wonderful, versatile tool. It opens up a socket for gdb (port 3333) and one for the user itself (4444) for telnet access (it's just really cool design!). Let's try it out on a separate terminal:
joequad joe [~/CortexM3]: telnet 127.0.0.1 4444 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. Open On-Chip Debugger > halt target state: halted target halted due to debug-request, current mode: Thread xPSR: 0x81000000 pc: 0x00000146 msp: 0x200000f8 > reset JTAG tap: lm3s9b9x.cpu tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4) > mdw 0x0 16 0x00000000: 20000100 00000189 0000017d 00000181 00000185 00000185 00000185 00000000 0x00000020: 00000000 00000000 00000000 00000185 00000185 00000000 00000185 00000185 > mdw 0x1000010 1 0x01000010: 00000054
Here we stopped the device and restartet it and dumped the interrupt vector table. We can see the initially loaded stack pointer is 0x20000100 and the addresses of the ISRs. Then we dumped the ROM version, located at 0x1000010 (see Stellaris ROM Users Guide). It's version 84 (0x54). YMMV.
To flash a firmware image, start up OpenOCD in the directory where the firmware image is located (for us and the "Hello World" example above that would be ~/CortexM3/stellaris/boards/dk-lm3s9b96/hello/gcc). Then do:
joequad joe [~/CortexM3]: telnet 127.0.0.1 4444 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. Open On-Chip Debugger > halt target state: halted target halted due to debug-request, current mode: Thread xPSR: 0x81000000 pc: 0x0000015e msp: 0x200000f8 > flash write_image erase hello.bin auto erase enabled wrote 15360 bytes from file hello.bin in 3.018887s (4.969 kb/s) > reset JTAG tap: lm3s9b9x.cpu tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4)
Presto, the image is on there!
Just for fun, I've coded Pong on the DK-LM3S9B96. It was really easy and a cool "Hello World" application. It's basically just glued some code together that TI provided, I had to do zero to none work for myself. As for the license - I'm unsure. It relies heavily on code by TI and under their license may (I think) only be used with TI devices. Have fun! Download pong.tar.bz2 (includes source code and binary). If you don't have the device, you can at least watch the video of me playing Pong.