Chapter 9.  Building and Running Sample Applications

The example programs in this tutorial are included, along with a Makefile, in the examples directory of the eCos distribution. The first program you will run is a hello world-style application, then you will run a more complex application that demonstrates the creation of threads and the use of cyg_thread_delay(), and finally you will run one that uses clocks and alarm handlers.

The Makefile depends on an externally defined variable to find the eCos library and header files. This variable is ECOS_INSTALL_DIR and must be set to the pathname of the install directory created in Section 7.2, “ Configuration Tool on Windows and Linux Quick Start”.

[Note]Notes
  • The variable ECOS_INSTALL_DIR is set in the environment of any shell started by the current eCos Configuration Tool and need not be specified from such shells. See Section 11.2, “Environment Variables”.

  • The Makefile of older versions of eCos and eCosPro used the variable INSTALL_DIR, did not make certain sanity checks and did not provide any useful error messages. INSTALL_DIR may still be used although if ECOS_INSTALL_DIR is set in the environment, or passed as an override to make, it will take precedence.

ECOS_INSTALL_DIR may be either be set in the shell environment or may be supplied on the command line. It is set by within shells opened by the eCos Configuration Tool using ToolsShell. To set it in another shell do the following in a bash shell where <BASE_DIR> is the base directory containing the _install directory (e.g. $HOME/ecos-work ):

$ export ECOS_INSTALL_DIR=<BASE_DIR>/arm_install

You can then run make without any extra parameters to build the examples.

Alternatively, if you can do the following:

$ make ECOS_INSTALL_DIR=<BASE_DIR>/arm_install

9.1.  eCos Hello World

The following code is found in the file hello.c in the examples directory:

9.1.1.  eCos hello world program listing

/* this is a simple hello world program */
  #include <stdio.h>
  int main(void)
  {
  printf("Hello, eCos world!\n");
  return 0;
}

To compile this or any other program that is not part of the eCos distribution, you can follow the procedures described below. Type this explicit compilation command (assuming your current working directory is also where you built the eCos kernel):

$ TARGET-gcc -g -I<BASE_DIR>/install/include hello.c -L<BASE_DIR>/install/lib -Ttarget.ld -nostdlib

The compilation command above contains some standard GCC options (for example, -g enables debugging), as well as some mention of paths (-I<BASE_DIR>/install/include allows files like cyg/kernel/kapi.h to be found, and -L<BASE_DIR>/install/lib allowsthe linker to find -Ttarget.ld).

The executable program will be called a.out.

[Note]Note

Some target systems require special options to be passed to gcc to compile correctly for that system. Please examine the Makefile in the examples directory to see if this applies to your target.

You can now run the resulting program using GDB in exactly the same the way you ran the test case before. The procedure will be the same, but this time run TARGET-gdb specifying -nw a.out on the command line:

$ TARGET-gdb -nw a.out

For targets other than the synthetic linux target, you should now run the usual GDB commands described earlier. Once this is done, typing the command "continue" at the (gdb) prompt ("run" for simulators) will allow the program to execute and print the string "Hello, eCos world!" on your screen.

On the synthetic linux target, you may use the "run" command immediately - you do not need to connect to the target, nor use the "load" command.

9.2. A Sample Program with Two Threads

Below is a program that uses some of eCos' system calls. It creates two threads, each of which goes into an infinite loop in which it sleeps for a while (using cyg_thread_delay()). This code is found in the file twothreads.c in the examples directory.

9.2.1.  eCos two-threaded program listing

#include <cyg/kernel/kapi.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

/* now declare (and allocate space for) some kernel objects,
  like the two threads we will use */
cyg_thread thread_s[2];	/* space for two thread objects */

char stack[2][4096];	/* space for two 4K stacks */

/* now the handles for the threads */
cyg_handle_t simple_threadA, simple_threadB;

/* and now variables for the procedure which is the thread */
cyg_thread_entry_t simple_program;

/* and now a mutex to protect calls to the C library */
cyg_mutex_t cliblock;

/* we install our own startup routine which sets up threads */
void cyg_user_start(void)
{
  printf("Entering twothreads' cyg_user_start() function\n");

  cyg_mutex_init(&cliblock);

  cyg_thread_create(4, simple_program, (cyg_addrword_t) 0,
  "Thread A", (void *) stack[0], 4096,
  &simple_threadA, &thread_s[0]);
  cyg_thread_create(4, simple_program, (cyg_addrword_t) 1,
  "Thread B", (void *) stack[1], 4096,
  &simple_threadB, &thread_s[1]);

  cyg_thread_resume(simple_threadA);
  cyg_thread_resume(simple_threadB);
}

/* this is a simple program which runs in a thread */
void simple_program(cyg_addrword_t data)
{
  int message = (int) data;
  int delay;

  printf("Beginning execution; thread data is %d\n", message);

  cyg_thread_delay(200);

  for (;;) {
    delay = 200 + (rand() % 50);

    /* note: printf() must be protected by a call to cyg_mutex_lock() */
    cyg_mutex_lock(&cliblock);
    printf("Thread %d: and now a delay of %d clock ticks\n", message, delay);
    cyg_mutex_unlock(&cliblock);
    cyg_thread_delay(delay);
  }
}

When you run the program (by typing continue at the (gdb) prompt) the output should look like this:

Starting program: <BASE_DIR>/examples/twothreads.exe
Entering twothreads' cyg_user_start()
function
Beginning execution; thread data is 0
Beginning execution; thread data is 1
Thread 0: and now a delay of 240 clock ticks
Thread 1: and now a delay of 225 clock ticks
Thread 1: and now a delay of 234 clock ticks
Thread 0: and now a delay of 231 clock ticks
Thread 1: and now a delay of 224 clock ticks
Thread 0: and now a delay of 249 clock ticks
Thread 1: and now a delay of 202 clock ticks
Thread 0: and now a delay of 235 clock ticks
[Note]Note

When running in a simulator the delays might be quite long. On a hardware board (where the clock speed is 100 ticks/second) the delays should average to about 2.25 seconds. In simulation, the delay will depend on the speed of the host processor and will almost always be much slower than the actual board. You might want to reduce the delay parameter when running in simulation.

Figure 9.1, “Two threads with simple print statements after random delays” shows how this multitasking program executes. Note that apart from the thread creation system calls, this program also creates and uses a mutex for synchronization between the printf() calls in the two threads. This is because the C library standard I/O (by default) is configured not to be thread-safe, which means that if more than one thread is using standard I/O they might corrupt each other. This is fixed by a mutual exclusion (or mutex) lockout mechanism: the threads do not call printf() until cyg_mutex_lock() has returned, which only happens when the other thread calls cyg_mutex_unlock().

You could avoid using the mutex by configuring the C library to be thread-safe (by selecting the component CYGSEM_LIBC_STDIO_THREAD_SAFE_STREAMS).

Figure 9.1. Two threads with simple print statements after random delays

Two threads with simple print statements after random delays