Wednesday, January 15, 2014

Developing STM32 microcontroller code on Linux (Part 7 of 8, building and running a simple STM32 program)

The first post of this series covered the steps to build and run code for the STM32. The second post covered how to build a cross-compiler for the STM32. The third post covered how to build a debugger for the STM32. The fourth post covered building and configuring OpenOCD for your development environment. The fifth post covered building the device library, libopencm3. The sixth post covered linker scripts and command-line options necessary for building and linking programs to run on the STM32. This post will cover building and running a program on the STM32.

In the previous posts we dealt with all of the set up necessary to build programs for the STM32. It is finally time to take advantage of all of those tools and build and run something. Recall that from previous posts, we already have an OpenOCD configuration file setup, a linker script setup, and a Makefile setup. All that really remains is for us to write the code, build it, and flash it to our device. The code below is very STM32F3DISCOVERY specific; that is, it very much requires that the GPIO for the LED be on GPIO bank E, pin 12 on the board. If you have one of the other STM32 DISCOVERY boards, you'll need to look at the schematics and find one of the GPIOs that are hooked to an LED.

We are going to take an extremely simple example from libopencm3. This example does nothing more than blink one of the LEDs on the board on and off continuously. While this is simple, it will validate that everything that we've done before is actually correct.

Here is the code:

$ cd ~/stm32-project
$ cat <<EOF > tut.c
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>

static void gpio_setup(void)
{
        /* Enable GPIOE clock. */
        rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_IOPEEN);

        /* Set GPIO12 (in GPIO port E) to 'output push-pull'. */
        gpio_mode_setup(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,
                        GPIO12);
}

int main(void)
{
        int i;

        gpio_setup();

        /* Blink the LED (PC8) on the board. */
        while (1) {
                /* Using API function gpio_toggle(): */
                gpio_toggle(GPIOE, GPIO12);     /* LED on/off */
                for (i = 0; i < 2000000; i++) /* Wait a bit. */
                        __asm__("nop");
        }

        return 0;
}
EOF
You should now be able to type "make", and the thing should build. Typing "make flash" should run OpenOCD, install the program to the board, and start blinking an LED. Remember that our Makefile required sudo access to actually run openocd. If you don't have sudo access, you can either add sudo access (by adding your user to the wheel group), or just su to root and run the openocd command by hand.

Monday, January 13, 2014

Developing STM32 microcontroller code on Linux (Part 6 of 8, building and linking STM32 programs)

The first post of this series covered the steps to build and run code for the STM32. The second post covered how to build a cross-compiler for the STM32. The third post covered how to build a debugger for the STM32. The fourth post covered building and configuring OpenOCD for your development environment. The fifth post covered building the device library, libopencm3. This post will cover linker scripts and command-line options necessary for building and linking programs to run on the STM32.

Once we have all of the previous steps done, we are achingly close to being able to build and run code on our target STM32 processor. However, there is one more set of low-level details that we have to understand before we can get there. Those details revolve around how our C code gets turned into machine code, and how that code is laid out in memory.

As you may know, compiling code to run on a target is roughly a two-step process:
  1. Turn C/C++ code into machine code the target processor understands. The output of this step are what are known as object files.
  2. Take the object files and link them together to form a coherent binary. The output of this step is generally an ELF file.
Let's talk about these two steps in more detail.

Compile step

During compilation, the compiler parses the C/C++ code and turns it into an object file. A little more concretely, what we want to have our cross-compiler do is to take our C code, turn it into ARM instructions that can run on the STM32, and then output that into object files.

To do this, we use our cross-compiler. As with any version of gcc, there are many flags that can be passed to our cross-compiler, and they can have many effects on the code that is output. What I'm going to present here is a set of flags that I've found works pretty well. This isn't necessarily optimal in any dimension, but will at least serve as a starting point for our code. I'll also point out that this is where we start to get into the differences between the various STM32F* processors. For instance, the STM32F4 processor has an FPU, while the STM32F3 does not. This will affect the flags that we will pass to the compiler.

