You are on page 1of 14

Interrupts

An Embedded Software Primer by David E. Simon

Chapter 4: Interrupts
1. 2. 3. 4.

Microprocessor Architecture Interrupt Basics The Shared-Data Problem Interrupt Latency

Microprocessor Architecture
Most microprocessors and their assembly languages are generally similar to one another. An assembly language is a more easily readable form of the instructions that a microprocessor actually works with. The assembly language is translated into binary numbers by a program called an assembler before execution. When translating C, most of the statements are converted into multiple assembly instructions by the compiler. Each microprocessor family will have a different assembly language (e.g. the Intel 8085, 8051 etc.) however the individual microprocessors in the same family generally have the same assembly language. Microprocessors typically have a set of (general-purpose) registers that can store data values.
3

Microprocessor Architecture (contd.)


The registers are used before performing any data operations like arithmetic. For the following sections assume the microprocessor has the registers R1, R2, R3 .etc. Apart from the above registers, microprocessors normally contain special registers like:
Program Counter: Contains the address of the next instruction to be executed. Stack Pointer: Contains address of the top of the processor stack. Accumulator: Used by the microprocessor to do arithmetic operations.

Microprocessor Architecture (contd.)


Some assembly language conventions:
The variables in the instruction are normally read from right to left. e.g. MOVE R1, R2 will move the contents of register R2 to register R1 A variable-name in an instruction refers to its address.
MOVE R5, Temperature

is read move the address of Temperature to register R5 A variable in parentheses refers to the value of the variable.
MOVE R5, (Temperature)

will place the value of Temperature in register R5. Anything following a semicolon is a comment.

Microprocessor Architecture (contd.)


Some important instructions include: Arithmetic: ADD R2, R3 add contents of R3 to R2 Bit-oriented: NOT R5 invert all the bits of R5 Jump (unconditional): Will unconditionally jump to an instruction specified by a label. Labels are followed by colons.
ADD R2, R3 NOT R2 JUMP NO_ADD ADD: ADD R2, R5 NO_ADD: MOVE R7, R2 ; R7 contains the final result ; Comments come here

The instruction adds contents of R3 to R2, inverts the result in R2, jumps unconditionally to the NO_ADD label (the instruction ADD R2, R5 never gets executed) and stores the result in R7.
6

Microprocessor Architecture (contd.)


Conditional Jump: Executes a jump if a certain condition is true.
SUBTRACT R2, R3 JCOND ZERO,NEXT . . NEXT: . .

Here jump is executed if result of the subtraction is zero. PUSH and POP: These are stack instructions. PUSH adjusts the stack pointer and adds data to the stack while POP retrieves the data and adjusts the pointer. CALL: Used to execute functions and subroutines. Followed by a RETURN instruction for getting back.
CALL SUBTRACT_THESE MOVE R7,R2 . . SUBTRACT_THESE: SUB R2,R3 RETURN

Interrupt Basics
Interrupts are triggered when certain events occur in the hardware. e.g. when a serial chip has sent data to a microprocessor and wants it to read it from its pin, it sends an interrupt to the processor, usually by sending a signal to one of the processors IRQ (interrupt request) pins. On receiving an interrupt, the microprocessor stops its current execution, saves the address of the next instruction on the stack and jumps to an interrupt service routine (ISR) or interrupt handler.
8

Interrupt Basics (cont.)


The ISR is basically a subroutine written by the user to perform certain operations to handle the interrupt with a RETURN instruction at the end. It is a good practice to save register state and reset the interrupt in ISRs. ISRs are similar to a CALL except that the call to the ISR is automatically made.

Interrupt Example
The following shows an e.g. of an ISR
Task Code ... MOVE R1, R7 MUL R1, 5 ADD R1, R2 DIV R1, 2 JCOND ZERO, END SUBTRACT R1, R3 ... ... END: MOVE R7, R1 ... ... ISR

PUSH R1 PUSH R2 ... ;ISR code comes here ... POP R2 POP R1 RETURN

10

Interrupts and State


