[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

2. Hello World Example

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


2.1 Overview

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!


2.2 Source Description

This section describes step by step the different parts of the `hello.c' example.


2.2.1 68HC11 Definitions

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];


2.2.2 68HC11 SIO Operations

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++);
}


2.2.3 Hello World

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!


2.3 Compile and Link

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
Generate symbolic debugging information

-Os
Optimize for speed and space

-mshort
Use 16-bit for integers (int type)

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
Tell the linker to use a link configuration based on the user specific `memory.x' file.

-o hello.elf
Tell the linker to use `hello.elf' as the output file name.

-mshort
Use 16-bit for integers (int type). This is not really used by the linker but necessary for a final link so that the `m6811-elf-gcc' gives to the linker the correct startup code and the libraries compatible with the program.


2.4 Running The Example

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:


2.4.1 Simulator

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!


2.4.2 GNU Debugger

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.


2.4.3 68HC11 Board

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] [ ? ]

This document was generated by Stephane Carrez on January, 30 2005 using texi2html