For the STM32F3, Cortex-M3 processor that I am using, here are the compiler flags: -Wall -Wextra -Wimplicit-function-declaration -Wredundant-decls -Wstrict-prototypes -Wundef -Wshadow -g -fno-common -mcpu=cortex-m3 -mthumb -mfloat-abi=hard -MD
Let's go through each of them. The -W* flags tell the compiler to generate compile-time warnings for several classes of common errors. I find that enabling these warnings and getting rid of them usually makes the code much better. The -g flag tells the compiler to include debugging symbols in the binary; this makes the code easier to debug, at the expense of some code space. The -fno-common flag tells gcc to place uninitialized global variables into the data section of the binary, which improves performance a bit. The -mcpu=cortex-m3 flag tells the compiler that we have a Cortex-M3, and thus to generate code optimized for the Cortex-M3. The -mthumb flag tells gcc to generate ARM thumb code, which is smaller and more compact than full ARM code. The -mfloat-abi=hard flag tells gcc that we want to use a hard float ABI; this doesn't make a huge difference on a processor without an FPU, but is a good habit to get into. Finally, the -MD flag tells gcc to generate dependency files while compiling, which is useful for Makefiles.

Linking step

Once all of the individual files have been compiled, they are put together into the final binary by the linker. This is more complicated when targeting an embedded platform vs. a regular program. In particular, we have to tell the linker not only which files to link together, but also how to lay the resulting binary out on flash and in memory.

We'll first start by talking about the flags that we need to pass to the linker to make this work. Here are the set of flags we are going to start with: --static -lc -lnosys -T tut.ld -nostartfiles -Wl,--gc-sections -mcpu=cortex-m3 -mthumb -mfloat-abi=hard -lm -Wl,-Map=tut.map
Again, let's go through each of them. The --static flag tells the linker to link a static, not a dynamically linked, binary. This flag probably isn't strictly necessary in this case, but we add it anyway. The -lc flag tells the linker to link this binary against the C library, which is newlib in our case. That gives us access to various convenient functions, such as printf(), scanf(), etc. The -lnosys flag tells the linker to link this binary against the "nosys" library. Several of the convenience functions in the C library require underlying implementations of certain functions to operate, such as _write() for printf(). Since we don't have a POSIX operating system that can provide these for us, the nosys library provides empty stub functions for these. If we want, we can later on define our own versions of these stub functions that will get used instead. The -T tut.ld flag tells the linker to use tut.ld as the linker script; we'll talk more about linker scripts below. The -nostartfiles flag tells the linker not to use standard system startup files. Since we don't have an OS here, we can't rely on the standard OS utilities to start our program up. The -Wl,--gc-sections flag tells the linker to garbage collect unused sections. That is, any sections that are not referenced are removed from the resulting binary, which can shrink the binary. The -mcpu=cortex-m3, -mthumb, and -mfloat-abi=hard flags have the same meaning as for the compile flags. The -lm flag tells the linker to link this binary against the math library. It isn't strictly required for our little programs, but most programs want it sooner or later. Finally, the -Wl,-Map=tut.map tells the linker to generate a map file and stick it into tut.map. The map file is helpful for debugging, but is informational only.

Linker script

As mentioned before, the linker script tells the linker how to lay out the resulting binary in memory. This script is highly chip specific. The details have to do with where the processor jumps to on reset, and where it expects certain things to be. Note that most chips are actually configurable (based on some jumper settings), so where it jumps to on reset can change. Luckily, for most off-the-shelf STM32 designs, including the DISCOVERY boards, it is always configured to expect the code to start out in flash. Therefore, the linker script tells the linker to lay out the code in flash, but to put the data and bss in RAM.

With all that said, libopencm3 actually makes this easy on you. They have default linker scripts for each of the chips that are supported. All you really need to do is to fill in a small linker script with the RAM and FLASH size of your chip, include the default libopencm3 one, and away you go.

So we are going to put all of the above together and write a Makefile and a linker script into the project directory we created in the last tutorial. Neither of these are necessarily the best examples of what to do, but they will get the job done. First the Makefile:

