banner

Cross GCC on a Win32 platform.


Discussion of this article is here.
Note: All the files needed for this project are zipped up here
My build script is here

Overview

This document outlines the process needed to build a GCC cross compiling system that is hosted on a Windows computer. Many developers would like to use the GNU tools but are constrained by the fact that either their employers will not provide them with another unix-like computer or they have no desire to learn another operating system for one task. There are solutions that allow you to use free software to do embedded development, but they are poorly documented from the Win32 user's perspective. My goal is to provide clarification on these issues and to point the user in the right direction as to where to get help when things go wrong.

The GNU tools are a very powerful and flexible set of tools to use for any development project. The fact that they can run on both unix and Windows is even better. Another aspect of the GNU tools that is helpful for embedded development is the fact that the tools support a broad range of target processors and architectures. With the development of uCLinux (pronounced you-see-lihn-ucks) a great deal of development for consumer gadgets is now done with these tools. As more people work with them and improve them, the tools grow in maturity and features. Commercial software is hard pressed to compete with the GNU tools on features.

The GNU tools biggest weakness is often the commercial vendor's main selling point - ease of use. There are documents out there that illustrate all the tasks that anyone would want to do, but they are scattered all over the internet in various languages. Collecting all of this information into a relevant set of documents for *your* task is a task in itself. But once you have invested the time in learning how the tools work, you will have the benefit of being able to use them almost anywhere, gaining a powerful development environment without having to pay an arm and a leg to a commercial vendor.

Part 1 - Unix on Windows

So anyway, enough of the benefits of the tool, lets get down to business. You've decided to do your development using the GNU tools, and you only have a Windows machine at your disposal. The first thing you need to do is to get some unix into your machine. This is done through the Cygwin library, which is a Windows DLL that provides a POSIX interface to programs. POSIX is the definition of how unix system should behave from the viewpoint of the program. Cygwin also allows you to install some compiled binary programs that come standard on a typical unix machine.

The place to get Cygwin is http://www.cygwin.com. Cygwin is maintained by Linux consulting giant RedHat, so it is unlikely to go anywhere soon. Here you will download the installation utility. When you run the installation utility, you will have the option to select various programs to download. You will need to select the following packages in order to build an embedded toolchain.

  1. GCC - The GNU Compiler Collection - You will need GCC in Cygwin to build your specialized tools for your target. All you need is GCC-CORE.
  2. Bintuils - a set of utility programs used by GCC and other tools. This should be selected automatically when you select GCC.
  3. Make - This is a project manager that helps you automate the building process.
  4. Perl - A scripting language.
  5. Flex - Used for pattern recognition
  6. Patchutils - Used to apply patches to programs.

This is the minimum that you will need to develop and run your tools. You may also need other pieces for your particular target or application.

Cygwin should provide you with a shell (called BASH) that you can use to do your work. I performed all the actions inside this shell. This is a much better environment as compared to the regular Windows command prompt. The setup utility will create a folder on your hard drive called /cygwin which will act as the root folder for this shell. So, if you see me refer to /home/brose/coldfire, know that on the hard drive it is in the C:\cygwin\home\brose\coldfire directory. So if you see forward slashes in a path, that is the unix file format and the actual file on the hard drive is in C:\cygwin\path\to\the\file.