Saving and Restoring the Context
As the above code demonstrated, the task code has no idea of the changes taking place in registers like R1 or R2 in the ISR. Hence if R1 is modified by the ISR, we might get an incorrect final result. Due to limited number of registers, the ISRs and task codes usually have to work with same registers. To solve this problem it is common practice to save the register contents onto the stack (saving the context) and restoring them at the end of the ISR (restoring the context). This is mandatory to prevent any bugs from appearing due to interrupts
11

Disabling Interrupts
Most microprocessors allow programs to disable interrupts. In most cases the program can select which interrupts to disable during critical operations and which to keep enabled by writing corresponding values into a special register. Nonmaskable interrupts however cannot be disabled and are normally used to indicate power failures or other serious event. Certain processors assign priorities to interrupts, allowing programs to specify a threshold priority so that only interrupts having higher priorities than the threshold are enabled and the ones below it are disabled.
12

The Shared-Data Problem


In many cases the ISRs need to communicate with the task codes through shared variables. Consider:
Void main (void) { Static int iTemperatures[2]: int iTEmp0, iTemp1: Void interrupt vReadTemperatures (void) { iTemperatures[0] = !! Read in value from hardware iTemperatures[1] = !! Read in value from hardware } while(TRUE) { iTemp0 = iTemperatures[0]; iTemp1 = iTemperatures[1]; if (iTemp0 != iTemp1) !!Set off howling alarm; } }

13

Shared Data (cont.)


The code continuously monitors two temperatures and sets off an alarm if they are different. An ISR reads the temperatures from the hardware. The interrupt might be invoked through a timer or through the temperature sensing hardware itself. Now, consider that the temperatures are 70 degrees and an interrupt occurs after iTemp0 = iTemperatures[0] is executed. The temperatures now become 75 degrees. Hence on returning from ISR, iTemp1 will be assigned 75 and an alarm will be set off even though the temperatures were the same.
14

The Shared-Data Problem (contd.)


Characteristics of the Shared-Data Bug The problem is due to the shared array iTemperatures. These bugs are very difficult to find as they occur only when the interrupt occurs in between the first 2 MOVE instructions, other than which code works perfectly.

15

The Shared-Data Problem (contd.)


Solving the Shared-Data problem
The problem can be solved by disabling the interrupts during the instructions that use the shared variable and re-enabling them later. The following modification to Fig. 4.4 (or 4.5) solves the problem. while (TRUE) { disable(); ; Disable interrupts iTemp0 = iTemperatures[0]; iTemp1 = iTemperatures[1]; enable(); ;Re-enable interrupts ... ;Remaining code same as in the previous example ... } In assembly DI and EI are used to disable and enable interrupts respectively.
16

Shared Data Problem


Atomic and Critical Section A part of a program is atomic if it cannot be interrupted. Hence the code between disable() and enable() above is atomic. Atomic might also be used for codes that only disable interrupts working with the same data. Hence if interrupts not working with the temperature variable (like pressure, time etc.) are left enabled code is still considered atomic and free of bugs.
17

Shared Data Examples


Few Examples
Consider an ISR that updates iHours, iMinutes and iSeconds every second through a hardware timer interrupt. The function to calculate the time can be written as follows: long iSecondsSinceMidnight (void) { long lReturnVal; disable(); lReturnVal = (((iHours*60) + iMinutes) * 60) + iSeconds; enable(); return (lReturnVal); } A problem in the above code arises when the function is called from a critical section that has disabled interrupts. The function will incorrectly enable interrupts on return. Fig. 4.10 solves this problem.

18

Data Changing in ISRs

static long int lSecondsToday;

Long lSecondsSinceMidnight (void) { long lReturn; /* When we read the same value twice, it must be good.*/ lReturn = lSecondsToday; while(lReturn != lSecondsToday) lReturn = lSecondsToday; return(lReturn); }

Void interrupt vUpdateTime (void) { ++lSecondsToday; if(lSecondsToday == 60L * 60L * 24L) lSecondsToday = 0L; }

19

Volatile Data (contd.)