$ cd ~/stm32-project
$ cat <<EOF > Makefile
CC=arm-none-eabi-gcc
LD=\$(CC)
OBJCOPY=arm-none-eabi-objcopy
OPENOCD=~/opt/cross/bin/openocd
CFLAGS=-Wall -Wextra -Wimplicit-function-declaration -Wredundant-decls -Wstrict-prototypes -Wundef -Wshadow -g -fno-common -mcpu=cortex-m3 -mthumb -mfloat-abi=hard -MD -DSTM32F3
LDFLAGS=--static -lc -lnosys -T tut.ld -nostartfiles -Wl,--gc-sections -mcpu=cortex-m3 -mthumb -mfloat-abi=hard -lm -Wl,-Map=tut.map
OBJS=tut.o

all: tut.bin

tut.bin: tut.elf
$( echo -e "\t" )\$(OBJCOPY) -Obinary tut.elf tut.bin

tut.elf: \$(OBJS)
$( echo -e "\t" )\$(CC) -o tut.elf \$(OBJS) ~/opt/cross/arm-none-eabi/lib/libopencm3_stm32f3.a --static -lc -lnosys -T tut.ld -nostartfiles -Wl,--gc-sections -mcpu=cortex-m3 -mthumb -mfloat-abi=hard -lm -Wl,-Map=tut.map

flash: tut.bin
$( echo -e "\t" )sudo \$(OPENOCD) -f stm32-openocd.cfg -c "init" -c "reset init" -c "flash write_image erase tut.bin 0x08000000" -c "reset run" -c "shutdown"

clean:
$( echo -e "\t")rm -f *.elf *.bin *.list *.map *.o *.d *~
EOF
You should notice a couple of things in the Makefile. First, we use all of the compiler and linker flags that we talked about earlier. Second, our objects ($OBJS) are tut.c, which we'll create in the next post. And third, we have a flash target that will build the project and flash it onto the target processor. This requires the OpenOCD configuration file that we created a couple of posts ago.

Now the linker script:

$ cat <<EOF > tut.ld
MEMORY
{
        rom (rx) : ORIGIN = 0x08000000, LENGTH = 256K
        ram (rwx) : ORIGIN = 0x20000000, LENGTH = 40K
}

/* Include the common ld script. */
INCLUDE libopencm3_stm32f3.ld
EOF
You'll notice that there isn't a lot here. We just have to define the RAM location and size, and the ROM (Flash) location and size, and the default libopencm3 linker script will take care of the rest.

We now have all of the parts in place. The next post will write, compile, and run a simple program on the board.

Friday, January 10, 2014

Developing STM32 microcontroller code on Linux (Part 5 of 8, building libopencm3)

The first post of this series covered the steps to build and run code for the STM32. The second post covered how to build a cross-compiler for the STM32. The third post covered how to build a debugger for the STM32. The fourth post covered building and configuring OpenOCD for your development environment. This post will cover building the device library, libopencm3.

