Page 2 of 2
Faster or safer
At this point the real world enters the picture and the small question of how long it takes to stack all of those registers starts to matter.
There are systems where the time it takes to get to an interrupt routine – the latency – is important. For example, if the system is controlling a chemical reaction it might be vital that it responds to a sensor setting in as fast as possible. In such cases even interrupting part way through some instructions may be worthwhile and the time to stack all those registers is definitely not a good idea!
Some processors solve the problem by not stacking registers before an interrupt but leave it to the interrupt routine to stack any registers it needs to make use of and then restore the registers it used before doing the RTI instruction.
This is fine but there are two problems – if the interrupt routine needs to use all of the registers it still has to stack them all and there is always the possibility that it will not restore them properly – bugs happen.
There have been lots of solutions to the problem, the best probably being the provision of a complete duplicate set of internal registers that are simply swapped in one operation. (Believe it or not this was incorporated into the design of the Z80 an early microprocessor - but it is hardly used today.) This worked but only in small systems for obvious reasons.
Today it is generally recognised that for any general-purpose system the hardware should be optimised to make a task switch as fast as possible but still using the stack to store everything. This has become easier as the hardware has become more sophisticated.
Paradoxically, it has also become harder as the hardware has become more sophisticated!
For example, most modern processors such as the Pentium make use of a cache memory and a “pipeline” of instructions to speed things up. You can think of the pipeline as a sort of assembly line approach to obeying a program where lots of instructions are in the process of being obeyed. Much of the speed of a modern processor comes from its pipeline construction and there is nothing worse than an action which causes the pipeline to stall – that is have to be cleared and restarted.
Guess what an interrupt does!
To avoid unwanted interaction between tasks the instruction pipeline has to be restarted when an interrupt occurs.
In the real world there is still a fundamental trade off between fast and safe interrupts. You can have a fast interrupt if you are happy about leaving the control of interaction between tasks to the writer of the interrupt routine or you can have safe interrupts if you are not too fussy about latency or overall performance.
The Two-culture Problem
There are two very definite views of interrupts depending on whether or not you are working with real time systems or bigger information processors. If you are building a device to control something – the temperature of a chemical process say – then the processor that you use is small and optimised for real-time control. It generally doesn’t have an instruction pipeline and there is no need to stack everything when an interrupt occurs.
The software is probably all written by one team of programmers and there is hardly any operating system in the way of getting the job done. In this environment it is hard not to think about interrupts as a blessing.
You can write interrupt handlers for all of the hardware devices that need to be use and ensure that they all interact to just the right degree. Actually in practice this isn’t as easy as it sounds and any bug that does get into the system can be next to impossible to find.
Consider for a moment the way that non-interrupt-using programs are debugged and tested. They are taken through a standard sequence of inputs and actions. Such programs always produce the same outputs for the same explicit inputs – if they don’t we generally suspect a hardware fault of some kind!
Now consider a program with a number of interrupt routines. In this case the exact sequence of events depends on the exact timing and order of the interrupts. Perhaps an error only makes itself known when interrupt A occurs while interrupt routine B is working after interrupt routine X. You can see that because the interrupts are caused by the outside world the number of ways that the program can execute the instructions is huge and the potential for undiscovered unwanted interactions is also huge.
Programs that don’t use interrupts are often called “deterministic” because they always do the same thing. Programs that use interrupts are called “asynchronous” or “non-deterministic” because of the way that they hop about all over the place while working.
In simple systems you can just about keep control of a non-deterministic program but as the system’s complexity increases non-deterministic programs become next to impossible to make work properly. So much so that most of the errors that occur in interrupt driven programs are put down to intermittent hardware faults!
For safety critical systems interrupts are usually banned because they are considered too dangerous to risk even though they do seem to make the programmer's task simpler.