 | Level: Introductory Peter Seebach (developerworks@seebs.plethora.net), Freelance author, Plethora.net
24 Feb 2006 The eCos embedded operating system offers an alternative to UNIX®-style operating systems for development work. This article examines how its architecture influences the development process, building a sample application and exploring the differences in architecture between eCos and UNIX.
The eCos embedded operating system is closer in design to embedded
systems
such as VxWorks than to embedded Linux®. In the UNIX environment, the operating system (OS)
provides services to separate programs which run under it. By contrast,
an eCos program contains both the entire OS and the target application.
While the application can be multithreaded, only one application can run
on the system at a given time.
With this overview, we can start talking about what it takes to
build a real application on eCos. You can't just load up a Web server,
perl, and some CGI scripts; you have to build everything as a single
executable.
The key distinction is address space. In UNIX systems, the kernel has its
own address space, which applications don't have access to, and each
application can (and most do) have separate address spaces. eCos only has one address space. Similarly, each program on the UNIX system is
a separate file, while with eCos, there is only one monolithic file.
In both cases, the OS code configures hardware and sets up device drivers,
then provides services to applications. In eCos, the library code can
call driver code directly, rather than needing to pass instructions to a
separate kernel. Under UNIX-like systems, the kernel runs a process (usually
init) which then launches other processes; under
eCos, the kernel runs a user-provided main function
which may, if it wants, run multiple threads.
This linking architecture increases the apparent size of an application; while
a native Linux "hello, world!" application might only be 4KB, a "hello,
world!" eCos application for the TAMS 3011 came out to about 326KB. On the
other hand, on eCos, that included the whole kernel; if you add in the
initrd filesystem image, the kernel, and the C library, the code used to perform the task on Linux is closer to 4.5MB.
The eCos system is open source, currently distributed under a slightly
modified version of the GPL. The modification creates an exemption for
programs linked with eCos that creates the same effect you get from running
a program under Linux; you are not required to release your program's source,
but you still have to release modifications to the eCos source. It is
available for free, and is royalty-free regardless of the number of developers
or the number of shipping products you make based on it.
How eCos works
For the UNIX users out there, a little bit more detail of how an embedded
OS like eCos works might be in order. In the UNIX environment, the OS
runs directly on the hardware. The OS loads programs, which then
communicate with it by means of system calls. By contrast, library
services are linked into the program; library services, although
distributed with the OS, run in the user program's address space, and use
the same system calls the user's code would to access OS services. (In
fact, you can omit the libraries, and just work directly with system
calls, but it would generally be silly to do that.)
The OS allocates memory space and loads programs from any arbitrary
storage medium into this memory. These programs access OS services by
means of "syscalls" (semantically the equivalent of BIOS and DOS INT calls
from the bad old days). In practice, it is neither necessary nor desirable
to expose the syscall mechanism directly to application code, so
applications talk to the OS through a library that translates requests
from the application layer into syscalls. The usual such library employed
in general-purpose Linux systems is glibc, which is a very large beast
indeed; see Resources for a reference on stripping it down somewhat.
Other library services, not inherently part of the OS, are provided either
through "shared objects" (dynamically linked libraries - a single copy of the
library is shared by all applications that require its functionality), or
by the developer of a program choosing to link statically with a given
library at build time.
One way to think of eCos is to see it as the kernel just being linked in
as a particularly large and complete library. In fact, this is exactly how
the build process works; a library is built, and your code is linked against
it. Rather than system calls being made to a separate program, they're just
regular function calls within a single large executable.
The exact set of features provided is up to the user. You can include
only the features you actually need, or you can include the entire
operating system. The ability to omit features you aren't using can help
save space. On the sample "hello, world" program, a reasonably complete
build is about 2MB, even though the stripped executable is only 326KB.
For applications designed to run on smaller embedded boards, this is very
useful.
While Linux systems allow a certain amount of granularity in configuration,
it's generally not nearly as fine-grained as the eCos configuration tool is.
You can remove nearly every feature of eCos, and very few
dependencies exist only because someone didn't want to maintain the
code for running without a given feature. Some tweaks might give preference
to RAM footprint, or to speed of execution. Many Linux features simply
cannot be removed with the regular tools.
Similarly, eCos has excellent support for boot configuration built into the
kernel; things that, on a Linux system, would be handled by the boot loader,
are built into eCos as part of the kernel, so it can be a single
self-contained image, to be run directly from flash or RAM.
An embedded system built on top of Linux will need enough memory to handle
the kernel, some userland support programs (such as init), and the application; it will need a root
filesystem with at least a few files in it, including configuration files
and shells. An embedded system built on eCos needs only one hunk of data (the
eCos application, including the kernel). This isn't to say you can't have
more; some eCos systems have filesystem code and multiple files. It
is, however, optional. You don't even need the hunk of data to be a file;
it can just be a chunk of flash memory that the machine jumps to and begins
executing.
Implementation notes
To get a feel for the development environment, start
out with a simple "hello, world" program. The program's source is
completely unexceptional:
Listing 1: "hello, world!"
#include <stdio.h>
int
main(void) {
printf("hello, world!\n");
return 0;
}
|
Building it requires a bit of preparation. The eCos build environment
assumes that you will develop using a "build tree" to build the customized
kernel library your application will be linked with. To start up, you
need two things: the compiler toolchain and some eCos-specific
tools in your PATH, and you need an eCos repository, set in the eponymous
environment variable ECOS_REPOSITORY.
The eCos kernel is built in C++, and thus links directly with C or C++
code. The kernel is built by creating a configuration file, using the
ecosconfig program. (You can also use a graphical interface, but I found the command line more
comfortable; the interfaces sometimes have slightly different feature
sets.)
To create a
new build routine, you run the command ecosconfig new
target. For instance, for the TAMS 3011, the command is
ecosconfig new moab405GPr. This brings in the
drivers for that particular platform; kernel modules and features can be
added with ecosconfig add feature. In
many cases, the default set works fine.
Once you've picked a feature set, you create the build tree by running
ecosconfig tree. This creates a compilation
tree which uses the source in ECOS_REPOSITORY
to build a local kernel; start the build by running make. Many build processes advise you to go make
a cup of coffee during the build; unless you're on a pretty slow computer,
you won't have time with eCos.
Building the application by hand is possible, but not worth the trouble;
you can just use the provided build_Makefile
script. In a new directory (for convenience, I recommend a sibling of the
kernel tree), copy the build_Makefile and build_Make.params files from the eCos repository's
examples directory; with the software package for the TAMS 3011, that's
/opt/mlb/ecos/examples.
The build_Makefile script builds a Makefile suitable for compiling
applications. The easiest way to use it is to pass it the variables SRCS and DST as
environment variables, and the path to the custom kernel. For instance:
Listing 2. Running build_Makefile
$ SRCS=main.c DST=main ./build_Makefile ../ecos_kernel
$ make
|
In the software distribution I got, the build_Makefile script needed to be marked executable
(chmod +x), and the resulting Make.params file
specified the -finit-priority flag, which isn't
supported by this release of gcc. No big deal;
remove the flag, run make, and you have an executable.
Running the executable
To run the executable, you have to load it on the target system, then
execute it. On the TAMS 3011, this is done using RedBoot. In fact,
RedBoot is another eCos application! If you don't need to flash your
application into memory, it's painfully easy to load it. Just copy it into
the tftp directory on your tftp server, and load it:
Listing 3. Loading and running an application
RedBoot> lo main
Using default protocol (TFTP)
Entry point: 0x00080000, address range: 0x00080000-0x000beb9c
RedBoot> go
(boot messages)
hello, world!
|
Note that the entry point doesn't need to be specified.
The executable's header contains the information needed, and the boot
loader can load it at the right place. I omitted the boot messages in the
example, but my system has about 40 lines of them; these are
messages from the eCos kernel code as it bootstraps the system, probes
devices, and so on.
Note that you don't get your prompt back. This is not a program being run
under another operating system; this is a new operating system being
loaded and executed. Since the new operating system doesn't do very much,
it's not very interesting to watch, but it establishes that the system is
loading the code you wrote and executing it.
It is possible to do without the boot loader, flashing your application into
a device to be loaded automatically by lower-level firmware, or to use a boot
loader other than RedBoot. However, since RedBoot is an eCos application, if
your target hardware supports eCos, it will also generally support RedBoot,
which makes RedBoot an appealing choice. For more information about RedBoot,
see Resources.
Debugging
It is, of course, dimly conceivable that something will one day go wrong.
Now, under UNIX, you attach a debugger to the process you want to debug.
On a single-process system, you can't do this as easily. You can use
hardware monitors, but in fact, if you build a default kernel
configuration, you can do well enough just using gdb over the serial port.
Debugging remotely can be a little weird. It is absolutely crucial that
the binary image you give to the debugger is the same image running on the
target; if you use even slightly different binaries, you can get
meaningless results. Because the libraries and kernel are all
linked together, a mismatch can result in very confused behavior
from the debugger. The documentation for the board mentions how to
configure gdb to attach over a serial line. Look for a string like
"$T0b40:000876a8;01:0012adc0;#7e"; this indicates that the system has
stopped execution and is waiting for a debugger to guide it. When you see
that, disconnect your terminal program and sic gdb on it.
Adding and removing features
The eCos configuration system is very flexible and encourages tuning to
remove unneeded drivers. You can add or remove single packages, and
dependencies are checked. The eCos configuration file (typically called
"ecos.ecc") contains a list of configuration flags and options, used by
the ecosconfig tree command to generate a build
tree targeting a specific platform and feature set.
To get a list of supported packages, use ecosconfig
list. This lists packages, along with their aliases (short names
you can use for them). For instance, the CYGPKG_IO_SERIAL package can also be referred to as
just "serial":
Package CYGPKG_IO_SERIAL (Serial device drivers):
aliases: serial io_serial
versions: current
|
One reason to remove drivers would be to reduce size; another is to
improve startup time. For instance, if you don't need both ethernet ports
on the 3011, disabling a driver reduces the time spent booting. In fact,
both can be removed for console-only activities. Removing drivers and
software is reasonably straightforward, and the dependency checking will
help you out, if you remove something without removing other things which
depended on it. (Use ecosconfig check to verify
your work.)
Debugger support is controlled separately, with the --enable-debug and --disable-debug options to ecosconfig.
Doing something
One of the tests I've used with other embedded systems is seeing how easy
it
is to play Zork on a system. Zork, for those who have missed it, is a
wonderful little text adventure, and getting it running is a good test for
a platform. I've grown accustomed to the Zork test, and perhaps even a little
complacent about it. On a generic embedded Linux, getting Zork running takes
maybe half an hour of work. On eCos, actually getting Zork running turns out to
take a bit of work, making it a good reminder of the point of the test: it
helps you see what's built into a system and how complete the developer
support is. Porting something to work on
eCos takes a few steps, and you can go about it in a number of ways.
To get Zork running, the obvious program to port is a Z-machine
interpreter. These generally use the curses library, which abstracts access
to cursor-addressable terminals (thus the name). Curses is very useful, but
it doesn't map well onto the typical uses of eCos, so it's not provided with
the system. Getting curses ported
turns out to be more interesting than you might think, because it depends
heavily on termcap (or terminfo), which in turn depends on databases on
disk.
The BSD curses library is dramatically smaller and somewhat simpler
than ncurses, so it makes a better starting point. In the next
article in this series, I'll show how to port Berkeley curses to
eCos and test a simple curses application, and I'll show more of the ways
in which curses tends to assume a full UNIX environment.
Resources Learn
Get products and technologies
Discuss
About the author  | |  | Peter Seebach has been using Macs since the Macintosh SE, and thinks this is the cutest one ever. He denies that his Mac mini was ever lost in the couch. You can reach him at developerworks@seebs.plethora.net. |
Rate this page
|  |