<productname>eCos</productname> Programming Concepts and Techniques Programming with eCos is somewhat different from programming in more traditional environments. eCos is a configurable open source system, and you are able to configure and build a system specifically to meet the needs of your application. Various different directory hierarchies are involved in configuring and building the system: the component repository, the build tree, and the install tree. These directories exist in addition to the ones used to develop applications. CDL Concepts About this chapter This chapter serves as a brief introduction to the concepts involved in eCos (Embedded Configurable Operating System). It describes the configuration architecture and the underlying technology to a level required for the embedded systems developer to configure eCos. It does not describe in detail aspects such as how to write reusable components for eCos: this information is given in the Component Writer’s Guide. Background Software solutions for the embedded space place particularly stringent demands on the developer, typically represented as requirements for small memory footprint, high performance and robustness. These demands are addressed in eCos by providing the ability to perform compile-time specialization: the developer can tailor the operating system to suit the needs of the application. In order to make this process manageable, eCos is built in the context of a Configuration Infrastructure: a set of tools including a Configuration Tool and a formal description of the process of configuration by means of a Component Definition Language. Configurations eCos is tailored at source level (that is, before compilation or assembly) in order to create an eCos configuration. In concrete terms, an eCos configuration takes the form of a configuration save file (with extension .ecc) and set of files used to build user applications (including, when built, a library file against which the application is linked). Component Repository eCos is shipped in source in the form of a component repository - a directory hierarchy that contains the sources and other files which are used to build a configuration. The component repository can be added to by, for example, downloading from the net. Component Definition Language Part of the component repository is a set of files containing a definition of its structure. The form used for this purpose is the Component Definition Language (CDL). CDL defines the relationships between components and other information used by tools such as the eCosConfiguration Tool. CDL is generally formulated by the writers of components: it is not necessary to write or understand CDL in order for the embedded systems developer to construct an eCos configuration. Packages The building blocks of an eCos configuration are called packages. Packages are the units of software distribution. A set of core packages (such as kernel, C library and math library) is provided by Red Hat: additional third-party packages will be available in future. A package may exist in one of a number of versions. The default version is the current version. Only one version of a given package may be present in the component repository at any given time. Packages are organized in a tree hierarchy. Each package is either at the top-level or is the child of another package. The eCos Package Administration Tool can be used to add or remove packages from the component repository. The eCos Configuration Tool can be used to include or exclude packages from the configuration being built. Configuration Items Configuration items are the individual entities that form a configuration. Each item corresponds to the setting of a C pre-processor macro (for example, CYGHWR_HAL_ARM_PID_GDB_BAUD). The code of eCos itself is written to test such pre-processor macros so as to tailor the code. User code can do likewise. Configuration items come in the following flavors: None: such entities serve only as place holders in the hierarchy, allowing other entities to be grouped more easily. Boolean entities are the most common flavor; they correspond to units of functionality that can be either enabled or disabled. If the entity is enabled then there will be a #define; code will check the setting using, for example, #ifdef Data entities encapsulate some arbitrary data. Other properties such as a set or range of legal values can be used to constrain the actual values, for example to an integer or floating point value within a certain range. Booldata entities combine the attributes of Boolean and Data: they can be enabled or disabled and, if enabled, will hold a data value. Like packages, configuration items exist in a tree-based hierarchy: each configuration item has a parent which may be another configuration item or a package. Under some conditions (such as when packages are added or removed from a configuration), items may be “re-parented” such that their position in the tree changes. Expressions Expressions are relationships between CDL items. There are three types of expression in CDL: CDL Expressions Expression Type Result Common Use (see ) Ordinary A single value legal_values property ListA range of values (for example “1 to 10”) legal_values property GoalTrue or False requires and active_if properties
Properties Each configuration item has a set of properties. The following table describes the most commonly used: Configuration properties Property Use Flavor The “type” of the item, as described above EnabledWhether the item is enabled Current_value The current value of the item Default_value An ordinary expression defining the default value of the item Legal_valuesA list expression defining the values the item may hold (for example, 1 to10) Active_ifA goal expression denoting the requirement for this item to be active (see below: Inactive Items) RequiresA goal expression denoting requirements this item places on others (see below: Conflicts) CalculatedWhether the item as non-modifiable MacroThe corresponding C pre-processor macro FileThe C header file in which the macro is defined URLThe URL of a documentation page describing the item HardwareIndicates that a particular package is related to specific hardware
A complete description of properties is contained in the Component Writer’s Guide.
Inactive Items Descendants of an item that is disabled are inactive: their values may not be changed. Items may also become inactive if an active_if expression is used to make the item dependent on an expression involving other items.
Conflicts Not all settings of configuration items will lead to a coherent configuration; for example, the use of a timeout facility might require the existence of timer support, so if the one is required the other cannot be removed. Coherence is policed by means of consistency rules (in particular, the goal expressions that appear as CDL items requires and active_if attributes [see above]). A violation of consistency rules creates a conflict, which must be resolved in order to ensure a consistent configuration. Conflict resolution can be performed manually or with the assistance of the eCos tools. Conflicts come in the following flavors: An unresolved conflict means that there is a reference to an entity that is not yet in the current configuration An illegal value conflict is caused when a configuration item is set to a value that is not permitted (that is, a legal_values goal expression is failing) An evaluation exception conflict is caused when the evaluation of an expression would fail (for example, because of a division by zero) An unsatisfied goal conflict is caused by a failing requires goal expression A bad data conflict arises only rarely, and corresponds to badly constructed CDL. Such a conflict can only be resolved by reference to the CDL writer. Templates A template is a saved configuration - that is, a set of packages and configuration item settings. Templates are provided with eCos to allow you to get started quickly by instantiating (copying) a saved configuration corresponding to one of a number of common scenarios; for example, a basic eCos configuration template is supplied that contains the infrastructure, kernel, C and math libraries, plus their support packages.
The Component Repository and Working Directories Each of the file trees involved in eCos development has a different role. Component Repository The eCos component repository contains directories for all the packages that are shipped with eCos or provided by third parties. The component repository should not be modified as part of application development.
Component repository
Purpose The component repository is the master copy of source code for all system and third party components. It also contains some files needed to administer and build the system, such as ecosadmin.tcl. How is it modified? You modify it by importing new versions of packages from a distribution or removing existing packages. These activities are undertaken using the eCos Package Administration Tool. When is it edited manually? Files in the component repository should only be edited manually as determined by the component maintainer. User Applications User application source code should not go into the component repository. Examples of files in this hierarchy: BASE_DIR/doc/ref/ecos-ref.html The top level HTML file for the eCos Reference Manual. BASE_DIR/prebuilt/pid/tests/kernel/&Version;/tests/thread_gdb.exe BASE_DIR/prebuilt/linux/tests/kernel/&Version;/tests/thread_gdb.exe Pre-built tests for the supported platforms, and the synthetic Linux target. BASE_DIR/examples/twothreads.c One of the example programs. BASE_DIR/ecosadmin.tcl The Tcl program which is used to import new versions of packages from a distribution or remove existing packages. BASE_DIR/packages/language/c/libm/&Version;/src/double/portable-api/s_tanh.c Implementation of the hyperbolic tangent function in the standard math library. BASE_DIR/pkgconf/rules.mak A file with make rules, used by the makefile.
Build Tree The build tree is the directory hierarchy in which all generated files are placed. Generated files consist of the makefile, the compiled object files, and a dependency file (with a .d extension) for each source file. Purpose The build tree is where all intermediate object files are placed. How is it modified? Recompiling can modify the object files. User applications User application source or binary code should not go in the build tree. Examples of files in this hierarchy ecos-work/language/c/libc/&Version;/src The directory in which object files for the C library are built. Install Tree The install tree is the location for all files needed for application development. The libtarget.a library, which contains the custom-built eCos kernel and other components, is placed in the install tree, along with all packages’ public header files. If you build the tests, the test executable programs will also be placed in the install tree. By default, the install tree is created by ecosconfig in a subdirectory of the build tree called install. This can be modified with the option (see ). Purpose The install tree is where the custom-built libtarget.a library, which contains the eCos kernel and other components, is located. The install tree is also the location for all the header files that are part of a published interface for their component. How is it modified? Recompiling can replace libtarget.a and the test executables. When is it edited manually? Where a memory layout requires modification without use of the eCos Configuration Tool, the memory layout files must be edited directly in the install tree. These files are located at install/include/pkgconf/mlt_*.*. Note that subsequent modification of the install tree using the Configuration Tool will result in such manual edits being lost. User applications User application source or binary code should not go in the install tree. Examples of files in this hierarchy install/lib/libtarget.a The library containing the kernel and other components. install/include/cyg/kernel/kapi.h The header file for the kernel C language API. install/include/pkgconf/mlt_arm_pid_ram.ldi The linker script fragment describing the memory layout for linking applications intended for execution on an ARM PID development board using RAM startup. install/include/stdio.h The C library header file for standard I/O. Application Build Tree This tree is not part of eCos itself: it is the directory in which eCos end users write their own applications. Example applications and their Makefile are located in the component repository, in the directory BASE_DIR/examples. There is no imposed format on this directory, but there are certain compiler and linker flags that must be used to compile an eCos application. The basic set of flags is shown in the example Makefile, and additional details can be found in .
Compiler and Linker Options eCos is built using the GNU C and C++ compilers. eCos relies on certain features of these tools such as constructor priority ordering and selective linking which are not part of other toolchains. Some GCC options are required for eCos, and others can be useful. This chapter gives a brief description of the required options as well as some recommended eCos-specific options. All other GCC options (described in the GCC manuals) are available. Compiling a C Application The following command lines demonstrate the minimum set of options required to compile and link an eCos program written in C. Remember that when this manual shows TARGET-gcc you should use the full name of the cross compiler, e.g. i386-elf-gcc, arm-elf-gcc, or sh-elf-gcc. When compiling for the synthetic Linux target, use the native gcc which must have the features required by eCos. $ TARGET-gcc -c -IINSTALL_DIR/include file.c $ TARGET-gcc -o program file.o -LINSTALL_DIR/lib -Ttarget.ld -nostdlib Certain targets may require extra options, for example the SPARClite architectures require the option . Examine the BASE_DIR/examples/Makefile or the “Global compiler flags” option (CYGBLD_GLOBAL_CFLAGS) in your generated eCos configuration) to see if any extra options are required, and if so, what they are. The following command lines use some other options which are recommended because they use the selective linking feature: $ TARGET-gcc -c -IINSTALL_DIR/include -I. -ffunction-sections -fdata-sections -g -O2 file.c $ TARGET-gcc -o program file.o -ffunction-sections -fdata-sections -Wl,--gc-sections -g -O2 \ -LINSTALL_DIR/lib -Ttarget.ld -nostdlib Compiling a C++ Application The following command lines demonstrate the minimum set of options required to compile and link an eCos program written in C++. Remember that when this manual shows TARGET-g++ you should use the full name of the cross compiler, e.g. i386-elf-g++, arm-elf-g++, or sh-elf-g++. When compiling for the synthetic Linux target, use the native g++ which must have the features required by eCos. $ TARGET-g++ -c -IINSTALL_DIR/include -fno-rtti -fno-exceptions file.cxx $ TARGET-g++ -o program file.o -LINSTALL_DIR/lib -Ttarget.ld -nostdlib Certain targets may require extra options, for example the SPARClite architectures require the option . Examine the BASE_DIR/packages/targets file or BASE_DIR/examples/Makefile or the “Global compiler flags” option (CYGBLD_GLOBAL_CFLAGS) in your generated eCos configuration) to see if any extra options are required, and if so, what they are. The following command lines use some other options which are recommended because they use the selective linking feature: $ TARGET-g++ -c -IINSTALL_DIR/include -I. -ffunction-sections -fdata-sections -fno-rtti \ -fno-exceptions -finit-priority -g -O2 file.cxx $ TARGET-g++ -o program file.o -W1,--gc-sections -g -O2 -LINSTALL_DIR/lib -Ttarget.ld -nostdlib Debugging Techniques eCos applications and components can be debugged in traditional ways, with printing statements and debugger single-stepping, but there are situations in which these techniques cannot be used. One example of this is when a program is getting data at a high rate from a real-time source, and cannot be slowed down or interrupted. eCos’s infrastructure module provides a tracing formalism, allowing the kernel’s tracing macros to be configured in many useful ways. eCos’s kernel provides instrumentation buffers which also collect specific (configurable) data about the system’s history and performance. Tracing To use eCos’s tracing facilities you must first configure your system to use tracing. You should enable the Asserts and Tracing component () and the component within it (). These options can be enabled with the Configuration Tool or by editing the file BUILD_DIR/pkgconf/infra.h manually. You should then examine all the tracing-related options in the Package: Infrastructure chapter of the eCos Reference Manual. One useful set of configuration options are: CYGDBG_INFRA_DEBUG_FUNCTION_REPORTS and CYGDBG_INFRA_DEBUG_TRACE_MESSAGE, which are both enabled by default when tracing is enabled. The following “Hello world with tracing” shows the output from running the hello world program (from ) that was built with tracing enabled: Hello world with tracing $ mips-tx39-elf-run --board=jmr3904 hello Hello, eCos world! ASSERT FAIL: <2>cyg_trac.h [ 623] Cyg_TraceFunction_Report_::set_exitvoid() exitvoid used in typed function TRACE: <1>mlqueue.cxx [ 395] Cyg_ThreadQueue_Implementation::enqueue() {{enter TRACE: <1>mlqueue.cxx [ 395] Cyg_ThreadQueue_Implementation::enqueue() }}RETURNING UNSET! TRACE: <1>mlqueue.cxx [ 126] Cyg_Scheduler_Implementation::add_thread() }}RETURNING UNSET! TRACE: <1>thread.cxx [ 654] Cyg_Thread::resume() }}return void TRACE: <1>cstartup.cxx [ 160] cyg_iso_c_start() }}return void TRACE: <1>startup.cxx [ 142] cyg_package_start() }}return void TRACE: <1>startup.cxx [ 150] cyg_user_start() {{enter TRACE: <1>startup.cxx [ 150] cyg_user_start() (((void))) TRACE: <1>startup.cxx [ 153] cyg_user_start() 'This is the system default cyg_user_start()' TRACE: <1>startup.cxx [ 157] cyg_user_start() }}return void TRACE: <1>sched.cxx [ 212] Cyg_Scheduler::start() {{enter TRACE: <1>mlqueue.cxx [ 102] Cyg_Scheduler_Implementation::schedule() {{enter TRACE: <1>mlqueue.cxx [ 437] Cyg_ThreadQueue_Implementation::highpri() {{enter TRACE: <1>mlqueue.cxx [ 437] Cyg_ThreadQueue_Implementation::highpri() }}RETURNING UNSET! TRACE: <1>mlqueue.cxx [ 102] Cyg_Scheduler_Implementation::schedule() }}RETURNING UNSET! TRACE: <2>intr.cxx [ 450] Cyg_Interrupt::enable_interrupts() {{enter TRACE: <2>intr.cxx [ 450] Cyg_Interrupt::enable_interrupts() }}RETURNING UNSET! TRACE: <2>thread.cxx [ 69] Cyg_HardwareThread::thread_entry() {{enter TRACE: <2>cstartup.cxx [ 127] invoke_main() {{enter TRACE: <2>cstartup.cxx [ 127] invoke_main() ((argument is ignored)) TRACE: <2>dummyxxmain.cxx [ 60] __main() {{enter TRACE: <2>dummyxxmain.cxx [ 60] __main() (((void))) TRACE: <2>dummyxxmain.cxx [ 63] __main() 'This is the system default __main()' TRACE: <2>dummyxxmain.cxx [ 67] __main() }}return void TRACE: <2>memcpy.c [ 112] _memcpy() {{enter TRACE: <2>memcpy.c [ 112] _memcpy() ((dst=80002804, src=BFC14E58, n=19)) TRACE: <2>memcpy.c [ 164] _memcpy() }}returning 80002804 TRACE: <2>cstartup.cxx [ 137] invoke_main() 'main() has returned with code 0. Calling exit()' TRACE: <2>exit.cxx [ 71] __libc_exit() {{enter TRACE: <2>exit.cxx [ 71] __libc_exit() ((status=0 )) TRACE: <2>atexit.cxx [ 84] cyg_libc_invoke_atexit_handlers() {{enter TRACE: <2>atexit.cxx [ 84] cyg_libc_invoke_atexit_handlers() (((void))) Scheduler: Lock: 0 Current Thread: <null> Threads: Idle Thread pri = 31 state = R id = 1 stack base = 800021F0 ptr = 80002510 size = 00000400 sleep reason NONE wake reason NONE queue = 80000C54 wait info = 00000000 <null> pri = 0 state = R id = 2 stack base = 80002A48 ptr = 8000A968 size = 00008000 sleep reason NONE wake reason NONE queue = 80000BD8 wait info = 00000000 Kernel Instrumentation Instrument buffers can be used to find out how many events of a given type happened in the kernel during execution of a program. You can monitor a class of several types of events, or you can just look at individual events. Examples of events that can be monitored are: scheduler events thread operations interrupts mutex operations binary semaphore operations counting semaphore operations condition variable operations event flag operations message box operations clock ticks, interrupts and alarms Examples of fine-grained scheduler event types are: scheduler lock scheduler unlock rescheduling time slicing Information about the events is stored in an event record. The structure that defines this record has type struct Instrument_Record: The list of records is stored in an array called instrument_buffer which you can let the kernel provide or you can provide yourself by setting the configuration option CYGVAR_KERNEL_INSTRUMENT_EXTERNAL_BUFFER. To write a program that examines the instrumentation buffers: Enable instrumentation buffers in the eCos kernel configuration. The component macro is CYGPKG_KERNEL_INSTRUMENT. To allocate the buffers yourself, enable the configuration option CYGVAR_KERNEL_INSTRUMENT_EXTERNAL_BUFFER. Include the header file cyg/kernel/instrmnt.h . #include <cyg/kernel/instrmnt.h> The Instrumentation_Record structure is not published in the kernel header file. In the future there will be a cleaner mechanism to access it, but for now you should paste into your code in the following lines: struct Instrument_Record { CYG_WORD16 type; // record type CYG_WORD16 thread; // current thread id CYG_WORD timestamp; // 32 bit timestamp CYG_WORD arg1; // first arg CYG_WORD arg2; // second arg }; Enable the events you want to record using cyg_instrument_enable() , and disable them later. Look at cyg/kernel/instrmnt.h and the examples below to see what events can be enabled. Place the code you want to debug between the matching functions cyg_instrument_enable() and cyg_instrument_disable() . Examine the buffer. For now you need to look at the data in there (the example program below shows how to do that), and future versions of eCos will include a host-side tool to help you understand the data. Using instrument buffers This program is also provided in the examples directory. /* this is a program which uses eCos instrumentation buffers; it needs to be linked with a kernel which was compiled with support for instrumentation */ #include <stdio.h> #include <pkgconf/kernel.h> #include <cyg/kernel/instrmnt.h> #include <cyg/kernel/kapi.h> #ifndef CYGVAR_KERNEL_INSTRUMENT_EXTERNAL_BUFFER # error You must configure eCos with CYGVAR_KERNEL_INSTRUMENT_EXTERNAL_BUFFER #endif struct Instrument_Record { CYG_WORD16 type; // record type CYG_WORD16 thread; // current thread id CYG_WORD timestamp; // 32 bit timestamp CYG_WORD arg1; // first arg CYG_WORD arg2; // second arg }; struct Instrument_Record instrument_buffer[20]; cyg_uint32 instrument_buffer_size = 20; int main(void) { int i; cyg_instrument_enable(CYG_INSTRUMENT_CLASS_CLOCK, 0); cyg_instrument_enable(CYG_INSTRUMENT_CLASS_THREAD, 0); cyg_instrument_enable(CYG_INSTRUMENT_CLASS_ALARM, 0); printf("Program to play with instrumentation buffer\n"); cyg_thread_delay(2); cyg_instrument_disable(CYG_INSTRUMENT_CLASS_CLOCK, 0); cyg_instrument_disable(CYG_INSTRUMENT_CLASS_THREAD, 0); cyg_instrument_disable(CYG_INSTRUMENT_CLASS_ALARM, 0); for (i = 0; i < instrument_buffer_size; ++i) { printf("Record %02d: type 0x%04x, thread %d, ", i, instrument_buffer[i].type, instrument_buffer[i].thread); printf("time %5d, arg1 0x%08x, arg2 0x%08x\n", instrument_buffer[i].timestamp, instrument_buffer[i].arg1, instrument_buffer[i].arg2); } return 0; } Here is how you could compile and run this program in the examples directory, using (for example) the MN10300 simulator target: $ make XCC=mn10300-elf-gcc INSTALL_DIR=/tmp/ecos-work-mn10300/install instrument-test mn10300-elf-gcc -c -o instrument-test.o -g -Wall -I/tmp/ecos-work-mn10300/install/include \ -ffunction-sections -fdata-sections instrument-test.c mn10300-elf-gcc -nostartfiles -L/tmp/ecos-work-mn10300/install/lib -W1,--gc-sections -o \ instrument-test instrument-test.o -Ttarget.ld -nostdlib $ mn10300-elf-run --board=stdeval1 instrument-test Instrument buffer output Here is the output of the instrument-test program. Notice that in little over 2 seconds, and with very little activity, and with few event types enabled, it gathered 17 records. In larger programs it will be necessary to select very few event types for debugging. Program to play with instrumentation buffer Record 00: type 0x0207, thread 2, time 6057, arg1 0x48001cd8, arg2 0x00000002 Record 01: type 0x0202, thread 2, time 6153, arg1 0x48001cd8, arg2 0x00000000 Record 02: type 0x0904, thread 2, time 6358, arg1 0x48001d24, arg2 0x00000000 Record 03: type 0x0905, thread 2, time 6424, arg1 0x00000002, arg2 0x00000000 Record 04: type 0x0906, thread 2, time 6490, arg1 0x00000000, arg2 0x00000000 Record 05: type 0x0901, thread 2, time 6608, arg1 0x48009d74, arg2 0x48001d24 Record 06: type 0x0201, thread 2, time 6804, arg1 0x48001cd8, arg2 0x480013e0 Record 07: type 0x0803, thread 1, time 94, arg1 0x00000000, arg2 0x00000000 Record 08: type 0x0801, thread 1, time 361, arg1 0x00000000, arg2 0x00000000 Record 09: type 0x0802, thread 1, time 548, arg1 0x00000001, arg2 0x00000000 Record 10: type 0x0803, thread 1, time 94, arg1 0x00000000, arg2 0x00000000 Record 11: type 0x0801, thread 1, time 361, arg1 0x00000001, arg2 0x00000000 Record 12: type 0x0903, thread 1, time 513, arg1 0x48009d74, arg2 0x48001d24 Record 13: type 0x0208, thread 1, time 588, arg1 0x00000000, arg2 0x00000000 Record 14: type 0x0203, thread 1, time 697, arg1 0x48001cd8, arg2 0x480013e0 Record 15: type 0x0802, thread 1, time 946, arg1 0x00000002, arg2 0x00000000 Record 16: type 0x0201, thread 1, time 1083, arg1 0x480013e0, arg2 0x48001cd8 Record 17: type 0x0000, thread 0, time 0, arg1 0x00000000, arg2 0x00000000 Record 18: type 0x0000, thread 0, time 0, arg1 0x00000000, arg2 0x00000000 Record 19: type 0x0000, thread 0, time 0, arg1 0x00000000, arg2 0x00000000