Wenton Davis' Cortex-M0 Toolchain Test Page



Description

Now that you have the toolchain installed, let's take a look at some of the more commonly used tools and how to use them.  There are a lot of tools included in the toolchain, and I don't intend to cover all of them.  (It'd be a whole other set of pages just to do that - maybe I'll take that project on, later.)




Resources

Discovery documents from ST Microelectronics




Basic Files Required

There are a few files that are going to be needed in all GNU Toolchain projects.  The first, most expected file is the main.c.  This file doesn't have to be named "main.c," but somewhere, must ultimately contain the main() function.  This is really the fundamental building block necessary in every 'C' program.  For this simple example, however, we will write a very simple program:


void main( void )
{
  while( 1 )  ;
}

This is just about the simplest possible main() that I can create.  Once it starts running, it get trapped in an infinite loop, and it never returns.  When a program runs within an operating system's control, the program runs, and when it ends, control is returned to the operating system.  "Bare metal" programs (programs running on a microcontroller without an operating system) would not have an operating system to return control to, so either the main() function will never return, or there must be a way to trap control after the main() function is completed.  (We'll revisit this, later.)

The next file is equally common, but in most development toolchains, it is usually obscured by the linker.  The "Common Run Time" file, usually crt.s or something similar, is a "wrapper" that the compiler wraps around the main() function.  It is responsible for initializing variables and memory spaces.  For bare metal applications, it can also be used to initialize the processor and various "before the main program runs" kinds of things.  For now, we will use an equally simple file, and it will become more complicated, later.


    .cpu cortex-m0
    .thumb
    
    .word 0x20002000
    .word _reset
    
    .thumb_func
_reset:
    bl  main
    b   .

This file looks like it is written in some alien language right now, but it will be clearer later.  The important parts to understand from it for now is the two entries that will form the vector table, containing the two .word statements.  The first one, 0x20002000, sets the initial location in memory for the stack pointer, and the second one, _reset defines the location in memory where the processor will begin running when it is reset.  Next, the label _reset is defined.  Note that we will not know where in memory this actual location is, nor do we care. It is up to the assembler and linker to deal with.  Next, the program calls the subroutine main() which we already know is the main function for the 'C' program.  Finally, the last line uses the special variable, ., which is a special variable used by the assembler to refer to "this location in memory."  this means that the last instruction loops back to the same location in memory, forming an infinite loop.  Just in case the main function returns, trap the flow of program exection because there is no operating system to return control to.

The next file is also mostly obscured by the linker.  This "linker descriptor" file used to tell the lnker what memory exists in the target system, so it can assign contents from the program into the memory that exists.  Because this is specifically targetting the STM32F051R8T6 chip (the chip used in the STM32F0DISCOVERY board), we look in the datasheet to find that there is flash ROM starting at address 0x08000000 with a length of 64K, and there is RAM starting at memory location 0x20000000 with a length of 8K.  The simplest linker descriptoer file we will call linker.ld for now, can be used to describe the memory:


MEMORY
{
    FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 64K
    RAM(rxw)  : ORIGIN = 0x20000000, LENGTH = 8K
}

At this point, it is possible to build the binary file needed to program the chip.  Although cumbersome, we can manually type in the commands:


> arm-none-eabi-as -o crt.o crt.s
> arm-none-eabi-gcc -mthumb -mcpu=cortex-m0 -c -o main.o main.c
> arm-none-eabi-ld -T linker.ld -o firstapp.elf crt.o main.o
> arm-none-eabi-objcopy -o binary firstapp.elf firstapp.bin

If everything has gone well, you should have a new file, firstapp.bin, which is the actualy binary file to be loaded into the CPU.  The binary file can be loaded into the chip using the command:


> st-flash write firstapp.bin 0x08000000

At this point, we will be a little disappointed because the program really doesn't do anything, but we have created a basic framework and we have a working toolchain.




Linux/Unix Makefile

The linux/unix system has a very useful tool called make that saves a lot of typing.  The make tool has been ported to Windoze as well, and can be found in the mingw package available.  there are minor changes that need to be made, mostly in terms of directory structures, but these are very minor.

The make utility depends on a file named, conveniently enough, makefile.  There is a lot of ways to write the makefile, and even more information in its structure.  Because of the complexity, I'm not going to get into the details, here.  In the following makefile, some simple variables are defined, then the "rules" are defined.  A "rule" begins with one line defining what to build followed by a colon, then the list of files needed to perform the rule.  For example, the firstapp.elf file depends on the files linker.ld, crt.o, and main.o.  If any of these files have timestamps that are newer than any existing firstapp.elf file, or if the firstapp.elf file is missing, the second line of the rule is used to build the target.  in this case, the linker is used to combine the object files into a single elf (executable) file.  It is important to remember that the lines containing instructions to execute begin with a tab character, NOT a series of spaces.  the makefile is shown:


CC = arm-none-eabi-gcc
AS = arm-none-eabi-as
LD = arm-none-eabi-ld
BIN = arm-none-eabi-objcopy
STL = st-flash
CFLAGS = -mthumb -mcpu=cortex-m0

all: app.bin

crt.o: crt.s
    $(AS) -o crt.o crt.s

main.o: main.c
    $(CC) $(CFLAGS) -c -o main.o main.c

app.elf: linker.ld crt.o main.o
    $(LD) -T linker.ld -o app.elf crt.o main.o

app.bin: app.elf
    $(BIN) -O binary app.elf app.bin

clean:
    rm -f *.o *.elf *.bin

flash: app.bin
    $(STL) write app.bin 0x8000000

erase:
    $(STL) erase

Again, there is a lot of things the makefile can do, but I won't go into it, here.




Time for a drink.  I guess water is a good choice, but I think I'll go for a root beer.

Next, lets see if we can make it actually do something!

home

If you need to reach me, you can always email me at email (wenton@ieee.org)