440 likes | 667 Views
dr. Stefan Dulman Embedded Software Group. Embedded Software. TI2720-C. 2. Interrupts. Lab schedule. Reminder: register for the lab! (send me an email). Lecture overview. Definition & characteristics Shared-data problem Interrupt latency. What is an Interrupt?.
E N D
dr. Stefan Dulman Embedded Software Group Embedded Software TI2720-C 2. Interrupts
Lab schedule • Reminder: register for the lab! (send me an email)
Lecture overview • Definition & characteristics • Shared-data problem • Interrupt latency
What is an Interrupt? • Asynchronous signal from hardware • Synchronous signal from software • Indicates the need for attention • Indicates the need for execution change
Why Interrupts? • Avoid wasting time in polling loops for external events • Make a system reactive and simple • Make debugging more fun CPU I2C Switches Internal errors Timers RS232 Buttons Sensors Actuators
Interrupts: Principle dev 1 IRQ contr Context switch CPU IRQ # dev N HW CALL ISR:PUSH R1 ... POP R1 RET Program: ... MOVE R1, (var-addr) MULT R1, 9 DIVIDE R1, 5 ADD R1, 32 ...
Where is the interrupt code? • Intel 8051 - default locations for interrupt service routines • Interrupt vector table • Where is the interrupt vector table? • Default location (Intel 80186) • Programmable location
Some questions… • Can a microprocessor be interrupted in the middle of an instruction? • Which interrupt goes first? Interrupt during another interrupt? • What happens immediately after the interrupts are enabled? • What is the status of interrupts when the processor starts?
XC3S400 Block Diagram • I/O, LEDs, SSD, buttons, RS232, … • Create your own core and set of peripherals (UART, PWM, timer, decoder)
Turn FPGA into a Microcontroller • High-speed designs in HW (e.g., VHDL) • XC3S400 clock 50 MHz, 400k gates ($129) • Low-speed designs in SW (e.g., C) • Need processor core that communicates with VHDL devices • No hard core provided (only in more expensive FPGAs) • So need soft core to execute programs! • Academia: zero-cost public domain VHDL soft core • Previous experience was bad (6502 core+SDCC tool chain) • So we built our own (well, SijmenWoutersen did) – X32
X32 Peripheral Interrupts buttons, LEDs, SSD, switches, timers, RS232, DPC, off-board I/O ports Peripheral 0 - K CPU multiple IRQ lines per peripheral possible (e.g., tx+rx IRQ per UART) IRQ0 IRQn Interrupt Controller CPU • peripherals used at the lab: • buttons, LEDs, SSD, UART (RS232 interface to console), • DPC (digital-PWM converter to control DC motor), • digital inputs (to read motor position encoder signals A and B)
X32: Interrupt Sources • CPU: divide-by-0, overflow (disable unless needed!) • Buttons, switches, I/O ports • positive AND negative edge-triggered • Timers: counter value > threshold reg • UART: rx buffer char received / tx buffer empty • Motor decoder engine: corrupt input signals
IRQ . IE IE_global . (priority > exec level) servicing / interrupt stdby IE’ scheduled acknowledge X32: Interrupt Controller interrupt IRQ0 Interrupt Controller acknowledge IEk, priok, vectork vector CPU priority exec level IRQn IE_global FSM for each IRQ:
X32 Interrupts • IRQ controller preprocesses multiple IRQ’s • Each device: (IRQ #, priority, associated with IRQ vector) • Vectored IRQ • Interrupts NOT disabled automatically • Automatic ISR preemption if prio IRQ > prio current ISR • Normal call saves context -> no interrupt keyword IRQ # IRQ contr CPU
Exercise: ISR version of leds.c … int done = 0; intbutton_state; void delay(int ms) { inttime = X32_clock; while(X32_clock - time < ms); } … void isr_buttons(void) { // delay(200); X32_leds = X32_buttons; if (X32_buttons > 0) { button_state = X32_buttons; printf("Button %d pressed\n", button_state); } else if (button_state > 0 && X32_buttons == 0) { printf("Button %d released\n", button_state); button_state= 0; } if (X32_buttons == 0x09) done = 1; } /*------------------------------------------- * main -- do the demo *-----------------------------------------*/ int main() { SET_INTERRUPT_VECTOR( INTERRUPT_BUTTONS, &isr_buttons); SET_INTERRUPT_PRIORITY(INTERRUPT_BUTTONS,10); ENABLE_INTERRUPT(INTERRUPT_BUTTONS); ENABLE_INTERRUPT(INTERRUPT_GLOBAL); printf("Hello World!\r\n"); while (! done) { X32_display = X32_clock; } DISABLE_INTERRUPT(INTERRUPT_GLOBAL); return 0; }
X32: Demo • Demo .. • (x32_projects.tgz, leds.c, leds_isr.c, buttons.c)
Lecture overview • Definition & characteristics • Shared-data problem • Interrupt latency
Shared-Data Problem? void isr_read_temps(void) { iTemp[0] = peripherals[..]; iTemp[1] = peripherals[..]; } void main(void) { ... while (TRUE) { tmp0 = iTemp[0]; tmp1 = iTemp[1]; if (tmp0 != tmp1) panic(); } } Book: page 92 …
Shared-Data Problem? void isr_read_temps(void) { iTemp[0] = peripherals[..]; iTemp[1] = peripherals[..]; } void main(void) { ... while (TRUE) { tmp0 = iTemp[0]; tmp1 = iTemp[1]; if (tmp0 != tmp1) panic(); } } Possible Context Switch NOT ATOMIC! Book: page 92 …
Finding this bug… • Can be very tricky • The bug does not occur always! • Frequency depends on • The frequency of interrupts • Length of the critical section • Problem can be difficult to reproduce • Advise: double check the access on data used by ISR!
Solving the Data-Sharing Problem? void isr_read_temps(void) { iTemp[0] = peripherals[..]; iTemp[1] = peripherals[..]; } void main(void) { ... while (TRUE) { if (iTemp[0] != iTemp[1]) panic(); } }
Solving the Data-Sharing Problem? void isr_read_temps(void) { iTemp[0] = peripherals[..]; iTemp[1] = peripherals[..]; } void main(void) { ... while (TRUE) { if (iTemp[0] != iTemp[1]) panic(); } } MOVE R1, (iTemp[0]) MOVE R2, (iTemp[1]) SUBSTRACT R1,R2 JCOND ZERO, TEMP_OK … … TEMP_OK: …
Solution #1 • Disable interrupts for the ISRs that share the data ... while (TRUE) { !! DISABLE INT tmp0 = iTemp[0]; tmp1 = iTemp[1]; !! ENABLE INT if (tmp0 != tmp1) panic(); } The critical section is now atomic
Atomic & critical section • A part of a program is atomic if it cannot be interrupted • Interrupts and program code share data • atomic can also refer to mutual exclusion • Two pieces of code sharing data • They can be interrupted • The instructions that must be atomic = critical section
Be careful! static intiSeconds, iMinutes; void interrupt vUpdateTime(void) { ++iSeconds; if (iSeconds>=60) { iSeconds=0; ++iMinutes; } } long lSeconds(void) { return (iMinutes*60+iSeconds); }
Be careful! static intiSeconds, iMinutes; void interrupt vUpdateTime(void) { ++iSeconds; if (iSeconds>=60) { iSeconds=0; ++iMinutes; } } long lSeconds(void) { disable(); return (iMinutes*60+iSeconds); enable(); }
Function calls and enable() • enable() can be a source of bugs! int f2 () { … disable(); … enable(); … } void function1 () { … // enter critical section disable(); … temp = f2(); … // exit critical section enable(); … } should test if this is fine
More on shared-data… static long intlSeconds; void interrupt vUpdateTime() { … ++lSecondsToday; … } long lGetSeconds() { return (lSecondsToday); }
More on shared-data… static long intlSeconds; void interrupt vUpdateTime() { … ++lSecondsToday; … } long lGetSeconds() { return (lSecondsToday); } MOVE R1,(lSecondsToday) MOVE R2,(lSecondsToday+1) … RETURN
Any issues here? static long intlSecondsToday; void interrupt vUpdateTime() { ++lSecondsToday; } long lGetSeconds() { long lReturn; lReturn = lSecondsToday; while (lReturn!=lSecondsToday) lReturn = lSecondsToday; return (lReturn); }
Any issues here? volatile static long intlSecondsToday; void interrupt vUpdateTime() { ++lSecondsToday; } long lGetSeconds() { long lReturn; lReturn = lSecondsToday; while (lReturn!=lSecondsToday) lReturn = lSecondsToday; return (lReturn); } Otherwise compiler might optimize this code!
Exercise: critical section /*-------------------------------------- * main -- do the demo *------------------------------------*/ intmain() { int copy1, copy2; … /* start the fireworks */ ENABLE_INTERRUPT(INTERRUPT_GLOBAL); demo_done= 0; x = y = 0; while (! demo_done) { DISABLE_INTERRUPT(INTERRUPT_BUTTONS); copy1 = x; copy2 = y; ENABLE_INTERRUPT(INTERRUPT_BUTTONS); X32_display = ((copy1&0xff)<<8) | (copy2&0xff); if (copy1 != copy2) printf("alarm: %d != %d\r\n",copy1,copy2); } DISABLE_INTERRUPT(INTERRUPT_GLOBAL); return 0; } … intdemo_done; int x, y; … void isr_buttons(void) { printf("isr: %d++\r\n",x); x++; y++; if (X32_buttons == 0x09) demo_done = 1; }
X32: Demo Demo .. (x32_projects.tgz, critical.c)
Lecture overview • Definition & characteristics • Shared-data problem • Interrupt latency
Interrupt Latency • Quick response to IRQ may be needed • Depends on previous rules: • The longest period of time in which interrupts are disabled • The time taken for the higher priority interrupts • Overhead operations on the processor (finish, stop, etc.) • Context save/restore in interrupt routine • The work load of the interrupt itself • worst-case latency = t_maxdisabled + t_higherprio ISRs + t_myISR + context switches
Example #1 EI EI DI DI ISR main IRQ ISR blocked ISR code Interrupt execution time – worst case scenario analysis
Example #2 DI EI lp ISR hp ISR main hp IRQ lp IRQ blocked hp ISR actual load Interrupt execution time – worst case analysis – in the presence of a second interrupt
Alternatives to disable() • Do not disable interrupts but write ingenious code • By alternating data buffers (Fig. 4.15/page 108) • By using queues (Fig. 4.16/page 109) • ISR and (main) code never access the same data • Problem: code becomes error-prone and hard to read • Rule of the thumb: • Keep it simple, just disable interrupts as long as: • keep the critical sections SHORT • keep the ISRs SHORT (to minimize latency)
Avoiding interrupts #1 Void main(void) { while (TRUE) { if (useB) if (tempB[0]!=tempB[1]) … ; else if (tempA[0]!=tempA[1]) … ; useB = !useB; } } static inttempA[2]; static inttempB[2]; static booluseB = FALSE; void interrupt readTemp() { if (useB) { tempA[0]=…; tempA[1]=…; } else { tempB[0]=…; tempB[1]=…; } } Alternating data buffers example (page 108)
Avoiding interrupts #2 • Make use of a circular queue (example page 109) • Interrupt “produces” temperature readings • Main code “consumes” temperature readings • Operation • Interrupt adds readings to the queue (modifies the head pointer) • Main code extracts readings from the queue (modifies the tail pointer) Queue New values Tail pointer Head pointer
X32: Demo Demo .. (x32_projects.tgz: timing.c)