Lecture 19 - Interrupts 10:10 Practical Matters Let's take a look at the schedule for the coming 4 weeks. 1. Course topics This week (3/12-3/16): Interrupts Next week (3/19-3/23): Timer Peripheral Third week (3/26-3/30): Sound Synthesis and Analysis Fourth week (4/2-4/6): Summary and Midterm II 2. Course work This week Lab 3 + HW 5 rolled out Due Date 3/13 HW 4 (UART, SW Timer) 3/20 HW 5 (Part of Lab 3) 3/27 Lab 3 (Driving Game) 4/3 HW 6 (A/D, Interrupts) 4/6 Midterm II 3. Travel day Schaumont has a travel day on 3/23. There will be an online (pre-recorded) lecture. 10:15 Interrupts and Interrupt Service Routines In a nutshell, the idea of an interrupt is the following: Interrupts allow the processor to suspend execution, call a software routine (named ISR or interrupt service routine), and subsequently resume execution. normal execution | | (external event) X ... Interrupt ----> Interrupt Service Routine (ISR) | | Clear Interrupt X <---------------------------+ | return | To the main program, the execution of an ISR is transparent. The main program is not aware that an ISR has run. Of course, the ISR could read/write global variables, and influence the behavior of the main program in this manner. A key observation is that an ISR is initiated by an external event in hardware, and it is not the software itself that decides when to call the ISR. Why do we need interrupts? To understand why this is useful, recall how the cyclic executive works: +-----------------+ event input ->| | event input ->| cycle executive +--> output(s) event input ->| | +-----------------+ The cyclic executive gives the illusion of parallel processing of event inputs because it quickly scans over all inputs, always doing just a tiny bit of work before continuing the scan: while (1) { if (event1()) do_something_1(); if (event2()) do_something_2(); if (event3()) do_something_3(); } As long as the while loop scans quickly enough, we will never 'loose an event' at the input. However, sometimes it is difficult, or even impossible, to guarantee that do_something_x() is a short function. An example is clearing of the LCD display. This is a function call that takes several 100ms to execute. We would like to make it shorter, but unfortunately the call is in a library - so we cannot change it. For situations like this, interrupts offer a solution. The idea of an interrupt is that you can take an event out of the cyclic-executive loop, and instead use it to directly initiate the execution of do_something_y() when it is needed. This interrupt is able to SUSPEND the execution of a slow do_something_x() (such as, clear the LCD display). Hence, an interrupt serves as the equivalent of a priority queue in an airport security line. When you (the interrupt) arrive, the regular customers wait, and the interrupt is allowed to proceed first. In an interrupt, there is a HARDWARE EVENT which initiates the do_something_x() in SOFTWARE. You can think of a hardware event as a flag in hardware that changes state (from 0 to 1). The hardware event is used as an interrupt request. When the software responds to the interrupt request, the software is said to 'service' the interrupt. The precise activities in the software ISR depend on the nature of the interrupt. Examples: Interrupt Cause Interrupt Service ------------------------------------------------------ UART receive buffer full Read receive buffer Timer overflow Perform a periodic action Port pin x has changed Read the new port status 10:25 From Hardware to Software and back: single interrupts Let's look at the activities occuring if we need to service one single interrupt. Hardware Hardware Software (Peripheral) (Interrupt Controller) Interrupt Trigger | | +---------- Interrupt Enable <----------- InterruptEnable() | | V V +-------+ | AND | +---+---+ | (1) +--------------> Interrupt Vector -- Vector --> to CPU Table ISR() { ---------+--------- ... Trigger | Vector Clear Interrupt Flag ---------+--------- } | 1 | ISR | | Clear Trigger <----------------------------------------------+ The hardware event that will result in the interrupt request is the INTERRUPT TRIGGER. The interrupt trigger is send to the INTERRUPT CONTROLLER. That is a peripheral that deals with the low-level interface between the CPU and the interrupt trigger. The interrupt controller will ignore any interrupt trigger unless a corresponding INTERRUPT ENABLE flag is set. That flag must be set by software. Every interrupt trigger has its own interrupt-enable bit in the interrupt controller. In addition, there is a global interrupt-enable for the entire interrupt controller. If the interrupt enable bit is set, AND there is an interrupt trigger, the interrupt controller will tell the CPU that it should execute the ISR(). However, the CPU does not know where the entry point of the ISR() is located. The entry point (starting address) of the ISR has a special name and is called INTERRUPT VECTOR. The interrupt controller maintains a table, called the INTERRUPT VECTOR TABLE, that remembers for each possible interrupt trigger, what interrupt vector must be used. When the interrupt controller receives a trigger, and that interrupt is enable, it will thus forward the interrupt vector corresponding to that trigger to the CPU. When the CPU receives the interrupt vector, it can execute the ISR(). An ISR is a routine that does not have any arguments; it is called without arguments (void) and it returns no arguments (void). If an ISR wants to communicate with any other part of the program, it must use global variables. At the end of the ISR, the ISR must CLEAR INTERRUPT FLAG before exiting the ISR. This results in a signal going back from the software into the hardware, to indicate that the servicing of the interrupt is complete. When the hardware sees the 'clear interrupt flag', it will clear the trigger in response. If the ISR forgets to call the 'clear interrupt flag', the interrupt trigger will remain pending, and the interrupt controller will call the ISR again as soon as it exits. 10:35 From Hardware to Software and back: multiple interrupts Now, let's look at the activities that occur when we need to service multiple interrupts. The challenge of this case, is that multiple interrupt triggers can happen at the same time. If an interrupt trigger has happened, and the corresponding interrupt enable flag is set, the interrupt is said to be PENDING. There can be several pending interrupts at the same time. However, the processor can only service one interrupt at a time, since it can only run one single ISR at the time. To solve this, the interrupt controller can associate an INTERRUPT PRIORITY with each interrupt trigger. The priority for each interrupt (in the case of Cortex-M, the CPU in MSP432) is a number from 0 to 7, with 0 indicating the highest priority. So, if there are two pending interrupts, one with priority 5 and another with priority 2, then the ISR associated with interrupt trigger priority 2 will run first, while the priority-5 interrupt will remain pending. Even ISRs can be interrupted by higher-priority interrupts, and this is called ISR PREEMPTION. It is also called NESTING AN INTERRUPT: an interrupt event that can interrupt another interrupt service routine. Therefore, with multiple interrupt triggers: - There will be multiple interrupt enable bits - There will be multiple interrupt vectors (associated with different ISR) - There will be multiple interrupt priority levels - There will be multiple clear interrupt-flag calls Hardware Hardware Software (Peripheral) (Interrupt Controller) Interrupt Enable 1 <----------- InterruptEnable() Interrupt Enable 2 <----------- InterruptEnable2() ... Interrupt Enable n Interrupt Trigger 1 Interrupt Trigger 2 ... Interrupt Trigger n Interrupt Vector Table ISR() { ---------+--------- ... Trigger | Vector Clear Interrupt Flag ---------+--------- } 1 | ISR 2 | ISR2 ISR2() { ... | ... ... Clear Interrupt Flag2 Interrupt Priorities | ---------+--------- Trigger | Priority ---------+--------- 1 | 7 2 | 0 ... | ... 10:40 Interrupt states We can now define the different processing states that are associated with an interrupt: INACTIVE: The interrupt trigger is absent, and the ISR is not running PENDING: The interrupt trigger is present, but the ISR is not yet running ACTIVE: The interrupt trigger is absent, and the ISR is running ACTIVE and PENDING: The interrupt trigger is present, and the ISR is running The normal case for an interrupt processing is as follows: INACTIVE | V PENDING (when the interrupt trigger occured) | V ACTIVE (when the CPU is running ISR(), and has not yet called + PENDING Clear Interrupt Flag) | V ACTIVE (when the CPU us running ISR(), and has called clear interrupt | flag) V INACTIVE (when the CPU has completed ISR processing) 10:45 Interrupt Latency The interrupt latency is the time it takes from first issuing an interrupt request to the time the ISR starts to execute is called the interrupt latency. The interrupt latency depends on (1) the time it takes for the CPU to switch context: => save registers, PC, fetch interrupt vector (2) the time it takes for interrupts at HIGHER priority to finish 10:47 Writing ISR in C: An example #include #include int main(void) { // Stop WDT WDT_A_holdTimer(); // Timer Initialization Timer32_initModule(TIMER32_0_BASE, TIMER32_PRESCALER_1, TIMER32_32BIT, TIMER32_PERIODIC_MODE); Timer32_setCount(TIMER32_0_BASE, 1500000); // 2 Hz // RED LED GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN0); GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN0); // BUTTON S1 GPIO_setAsInputPin (GPIO_PORT_P5, GPIO_PIN1); // Enable interrupts from Timer32 INT1 and start the timer Interrupt_enableInterrupt(INT_T32_INT1); Timer32_startTimer(TIMER32_0_BASE, false); // Enabling MASTER interrupts Interrupt_enableMaster(); // and done while (1) ; } void T32_INT1_IRQHandler() { GPIO_toggleOutputOnPin(GPIO_PORT_P2, GPIO_PIN0); if (GPIO_getInputPinValue(GPIO_PORT_P5, GPIO_PIN1) != 0) // clear interrupt only when button is not pressed Timer32_clearInterruptFlag(TIMER32_0_BASE); } Describe: - Interrupt Enable Flag - Interrupt Service Routine - Interrupt Vector Table Demonstrate: - Effect of not clearing the interrupt flag 10:57 Conclusions Basics of Interrupts Terminology - Interrupt Trigger - Interrupt Enable (Flag) - Interrupt Controller - Interrupt Vector Table - Interrupt Priority - Pending, active and inactive interrupt Activities - Clearing an Interrupt Flag - Nesting an Interrupt