|
3
Gordon Drive, P.O.Box 1347 Rockland, Maine 04841 U.S.A.
|
|
© 2004 Avocet Systems, Inc.
|
Call
Us Today at 207-596-7766 ("Picton Press")
|
|
Avocet Systems, Inc. : The Complete Solution for Embedded Systems
Development Tools
|
|
|
Hints
Single Stepping in ISRs
Abstract
Single stepping in Interrupt Service Routines (ISRs) can cause all sorts of
trouble. Here's our perspective on the matter.
Are you willing to give up most of your debugging resources just when you need
them most - in debugging the real time code inside of an interrupt service routine
(ISR)? Many emulators cannot breakpoint or single step inside an ISR, forcing
you to debug by guess and by golly in these, the most difficult to troubleshoot
sections of your code.
Softaid's emulators support all forms of debugging in an ISR, including single
stepping and breakpointing. We feel that heavy interrupt usage is a characteristic
of most embedded systems, and that our users often purchase their emulators
to deal with just these sorts of problems.
Though this discussion uses 80188 example code, it holds true for all of the
processors Softaid supports.
Debugging interrupts can be a challenge if you are not aware of your system's
behavior when stopped at a breakpoint. Consider the following timer ISR:
Most ISRs reenable interrupts just before the return, as shown in this example.
Every processor defers the actual interrupt reenable until the instruction after
the STI (Enable Interrupt) or IRET executes, so interrupts don't come back on
until the ISR has completed and has returned to the main line code.
Code like this is easy to debug. Any Softaid emulator will merrily single step
and breakpoint all day long without problem.
Suppose we modify the code a little as follows:
Now interrupts come back on long before the ISR completes. Single step through
this code and you'll face an interesting problem: if the timer that invokes
the code has a periodic rate of, say, 1 millisecond, when you are stopped at
a breakpoint in the ISR after the STI instruction the service routine will be
restarted by the timer issuing another interrupt. That is, two or more versions
of the ISR may be active at a time.
Emulators single step by placing a temporary breakpoint at the next instruction
that will execute, and then letting the code run at full speed. A pending interrupt
will preempt the execution path and restart the ISR (if interrupts are on, of
course). Thus, in this case, if the program counter was pointing to label "a"
and you press STEP, the emulator will follow the pending interrupt, restart
the ISR, and halt at the instruction after "a", stopping in a new
incarnation of Timer_ISR. The previous one will have been suspended - the stack
will have additional pushes from the routine's new invocation.
One company we work with wrote code to count timer ticks in a register as shown
in the example at label "a". He simply incremented register BX each
time the ISR ran. Mainline code watched BX to track time. While single stepping
though the code he noticed that BX seemed to magically change after each step.
The explanation is simple - while the emulator was stopped waiting for him to
initiate the next single step another timer interrupt became valid. Pressing
STEP allowed the program to run, but it followed the interrupting path, eventually
breakpointing on the next instruction but only after finishing another interrupt.
Though the emulator was running correctly (it indeed followed the flow of the
target code - the fact that the real time nature of the system had been modified
by stopping at a breakpoint caused additional incarnations of the ISR to run),
this can make debugging harder. Several solutions exist.
The best is to never reenable interrupts until the ISR returns. Admittedly this
is not always feasible but it is the best design practice as it eliminates the
possibility of non-reentrant code slipping in after the STI and causing hard-to-find
bugs.
Another solution is to write your ISRs with handshaking, so only one version
of each can be active at a time. We can recode the example above as follows:
The added complexity costs little in execution time or program space, yet greatly
eases debugging. It's also a sensible precaution - it's awfully foolish to run
multiple copies of a routine when even one is possibly not bug-free (that's
why you were single stepping in the first place - right?).
Note that this entire discussion is relevant only if your interrupts can re-occur
while you are stepping through a routine, and only if interrupts come back on
before the ISR terminates. Write your code as shown in the first example, use
a Softaid emulator, and you can ignore the rest of this paper.
Copyright 1994, Softaid, Inc.
|
|
|