However certain compilers will optimize the code. It will read lSecondsToday in one or more registers and instead of updating the value before saving it to lReturn it will read the value from the register every time instead of from memory. Some compilers might also remove the while-loop during optimization causing the same bug with a single read. This is prevented by declaring lSecondsToday as volatile. This warns the C compiler that the variable might change due to interrupts or other routines and not to optimize code pertaining to it. static volatile long int lSecondsToday;
20

10

Interrupt Latency
Interrupt latency is the amount of time taken to respond to an interrupt. This depends on several factors:1. 2. 3.

4.

Longest period during which the interrupt is disabled Time taken to execute ISRs of higher priority interrupts Time taken for the microprocessor to stop the current execution, do the necessary bookkeeping and start executing the ISR Time taken for the ISR to save context and start executing instructions that count as a response

The third factor is measured by knowing the instruction execution times from the processor manual, when instructions are not cached.
21

Interrupt Latency (cont.)


Factor 1 is affected by your SW Architecture Dont disable interrupts too long Factors 2 and 4 are controlled by writing short efficient code. Make ISRs short Factor 3 depends on hardware and is not under software control. Pick a good processor
22

11

Latency and Disabling Ints


Disabling Interrupts
Disabling interrupts increases interrupt latency so this period should be kept as short as possible. Consider the scenario in which two different task codes disable interrupts for 125 and 250 s respectively. In this case the max. time taken to start executing an ISR will be 250 s and not 125+250 s because ISR is started as soon as the interrupts are reenabled in either one of the atomic sections.

23

Disabling Interrupts
Alternatives to Disabling Interrupts As disabling interrupts increases latency, some alternatives might be required in certain situations. Code on the following slide uses two array sets iTemperaturesA and iTemperaturesB and a variable (fTaskCodeUsingTempB) to ensure that the ISR always works with the array not being used by the task code.
24

12

Disabling Interrupts (cont.)


Static int iTemperaturesA[2]; Static int iTEmperaturesB[2]; Static BOOL fTaskCodeUsingTempsB = False; Void main(void) { while(TRUE) Void interrupt vRead Temperatures (void) { if(fTaskCodeUsingTempsB) { iTemperatures[0] = !!read in value from HW iTemperatures[1] = !!read in value from HW } Else { iTemperaturesB[0] = !!read in value from HW iTemperaturesB[1] = !!read in value from HW } } }
25

{ if(fTaskCodeUsingTempsB) if(iTemperaturesB[0] != iTemperaturesB[1]) !!Set off howling alarm; else if(iTemperaturesA[0] != iTemperaturesA[1]) !!Set off howling alarm;

fTaskCodeUsingTempsB = !fTaskCodeUsingTempsB; }

Interrupt Latency (contd.)


The following code segment uses a queue. The ISR writes data at the head of the queue and increments iHead by 2 while the task code starts reading from the tail of the queue. These alternatives are not robust and even minor changes might introduce bugs and hence should only be used as the last option.
26

13

Interrupt Latency (contd.)


#define QUEUE_SIZE 100 Int iTemperaturesQueue[QUEUE_SIZE]; Int iHead = 0; Int iTail = 0; /*Place to add next item*/ /*Place to read next item*/

Void interrupt vReadTemperatures (void) { /*If the queue is not full..*/ if (!((iHead+2==iTail) || (iHead==QUEUE_SIZE-2 && iTail==0))) { iTemperatureQueue[iHead] = !!read one temperature; iTemperatureQueue[iHead + 1] = !!read other temperature; iHead += 2; if(iHead == QUEUE_SIZE) iHead = 0; } else !!throw away next value }
27

Interrupt Latency (contd.)


Void main (void) { int iTemperature 1, iTemperature2;

while(TRUE) { /*If there is any data..*/ if(iTail != iHead) { iTemperature1= iTemperatureQueue[iTail]; iTemperature2= iTemperatureQueue[iTail + 1]; iTail += 2; if(iTail == QUEUE_Size) iTail = 0; !! Do something with iValue; } } }
28

14

You might also like