As mentioned in the introductory post, it makes our life a lot easier if we use a device library. This is a library that abstracts the low-level details of the hardware registers away from us, and gives us a nice consistent API to use. While ST provides one these directly, it is not open-source (or more specifically, it's open-source status is murky). Luckily there is libopencm3, an open-source re-implementation that is also a better library in my opinion. As usual, I'm going to compile a certain version of libopencm3; newer or later versions may or may not work better for you.

As before, we start out by exporting some environment variables:

$ export TOPDIR=~/cross-src
$ export TARGET=arm-none-eabi
$ export PREFIX=~/opt/cross
$ export BUILDPROCS=$( getconf _NPROCESSORS_ONLN )
$ export PATH=$PREFIX/bin:$PATH
The TOPDIR environment variable is the directory in which the sources are stored. The TARGET environment variable is the architecture that we want our compiler to emit code for. For ARM chips without an operating system (like the STM32), we want arm-none-eabi. The PREFIX environment variable is the location we want our cross-compile tools to end up in; feel free to change this to something more suitable. The BUILDPROCS environment variable is the number of processors that we can use; we will use all of them while building to substantially speed up the build process. Finally, we need to add the location of the cross-compile binaries to our PATH so that later building stages can find it.

Now that we have our environment set up, we can get the code. Note that unlike most of the other tools covered in this tutorial, libopencm3 does not do releases. They expect (more specifically, require) that you clone the latest version and use that. That's what we are going to do here. As of this writing, the latest libopencm3 git hash tag is a909b5ca9e18f802e3caef19e63d38861662c128. Since the libopencm3 developers don't guarantee API stability, all of the steps below will assume the API as of that hash tag. If you decide to use a newer version of libopencm3, you may have to update the example code I give you to conform to the new API. With that out of the way, let's get it:

$ sudo yum install git
$ cd $TOPDIR
$ git clone git://github.com/libopencm3/libopencm3.git
$ cd libopencm3
$ git checkout -b clalancette-tutorial \
a909b5ca9e18f802e3caef19e63d38861662c128
What we've done here is to clone the repository, then checkout a new branch with the head at hash a909b5ca9e18f802e3caef19e63d38861662c128. This ensures that even if the library moves forward in the future, we will always use that hash tag for the purposes of this tutorial. Next we build the library:

$ unset PREFIX
$ make DETECT_TOOLCHAIN=1
$ make DETECT_TOOLCHAIN=1 install
$ export PREFIX=~/opt/cross
Here we need to unset PREFIX because libopencm3 uses PREFIX for the toolchain name prefix (arm-none-eabi), not the path prefix. Once we've done that, we can tell libopencm3 to detect the toolchain, and then use it to build libopencm3. Finally we use the install target to install the headers and the static libraries (.a files) to our toolchain. Assuming this is successful, everything necessary should be in ~/opt/cross/arm-none-eabi/, with the libraries in lib/libopencm3* and the header files in include/libopencm3. Note that there is one .a file per chip that is supported by libopencm3; we'll return to this later when we start building code for our chip.

Thursday, January 9, 2014

Developing STM32 microcontroller code on Linux (Part 4 of 8, building openocd)

The first post of this series covered the steps to build and run code for the STM32. The second post covered how to build a cross-compiler for the STM32. The third post covered how to build a debugger for the STM32. This post is going to cover building OpenOCD for your development environment.

As mentioned in the introductory post, we need OpenOCD so we can take binaries that we build and upload them onto the STM32. OpenOCD is a highly configurable tool and understands a number of different protocols. For our purposes, we really only need it to understand STLinkV2, which is what the STM32 uses. Also note that unlike previous posts, this post does not need or build a cross-compiled tool. That's because OpenOCD itself runs on our development machine, so we just need to do a normal compile. As before, I'm going to compile a certain version of OpenOCD (0.7.0). Newer or older versions may work, but your mileage may vary.

As before, we start out by exporting some environment variables:

$ export TOPDIR=~/cross-src
$ export TARGET=arm-none-eabi
$ export PREFIX=~/opt/cross
$ export BUILDPROCS=$( getconf _NPROCESSORS_ONLN )
$ export PATH=$PREFIX/bin:$PATH
The TOPDIR environment variable is the directory in which the sources are stored. The TARGET environment variable is the architecture that we want our compiler to emit code for. For ARM chips without an operating system (like the STM32), we want arm-none-eabi. The PREFIX environment variable is the location we want our cross-compile tools to end up in; feel free to change this to something more suitable. The BUILDPROCS environment variable is the number of processors that we can use; we will use all of them while building to substantially speed up the build process. Finally, we need to add the location of the cross-compile binaries to our PATH so that later building stages can find it.

Now we are ready to start. Let's fetch openocd:

$ cd $TOPDIR
$ wget http://downloads.sourceforge.net/project/openocd/\
openocd/0.7.0/openocd-0.7.0.tar.gz
To start the compile, we first need to install a dependency:

$ sudo yum install libusbx-devel
Now let's unpack and build openocd:

$ tar -xvf openocd-0.7.0.tar.gz
$ cd openocd-0.7.0
$ ./configure --enable-stlink --prefix=$PREFIX
$ make
$ make install
Here we are unpacking, configuring, building, and installing OpenOCD. The configure flags require a bit of explanation. The --enable-stlink flag means to enable support for STLink and STLinkV2, which is what we need for this board. The --prefix flag tells the build system to install OpenOCD to our ~/opt/cross location. This isn't strictly correct; this isn't a cross compile tool. However, it is convenient to have everything in one place, so we install it there.

Assuming everything went properly, we should now have a openocd binary in ~/opt/cross/bin. There will also be a bunch of configuration files installed to ~/opt/cross/share/openocd. These are important as these are pre-canned configuration files provided by OpenOCD. While it is possible to create your own from scratch, the syntax is baroque and it is a lot more work than you would think. Luckily OpenOCD already comes with scripts for STLinkV2 and STM32, so we'll just use those.

In order to have a working configuration, we are going to start creating our "project" directory. This is where the code that eventually runs on the STM32 is going to be placed. I'm going to call my directory ~/stm32-project; feel free to change it for your project. So we do:

$ mkdir ~/stm32-project
$ cd ~/stm32-project
$ cat <<EOF > stm32-openocd.cfg
source [find interface/stlink-v2.cfg]
source [find target/stm32f3x_stlink.cfg]
reset_config srst_only srst_nogate
EOF
Here we create the project directory, cd into it, and then create the configuration file for OpenOCD. The configuration file deserves a bit of explanation. First, we tell it to "find" the stlink-v2.cfg configuration file. Where it looks depends on the PREFIX we configured, so in our case it is going to look through ~/opt/cross/share/openocd for that file (where it should find it). Next we tell OpenOCD to "find" the stm32f3x_stlink.cfg file. Again, that file is located in ~/opt/cross/share/openocd, and it again should find it. Note that if you have a different STM32 chip, you should substitute f3x with whatever version of the chip you have. Finally there is a line about reset_config srst_only, and srst_nogate. I honestly don't know what those are for, though they seem to be necessary.

That's it for OpenOCD. Everything should be built, configured, and ready to go.

Wednesday, January 8, 2014

Release of ruby-libvirt 0.5.2

This is a release notification for ruby-libvirt 0.5.2. ruby-libvirt is a ruby wrapper around the libvirt API. The changelog between 0.5.1 and 0.5.2 is:
  • Fix to make sure we don't free more entries than retrieved (potential crash)
Version 0.5.2 is available from http://libvirt.org/ruby:

Tarball: http://libvirt.org/ruby/download/ruby-libvirt-0.5.2.tgz
Gem: http://libvirt.org/ruby/download/ruby-libvirt-0.5.2.gem

It is also available from rubygems.org; to get the latest version, run:

$ gem install ruby-libvirt

As usual, if you run into questions, problems, or bugs, please feel free to mail me (clalancette@gmail com) and the libvirt mailing list.

Thanks to Guido Günther for the patch to fix this problem.

Developing STM32 microcontroller code on Linux (Part 3 of 8, building gdb)

The first post of this series covered the steps to build and run code for the STM32. The second post covered how to build a cross-compiler for the STM32. This post is going to cover how to build a debugger for the STM32.

Building a debugger isn't strictly necessary for developing on the STM32. However it can make certain debugging tasks easier, and it is relatively simple to do, so we'll do it here. As with the tools in the last post, the version of gdb used (7.6) worked for me. Your mileage may vary. If you fail to cross-compile gdb, then try a slightly newer or older version and try again on your development setup. If you can't build gdb, you can safely skip this step, though you may run into some problems later.

To build gdb, we'll assume you installed the tools to the path from the last post. If you changed path, you'll have to edit the PREFIX path below.

As before, we start out by exporting some environment variables:

$ export TOPDIR=~/cross-src
$ export TARGET=arm-none-eabi
$ export PREFIX=~/opt/cross
$ export BUILDPROCS=$( getconf _NPROCESSORS_ONLN )
$ export PATH=$PREFIX/bin:$PATH
The TOPDIR environment variable is the directory in which the sources are stored. The TARGET environment variable is the architecture that we want our compiler to emit code for. For ARM chips without an operating system (like the STM32), we want arm-none-eabi. The PREFIX environment variable is the location we want our cross-compile tools to end up in; feel free to change this to something more suitable. The BUILDPROCS environment variable is the number of processors that we can use; we will use all of them while building to substantially speed up the build process. Finally, we need to add the location of the cross-compile binaries to our PATH so that later building stages can find it.

Next we'll download, unpack, and build gdb:

$ cd $TOPDIR
$ wget ftp://ftp.gnu.org/gnu/gdb/gdb-7.6.tar.gz
$ tar -xvf gdb-7.6.tar.gz
$ mkdir build-gdb
$ cd build-gdb
$ ../gdb-7.6/configure --target=$TARGET --prefix=$PREFIX \
--enable-interwork
$ make -j$BUILDPROCS
$ make install
We download gdb, unpack it, then configure and build it. The flags to configure deserve some explanation. The --target flag tells gdb what target you want the tools to build for; that is, what kind of code will be emitted by the code. In our case, we want ARM with no operating system. The --prefix flag tells gdb that we want our debugger to be installed to $PREFIX. The --enable-interwork flag allows binutils to emit a combination of ARM and THUMB code; if you don't know what that is, don't worry about it for now. Assuming this step went fine on your development machine, there should be a binary in ~/opt/cross/bin (or whatever your top-level output directory is) called arm-none-eabi-gdb.

Tuesday, January 7, 2014

Developing STM32 microcontroller code on Linux (Part 2 of 8, building the cross-compiler)

The first post of this series covered the steps to build and run code for the STM32. This post is going to cover how to build a cross-compiler for the STM32.

The steps to build a cross-compiler are somewhat covered here and here. In theory, building a cross-compiler is a pretty straightforward process:
  1. Cross compile binutils, to get things like as (assembler), ld (linker), nm (list object symbols), etc.
  2. Cross compile gcc, which gives you a C and C++ compiler.
  3. Cross compile newlib, which gives you a minimal libc-like environment to program in.
However, there is a big gotcha. Not all combinations of binutils, gcc, and newlib work together. Worse, not all combinations of them build on all development environments, which can make this something of a frustrating experience. For instance, it is known that binutils < 2.24 does not build on machines with texinfo 5.x or later. Thus, on modern machines (like Fedora 19), you must use binutils 2.24 or later. Also, I found that the latest newlib of this writing (2.1.0) does not build on Fedora 19. Your mileage may vary, and this will almost certainly change in the future; the best advice I can give is to start with the latest versions of the packages and then slowly back off the ones that fail until you get a relatively recent combination that works. For the purposes of this post, I ended up using binutils 2.24, gcc 4.8.2, and newlib 2.0.0. This combination builds just fine on Fedora 19.

Now onto the steps needed to build the cross compiling environment. We first need to make sure certain tools are installed. We'll install the development tools through yum:

$ sudo yum install gcc make tar wget bzip2 gmp-devel \
mpfr-devel libmpc-devel gcc-c++ texinfo ncurses-devel
Next we fetch the relevant versions of the packages:

$ mkdir ~/cross-src
$ cd ~/cross-src
$ wget ftp://ftp.gnu.org/gnu/binutils/binutils-2.24.tar.gz
$ wget ftp://ftp.gnu.org/gnu/gcc/gcc-4.8.2/gcc-4.8.2.tar.bz2
$ wget ftp://sources.redhat.com/pub/newlib/newlib-2.0.0.tar.gz
Next we set some environment variables. This isn't strictly necessary, but will help us reduce errors in the following steps:

$ export TOPDIR=~/cross-src
$ export TARGET=arm-none-eabi
$ export PREFIX=~/opt/cross
$ export BUILDPROCS=$( getconf _NPROCESSORS_ONLN )
$ export PATH=$PREFIX/bin:$PATH
The TOPDIR environment variable is the directory in which the sources are stored. The TARGET environment variable is the architecture that we want our compiler to emit code for. For ARM chips without an operating system (like the STM32), we want arm-none-eabi. The PREFIX environment variable is the location we want our cross-compile tools to end up in; feel free to change this to something more suitable. The BUILDPROCS environment variable is the number of processors that we can use; we will use all of them while building to substantially speed up the build process. Finally, we need to add the location of the cross-compile binaries to our PATH so that later building stages can find it.

Now we can start building. We first need to build binutils:

$ cd $TOPDIR
$ tar -xvf binutils-2.24.tar.gz
$ mkdir build-binutils
$ cd build-binutils
$ ../binutils-2.24/configure --target=$TARGET --prefix=$PREFIX \
--enable-interwork --disable-nls
$ make -j$BUILDPROCS
$ make install
Basically we are unpacking binutils, doing an out-of-tree build (recommended), and then installing it. The flags to configure deserve some explanation. The --target flag tells binutils what target you want the tools to build for; that is, what kind of code will be emitted by the code. In our case, we want ARM with no operating system. The --prefix flag tells binutils that we want our tools to be installed to $PREFIX. The --enable-interwork flag allows binutils to emit a combination of ARM and THUMB code; if you don't know what that is, don't worry about it for now. Finally, the --disable-nls flag tells binutils not to build translation files, which speeds up the build. Assuming this step went fine on your development machine, there should be a set of tools in ~/opt/cross/bin (or whatever your top-level output directory is) called arm-none-eabi-*. If this didn't work, then you might want to try a newer or older version of binutils; you can't proceed any further without this working.

With binutils built, we can now move on to gcc:

$ cd $TOPDIR
$ tar -xvf newlib-2.0.0.tar.gz
$ tar -xvf gcc-4.8.2.tar.bz2
$ mkdir build-gcc
$ cd build-gcc
$ ../gcc-4.8.2/configure --target=$TARGET --prefix=$PREFIX \
--enable-interwork --disable-nls --enable-languages="c,c++" \
--without-headers --with-newlib \
--with-headers=$TOPDIR/newlib-2.0.0/newlib/libc/include
$ make -j$BUILDPROCS all-gcc
$ make install-gcc
Here we are unpacking gcc and newlib (which is required for building gcc), doing an out-of-tree build of the initial part of gcc, and then installing it. The flags to configure deserve some explanation. The --target flag tells gcc what target you want the tools to emit code for. The --prefix flag tells gcc that we want our tools to be installed to $PREFIX. The --enable-interwork flag allows gcc to emit a combination of ARM and THUMB code. The --disable-nls flag tells gcc not to build translation files, which speeds up the build. The --enable-languages flag tells gcc which compilers we want it to build; in our case, both the C and C++ compilers. The --without-headers --with-newlib and --with-headers flags tells gcc that it not to use internal headers, but rather to use newlib and the headers from newlib. Assuming this step finished successfully, there should be a file called ~/opt/cross/bin/arm-none-eabi-gcc, which is the initial compiler. Again, if it didn't work, then you might want to try a newer or older version of gcc; you can't proceed any further without this.

With the initial compiler built, we can now build newlib:

$ cd $TOPDIR
$ mkdir build-newlib
$ cd build-newlib
$ ../newlib-2.0.0/configure --target=$TARGET --prefix=$PREFIX \
--enable-interwork
$ make -j$BUILDPROCS
$ make install
Since we've already unpacked newlib, we skip that step. Here we are doing an out-of-tree build of newlib, using the compiler that we built in the last step. The configure flags have the same meaning as previously.

With newlib built, we can now go back and finish the build of gcc (the last step!):

$ cd $TOPDIR/build-gcc
$ make -j$BUILDPROCS
$ make install
This finishes the gcc build, and installs it to $PREFIX. That's it! You should now have a $PREFIX directory full of tools and headers useful for building code to run on the STM32.

Update Jan 8, 2014: Updated the formatting so it is more readable.

Monday, January 6, 2014

Developing STM32 microcontroller code on Linux (Part 1 of 8, introduction)

Recently I've been playing with the STM32, which is a small microcontroller made by ST. These seem to be pretty great microcontroller chips; they are relatively fast (depending on what model you get), have a decent amount of flash (up to 1MB), and have a decent amount of memory (up to 192KB). It is also easy to get development boards for them; there is a line of boards called the STM32DISCOVERY boards that are really cheap and easy to get. It is possible to work on these chips entirely with open source tools, which is important to me.

This series of posts will go through all of the steps necessary to develop on these boards. Note that all of this is covered elsewhere on the web, but a lot of the information is either outdated or scattered. I'll build all of the pieces from the ground up to get a working set of tools and binaries that you can use to develop your own STM32 applications.

To start with, I'm going to describe my hardware setup. I have a laptop running Fedora 19 x86_64. This is my main development machine, and this is going to be the host for everything I do with the STM32. For an STM32 board, I have an STM32F3DISCOVERY board, as shown here. However, note that for most of the posts, the exact board that you have isn't that important. As long as it is one of the STM32F*DISCOVERY boards, the steps below will mostly apply. The differences will become more important when we start to actually write code that deals with the GPIOs (as the GPIOs differ per board), but for the development environment they are really all quite similar.

This series of posts will do the steps in the following order:
  1. In order to do anything, we need a cross compiler. This is a set of tools that runs on our development environment (Fedora 19 x86_64), but emit instructions for our target hardware (STM32 ARM). Besides the C/C++ compiler, this also includes things like the assembler and linker. Part of the cross-compile toolchain also includes a minimal libc-like environment to program in, which gives you access to <stdio.h> and other familiar header files and functions. We will build a cross compile environment from binutils, gcc, and newlib.
  2. Once we have a cross-compiler, we need some way to debug the programs we write. The simplest thing to do here is to build gdb, the GNU debugger. Unfortunately we can't just use the system gdb, as that generally only understands how to debug and disassemble code on your development machine architecture. So we'll build our own version of gdb that understands ARM.
  3. With the debugger finished, we need some way to take the compiled version of our code and put it onto the target device. The STM32 devices use something called STLinkV2, which is a multi-purpose communication protocol (generally over USB). In order to upload our code to the device, we need a piece of software the speaks this protocol. Luckily there is OpenOCD, the Swiss Army Knife of communication protocols. We'll need to build a version of this that runs on our development machine, but knows how to speak STLinkV2. In this step we'll also build a configuration file that can communicate over STLinkV2.
  4. With the communications taken care of, we need a device library. This is basically an abstraction layer that allows us to talk directly to the hardware on the target device. For the purposes of these posts we are going to use libopencm3. This step will build libopencm3 for the target device.
  5. Once we have libopencm3 built, we have to know how to link programs so that they run on the STM32. This step will discuss linker scripts and command-line directives necessary to build programs that run on the STM32.
  6. Here we build our first simple program, upload it to the STM32, and watch it run! Finally!
  7. For bonus, I discuss running a simple Real-Time Operating System on the STM32, FreeRTOS. Using this will allow you to define several tasks and have the RTOS switch between them, much like tasks on a full-fledged OS. This opens up new possibilities and new problems, some of which will be discussed.
Whew, that's a lot of steps just to get the equivalent of "Hello World" running on the board. However, it should be educational and collect a lot of this information together in one place.

Friday, January 3, 2014

Oz 0.12.0 release

I'm pleased to announce release 0.12.0 of Oz. Oz is a program for doing automated installation of guest operating systems with limited input from the user. Release 0.12.0 is a bugfix and feature release for Oz. Some of the highlights between Oz 0.11.0 and 0.12.0 are:
  • Fixes to concurrent oz-install invocations
  • Python 3 compatibility in the test suites
  • Support for Ubuntu 12.04.3
  • Support for Mageia
  • Allow a MAC address to be passed in (instead of auto-generated)
  • Support for RHEL5.10
  • Support for Ubuntu 13.10
  • Use lxml instead of libxml2 for XML document processing (it has much better error messages)
  • Remove the unused "tunnels" functionality
  • Support FreeBSD 10.0
  • Remove deprecated functions from the Guest class
  • Speed up guest customization on guests that support NetworkManager
  • Follow subprocess commands as they are executed (makes debugging easier)
  • Ensure that any paths from the user are absolute, otherwise things don't work properly
  • Add support for OpenSUSE 13.1
  • Add support for Fedora 20
  • Add support for RHEL-7
A tarball and zipfile of this release is available on the Github releases page: https://github.com/clalancette/oz/releases. Packages for Fedora-19, Fedora-20, and EPEL-6 have been built in Koji and will eventually make their way to stable. Instructions on how to get and use Oz are available at http://github.com/clalancette/oz/wiki. If you have questions or comments about Oz, please feel free to contact me at clalancette at gmail.com, or open up an issue on the github page: http://github.com/clalancette/oz/issues. Thanks to everyone who contributed to this release through bug reports, patches, and suggestions for improvement.