Using GCC with TI Stellaris Launchpad – A more in depth look
December 4, 2012 18 Comments
This is the first in a series of posts that will be going over various aspects of using the new Stellaris Launchpad with GCC. This post is going to be a rundown of how the various compiler flags, linker scripts, libraries and drivers work together to give us a working program for our dev board.
A couple of weeks ago the folks over at Recursive Labs posted about using the ARM gcc toolchain to build binaries for the TI Stellaris Launchpad. The directions that RL laid out were pretty straight forward, they made use of the Summon ARM Toolchain (SAT) to get a working copy of GCC and newlib. After that it was a matter of wading through the maze of TI supplied Makefiles to get the proper flags to build our projects.
Multilibs and the Awesomeness of SAT
I’ve never used SAT before to build and ARM toolchain and I have to say that its really fantastic, and the folks that have been working on it are under no certain terms, considered a hero by me. When you build the toolchain it will build it with Multilib support by default, which means it will compile a version of newlib (libc, libm) and libgcc for various ARM processors (Cortex-M0, M3, and our M4).
Not only that, it will build libraries for our Cortex-M4 in both softfp and hard floating point unit flavors so we can bounce back and forth between the two just by changing a gcc flag.
GCC
There are a bunch flags that are being passed to the compiler that at first glance didn’t make a bunch of sense. After a little digging around I’ve found what most out what most of the flags stand for.
CFLAGS += -mthumb #Using the Thumb Instruction Set
CFLAGS += -mcpu=cortex-m4 #The CPU Variant
CFLAGS += -mfloat-abi=softfp #Which floating point ABI to use
CFLAGS += -mfpu=fpv4-sp-d16 #The type of FPU we are using
CFLAGS += -Os #Compile with Size Optimizations
CFLAGS += -ffunction-sections #Create a separate function section
CFLAGS += -fdata-sections #Create a separate data section
CFLAGS += -MD #Create dependency files (*.d)
CFLAGS += -std=c99 #Comply with C99
CFLAGS += -Wall #Be anal Enable All Warnings
CFLAGS += -pedantic #Be extra anal More ANSI Checks
CFLAGS += -Dgcc #Flag used in driverlib for compiler specific flags
CFLAGS += -DPART_LM4F120H5QR #Flag used in driverlib for specifying the silicon version.
CFLAGS += -DTARGET_IS_BLIZZARD_RA1 #Used in driverlib to determine what is loaded in rom.
Loader
Another thing that could use some de-mystification is the loader scripts that tell the linker where to put all the data, text, and bss sections of the executable you build when its flashed to the device. There is one included with each of the demo projects and if you open them all up, you’ll notice that they are all the same. So you can pick one and re-use it with each of your projects.
After all of the sources are compiled and you have a stack of object files the next step is to link them together using arm-none-eabi-ld
. This is also the step where the stdlib functions get brought into the mix as well. Unlike compiling a normal program there are a couple extra things that need to be done here.
LDFLAGS += -T blinky.ld #Path to Linker Script
LDFLAGS += --entry ResetISR #Name of the application entry point
LDFLAGS += --gc-sections #Tell the linker to ignore functions that aren't used.
Using GCC as the Linker
Normally when you compile a program, gcc will perform a lot of magic for you behind the scenes by compiling and linking your program all in one step. Here we are doing it in three steps, compiling all of the source files, linking all the objects together into and ELF executable, and then copying the relevant sections out of the ELF executable and dumping them in a raw binary format that can be flashed to the device.
If you were interested in skipping the middle step that would be possible but the flags can get kind of messy. Since we need to pass in a few arguments to the linker itself (listed below), we do that by prefacing all the arguments with -Wl,<ARG>
. It would look something like this.
arm-none-eabi-gcc $(CFLAGS) -c blinky.c
arm-none-eabi-gcc $(CFLAGS) -c startup_gcc.c
arm-none-eabi-gcc $(CFLAGS) -Wl,--script=blinky.ld -Wl,--entry ResetISR \
-Wl,--gc-sections -o blinky.out blinky.o startup_gcc.o libm.a libc.a libgcc.a
Is there any advantage to doing this? I don’t think so, but I thought it was worth a blurb to show the difference.
Using StellarisWare’s DriverLib
When you are writing your program and want to use the functionality within driverlib to setup the peripherals you will need to compile driverlib with gcc. After you have built your toolchain and added the bin directory to your PATH variable cd into the Stellarisware root folder and type make
. This will go through and build all the projects and driverlib, allowing you to link the function calls into your programs.
With that out of the way, now we’re going to discover why you don’t actually need to use any of it!
ROM, MAP, and Driverlib
One thing that confused me for a bit in using Stellarisware was the difference between calls to driverlib functions being prefixed with ROM_
or MAP_
. Confusion that would have been easily remedied if I had bothered to read the manual, but thats no fun. So to save anyone else a trip to the documentation center here is a brief rundown of what’s happening.
The Stellaris Launchpad comes preloaded with a copy of driverlib in ROM so that when you are creating your programs you don’t need to link in a copy of driverlib yourself. What the MAP_
and ROM_
prefixes are meant to accomplish is increase the portability of applications you write for the LaunchPad. Different versions of the hardware will have different subsets of driverlib loaded into ROM.
//Has the defines for the constants passed to the
//functions as well as the stubs for the plain function calls
#include "driverlib/sysctl.h"
//Has the definitions for ROM_XXX function calls
#include "driverlib/rom.h"
//Has the definitions for MAP_XXX function calls
//and relies on the information in rom.h to work.
#include "driverlib/rom_map.h"
/* This function call to set the clock rate will require that you link
* Driverlib as part of the compilation process.
*/
SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
/* This function call to set the clock rate will not require that you
* link in driverlib during compilation. But will fail to compile if
* the particular device you are using does not have this function loaded into ROM.
*/
ROM_SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
/* This function call to set the clock rate will not require that you
* link in driverlib during compilation, unless the device you are
* using doesn't have the function in ROM, then it will link against driverlib.
*/
MAP_SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
One thing that may be important to note about using this feature would be the -DTARGET_IS_BLIZZARD_RA1
flag that is passed to gcc during compilation, this tells the preprocessor what device the code is being compiled for and maps in the correct function calls for MAP_
ROM_
calls. Not passing this in will result in compilation errors.
Another thing to note that has burned me a few times is if you ever nest these function calls like this
//Won’t Work
MAP_UARTConfigSetExpClk(UART0_BASE, MAP_SysCtlClockGet(), 115200, UART_CONFIG_PAR_NONE);
//Should Work
MAP_UARTConfigSetExpClk(UART0_BASE, (MAP_SysCtlClockGet()) , 115200, UART_CONFIG_PAR_NONE);
You need to make sure that you wrap the inner calls to MAP_
ROM_
functions in ()
other wise the preprocessor will get them mucked up and will result in a compilation error, or, and this is what happened to me, will result in a runtime error where the UART will stop working for apparently no reason.
So if you plan on moving your code to a different development platform down the road, it would be be a smart idea to use the MAP_
version of the function calls to save yourself some hassle down the road. If not, at least use the ROM_
calls to save yourself some code size. If you end up needing to modify the code in driverlib you can just make a call to the regular function and it will link in your modified version.
If you want further reading check out the Driverlib User’s Guide (SW-DRL-UG-9453.pdf
) in the doc/ folder of your Stellarisware directory.
The Goods
All the of the code presented here can be found on my github account located here. There is some extra functionality included in the code that I haven’t covered yet, so if you want a sneak peak at whats coming, take a look.
wget https://github.com/eehusky/Stellaris-GCC/archive/b1-intro.tar.gz
tar xf b1-intro.tar.gz
cd Stellaris-GCC-b1-intro/prog0
Disclaimer:
This is my first attempt at “blogging”, so if anyone has a comments/criticisms please drop a comment below. Any feedback would be greatly appreciated.
This Series
Stellaris-GCC: Intro
Stellaris-GCC: Newlib-SysCalls-Stacks-Heaps-Oh My!
Stellaris-GCC: The Hard FPU
Stellaris-GCC: SD-Cards and File Systems
References
Recursive Labs: Programming the Stellaris Launchpad with GNU/Linux
GitHub: EEhusky – Stellaris-GCC
GitHub: summon-arm-toolchain
GitHub: lm4tools
TI: Stellaris Ware Download
gnu.org: ARM-Options
Pingback: A study of GCC and the TI Stellaris
Well this is a nice writup!
I’ve a question: in your linker script you’re allocating the heap after the stack starting at the end of ram, isn’t the stack supposed to grow downwards and the heap upwards under that?
Yes it is, and I wish I had a compelling reason for doing it this way. But if I had to venture a guess, when I was working on this I defined the stack first and never switched it around.
In the next post I actually go over this so check back some time in the next week!
This is a good starting point for the launchpad. However …
Didn’t you have problems with malloc in newlib ?
As I remember, I did this switching once, and default malloc understood that it ran out of memory (it checks that the stack pointer > heap pointer) and returned 0.
I don’t recall having any problems with it, but I didn’t thoroughly test it, or care, since printf worked and all I was really after at that point was printing floating point numbers.
I’m realizing now I should have left that stuff out of the repo until I post the next article (Tomorrow Hopefully) as most of it is incorrect/hacky.
Pingback: – A study of GCC and the TI Stellaris
Thank you very much for this rundown. I was wondering about the driverlib in ROM feature of the Stellaris Launchpad and now I know.
First of all, thank you very much for your post. It has been of great help.
I use linux, and since the support of Code Compiler Studio is not very good (to say it nicely) I followed these steps to be able to program my Stellaris Launchpad.
I am following this serie of tutorials ( http://processors.wiki.ti.com/index.php/Getting_Started_with_the_Stellaris_EK-LM4F120XL_LaunchPad_Workshop ) and currently I am at lab 4, where interruptions are introduced.
When I try to compile the provided example, I get the following error:
startup_ccs.c:(.text.ResetISR+0x0): undefined reference to `_c_int00′
Which refers to the following fragment of code:
ResetISR(void)
{
//
// Jump to the CCS C initialization routine. This will enable the
// floating-point unit as well, so that does not need to be done here.
//
__asm(” .global _c_int00\n”
” b.w _c_int00″);
…
After many headaches a Google search suggested that it might be caused because I don’t have a runtime library which is provided by CCS. Now I am dowloading it only because of that file, but I really don’t know what I am supposed to do with it.
Has anyone else encountered with this problem? Is there any way to be able to use interruptions without installing CCS?
Take a look at this. (ignore line 193 for now)
https://github.com/eehusky/Stellaris-GCC/blob/master/proj0/src/startup.c#L182
When the processor powers up the first Instruction address to be loaded in the PC register is the beginning of this Function. What you want to do inside the ResetISR is perform any hardware setup routines that would need to be called when the device is powered up. In this case we are copying the data section from FLASH to RAM and zeroing out the BSS section. After that is complete you need to call the entry point to your actual program, i.e. main.
What the code you posted is doing is calling the C run time initialize routine provided by CCS (were using the one provided by newlib), which in turn calls your main().
If you are compiling this with GCC, you may want to take a look at the startup_gcc.c file that is included in all the example projects of Stellarisware. Those examples will work right out the box.
Oh, I feel like an idiot for having problems with this, thank you very much!
I followed the tutorial without thinking about the difference between startup_gcc.c and startup_ccs.c … And just used _ccs.c, as the tutorial said.
Thanks again for taking your time in answering me, and I hope you keep up the quality of your posts! 😉
Very Cool, I just ordered the TI Stellaris Launchpad. I will start following your progress. Can’t wait to get the hardware.
Thanks
Joe
Hello! I was wondring if you tried to build any of the LAB examples with GCC. I can build all the examples on the StellarisWare but not the examples of the LABs.
undefined reference to `SysCtlClockSet’
undefined reference to `SysCtlPeripheralEnable’
undefined reference to `GPIOPinWrite’
etc.
The LAB examples: http://dl.dropbox.com/u/31742622/Labs.zip
thanks you.
I’ve never seen any of those before. But a quick solution to your problem would be to preface those function calls with MAP_, so MAP_SysCtlClockSet. Otherwise you need to link driverlib at compile time.
Thanks you! I was linking in the wrong .a file. It’s wierd how vision work some times. Once I read you comment I spoted it right away. Thanks.
I am waiting for the other lessons of your.
Very nice post.
I think the link “This Series – Stellaris-GCC: Intro” at the end points to a wrong date. 🙂
Avoiding the ROM and MAP functions can be nice when debugging. I have a debug build of driverlib and only use it when I need to debug a peripheral.
Also, from what I can tell using gcc to link eventually calls ld but it makes some decisions about where to source some of your libraries. I vaguely recall TI’s makefiles using gcc to locate the libraries and then calling ld directly to emulate this. FWIW I link with ld and don’t specify any libs except driverlib and everything seems to work OK.
Keep up the good work. I like your style. It’s very clear.
Thanks for the great post and the example files. I noticed your samples don’t have a copyright notice in them. I would like to give credit where credit is due and properly assign the modified versions in my project. Do you have any preferences on how to do this?
Thanks!
I do not have a preference, I’ve been trying to get around to uploading newer revisions (Holidays and moving are getting in the way), when I do I’ll add a MIT License to my work.
If you want to put a blurb in there with my email address, EEHusky@gmail.com or a link to my GitHub, that’s more than enough for me. Free Software