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.
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.
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.
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.
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.0Note 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.
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.
../gcc-3.2.3/configure --target=$target --prefix=$prefix \ --with-gnu-as --with-gnu-ld --with-newlib --verbose \ --enable-threads --enable-languages="c,c++"
$ 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"
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"
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 + 0x21The 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,#SECMBARSince 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
# # 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.s19The 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.gdbto 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
$ 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