You will need to create a working folder for all this stuff. Since I am working with a Motorola Coldfire (MCF5249) processor, I will put all of the stuff in the 'coldfire' directory in my Cygwin home directory, /home/brose. You will also need several subfolders underneath this. I used the following directories in my setup. (Note: the '~' character is shorthand for the home directory, so '~' is equivalant to /home/brose. Also, the $ represents the command prompt, and should not be typed if you are using this as a command guide.

$ cd ~
$ mkdir coldfire
$ mkdir coldfire/tools
$ cd coldfire/tools
$ mkdir archive
$ mkdir bin
$ mkdir build
$ mkdir src

I downloaded the source archives (.gz or .bz2) into the archive folder. I then extracted them into the src folder. Once in the src folder you can build them in the build folder and install them to the bin folder. See the command listing for details. Also I have a shell script here that you can use to build the tools. Simply place this shell script in the tools folder, alter it to your situation, and run it. It should build you a complete set of tools in the tools folder.

Part 2 Getting the tool sources

Now that you have downloaded and setup Cygwin, you can now download and build the tools you will use to develop programs for your target. These tools are downloaded in source form and will be compiled to run. Once compiled you should be able to take the programs and move them to any other Windows machine with Cygwin installed.

The source packages you will need to download are Binutils, GCC, and GDB, which is the GNU Debugger. The debugger will let you load, execute and examine software running on your target. This is a must have if you have a prototype board that has an empty ROM. The above tools are all downloaded from the GNU website at http://www.gnu.org. For this document, I used the following versions.

  1. binutils - 2.15
  2. gcc - 3.4.2
  3. Newlib - Newlib is basically the standard C library written for embedded devices.
  4. m68k-bdm-1.3.0
  5. gdb - 6.0 (also needs a patch to work with the BDM tools)
  6. GiveIO - needed by the m68k-bdm tool.

The m68k-bdm-1.3.0 is a driver that is needed to connect GDB to the target via a BDM (Background Debugging Module) connector, which is used by Motorola processors. This software can be found on Sourceforge (a popular open source website) at http://sourceforge.net/projects/bdm. This is a unix package and is not specific to Windows users.

One thing to be careful with is GDB. GDB needs to be altered a bit (patched) so that it will connect with the BDM connector to the target. This patch is usually a version or two behind the main GDB tree. So your best bet is to find the patch first, then get the appropriate version of GDB for that patch. In my case, GDB 6.2 was out, but I could only find the patch for 6.0. So I was stuck with GDB 6.0. The patch is at the Sourceforge BDM website.

For NT-based Windows (NT/2000/XP) you will also need another driver called GiveIO, which is used by the BDM driver to interface between the unix/cygwin driver and the Windows I/O drivers. This package will give your I/O drivers access to the hardware they need (like the LPT port for your BDM cable). This is spelled out in the README file in the m68k-bdm-1.3.0 package. There is a version on the 'net which includes a setup program that takes care of all the nitty gritty details. I found this by searching through Google for "sourceforge giveio". It is part of the package PyParallel, which makes I/O ports available to python programmers.

Assuming that the source archives are in the ~/coldfire/archive folder, you can extract them using the appropriate command into the ~/coldfire/src folder. This is generally done with one of the two commands. The differences are discussed below.

$ cd ~/coldfire/src
$ tar -xzvpf ../archive/package.gz
$ tar -xjvpf ../archive/package.bz2

After the sources have been extracted, this is what the root of the src directory looks like.

/home/brose/coldfire/tools/src
drwxr-xr-x   15 brose    None            0 Sep 23 13:59 binutils-2.15
drwxr-xr-x   18 brose    None            0 Sep  6 20:21 gcc-3.4.2
drwxr-xr-x   15 brose    None            0 Oct  5 15:14 gdb-6.0
drwxr-xr-x   13 brose    None            0 Oct  5 13:24 m68k-bdm-1.3.0
Note that the 'z' argument is used to unpack a gz archive and 'j' is used if it is a bz2 archive. The other options are to tell it to xtract the archive in a verbose manner and to preserve permissions and file structure ( To find out more about tar, type the command 'tar --help | less'). Once you complete this, you should have a src folder like the one listed above.

Part 3 - Building the tools

Now that we have the sources, it is possible to build the tools. The process is more or less the same for all the packages. To make things easier, we can set up some environment variables for some of the flags passed into the makefiles. The '--target' flag is used to specify the target environment. In my case, since I am developing code for the Motorola Coldfire processors without an operating system, my target is "m68k-elf". You can find out more about targets by looking in the AS (GNU Assembler) or GCC manuals.

The prefix name is the path to the location where you want the files installed. The binaries will end up in the "bin" subdirectory of the prefix folder. For my system, I want to place them in the ~/coldfire/tools/bin folder. But because of some shell script ideosyncrasies the '~' should not be used. Instead, use the $HOME environment variable. So your prefix statement should look like "prefix=$HOME/coldfire/tools" (without the quotes).

Once your environment is set up, the build process is pretty easy and can be generalized as follows. Substitute whatever package you are building for [package]. Important: You need to build the tools in the following order - buinutils, gcc, m68k-bdm-gdb, gdb.

$ prefix=$HOME/coldfire/tools
$ target=m68k-elf
$ cd ~/coldfire/tools/build
$ mkdir [package]
$ cd [package]
$ ../../src/[package-x.y]/configure --target=$target --prefix=$prefix
$ make
$ make install

Of course it can't be this simple. There are some subtle differences to watch out for.

  1. The first is with GCC. GCC has many options that need to be enabled in order to build our system. a typical configure line looks like this.
    ../gcc-3.2.3/configure --target=$target --prefix=$prefix \
       --with-gnu-as --with-gnu-ld --with-newlib --verbose \
       --enable-threads --enable-languages="c,c++"
    
  2. The next difference is for the BDM driver m68k-bdm-x.y. This one's target is the host machine, so don't specify a target. The driver can be loaded into the regular install directory with the other tools.
  3. GDB needs to be patched to work with the BDM adapter. So you need to apply the patch before configuring.
  4. GDB needs to be built with some more flags, mostly relating to the fact that it is debugging the target, which is likely another architecture. So when you build GDB, your commands should look like this...
$ cd ~/coldfire/tools/src/gdb-6.0
$ patch -p1 < gdb-6.0-bdm-m68k.patch
$ cd ~/coldfire/tools/build/gdb
$ ../../src/gdb-6.2/configure --prefix=$prefix/bin --target=m68k-bdm-elf
$ make install CFLAGS="-I $HOME/coldfire/tools/src/m68k-bdm-1.3.0/lib
  -I $HOME/coldfire/tools/src/m68k-bdm-1.3.0/driver" LDFLAGS="-L 
  $HOME/coldfire/tools/src/m68k-bdm-1.3.0/lib -L $HOME/coldfire/tools/lib"





Command listing

The BDM drivers and patches for GDB were downloaded by web browser from http://sourceforge.net/projects/bdm instead of FTP and so they are not listed here. Remember, these commands are issued in the Cygwin shell, not the Windows command line. The file gdb-6.0-bdm-m68k.patch is downloaded into the ~/coldfire/tools/src/gdb-6.0 directory. The file m68k-bdm-1.3.0.tar.bz2 is downloaded into the ~/coldfire/tools/archive directory.

Before testing the BDM interface with bdm-chk, you obviously need to plug in and power up a board and connect the BDM module, etc. If you do not have this equipment, it is safe to skip this test.

Lines without '$' are not commands, but rather things to do with other programs like FTP or a browser (http...).

$ cd ~
$ mkdir coldfire
$ mkdir coldfire/tools
$ mkdir coldfire/tools/archive
$ mkdir coldfire/tools/bin
$ mkdir coldfire/tools/src
$ mkdir coldfire/tools/build
$ mkdir coldfire/tools/build/binutils
$ mkdir coldfire/tools/build/gcc
$ mkdir coldfire/tools/build/newlib
$ mkdir coldfire/tools/build/gdb
$ mkdir coldfire/tools/build/m68k-bdm

$ prefix=$HOME/coldfire/tools
$ target=m68k-elf
$ PATH=$HOME/coldfire/tools/bin:$PATH

$ cd archive
( ftp ftp://ftp.gnu.org/gnu/binutils/binutils-2.15.tar.bz2 )
( ftp ftp://ftp.gnu.org/gnu/gcc/gcc-3.4.2/gcc-3.4.2.tar.bz2 )
( ftp ftp://ftp.gnu.org/gnu/gdb/gdb-6.0.tar.bz2 )
( http m68k-bdm-1.3.0.tar.bz2 )
$ cd ~/coldfire/tools/src
$ tar -xjvpf ../archive/binutils-2.15.tar.bz2
$ tar -xjvpf ../archive/gcc-3.4.2.tar.bz2
$ tar -xjvpf ../archive/gdb-6.0.tar.bz2
$ tar -xjvpf ../archive/m68k-bdm-1.3.0.tar.bz2

$ cd ~/coldfire/tools/build/binutils
$ ../../src/binutils-2.15/configure --target=$target --prefix=$prefix
$ make all
$ make install


$ cd ~/coldfire/tools/build/gcc
$ ../../src/gcc-3.4.2/configure --target=$target --prefix=$prefix
$ make all-gcc
$ make install-gcc

$ cd ~/coldfire/tools/build/m68k-bdm
$ ../../src/m68k-bdm-1.3.0/configure --prefix=$prefix
$ make 
$ make install
$ cd $prefix
$ bdm-chk /dev/bdmcf0

$ cd ~/coldfire/tools/src/gdb-6.0
( http gdb-6.0-bdm-m68k.patch )
$ patch -p1 < gdb-6.0-bdm-m68k.patch
$ cd ~/coldfire/tools/build/gdb
$ ../../src/gdb-6.0/configure --prefix=$prefix/bin --target=m68k-bdm-elf
$ make CFLAGS="-I$HOME/coldfire/tools/src/m68k-bdm-1.3.0/lib -I$HOME/coldfire/tools/src/m68k-bdm-1.3.0/driver"  LDFLAGS="-L$HOME/coldfire/tools/src/m68k-bdm-1.3.0/lib -L$HOME/coldfire/tools/lib"
$ make install CFLAGS="-I$HOME/coldfire/tools/src/m68k-bdm-1.3.0/lib -I$HOME/coldfire/tools/src/m68k-bdm-1.3.0/driver" LDFLAGS="-L$HOME/coldfire/tools/src/m68k-bdm-1.3.0/lib -L$HOME/coldfire/tools/lib"

Using your tools

Once the tools are working, now we need to do something with them. I have an evaluation board which has the ColdFire 5249 processor, an ethernet chip, external flash, and external SRAM. Knowing what you have is important because you need to create your memory map that controls how these external components are accessed.

The board came with some code in flash that contains a command line interpreter and some tools for doing various things. One of the commands is the 'mmap' command, which displays the current memory map.

      Type           Start         End      Port Size
  ---------------------------------------------------
  SDRAM            0x00000000   0xFFFFFFFF   32-bit
  Vector Table     0x00000000   0x000003FF   32-bit
  MBAR             0x10000000   0x100003FF   32-bit
  MBAR2            0x80000000   0xBFFFFFFF   32-bit
  Internal SRAM1   0x20000000   0x2000FFFF   32-bit
  Internal SRAM0   0x20010000   0x20017FFF   32-bit
  Flash            0xFFE00000   0xFFFFFFFF   16-bit
    Chip Selects
  ----------------
  CS0  Flash
  CS1  Ethernet
  CS2  not in use
  CS3  not in use

This is a good starting point to work with and I figured the best way to do something right is to copy something that works. So we can see that there are several things we should initialize. The obvious place to start is to get the internal SRAM up and running because up to this point (assuming that we have a BDM connection to the processor) we are only talking to the processor and not relying on initializing any external parts (which usually requires initializing some internal registers). This simplicity makes things a bit easier.

The MCF5249 comes with two 16k banks of SRAM in the processor package. Once initialized by the debugger, we can download some boot code into the SRAM, point the program counter at the code's entry point and then run the code. That code can perform some more elaborate initialization of the peripherals and then jump to the application. In production, our boot code would be loaded into the flash part. When the processor resets, it asserts the chip select for the flash and latches the bus so that the processor can talk to the flash part, and fetch instructions from it. So the boot code we are now loading into SRAM, will eventually end up in the boot sector of our EEPROM.

To initialize the on-board SRAM, we simply write the base address (where we want the SRAM to appear in the memory map) into RAMBAR0 and RAMBAR1. Other Coldfire processors may have more or fewer SRAM modules on board. For our purposes, we only need the one, as 16k is enough for us to use at the moment. So for the first module, we will put it at location 0. This is equivalent to the following GDB commands.

set $srambase = 0x00000000
set $rambar = $srambase + 0x21
The 0x21 is added to the register to set 2 bits, one to prevent CPU space instructions (like MOVEC) from interacting with the SRAM module, and the other to mark the SRAM address as valid, and therefore enable the part.

After the SRAM is initialized we can go ahead and place some code in there and run it. Writing the code itself can be a bit of a chore, because gcc or as may not be aware of all of the registers in the CPU that you are using. This can lead to a good deal of frustration as you create code that should work, but doesn't. So I will show some techniques that will allow you to verify that the code is as it should be.

In my particular case, I decided to blink some LED's that are connected to some GPIO pins. Several things need to happen in order to do this. First, the secondary module base address register (SECMBAR) needed to be initialized, and then the GPIO registers, which are indexed off of the SECMBAR, need to be initialized. The assembler is not aware of the SECMBAR register, so we need to have an equate to make it work. Then we can use the equate in an instruction to initialize the register. The code looks like this.

.set	MBAR2_BASE, 0x80000000 /* Base address of MBAR2 peripherals area */
.set	SECMBAR,    0xC0E	   /* Secondary module base address register 1 */

        move.l      #(MBAR2_BASE+0x0001),%D0
        movec.l    %D0,#SECMBAR
Since there is no SECMBAR register in the assembler, we need to generate one using the movec opcode, which moves a value into an area in "CPU space". If you look in the processor's datasheet, you can find a list of all the registers, and their CPU space addresses. Simply define a constant, and use it as shown above to initialize your processor's extended register set. I will illustrate below how to doublecheck that the instruction was created properly.

Once you have created your application software you need some way to get it into the machine. You can download it to the SRAM, but when you start up the BDM debugger, the processor is frozen in its reset state. So you need the debugger to send commands to the processor to run a few instructions to initialize enough things so that your program will work. In the case of the ColdFire 5249, the debugger needs to initialize the Status Register to disable interrupts, and initialize the SRAM Base Address Register (RAMBAR). Once this is done, the debugger can load the program into SRAM and run it.

The debugger script below does this. The play-by-play for the script is

  1. Connect to the processor via the BDM interface
  2. Set the status register to disable interrupts
  3. Create a constant called 'srambase'
  4. Initialize the SRAM module to be addressed at 0x20000000 (The 0x21 added to it is to enable the SRAM (0x01) and disable CPU space (the movec instruction) accesses in this region (0x20).
  5. Load the program into SRAM (the linker file must reflect the location of SRAM).
  6. Set the program counter to fetch the instructions at the SRAM base.
  7. Display the first 5 instructions (Notice that the addresses are not multiples of 4. This is because some instructions are 6 or even 8 bytes long, with their immediate data).
#
# GDB Init script for the Coldfire 5249 processor.
#
target bdm /dev/bdmcf0
set $sr = 0x2700
set $srambase = 0x20000000
set $rambar = $srambase + 0x21
set $pc = $srambase
load boot.elf
sym boot.elf
break blinky.c:37

The program that is written is simple enough. It initializes the processor and then jumps to a small routine that toggles some of the general purpose I/O pins on the processor that are connected to some LEDs on my development board. The processor initialization code is written in assembly and is quite lengthy (It is the 5249init.asm file in the archive). It basically consists of initializing the nessecary registers and then JMP'ing to the entry point in the LED blinking code. The LED code is written in C and is quite easy to follow. The only real things to note are that the addresses in the C program must match those in the assembly code.

// Memory-mapped registers within MBAR2 peripherals area
#define MBAR2_BASE  (0x80000000)
#define GPIOREAD    (MBAR2_BASE+0x00000000)
#define GPIOWRITE   (MBAR2_BASE+0x00000004)
#define	true        (1)
#define	false       (0)


unsigned    TAG;
unsigned    COUNT;
unsigned    BEFORE;
unsigned    AFTER;

#define FLASH_LED_DELAY    (0x10)
#define D17_LED_BIT        (12)
#define D18_LED_BIT        (9)


unsigned * gpiord_ptr = (unsigned *)GPIOREAD;
unsigned * gpiowr_ptr = (unsigned *)GPIOWRITE;

void _entry( void )
{
	unsigned		dword = 0;
	unsigned 			i = 0;
	
	do
	{
		// LED D17 on, D18 off
		dword = *gpiord_ptr;
		dword |=  (0x01 << D17_LED_BIT);
		dword &= ~(0x01 << D18_LED_BIT);
		*gpiowr_ptr = dword;
	
		for(i =0; i < 3000000; i++);
    
		// LED D18 on, D18 off
		dword = *gpiord_ptr;
		dword &= ~(0x01 << D17_LED_BIT);
		dword |=  (0x01 << D18_LED_BIT);
		*gpiowr_ptr = dword;
	
		for(i =0; i < 3000000; i++);
		
	} while (true);

}

In order to build this program you need to assemble the initialization code, compile the C program, and link it all together. Here is the makefile that I use to generate the program. Note that the rule to build the S19 record is not really needed. However, many EEPROM programmers accept the S19 format, but not the ELF format, so knowing how to convert from one to the other is quite handy. Also, the $@ means the rule target, and the $? means the rule dependencies.

# Makefile for coldfire based devices

ASSEMBLE=m68k-elf-as
COMPILE=m68k-elf-gcc
LINK=m68k-elf-ld
OBJCOPY=m68k-elf-objcopy

OBJECTS = 5249init.o blinky.o
	
TARGET = -m5200

# DEPENDENTS =

# Do linking and output generation

boot.s19 : boot.out
	$(OBJCOPY) -S -O srec boot.elf boot.s19

boot.out : $(OBJECTS) boot.dld
	$(LINK) --cref -Map boot.map -o boot.elf $(OBJECTS) boot.dld

blinky.o: blinky.c
	$(COMPILE) $(TARGET) -g -c $? -o $@

5249init.o: 5249init.asm
	$(ASSEMBLE) $(TARGET) -l $? -o $@

clean :
	rm *.o blinky.asm boot.map boot.elf boot.s19
The boot.dld file is a script that tells the linker how to handle these files and where to locate them in RAM. This is needed since the program is not running with an operating system that can relocate the program anywhere in memory. Basically it assignes some names to the various memory blocks. In this case I have an SRAM module at 0x20000000. This is the value that must be synchronized with the GDB script because if the two don't match, the debugger will try to load the program into the wrong location and it just won't work.

MEMORY
	{
	sram		:   ORIGIN = 0x20000000,    LENGTH  = 0x00004000
	}

/*
;****************************************************************************
;*			COMMON SECTIONS
;****************************************************************************
*/

SECTIONS
{
	.text   	:	{ * (.text) } > sram
}

Once all these files are created, then you simply invoke 'make' to build the program. You can then invoke the debugger with the command

m68k-bdm-elf-gdb -x 5249.gdb
to initialize the processor and load the program. Once you have the GDB prompt, you can run the program by typing 'c' for continue. The program will run to the breakpoint, where everything will be halted and you can do your job.

If a command line debugger is not your cup of tea, you can use a program called the Data Display Debugger (DDD). This is a unix program which can be made to run on a Windows platform. You will need to use the Cygwin installer to install the X Windows system on your Windows machine. The specific package that you need is the 'xorg-x11-base' package from the 'X11' category. You can get more information about this package at http://x.cygwin.com/.

By default, X Windows will create a window and run all X programs insde that window. If you would like a more natural feel to it, you can have X run out of sight. The functionality will be there, but now when you run X Windows programs, they will be on the desktop, just like all your other programs. To do this you simply pass X Windows some arguments when it starts up.

	/usr/X11R6/bin/XWin.exe -multiwindow

I put it all into a script file called 'startx' with the following commands. I used the echo and redirection command, because most Windows/DOS editors will insert CR/LF at the end of a line, which can bork up a unix shell script. Once it is created, most editors are smart enough to realize it is a Unix file and will not place those pairs in there. But if you have problems with your scripts, it might be because your editor has inserted a CR/LF at the end of a line.
$ echo !#/bin/sh > $HOME/startx
$ echo /usr/X11R6/bin/XWin.exe -multiwindow >> $HOME/startx

Once X Windows is installed you can download DDD via Cygwin and invoke it like this...

ddd --debugger m68k-bdm-elf-gdb boot.elf --command 5249.gdb

DDD should start up, and run the debugger script that initializes the processor, downloads the contents to RAM, and gets everything going. DDD itself is fairly intuitive to use and should be much easier on the eyes when compared to gdb.


Thanks to the following people who have provided valuable feedback to this page:
Mike A.