Getting Started with µMPS2
Introduction
Writing your own programs (i.e. a simple operating system) for a bare machine such as µMPS is not necessarily very hard, but the task is in certain respects inherently different from that of writing programs for a "hosted" environment. The purpose of this guide is to give you a panoramic view of the µMPS programming environment, and thus help you avoid getting lost in the many pages of the available documentation. That being said, this short guide is not a substitute for the longer, official, documents.
Installation and Package Overview
The obvious first step is to get µMPS2 up and running on your system. See the Download page for information on obtaining and installing µMPS2.
What does the µMPS2 package consist of? Besides the emulator (umps2), several other components are distributed and installed with µMPS2:
- Bootstrap and execution ROM (also known as the BIOS) images
-
The bootstrap ROM, of which two versions are provided (see the section on loading below), contains the boot loader, which loads your operating system into RAM, performs necessary initializations, and finally transfers control to the OS.
The execution ROM contains the core, low-level, exception handling code and exposes a higher-level exception handling interface to the OS. In practice, this means you don't need to write exception handling code in assembly and you can enjoy the comfort of the C language.
- Object file support tools
- The following tools, which run on the host system, are provided:
- umps2-elf2umps
- This tool is used to convert files in the ELF format to files in a native µMPS format.
- umps2-objdump
- This tool is used to inspect files in the µMPS .aout or .core object formats.
- Target utility library, startup modules, and headers
-
These components are provided to simplify the task of writing programs for µMPS. As an example, it usually possible to write all your code in C when using them, making familiarity with MIPS assembly optional.
The utility library, called libumps, allows you to conveniently invoke BIOS services from C, access coprocessor registers, and issue coprocessor instructions.
The header files contain useful definitions, such as register map definitions for various device and machine registers, that can save you time and debugging effort.
Toolchain
Since you will hopefully be writing your programs in C, you will need a compiler that supports our target platform. Your host compiler cannot be used for this purpose, as it will generally support a processor architecture different from the target one (MIPS-I). Instead, to compile programs for µMPS you will need to use a separate compiler, called a cross compiler. More generally, we refer to the cross compiler and the associated set of programming tools as a cross toolchain. Thus, the build environment for µMPS programs will run on your host system, and will consist of a cross GNU toolchain and the µMPS object file utilities.
Object File Formats
The cross GNU toolchain we use is configured for an ELF target, the standard object file format on virtually all modern Unix-like systems. While very flexible, ELF requires somewhat complex runtime support. For this reason, µMPS comes with support for simplified object file formats, based on the original Unix a.out format. Two variations of the executable file format are provided: the .aout format is used for regular executables (i.e. "userland") while the .core file format is used for kernel images.
Building the Cross Toolchain
Before moving on, you need a working µMPS cross toolchain. Detailed instructions on using the GNU toolchain for µMPS development are given in a separate document [PDF], which explains everything from how to build the cross toolchain to MIPS-specific compiler details relevant to µMPS programmers.
For the impatient, we provide a simple (but not quite foolproof!) Bash script that will take care of downloading and building the toolchain automatically. The default installation prefix is $HOME/umpsxt; you can of course tweak this and other options to taste (see the comment at the top of the script). Simply run the script in a directory where you want the build to take place, e.g.:
$ cd umpsxt-bld $ ls -F build-umpsxt* $ ./build-umpsxt [...] $ export PATH=$PATH:$HOME/umpsxt/bin $ mipsel-elf-gcc --version mipsel-elf-gcc (GCC) 4.6.2
If all the dependencies were met (don't worry, configure will complain if they were not), you should now have a functional cross toolchain on your system under $HOME/umpsxt.
Booting in µMPS
Unless you are writing a program that can reside entirely in read-only memory (ROM), your program (i.e. the operating system) must somehow be loaded into RAM — typically from secondary storage — before much can happen. Since writing a boot loader is a non-trivial task for the inexperienced programmer, this would cause a major hurdle; µMPS offers two solutions for this problem, both of which free you from having to write your own loader:
- The tapeboot.rom.umps bootstrap ROM was devised to load the OS from the first tape device (tape 0), performing very much the same task as traditional boot loaders.
- The other option supported by µMPS, even more convenient then the previous, is to have the OS (i.e. the .core image) already preloaded in RAM on boot. When using this feature, you shoud use the coreboot.rom.umps bootstrap ROM.
Hello World
Now that the emulator and the toolchain are installed, we are ready to compile and run a sample program. Following tradition, the program prints the venerable "hello, world" message on a µMPS terminal.
Download the example program [.tar.gz]The example consists of a single C module, hello.c. You will have noted that our Hello world program is significantly more complex than the average version found in programming literature. The reason for this is that we obviously have to program the terminal device directly, since we do not have higher-level abstractions at our disposal. These details are irrelevant to us at this point, though — we are simply interested in building and running the program.
Use the included Makefile to build the program. There are several things to note about about the Makefile:- MIPS-specific compiler options are used to tweak code generation as suitable for a standalone, bare-metal program (e.g. an OS).
- The standard startup module for kernels (crtso.S) is used.
- We use the standard linker script for kernel executables, umpscore.ldscript.
Running the Program
To run the above example you need to create a new machine. This is performed using the GUI and is easy enough that it should require little explanation: simply create a new machine configuration (giving it any name you like) in the directory where the example program resides.
As it turns out, we do not need to change any settings as the defaults are exactly what we want: the only installed device is terminal 0, the core file is preloaded in RAM, and the bootstrap ROM is coreboot.rom.umps.
You can now power on the machine and start it; the greeting message should appear in the terminal 0 window:
Further Reading
The µMPS2 machine architecture is described in µMPS2 Principles of Operation (see the Documentation page). The previous editions of that book described earlier revisions of the architecture.
There are times when you may want or need to program in MIPS assembly language, such as when writing low-level exception handling code, or when learning assembly programming for it's own sake. In such cases, you will need a reference on MIPS architecture and assembly language, of which there are plenty. One of the standard texts is MIPS RISC Architecture by Garry Kane (Prentice Hall, Englewood Cliffs, NJ 07637; ISBN: 0-13-584293-X).