Keywords: sdccman

Subsections

- 3.8.1.1 Common interrupt pitfall: variable not declared volatile
- 3.8.1.2 Common interrupt pitfall: non-atomic access
- 3.8.1.3 Common interrupt pitfall: stack overflow
- 3.8.1.4 Common interrupt pitfall: use of non-reentrant functions

## 3.8.1 General Information

SDCC allows interrupt service routines to be coded in C, with some extended keywords.

> void timer_isr (void) __interrupt (1) __using (1)
> {
> ...
> }

The optional number following the __interrupt keyword is the interrupt number this routine will service. When present, the compiler will insert a call to this routine in the interrupt vector table for the interrupt number specified. If you have multiple source files in your project, interrupt service routines can be present in any of them, but a prototype of the isr MUST be present or included in the file that contains the function main. The optional (8051 specific) keyword __using can be used to tell the compiler to use the specified register bank when generating code for this function.
Interrupt service routines open the door for some very interesting bugs:

###
3.8.1.1 Common interrupt pitfall: variable not declared volatile

If an interrupt service routine changes variables which are accessed by other functions these variables have to be declared volatile. See `http://en.wikipedia.org/wiki/Volatile_variable`.

###
3.8.1.2 Common interrupt pitfall: non-atomic access

If the access to these variables is not atomic (i.e. the processor needs more than one instruction for the access and could be interrupted while accessing the variable) the interrupt must be disabled during the access to avoid inconsistent data.
Access to 16 or 32 bit variables is obviously not atomic on 8 bit CPUs and should be protected by disabling interrupts. You're not automatically on the safe side if you use 8 bit variables though. We need an example here: f.e. on the 8051 the harmless looking ”flags |= 0x80;” is not atomic if flags resides in xdata. Setting ”flags |= 0x40;” from within an interrupt routine might get lost if the interrupt occurs at the wrong time. ”counter += 8;” is not atomic on the 8051 even if counter is located in data memory.
Bugs like these are hard to reproduce and can cause a lot of trouble.

###
3.8.1.3 Common interrupt pitfall: stack overflow

The return address and the registers used in the interrupt service routine are saved on the stack so there must be sufficient stack space. If there isn't variables or registers (or even the return address itself) will be corrupted. This stack overflow is most likely to happen if the interrupt occurs during the ”deepest” subroutine when the stack is already in use for f.e. many return addresses.

###
3.8.1.4 Common interrupt pitfall: use of non-reentrant functions

A special note here, integer multiplicative operators and floating-point operations might be implemented using external support routines, depending on the target architecture. If an interrupt service routine needs to do any of these operations on a target where functions are non-reentrant by default, then the support routines (as mentioned in a following section) will have to be recompiled using the —stack-auto option and the source file will need to be compiled using the —int-long-reent compiler option.
Note, the type promotion required by ANSI C can cause 16 bit routines to be used without the programmer being aware of it. See f.e. the cast (unsigned char)(tail-1) within the if clause in section 3.11.2.

Calling other functions from an interrupt service routine on a target where functions are non-reentrant by default is not recommended, avoid it if possible. Note that when some function is called from an interrupt service routine it should be preceded by a #pragma nooverlay if it is not reentrant. Furthermore non-reentrant functions should not be called from the main program while the interrupt service routine might be active. They also must not be called from low priority interrupt service routines while a high priority interrupt service routine might be active. You could use semaphores or make the function critical if all parameters are passed in registers.
Also see section 3.7 about Overlaying and section 3.10 about Functions using private register banks.