NVIC is an on-chip controller that provides fast and low latency response to interrupt-driven events in ARM Cortex-M MCUs. In this tutorial, We will explain the role of the nested vectored interrupt controller (NVIC) in interrupt handling requests of ARM Cortex-M microcontrollers. At the start, we will explain the exception and interrupt concepts that are related to Cortex-M architecture. After that, we will also see how interrupts are handled by the Nested Vectored Interrupt Controller (NVIC) of ARM MCU. In the last section, we will discuss what is the need of prioritizing an interrupt or exception.
Before understanding nested vectored interrupt controller, lets have a breif introduction of interrupts and interrupt hanlders.
Interrupts can be defined as a system exception or peripheral interrupts which can cause the program flow to jump to a different position. As the name implies, interrupts get in the way of normal program execution. An interrupt can be generated from both hardware (i.e. external inputs, reset button, or other peripheral) and software (i.e. internal inputs or kernel interrupts). Almost every microcontroller, nowadays, supports interrupt capability.
What happens when Interrupt Occurs?
Whenever a hardware or software exception occurs that specific peripheral or external/internal input requires a response of that interrupt. As a response, a function call occurs and the required response is executed in the form of a piece of code known as a Service routine (SR) or Interrupt Service Routine (ISR). After that set of instructions in the service, the routine is executed the control shifts back to the main program in which the interrupt occurred. To be specific when an interrupt occurs the following set of steps is executed.
- An interrupt is generated by any hardware or software source.
- The processor suspends the currently running task as the response of the interrupt.
- After that, the processor then services the interrupt service routine (also known as interrupt handler) to service the interrupt that occurred.
- The processor then returns to the suspended task and resumes its normal program execution.
What is Nested Vectored Interrupt Controller?
In Cortex-M microcontrollers, a nested vectored interrupt controller usually known as NVIC is used to handle all the interrupts and exceptions that Cortex-M supports.
The nested vectored interrupt controller is basically an integrated part of Cortex-M because of its tight integration with the cortex-M core. We can also configure the interrupt controller according to our needs using specific registers. The mode of operation of most of the interrupt registers is privileged i.e. they can only be accessed in privileged mode, but if the interrupt is a software interrupt than these registers can be accessed in user mode also. Followings are the main responsibilities of NVIC:
- Interrupts handling
- Programmable interrupt feature
- Interrup tail chaining
- Low interrupt latency management
Interrupts and Exceptions in ARM Cortex-M
There are a total of 256 interrupts that Cortex-M supports. Interrupt numbered from 0-15 i.e. the first 16 interrupts are dedicated to system interrupts and all the other interrupts i.e. 16-255 are referred to as a user or peripheral interrupts. According to the nomenclature of ARM, an interrupt is a special kind of exception. In simple words, we can say that all interrupts are exceptions but all exceptions are not interrupted. As we now have a firm background of normal interrupts and Cortex-M interrupts handler we can now jump to the nested vectored interrupt controller (NVIC) in more depth.
Along with processing the interrupt, the NVIC also contains a timing module known as SYSTICK timer. This module is used as a reference for the timer and the system (as the name implies) uses this timer for managing its tasks. As we have discussed that Cortex-M supports a total of 256 system and user exceptions/interrupts, this is also true for NVIC. It supports 240 (16-255) user interrupts and 16(0-15) system exceptions. These numbers are not constants, but they can vary depending upon the manufacturer of the hardware.
Main Features of Nested Vectored Interrupt Controller
In the preceding section, we will discuss main responsibilites and features of NVIC.
NVIC Interrupts Handling
The major role of NVIC is to handle the interrupt. Cortex-M includes a vector table that contains the address of the interrupt service routines (ISRs) along with each interrupt number. Below is given the list of steps that are evaluated whenever an interrupt occurs.
- The Execution of the current task is suspended.
- The current state of the program is preserved.
- The NVIC initiates a call to that vector table, locates the interrupt number that has occurred, picks the address of the service routine from that vector table.
- After that, the service corresponding to the interrupt is executed.
- The state of the program is retained and normal execution of the task is resumed.
You can check read this in-depth guide on interrupt vector table:
How NVIC Manages Interrupts priority
The Cortex-M architecture provides the users with a feature to prioritize the interrupts according to their use and need. The first 16 exceptions cannot be prioritized as they are system exceptions and the system will not let the user change the priority of its interrupts. Also, these interrupt are only accessible in privileged mode. However, the user interrupt (16-255) are programmable by the user. These interrupts, either software or hardware can be prioritized by the user and NVIC decodes interrupts priority before processing them. This facility allows the user of Cortex-M to configure the microcontrollers based on Cortex-M architecture for a wide variety of applications.
What is term “Nested” in NVIC?
Nesting of interrupts is the major concept when talking about nested vectored interrupt controller. This concept is somewhat similar to nested for-loops, i.e. processing an interrupt (with higher priority) with in another interrupt (with lower priority). This can be implemented using NVIC as NVIC allows us to set the priority of every interrupt and the interrupts with higher priorities can preempt the interrupts with lower priority resulting in interrupts with in an interrupts and this preemption of interrupts is known as interrupt nesting.
As we mentioned earlier, ARM Cortex-M microcontrollers have 0-255 exceptions/interrupts and each exception has a priority and some exceptions are user-programmable. That means some interrupt will have higher priority than others during program execution. Furthermore, some interrupts can be configured as critical interrupts or non-maskable. That means they cannot be disabled.
The role of NVIC is to manage all low and high priority interrupts in such a way that the higher priority interrupts always gets to execute before a lower priority interrupts even if the lower priority interrupts occurs earlier. For example, if a low priority interrupt has occurred and being executed. While a low priority interrupt is still executing, a higher priority interrupt occurs. ARM CPU will pause the low priority interrupt and starts to execute high priority interrupt as shown in the figure below:
In short, the role of NVIC is to decode priorities of each interrupt and handle them according to their interrupt number and priority.
NVIC Role in Interrupt Latency control
NVIC helps to prioritize the interrupts and it helps in reducing the interrupt latency. Interrupt latency is defined as the time interval from the time of interrupt occurrence and the time at which the 1st statement of interrupt service routine is executed. NVIC helps in reducing the interrupt latency by providing a faster response to the application. This in turn executes the interrupts quickly and resumes the program flow.
NVIC Interrupt Tail-Chaining
One other concept that NVIC supports is tail chaining of interrupt. This is another name for nesting of interrupts, and it helps in executing the interrupts back to back without the problem of context switching. Without nested interrupt controller the coming interrupt goes in the pending state if an interrupt is already being executed unless that running interrupt completes its service routine and go back to the program and do complete context switch. In nested interrupts, however, we do not have to do this, and the next interrupts got executed within the first interrupt before giving the control back to the calling program.
When a context-switch occurs from a low priority task to a higher priority task, but the execution of interrupt hanlder of a low priority task is still pending. In this process, low priority interrupts chained and gets to execute only when all high priority tasks finished their execution.
Configuring an Interrupt
The first 16 exceptions of nested vectored interrupt controller (NVIC) are dedicated as system exceptions and we (as a user) are not allowed to configure or change anything of these exceptions, the system handles all this. The next 240 interrupts are user interrupts and it is our duty to configure and prioritize all the interrupts we need in our application out of these 16 – 255 interrupts. In this section, we will discuss how to initiate an interrupt and the steps of configuring an interrupt to make it ready to use for the application.
- The first step of configuring the interrupt is enabling the interrupts globally. Every microcontroller has a dedicated register of enabling all the interrupts present in the microcontroller, we will set the specific bit of the register and enable all the interrupts of the microcontroller. We can see the registered name, address, and enable bit from the technical reference manual of that microcontroller.
- In the next step, we will set the priority of each interrupt we are going to use. This is an optional step and it needs to be implemented if we want to prioritize our interrupts. If priority is not our need at the time we can simply skip this step. Doing so will set the priority of all the interrupts to 0, which is the highest configurable priority of an interrupt and if 2 interrupts occur simultaneously, the system will decide which interrupt to handle first.
- The last step is enabling the interrupts we want to use. For instance, if we want to use only interrupts 20 and 21 we do not need to enable all the 240 interrupts. we will only enable these 2 interrupts in our application.
- Accessing Memory Mapped Peripherals Registers of Microcontrollers
- Microcontroller Booting Process – Reset Sequence
- Bare Metal Embedded Systems Build Process using GNU Toolchain
- What is Microcontrollers startup file – Understand its various Functions
- Bare Metal Embedded Systems Linker Script File
- Microcontroller Memory Organization and Types – Explained with Memory Segments
- FreeRTOS Interrupt Management Examples with Arduino
- FreeRTOS Binary Semaphore – Examples of Tasks Interrupt Synchronization using Arduino