Introduction to Cyber-Physical Systems [3] : Inturrupts

In this article we are going to discuss some low-level concepts. in embedded systems and cyber-physical-systems activities are oriented around I/O, input received from environment and output given back to the environment. the mechanisms that we will discuss may not be directly used as you will often find libraries that handle them for you but it is important to understand how these concepts work because they greatly affect the behavior of the applications at minimum they affect the timing of programs.
Lets look at two input and output mechanisms that are commonly used with embedded processors.

Polling



  • Main loop checks each I/O device periodically
  • If ready for output, produce output.
  • If input is ready, read input.

Polling is considered the simplest and easiest to control, in many safety critical systems it may be the only available option that can be used. If you were designing a safety critical system like for example a flight control system for a commercial aircraft you will be restricted to use mechanism like polling because it is very easy to analyze and assure that its behavior is completely characterized . Polling is however rather inefficient in doing things and it can be rather difficult to make effective use of your processor with polling.
In polling you will reach a point where you simply check each I/O registers for the status of the first I/O device that you are going to read.
I/O registers are memory mapped registers so they are memory addresses, they tell you the status of the I/O device, or they provide the data for the I/O device or they can control the device. so the processor starts checking the devices if any of them are ready it operates on its data, else it goes to the next device, eventually when it completes the loop, it loops back and start the whole process again, so there is no concurrency in the program.
There are certain disadvantages to this mechanism. for example you cannot proceed with your program until you get data from the device you are currently inspecting. so your program is blocked while it is accessing the device and cannot see the status of the other devices which may have new data available to them. this creates a robustness  problem as your program could block as a result of a failure in one of the devices.

Now, let's look at a concrete example.So this is a processor that we looked at before in the memory module.So this is an Atmel AVR 8-bit microcontroller.This is a microcontroller that's typical, for example, of the processors in the Arduino open-source hardware platforms.

while(! (UCSR0A & 0x20) );
UDR0=x;

this is a simple polling mechanism for accomplishing a write to the serial port. the empty while loop will continue executing until the memory mapped register UCSR0A anded with the hex value 0x20 evaluates to true then it will exit the empty loop and send data using the memory mapped register UDR0 . the loop will continue to execute until the device is ready. let's suppose that serial port transmits at 57,600 baud (baud means bits per second ) so it is going to transmit 57600 bits per seconds at most , transmitting 8 bits takes 139 microseconds but it will take a while longer because it has to transmit start, stop bits. if the processor operates at 18 megahertz it will execute 18 million instructions per second. you can do the math to discover that you wasted 2500 cycles for each iterations of the loop. and you are wasting valuable processor time here. this is one of the disadvantages of polling.

Interrupts

interrupts give us a mechanism that is more concurrent and enables more effective use of processor time and more responsive applications. You don't have to block the processor while it is waiting for activities. instead when and only when a device needs serving it will notify the processor that it is ready to be served and the processor will proceed to serve it without wasting time in polling the device for its status periodically. in our previous example of writing to a serial port the steps will be quite different. you will have to do some setup which involves setting the peripheral devices by registering the interrupt service routing for your device , the interrupt service routing is the code which will be called when the device notifies the processor that it needs serving. You then just begin executing your application code, when an interrupt request occurs, the program that is executing is suspended, and you do what is called context switch (switching the context of execution to another context to save the old state of the processor) and then you execute the interrupt service routing, then you resume executing the code you were doing after restoring the processor state to before executing the interrupt. The program that is executing and the interrupt service routing in effect execute concurrently, they are not really executing in parallel certainly not in the case of single core processors but they are said to be executing concurrently because they are executing in the same time in the sense that you cant actuall tell which  one is executing at any given time and the interleaving of the two programs is really quite arbitrary. it is going to be determined by external timing and rather difficult to control precisely. It is very difficult to tell at what point in the execution of the program this interrupt service routine will be active.

Let's look at an example from the AVR processor, the Atmega168 reference maual that explains that there are a set of memory addresses that are devoted to handling inturrpts. the atmega168 is an 8-bit microprocessor so that data is handled by data path is 8-bit and it has 16-bit addresses so the addresses in the table is 16 bit.



you can see that the addresses for IRQ handlers which are called automatically when an interrupt occurs at these devices, a device can raise an interrupt by changing the voltage on one of the interrupt pins which jumps to a designated address that contains an instruction to jump to an external code ( Interrupt service routine ) that will be be executed to handle the corresponding interrupt request. you will notice that at address 0 the code jumps to a Reset Handler, this ISR is responsible of resetting the system it is called when system is restarted either by power failure / shortage or by requesting physical reset of the system. you will notice that the addresses are interleaved by 2 which is fairly logical as 16 bits are required for the address but because both the jmp instruction itself and the address has to be encoded in the 2 bytes, not all addresses are reachable.

 Normally you would override these addresses with your interrupt service routine to determine their behavior, this behavior depends on the processor. A typical response in a processor might be to first disable further interrupts by setting a pin in a control register, second the processor has to push the current position of the program counter to the stack to be able to continue executing from the point it stopped at. then it copies the program counter with the address corresponding to the particular interrupt that occurred then it will begin executing the ISR finally it returns from interrupt by retrieving the old program counter and continuing to execute the old program. It is important to save the old state of the processor before executing an ISR so it is the responsibility of the ISR to save and restore the values of any registers that it is using. and to re-enable further interrupts once it finished executing the ISR. Depending on the processor there may exist a return from interrupt instruction which automate this procedure.