April
1998, Issue 93
Software
Development for RTOSs
DEBUGGING
TECHNIQUES
Development
systems for real-time applications usually have debuggers
available for application code. These debuggers vary
from being simple monitors, which are used to examine
or modify memory and control execution of the application,
or sophisticated source-level debuggers.
Source-level
debuggers let us correlate the instructions and data
in the application with the source code from which it
was built. We can set breakpoints by simply locating
a specific line in the source code module, and examine
variables.
Debuggers,
just like the development system in general, can be
target or host based. Target-based debuggers run on
the target in conjunction with the RTOS and the application
to be debugged. The communicates with the system or,
if the RTOS supports it, via a network connection.
Target-based
debuggers usually have the best performance, since they
run on the same machine, which reduces the communication
latency for reading and writing memory. Tracing execution
flow of an application, where the program is singled
stepped showing what instructions are being executed
is also fastest using the target-based debugger, since
it has access to the software interrupts necessary to
implement this.
Sometimes
when running target-based debuggers, we have to deal
with a reduced level of functionality for the user interface.
For example, when I run a target-based debugger on QNX
over telnet, I may only have access to a basic character-based
interface.
Host-based
(or remote) debuggers have two components. The actual
debugger, which runs on the host, is responsible for
managing the user interface and dealing with reading
and write the symbol table. It communicates with the
target, which has to be running a monitor, using a communication
channel. Usually, this is a serial interface or a parallel
interface with the target. Photo
3 shows a screen shot of codeview, a remote debugger
used with Phar Laps ETS.
If
you remember, in the ETS example, I used diskkern.bin,
which was
booted from the floppy disk. With diskkern.bin, the
command-line program runemb was used to download and
run the application to the target.
When
we want to use the debugger, we simply use
cvemb, which
also allows us to download the image. Once it has been
downloaded, we can control our application and examine
memory/registers using the debugger running on the development
host.
In
this environment, the debugger is also capable of attaching
to an already running application. This makes it possible
for us to debug real-time applications running from
a boot ROM. Ill explain more about how we can
run the application from boot ROM a little later.
One
thing to keep in mind, is that when we are debugging
a real-time application, the application may will not
perform in real-time once the program is stopped at
breakpoint or when we are tracing or single stepping
through the application. In our servo example, the system
simply stops and the scope display flatlines.
To
debug real-time applications running in real time, we
need hardware-based debugging facilities. One of these
is the in-circuit emulator (ICE). Here, we replace the
CPU in the embedded system with a system that emulates
the CPU running in real time.
ICE
systems are usually attached to a development host through
a serial or parallel interface, but some are also network
based. The user interface on the development host, is
usually the same as software based debuggers and has
about the same functionality of downloading code, examining
memory and registers. However, ICE-based debuggers allow
real-time tracing of the target.