[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This chapter illustrates through an example how you can build a 68HC11 bootstrap program, compile it, link it and have it run on the target board, in the GNU debugger or in the simulator.
2.1 Overview Quick overview of hello example 2.2 Source Description Presents step by step the example 2.3 Compile and Link Explains how to compile and link the example 2.4 Running The Example Running the example
This example is a simple hello program written in C that prints
Hello World!
on the 68HC11 serial line. It implements a print
function which controls the serial line to send the message and it
defines the main to print the hello message.
The source comes with the GNU Embedded Libraries package in the `hello/hello.c' file.
To compile this program, you can use the following command:
m6811-elf-gcc -g -Os -mshort -Wl,-m,m68hc11elfb -o hello.elf hello.c |
This will produce the `hello.elf' file. This file in a binary file in the ELF/DWARF-2 standard format. It contains the 68HC11 binary code, the symbols and the debugging information suitable for the GNU debugger. To obtain the Motorola S19 file corresponding to the program, you can use the following command:
m6811-elf-objcopy --only-section=.text --only-section=.rodata \ --only-section=.vectors --only-section=.data \ --output-target=srec hello.elf hello.s19 |
and this will produce `hello.s19' file in Motorola S19 format.
To run or test this program, you can use the simulator or gdb:
m6811-elf-run hello.elf |
The program will print the following message:
Hello world! |
This section describes step by step the different parts of the `hello.c' example.
The hello example writes a message on the serial line. To do this we have to control the 68HC11 SCI and we must access the I/O ports and control them.
A first part defines several flags and values that represent the 68HC11 SIO registers. The complete list of flags are available when you include the `<sys/ports.h>' file and it is recommended to use the following in your own programs:
#include <sys/ports.h> |
The flags are defined in `<asm-m68hc11/ports_def_F.h>', `<asm-m68hc11/ports_def_E.h>' or `<asm-m68hc12/ports_def.h>' depending on the microcontroller.
Below is the list of flags used by the hello example (several SCI flags have been removed for clarity):
#define M6811_BAUD 0x2B /* SCI Baud register */ #define M6811_SCCR1 0x2C /* SCI Control register 1 */ #define M6811_SCCR2 0x2D /* SCI Control register 2 */ #define M6811_SCSR 0x2E /* SCI Status register */ #define M6811_SCDR 0x2F /* SCI Data (Read => RDR, Write => TDR) */ /* Flags of the SCCR2 register. */ #define M6811_TE 0x08 /* Transmit Enable */ #define M6811_RE 0x04 /* Receive Enable */ /* Flags of the SCSR register. */ #define M6811_TDRE 0x80 /* Transmit Data Register Empty */ /* Flags of the BAUD register. */ #define M6811_SCP1 0x20 /* SCI Baud rate prescaler select */ #define M6811_SCP0 0x10 #define M6811_SCR2 0x04 /* SCI Baud rate select */ #define M6811_SCR1 0x02 #define M6811_SCR0 0x01 #define M6811_BAUD_DIV_1 (0) #define M6811_BAUD_DIV_3 (M6811_SCP0) #define M6811_BAUD_DIV_4 (M6811_SCP1) #define M6811_BAUD_DIV_13 (M6811_SCP1|M6811_SCP0) #define M6811_DEF_BAUD M6811_BAUD_DIV_4 /* 1200 baud */ |
The 68HC11 SCI registers can be accessed in C by reading and writing the memory. In the program, the I/O ports are represented by an array of bytes which is mapped at a given fixed address. This array starts at beginning of the I/O register map. It is declared as extern so that the address is defined at link time.
extern volatile unsigned char _io_ports[]; |
The volatile keyword is important as it tells GCC to avoid any optimisation when reading the memory. This is necessary otherwise GCC would simply remove the busy wait loop to detect that the transmitter is ready.
For example to write the SCCR2
SCI register we will do
the following:
_io_ports[M6811_SCCR2] = M6811_TE; |
whereas to read the SCCR1
SCI register we will do
the following:
unsigned char flags = _io_ports[M6811_SCSR]; |
A second part defines some functions to write characters on the SIO. They access the IO register through the `_io_ports' global variable.
These operations can be used in your program by using the following include:
#include <sys/sio.h> |
A first operation named `serial_send' is defined to send the character on the serial line. This function is defined as follows:
static inline void serial_send (char c) { /* Wait until the SIO has finished to send the character. */ while (!(_io_ports[M6811_SCSR] & M6811_TDRE)) continue; _io_ports[M6811_SCDR] = c; _io_ports[M6811_SCCR2] |= M6811_TE; } |
It sends the character `c' on the serial line. Before sending, it waits for the transmitter to be ready. Once the function returns, the character is in the SCI queue and it may not be sent completely over the serial line.
A second function called `serial_print' uses `serial_send'
to send a complete string over the serial line. It iterates over
the character string and send each of them until the end of the
string represented by a 0
is reached.
void serial_print (const char *msg) { while (*msg != 0) serial_send (*msg++); } |
The last part represents the main function. In C and C++, the main function must be called `main'. When this `main' function is entered, the stack pointer is initialized, the global variables are initialized but the SIO as well as other 68HC11 devices are not yet initialized. In this example, we first configure the SIO by setting the baud rate and the character format. The SIO must then be started by enabling the transmitter and receiver.
The SIO initialization is inlined here for clarity but it is implemented by the `serial_init' operation available when the `<sys/sio.h>' include file is used.
Once the SIO is initialized, we can write messages using `serial_print'.
int main () { /* Configure the SCI to send at M6811_DEF_BAUD baud. */ _io_ports[M6811_BAUD] = M6811_DEF_BAUD; /* Setup character format 1 start, 8-bits, 1 stop. */ _io_ports[M6811_SCCR1] = 0; /* Enable receiver and transmitter. */ _io_ports[M6811_SCCR2] = M6811_TE | M6811_RE; serial_print ("Hello world!\n"); return 0; } |
In this example, the `main' function returns and this will give
back control to the startup code which will in turn call `exit'.
The `exit' will loop forever arround a wai
instruction.
Indeed, a real 68HC11 hardware has no way to exit!
Now that we have the C source file, it's necessary to compile, assemble and link it. The compilation is the process by which the 68HC11 instructions are generated from the `hello.c' source file. The assembling is the process which transforms the 68HC11 instructions into the 68HC11 binary code. The link is the final process which collects together all binary files, organizes them in memory and assigns them final addresses. These processes can be separated or run in a single command.
To compile the program, you will use the `m6811-elf-gcc' driver. This driver invokes the pre-processor, the compiler, the assembler and the linker depending on options and files that you give as arguments. For the example, you will issue the following command:
m6811-elf-gcc -g -Os -mshort -Wl,-m,m68hc11elfb -o hello.elf hello.c |
The driver uses the file extension to decide what must be done for a given file. For `hello.c' file it will use the C compiler. We use the following options to control the compilation and assembling passes:
-g
-Os
-mshort
Once the `hello.c' file is compiled and assembled, the driver will run the linker. The link pass is a non-trivial process although it has been simplified. One important role of the linker is to map the program in memory and assign each symbol an address. To tell the linker where it must put the program we use the `memory.x' file to describe the memory. This file is used by the linker when we use the `-Wl,-m,m68hc11elfb' option. It contains the following definitions:
MEMORY { page0 (rwx) : ORIGIN = 0x0, LENGTH = 256 text (rx) : ORIGIN = 0x0, LENGTH = 256 data : ORIGIN = 0x0, LENGTH = 0 } |
The `MEMORY' part describes the memory regions that the board provides. The example uses the 68HC11 bootstrap mode as this is available for most target boards. It does not depend on the RAM and ROM characteristics of the target. In the bootstrap mode, we can rely only on the 68HC11 internal RAM mapped at address 0. The important definition for this is the `text' region. It indicates that it starts at 0 and can contain 256 bytes. The `rx' flags indicate that this `text' region is both readable (`r') and executable (`x'). The `text' region is used by the linker to put the program code in it. The `data' region is used by the linker to put the global and static variables. In this example, we defined this region as empty so that we can detect problems in using the bootstrap mode.
The `memory.x' file also defines the following part:
PROVIDE (_stack = 0x0100-1); PROVIDE (_io_ports = 0x1000); |
These two definitions create two symbols named `_stack' and
`_io_ports' and assign them a value. The `_stack' symbol
represents the top of the stack and the `_io_ports' symbol
represents the 68HC11 I/O registers that we used in the program.
The I/O registers are mapped at address 0x1000
by default.
For the link pass, the following options are important:
-Wl,-m,m68hc11elfb
-o hello.elf
-mshort
Now that the hello program is compiled and linked, you want to test and have it run somewhere. There are several ways to run the example:
The simulator is the quickest and easiest way to test the example. You don't need to have a target board. Just type the following command:
m6811-elf-run hello.elf |
The simulator creates a virtual 68HC11 running at 8Mhz and simulates all its instructions with the internal devices. The program prints the following:
Hello world! |
The GNU Debugger can be used to test and debug the example. It provides the simulator and allows debugging at source level with it. The process is more complex but remains quite easy. Start the debugger as follows:
m6811-elf-gdb hello.elf |
Then, you must tell GDB to connect to the simulator. This is done by using the `target sim' command:
(gdb) target sim |
The simulator is ready and the program must be loaded in memory. This is done by using the `load' command as follows:
(gdb) load |
We can now execute the program by starting the simulator:
(gdb) run |
The simulator print the hello message in the GDB output console.
Using a real 68HC11 board is a little bit more complex. First you must configure your board to start with the 68HC11 bootstrap mode.
Now, you must connect the 68HC11 board and your host computer with a serial cable. The serial line must be configured at 1200 baud, no parity and one stop bit. It must not use the RTS/CTS control flow.
To upload the program you will use a program loader on the host computer. Some loaders are able to use the `hello.elf' ELF file directly while some others can only read the `hello.s19' file.
loader -d /dev/ttyS0 hello.elf |
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |