The Bare Metal

As a first step in the development of embedded software, it may be of interest to create and run a simple program. It could also be of interest to create a minimal program, which is possible to run without using an operating system. Performing such an exercise will illustrate the necessary steps for writing, compiling, and executing a program on the chosen hardware platform. It will also, since the goal is to run without an operating system, provide insight into the mechanisms used by the hardware to start a stand-alone, small, program.

The program to be executed is a program, of the Hello World-type. It is written in C, as many embedded programs are, and its source code is shown in Figure 1.

#include "console.h"

int main(void)
{
    console_put_string("Hello from a bare metal C-program!\n"); 
    return 0; 
}

Figure 1. A program to be run on a processor, without any operating system. The program in Figure 1 uses a function console_put_string. The function console_put_string prints a string. The string is given as argument to the function. The actual printing needs to be done so that the user can see the printed string. This can be accomplished by sending the string to an appropriate output unit.

Our goal is to run the program as a bare metal program, without the help of an operating system. This means that the program is the only program running on the computer, and that we have to ensure that it can be started without the help of an operating system.

When running the program on a Beagleboard with an ARM-processor, it could be interesting to try to download the program, and then, after it has been downloaded, to make the program run without the help of an operating system. The function console_put_string can be designed to print a string by sending it over a serial communication link, connected to a host computer, where the received characters can be viewed, using a program such as GTKTerm.

Calling main

The program in Figure 1 contains a function called main. This is common for programs written in C. When a C-program is executed under the control of an operating system, the main-function is called automatically when the program is started. When a C-program is executed as a bare metal program, the main-function will not be called automatically.

The main function in a bare-metal program can be called by adding extra code, outside of the program, and making sure that this added code calls main. The added code is referred to as startup code. Of course it must be ensured that the startup code is executed.

The purpose of the startup code is to perform a minimum of initialization, and when this is done, call the main-function. This initialization often involves operations that are specific for the chosen hardware, such as writing certain values to processor registers. For this reason, it may be convenient to write the startup code in assembly.

When using a Beagleboard it is possible to download an executable file from a host computer, connected to the Beagleboard via a serial cable. The startup code can be placed inside the executable file. By placing the startup code so that it is executed as the first piece of code when the downloaded executable file is started, the startup code can perform its task of calling the main-function, and in this way the program is started. The actual download is performed by code which is executed during start of the Beagleboard. This code is referred to as a boot monitor. The boot monitor is started by code that executes at the very first moments, after the Beagleboard is powered on. This, initial code, is referred to as boot code.

Read more

In the beginning

The startup code in Figure 2 calls the main-function. In this case, as shown in Figure 1, it is a main-function of a small stand-alone program. In a larger system, it may be the main-function of an operating system, for example Linux.

As described above, the startup code is executed when starting a program, containing the startup code as well as executable code corresponding to the program in Figure 1, that has been downloaded as an executable file to the Beagleboard.

One might wonder how the very first code that runs, when a computer starts, is invoked. Considering a scenario where a computer is started, by switching on the power, it can be seen in Section 2.15.3 of the ARM Cortex A8 Technical Reference Manual, that execution starts at an address specified by the reset exception vector. This is a predefined place in memory, where the address to the very first code to be executed is stored. This code has the task of performing necessary initialisations. The initialisations done may involve initialisations of important devices such as interrupt controller and cache controller, and also initialisation of communication devices, such as serial ports and network interfaces, may be necessary. When the initialisations are done, startup code like the one in Figure 2 is executed.

Read more

From source to binary

Executable code which is possible to run without an operating system can be created from the source code shown in Figure 1. The executable code can be executed on a computer with an ARM-processor, such as the Beagleboard, by downloading the executable code to the Beagleboard. The executable code is created by compiling the source code in Figure 1, and then linking the resulting object code with object code generated from the startup code in Figure 2.

The compilation is done using a compiler for the chosen hardware. For the case of a computer with an Intel-compatible processor, which is running some flavour of the Linux operating system, it is possible to use a cross-compiler for ARM. Information about download and installation of an ARM cross-compiler, for Ubuntu Linux, can be found in this article on installation of an ARM cross compiler in Ubuntu.

For the case of a Mac computer with an Intel-compatible processer, which is running the Mac OS X operating system, it is possible to use a cross-compiler for ARM. Information about download and installation of an ARM cross-compiler, on a Mac running OS X Mountain Lion, can be found in this article on installation of an ARM cross compiler in Mac Mountain Lion.

It may also be possible to create executable code, which is possible to run without an operating system, on a computer which runs some variant of Microsoft Windows. For this case, it is recommended to use an add-on program which gives the user a Unix/Linux-like environment. Examples of such programs are Cygwin and MinGW.

It will be assumed, throughout the remainder of the book, that if nothing else is stated, the examples shown are compiled and linked using a personal computer with Linux. When knowledge about other environments is available, it will be shared using links to places where additional information is available.

The actual execution of the programs will be done on a computer in the form of a Beagleboard, equipped with an ARM-processor.

As an alternative means for execution, a computer simulator will be used.

Assume that the source code in Figure 1 is stored in a file named bare_metal.c. Assuming that a compiler is available, as described above, the file bare_metal.c can be compiled using the command

arm-none-eabi-gcc -c -mcpu=cortex-a8 -Wall bare_metal.c

In the above command the argument -mcpu=cortex-a8 is used. The purpose of this argument is to instruct the compiler to generate code for an ARM Cortex-A8 processor. The argument -Wall is also used, for the purpose of enabling all warnings. As a result of the command, an object file named bare_metal.o is generated.

The startup code in Figure 2 is written in assembly language. An assembler can be used for the purpose of translating the assembly code in Figure 2 to object code.

Read more

Make it run

A file prog.srec, containing executable code which is generated as described in Section From source to binary, from source code as shown in Figure 1 and from assembly code as shown in Figure 2, can be executed on a Beagleboard.

The file prog.srec can be downloaded from a computer to a Beagleboard, using the program GTKTerm.

Before performing the download, the port and the communication speed must be assigned values. This is done inside the GTKTerm program. Typical values may be /dev/ttyUSB0 for the port and 115200 for the communication speed.

The program can be downloaded by first resetting the Beagleboard, using one of its two white buttons, and then, before the onboard Linux distribution is started, stop the boot process by pressing a key. This makes the Beagleboard ready for receiving U-Boot commands.

The command loads can be used to initiate a download. Using the mouse or the command Ctrl-R, the srec-file to be downloaded shall be located.

When the download is complete, the program can be started. This is done by giving the command go followed by the start address of the program. The start address shall be selected as specified in the linker script in Figure 3.

Read more

Read more

You can read more about bare-metal programming for ARM e.g. from this article about bare-metal programming in ARM. Another source of information could be a series of articles from EE Times.

Read more