Programming With <productname>eCos</productname> Programming With <productname>eCos</productname> The following chapters of this manual comprise a simple tutorial for configuring and building eCos, building and running eCos tests, and finally building three stand-alone example programs which use the eCos API to perform some simple tasks. You will need a properly installed eCos system, with the correct versions of the GNU toolchain. On Windows you will be using the bash command line interpreter that comes with Cygwin, with the environment variables set as described in the toolchain documentation. The Development Process Most development projects using eCos would contain some (or most) of the following: <productname>eCos</productname> Configuration eCos is configured to provide the desired API (the inclusion of libc, uitron, and the disabling of certain undesired funtions, etc.), and semantics (selecting scheduler, mutex behavior, etc.). See . It would normally make sense to enable eCos assertion checking at this time as well, to catch as many programming errors during the development phase as possible. Note that it should not be necessary to spend much time on eCos configuration initially. It may be important to perform fine tuning to reduce the memory footprint and to improve performance later when the product reaches a testable state. Integrity check of the <productname>eCos</productname> configuration While we strive to thoroughly test eCos, the vast number of configuration permutations mean that the particular configuration parameters used for your project may not have been tested. Therefore, we advise running the eCos tests after the project's eCos configuration has been determined. See . Obviously, this should be repeated if the configuration changes later on in the development process. Application Development - Target Neutral Part While your project is probably targeting a specific architecture and platform, possibly custom hardware, it may be possible to perform part of the application development using simulated or synthetic targets. There are three good reasons for doing this: It may be possible by this means to perform application development in parallel with the design/implementation of the target hardware, thus providing more time for developing and testing functionality, and reducing time-to-market. The build-run-debug-cycle may be faster when the application does not have to be downloaded to a target via a serial interface. Debugging is also likely to be more responsive when you do not have to to communicate with the remote GDB stubs in RedBoot via serial. It also removes the need for manually or automatically resetting the target hardware. New hardware can often be buggy. Comparing the behaviour of the program on the hardware and in the simulator or synthetic target may allow you to identify where the problems lie. This approach is possible because all targets (including simulators and synthetic ones) provide the same basic API: that is, kernel, libc, libm, uitron, infra, and to some extent, HAL and IO. Synthetic targets are especially suitable as they allow you to construct simulations of elaborate devices by interaction with the host system, where an IO device API can hide the details from the application. When switching to hardware later in the development cycle, the IO driver is properly implemented. Simulators can also do this, but it all depends on the design and capabilities of the simulator you use. Some, like SID or Bochs provide complete hardware emulation, while others just support enough of the instruction set to run compiled code. Therefore, select a simulator or synthetic target and use it for as long as possible for application development. That is, configure for the selected target, build eCos, build the application and link with eCos, run and debug. Repeat the latter two steps until you are happy with it. Obviously, at some time you will have to switch to the intended target hardware, for example when adding target specific feature support, for memory footprint/performance characterization, and for final tuning of eCos and the application. Application Development - Target Specific Part Repeat the build-run-debug-cycle while performing final tuning and debugging of application. Remember to disable eCos assertion checking if you are testing any performance-related aspects, it can make a big difference. It may be useful to switch between this and the previous step repeatedly through the development process; use the simulator/synthetic target for actual development, and use the target hardware to continually check memory footprint and performance. There should be little cost in switching between the two targets when using two separate build trees. <!-- <xref> --><!-- <index></index> -->Configuring and Building <productname>eCos</productname> from Source This chapter documents the configuration of eCos. The process is the same for any of the supported targets: you may select a hardware target (if you have a board available), any one of the simulators, or a synthetic target (if your host platform has synthetic target support). <!-- <xref> --><productname>eCos</productname> Start-up Configurations There are various ways to download an executable image to a target board, and these involve different ways of preparing the executable image. In the eCos Hardware Abstraction Layer (HAL package) there are configuration options to support the different download methods. summarizes the ways in which an eCos image can be prepared for different types of download. This is not an exhaustive list, some targets define additional start-up types of their own. Where a ROM Monitor is mentioned, this will usually be RedBoot, although on some older, or low resource, targets you may need to use CygMon or the GDB stubs ROM, see the target documentation for details. Configuration for various download methods Download method HAL configuration Burn hardware ROM  ROM or ROMRAM start-up Download to ROM emulator  ROM or ROMRAM start-up Download to board with ROM Monitor  RAM start-up Download to simulator without ROM Monitor  ROM start-up Download to simulator with ROM Monitor  RAM start-up Download to simulator ignoring devices  SIM configuration Run synthetic target  RAM start-up
You cannot run an application configured for RAM start-up on the simulator directly: it will fail during start-up. You can only download it to the simulator if you are already running RedBoot in the simulator, as described in the toolchain documentation or you load through the SID GDB debugging component. This is not the same as the simulated stub, since it does not require a target program to be running to get GDB to talk to it. It can be done before letting the simulator run or you use the ELF loader component to get a program into memory. Configuring eCos' HAL package for simulation should rarely be needed for real development; binaries built with such a kernel will not run on target boards at all, and the MN10300 and TX39 simulators can run binaries built for stdeval1 and jmr3904 target boards. The main use for a ``simulation'' configuration is if you are trying to work around problems with the device drivers or with the simulator. Also note that when using a TX39 system configured for simulator start-up you should then invoke the simulator with the option instead of If your chosen architecture does not have simulator support, then the combinations above that refer to the simulator do not apply. Similarly, if your chosen platform does not have RedBoot ROM support, the combinations listed above that use RedBoot do not apply. The debugging environment for most developers will be either a hardware board or the simulator, in which case they will be able to select a single HAL configuration.
<!-- <index></index> --> Configuration Tool on Windows and Linux Quick Start This section described the GUI based configuration tool. This tool is probably more suited to users who prefer GUI's. The next section describes a CLI based tool which Unix users may prefer. Note that the use of the Configuration Tool is described in detail in . The Configuration Tool (see ) has five main elements: the configuration window, the conflicts window, the properties window, the short description window, and the output window.
Configuration Tool
Start by opening the templates window via Build->Templates. Select the desired target (see ).
Template selection
Make sure that the configuration is correct for the target in terms of endianness, CPU model, Startup type, etc. (see ).
<!-- <conditionaltext> --><!-- <xref> -->Configuring for the target
Next, select the Build->Library menu item to start building eCos (see ). The application will configure the sources, prepare a build tree, and build the libtarget.a library, which contains the eCos kernel and other packages.
Selecting the Build Library menu item
The Save As dialog box will appear, asking you to specify a directory in which to place your save file. You can use the default, but it is a good idea to make a subdirectory, called ecos-work for example.
Save file dialog
The first time you build an eCos library for a specific architecture, the Configuration Tool may prompt you for the location of the appropriate build tools (including make and TARGET-gcc) using a Build Tools dialog box (as shown in ). You can select a location from the drop down list, browse to the directory using the Browse button, or type in the location of the build tools manually.
Build tools dialog
The Configuration Tool may also prompt you for the location of the user tools (such as cat and ls) using a User Tools dialog box (as shown in ). As with the Build Tools dialog, you can select a location from the drop down list, browse to the directory using the Browse button, or type in the location of the user tools manually. Note that on Linux, this will often be unnecessary as the tools will already be on your PATH.
User tools dialog
When the tool locations have been entered, the Configuration Tool will configure the sources, prepare a build tree, and build the libtarget.a library, which contains the eCos kernel and other packages. The output from the configuration process and the building of libtarget.a will be shown in the output window. Once the build process has finished you will have a kernel with other packages in libtarget.a. You should now build the eCos tests for your particular configuration. You can do this by selecting Build -> Tests. Notice that you could have selected Tests instead of Library in the earlier step and it would have built both the library and the tests, but this would increase the build time substantially, and if you do not need to build the tests it is unnecessary.
Selecting the Build Tests menu item
will guide you through running one of the test cases you just built on the selected target, using GDB.
<!-- <index></index> --> Ecosconfig on Windows and Linux Quick Start As an alternative to using the graphical Configuration Tool, it is possible to configure and build a kernel by editing a configuration file manually and using the ecosconfig command. Users with a Unix background may find this tool more suitable than the GUI tool described in the previous section. Manual configuration and the ecosconfig command are described in detail in . To use the ecosconfig command you need to start a shell. In Windows you need to start a CygWin bash shell, not a DOS command line. The following instructions assume that the PATH and ECOS_REPOSITORY environment variables have been setup correctly as described in . They also assume Linux usage but equally well apply to Windows running Cygwin. Before invoking ecosconfig you need to choose a directory in which to work. For the purposes of this tutorial, the default path will be BASE_DIR/ecos-work. Create this directory and change to it by typing: $ mkdir BASE_DIR/ecos-work $ cd BASE_DIR/ecos-work To see what options can be used with ecosconfig, type: $ ecosconfig --help The available packages, targets and templates may be listed as follows: $ ecosconfig list Here is sample output from ecosconfig showing the usage message. Getting <!-- <index></index> --> help from ecosconfig $ ecosconfig --help Usage: ecosconfig [ qualifier ... ] [ command ] commands are: list : list repository contents new TARGET [ TEMPLATE [ VERSION ] ] : create a configuration target TARGET : change the target hardware template TEMPLATE [ VERSION ] : change the template add PACKAGE [ PACKAGE ... ] : add package(s) remove PACKAGE [ PACKAGE ... ] : remove package(s) version VERSION PACKAGE [ PACKAGE ... ] : change version of package(s) export FILE : export minimal config info import FILE : import additional config info check : check the configuration resolve : resolve conflicts tree : create a build tree qualifiers are: --config=FILE : the configuration file --prefix=DIRECTORY : the install prefix --srcdir=DIRECTORY : the source repository --no-resolve : disable conflict resolution --version : show version and copyright $ ecosconfig output — <!-- <index></index> --> list of available packages, targets and templates $ ecosconfig list Package CYGPKG_CYGMON (CygMon support via eCos): aliases: cygmon versions: &Version; Package CYGPKG_DEVICES_WALLCLOCK_DALLAS_DS1742 (Wallclock driver for Dallas 1742): aliases: devices_wallclock_ds1742 device_wallclock_ds1742 versions: &Version; Package CYGPKG_DEVICES_WALLCLOCK_SH3 (Wallclock driver for SH3 RTC module): aliases: devices_wallclock_sh3 device_wallclock_sh3 versions: &Version; Package CYGPKG_DEVICES_WATCHDOG_ARM_AEB (Watchdog driver for ARM/AEB board): aliases: devices_watchdog_aeb device_watchdog_aeb versions: &Version; Package CYGPKG_DEVICES_WATCHDOG_ARM_EBSA285 (Watchdog driver for ARM/EBSA285 board): aliases: devices_watchdog_ebsa285 device_watchdog_ebsa285 versions: &Version; … Selecting a <!-- <index></index> --> Target To configure for a listed target, type: $ ecosconfig new <target> For example, to configure for the ARM PID development board, type: $ ecosconfig new pid You can then edit the generated file, ecos.ecc, setting the options as required for the target (endianess, CPU model, Startup type, etc.). For detailed information about how to edit the ecos.ecc file, see the CDL Writer's Guide and . Create a build tree for the configured target by typing: $ ecosconfig tree If there are any problem with the configuration, ecosconfig will tell you. The most likely cause of this is mistakes when editing the ecos.ecc file. You can check whether the configuration you have made is correct, without building the tree with the following command: $ ecosconfig check If this reports any conflicts you can get ecosconfig to try and resolve them itself by typing: $ ecosconfig resolve See for more details. You can now run the command make or make tests, after which you will be at the same point you would be after running the Configuration Tool — you can start developing your own applications, following the steps in . The procedure shown above allows you to do very coarse-grained configuration of the eCos kernel: you can select which packages to include in your kernel, and give target and start-up options. But you cannot select components within a package, or set the very fine-grained options. To select fine-grained configuration options you will need to edit the configuration file ecos.ecc in the current directory and regenerate the build tree. You should follow the manual configuration process described above very carefully, and you should read the comments in each file to see when one option depends on other options or packages being enabled or disabled. If you do not, you might end up with an inconsistently configured kernel which could fail to build or might execute incorrectly.
Running an <productname>eCos</productname> Test Case In or you created the eCos test cases as part of the build process. Now it is time to try and run one. Using the <application>Configuration Tool</application> Test executables that have been linked using the Build->Tests operation against the current configuration can be executed by selecting Tools->Run Tests. When a test run is invoked, a property sheet is displayed, see . Press the Uncheck All button and then find and check just one test, bin_sem0 for example.
Run tests
Now press the Properties button to set up communications with the target. This will bring up a properties dialog shown in . If you have connected the target board via a serial cable, check the Serial radio button, and select the serial port and baud rate for the board. If the target is connected via the network select the TCP/IP button and enter the IP address that the board has been given, and the port number (usually 9000).
Properties dialog box
Click OK on this dialog and go back to the Run Tests dialog. Press the Run button and the selected test will be downloaded and run. The Output tab will show you how this is progressing. If it seems to stop for a long time, check that the target board is correctly connected, and that eCos has been correctly configured -- especially the start-up type. When the program runs you should see a couple of line similar to this appear: PASS:<Binary Semaphore 0 OK> EXIT:<done> This indicates that the test has run successfully. See for further details.
Using the command line Start a command shell (such as a Cygwin shell window in Windows) with the environment variables set as described in the toolchain documentation. Change to the directory in which you set up your build tree, and invoke GDB on the test program. To run the bin_sem0 test (which will test the kernel for the correct creation and destruction of binary semaphores) type: $ TARGET-gdb -nw install/tests/kernel/&Version;/tests/bin_sem0 You should see output similar to the following in the command window: GNU gdb THIS-GDB-VERSION Copyright 2001 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "--host=THIS-HOST --target=THIS-TARGET". (gdb) If you are trying to run a synthetic target test on Linux, skip the following connection and download steps. Otherwise, connect to the target by typing: (gdb) set remotebaud 38400 (gdb) target remote /dev/ttyS0 on Linux or (gdb) set remotebaud 38400 (gdb) target remote com1 on Windows or (gdb) target sim to use a simulator in either host O/S. Check the documentation for the target board for the actual baud rate to use when connecting to real targets. You will see output similar to the following: Remote debugging using /dev/ttyS1 0x0000d50c in ?? () at BASE_DIR/kernel/&Version;/src/common/kapi.cxx:345 Current language: auto; currently c++ (gdb) Or if you are using the simulator: Connected to the simulator. (gdb) Now download the program to the target with (gdb) load You should see output similar to the following on your screen: Loading section .text, size 0x4b04 lma 0x108000 Loading section .rodata, size 0x738 lma 0x10cb08 Loading section .data, size 0x1c0 lma 0x10d240 Start address 0x108000, load size 21500 Transfer rate: 24571 bits/sec, 311 bytes/write. (gdb) You are now ready to run your program. If you type: (gdb) continue you will see output similar to the following: Continuing. PASS:<Binary Semaphore 0 OK> EXIT:<done> If you are using a simulator or the synthetic target rather than real hardware, you must use the GDB command “run” rather than “continue” to start your program. You can terminate your GDB session with Control+C, otherwise it will sit in the “idle” thread and use up CPU time. This is not a problem with real targets, but may have undesirable effects in simulated or synthetic targets. Type quit and you are done. Testing Filters While most test cases today run solely in the target environment, some packages may require external testing infrastructure and/or feedback from the external environment to do complete testing. The serial package is an example of this. The network package also contains some tests that require programs to be run on a host. See the network Tests and Demonstrations section in the network documentation in the eCos Reference Guide. Here we will concentrate on the serial tests since these are applicable to more targets. Since the serial line is also used for communication with GDB, a filter is inserted in the communication pathway between GDB and the serial device which is connected to the hardware target. The filter forwards all communication between the two, but also listens for special commands embedded in the data stream from the target. When such a command is seen, the filter stops forwarding data to GDB from the target and enters a special mode. In this mode the test case running on the target is able to control the filter, commanding it to run various tests. While these tests run, GDB is isolated from the target. As the test completes (or if the filter detects a target crash) the communication path between GDB and the hardware target is re-established, allowing GDB to resume control. In theory, it is possible to extend the filter to provide a generic framework for other target-external testing components, thus decoupling the testing infrastructure from the (possibly limited) communication means provided by the target (serial, JTAG, Ethernet, etc). Another advantage is that the host tools do not need to know about the various testing environments required by the eCos packages, since all contact with the target continues to happen via GDB.
<!-- <xref> -->Building and <!-- <index></index> -->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 INSTALL_DIR and must be set to the pathname of the install directory created in . INSTALL_DIR may be either be set in the shell environment or may be supplied on the command line. To set it in the shell do the following in a bash shell: $ export INSTALL_DIR=BASE_DIR/ecos-work/arm_install You can then run make without any extra parameters to build the examples. Alternatively, if you can do the following: $ make INSTALL_DIR=BASE_DIR/ecos-work/arm_install <productname>eCos</productname> Hello World The following code is found in the file hello.c in the examples directory: <productname>eCos</productname><!-- <index></index> --> 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 -IBASE_DIR/ecos-work/install/include hello.c -LBASE_DIR/ecos-work/install/lib -Ttarget.ld -nostdlib The compilation command above contains some standard GCC options (for example, enables debugging), as well as some mention of paths ( allows files like cyg/kernel/kapi.h to be found, and allows the linker to find ). The executable program will be called a.out. 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 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. 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. <productname>eCos</productname> <!-- <index></index> -->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 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. 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).
Two threads with simple print statements after random delays
More Features — <!-- <index></index> -->Clocks and Alarm Handlers If a program wanted to execute a task at a given time, or periodically, it could do it in an inefficient way by sitting in a loop and checking the real-time clock to see if the proper amount of time has elapsed. But operating systems usually provide system calls which allow the program to be informed at the desired time. eCos provides a rich timekeeping formalism, involving counters, clocks, alarms, and timers. The precise definition, relationship, and motivation of these features is beyond the scope of this tutorial, but these examples illustrate how to set up basic periodic tasks. Alarms are events that happen at a given time, either once or periodically. A thread associates an alarm handling function with the alarm, so that the function will be invoked every time the alarm “goes off”. A Sample Program with Alarms simple-alarm.c (in the examples directory) is a short program that creates a thread that creates an alarm. The alarm is handled by the function test_alarm_func(), which sets a global variable. When the main thread of execution sees that the variable has changed, it prints a message. A sample <!-- <index></index> -->program that creates an alarm /* this is a very simple program meant to demonstrate a basic use of time, alarms and alarm-handling functions in eCos */ #include <cyg/kernel/kapi.h> #include <stdio.h> #define NTHREADS 1 #define STACKSIZE 4096 static cyg_handle_t thread[NTHREADS]; static cyg_thread thread_obj[NTHREADS]; static char stack[NTHREADS][STACKSIZE]; static void alarm_prog( cyg_addrword_t data ); /* we install our own startup routine which sets up threads and starts the scheduler */ void cyg_user_start(void) { cyg_thread_create(4, alarm_prog, (cyg_addrword_t) 0, "alarm_thread", (void *) stack[0], STACKSIZE, &thread[0], &thread_obj[0]); cyg_thread_resume(thread[0]); } /* we need to declare the alarm handling function (which is defined below), so that we can pass it to cyg_alarm_initialize() */ cyg_alarm_t test_alarm_func; /* alarm_prog() is a thread which sets up an alarm which is then handled by test_alarm_func() */ static void alarm_prog(cyg_addrword_t data) { cyg_handle_t test_counterH, system_clockH, test_alarmH; cyg_tick_count_t ticks; cyg_alarm test_alarm; unsigned how_many_alarms = 0, prev_alarms = 0, tmp_how_many; system_clockH = cyg_real_time_clock(); cyg_clock_to_counter(system_clockH, &test_counterH); cyg_alarm_create(test_counterH, test_alarm_func, (cyg_addrword_t) &how_many_alarms, &test_alarmH, &test_alarm); cyg_alarm_initialize(test_alarmH, cyg_current_time()+200, 200); /* get in a loop in which we read the current time and print it out, just to have something scrolling by */ for (;;) { ticks = cyg_current_time(); printf("Time is %llu\n", ticks); /* note that we must lock access to how_many_alarms, since the alarm handler might change it. this involves using the annoying temporary variable tmp_how_many so that I can keep the critical region short */ cyg_scheduler_lock(); tmp_how_many = how_many_alarms; cyg_scheduler_unlock(); if (prev_alarms != tmp_how_many) { printf(" --- alarm calls so far: %u\n", tmp_how_many); prev_alarms = tmp_how_many; } cyg_thread_delay(30); } } /* test_alarm_func() is invoked as an alarm handler, so it should be quick and simple. in this case it increments the data that is passed to it. */ void test_alarm_func(cyg_handle_t alarmH, cyg_addrword_t data) { ++*((unsigned *) data); } When you run this program (by typing continue at the (gdb) prompt) the output should look like this: Starting program: BASE_DIR/examples/simple-alarm.exe Time is 0 Time is 30 Time is 60 Time is 90 Time is 120 Time is 150 Time is 180 Time is 210 --- alarm calls so far: 1 Time is 240 Time is 270 Time is 300 Time is 330 Time is 360 Time is 390 Time is 420 --- alarm calls so far: 2 Time is 450 Time is 480 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 0.3 seconds (and 2 seconds between alarms). 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. Here are a few things you might notice about this program: It used the cyg_real_time_clock() function; this always returns a handle to the default system real-time clock. Clocks are based on counters, so the function cyg_alarm_create() uses a counter handle. The program used the function cyg_clock_to_counter() to strip the clock handle to the underlying counter handle. Once the alarm is created it is initialized with cyg_alarm_initialize(), which sets the time at which the alarm should go off, as well as the period for repeating alarms. It is set to go off at the current time and then to repeat every 200 ticks. The alarm handler function test_alarm_func() conforms to the guidelines for writing alarm handlers and other delayed service routines: it does not invoke any functions which might lock the scheduler. This is discussed in detail in the eCos Reference Manual, in the chapter The eCos Kernel. There is a critical region in this program: the variable how_many_alarms is accessed in the main thread of control and is also modified in the alarm handler. To prevent a possible (though unlikely) race condition on this variable, access to how_many_alarms in the principal thread is protected by calls to cyg_scheduler_lock() and cyg_scheduler_unlock(). When the scheduler is locked, the alarm handler will not be invoked, so the problem is averted.