Interrupt Handling in ARM Cortex M Core
Saravana Pandian Annamalai
CEO @ Embien Technologies | Automotive | Embedded
In our earlier blogs on ARM Interrupt architectures, we explored the ARM exception models and registers. Also, we went through different kinds of Interrupt controllers being used. In the upcoming articles, we will primarily see ARM Interrupt handling from the firmware/software perspective including operating systems like FreeRTOS, Linux and bare metal. To begin with, this blog will discuss interrupt handling in ARM Cortex M MCUs.
Cortex M Vector Table
As discussed earlier, the ARM Cortex M series of MCUs typically caters to lower end application with the core running between a few MHz to a maximum 150MHz. To target low-cost tools and ease of development, the interrupt architecture is designed to be simpler and straight forward. The vector table in ARM Cortex M series looks like:
Typically, on power-on reset, the Vector table base address is defined to be at 0. The ARM core, up on boot up, loads the stack pointer with the value stored at offset 0. And then it loads the Program counter with the address available at offset 4 and starts executing the same.
Thus, the vector table design is such that the stack is operational before the core starts executing thereby eliminating the need for an assembly code to set things up for calling functions. In the firmware perspective, this is a major advantage. There is no need to write any assembly code.
Following these 2 words, the table should hold the addresses of the exception handlers. The first 14 of them are pre-defined ad reserved for handling specific to the core and its execution. From offset, 0x40, the SoC specific interrupt handlers are defined and can be customized by the silicon vendor.
It can also be noted that there is a priority associated with each of these exceptions. Lower the number the higher the priority. Some of the major exceptions defined by ARM are
Reset Handler
At the highest priority of -3, this is the entry point of execution. Loaded to PC on power on reset, this is responsible for initializing the system peripherals and start executing the firmware/OS.
NMI Handler
At a priority only next to Reset Handler (-2), as the name suggests this cannot be masked by software. It is typically triggered by a specialized peripheral unit that can be connected to a critical functionality.
HardFault Handler
This exception is caused when there is an error during exception processing. At a higher priority of -1 than other exception, this can be used to recover from issues during exception handling.
MemManage Handler
Caused due to memory protection faults, the priority level can be configured by the firmware.
BusFault Handler
Caused during to memory access – either during instruction fetch or data access, the priority level can be configured by the firmware
UsageFault Handler
Caused during to instruction executing, the priority level can be configured by the firmware. The handler is called when one of the following errors occurs
SVCall Handler
Known as the Supervisor Call, this handler is called up on the core executing a SVC instruction. This is typically used in OS environments to execute system services.
PendSV Handler
This is typically used in OS environments to perform context switching.
SysTick Handler
The ARM Cortex M core defines a specialize timer module to keep track of the System time. This handler is executed once this timer value reaches 0.
With this understanding of Cortex M vector table, now we will see how the firmware handles exceptions in software.
Setting up Cortex M Vector Table
To practically understand Cortex M Interrupt handling, we will take an example of software implementation of FreeRTOS running on NXP K66 MCU compiled using GCC tool chain. The first and foremost step is to define the vector table and place it in the Vector base location. The vector table for the device looks like this:
领英推荐
As it can be seen, the interrupt handlers are clubbed together as an array with the address to the top of the stack (lowest address as it grows towards higher address) as the first element. This array is placed in address 0, via linker script mechanism. In this example, using the section (.isr_vector) keyword, the location of the vector table is set to 0.
Also, it can be noted that there are hundreds of interrupt handlers supported. Even this example has close to 115 handlers. Obviously, a firmware system will not have all these implemented, hardly 20-30 interrupts will be used in a system. Instead of asking the user to define handlers, the start-up code provided by the silicon vendors, will have dummy functions (a while (1) loop) defined with weak reference.
So, when the developer defines an interrupt handler with the same name in his code, this weak function will be discarded and user-defined function linked against instead.
Cortex M Interrupt Handling
With the vector table installed, the functions that are needed can be added one by one in the firmware. As a first step, the Reset Handler has to be created. At typical, reset handler will look like as below.
This minimalistic handler, disables all interrupts up on entry, configures the core and major peripherals via SystemInit function. Then it initializes the data and bss sections. Finally, it enables the interrupts before jumping to the main function.
Now we will see how a peripheral is configured for interrupt operation based on the Systick unit.
As the code listing shows, the function configures various registers needed for proper operation of the Systick interrupt such as LOAD, VAL, CTRL etc. IT=t also configures the priority of the interrupt.
As mentioned earlier, in Cortex M architecture, each of the interrupts has an associated priority. Depending on the implementation, there could be n number of bits corresponding to each interrupt number. Lower the number, higher the priority/urgency and can be set via the NVIC_SetPriority CMSIS API. With this mechanism, it is possible for the interrupts to be prioritized and only the higher priority one will be serviced if more than one interrupt is pending at the same time.
Typical handler for SysTick looks like:
Cortex M Interrupt Handling via Assembly
So far, we have not writing a single line of assembly code and still able to do all the required functionality in software. In some cases, there will be a need to do assembly code. In these cases, the handler can be defined as naked function (Which will not generate any stack push/pop and return codes) and implement them in assembly.
For example, in FreeRTOS, the SVC Handler implementation is as below:
Thus, with compiler extensions, it is possible to include assembly based interrupt handling as well.
Switching Vector tables
In the above examples, we have noted that the vector table is located at address 0. But there are cases where we will need to place it at a different location. For example, the flash location could be at address 0, and the RAM, where the user wants to place the vector table could be elsewhere. Or it could be dual A/B firmware or a bootloader/application firmware each sitting at a different location. So, based on the current code being executed, the vector table location differs.
ARM provides a simple mechanism to switch the base address of the vector table. It provides a Vector table offset register at address 0xE000ED08 in the NVIC group, where the base address can be programmed. For example, to switch the vector location to address 0x20000000, following lines will suffice.
((volatile uint32_t )0xE000ED08) = 0x20000000;
Conclusion
Thus, ARM Cortex M architecture provides a very convenient way of handling interrupts needing no knowledge of ARM Assembly language. Furthermore, it offers a way to relocate the vector table just by changing only one register. In the upcoming blogs, we will continue to explore the interrupt handling in different embedded system architectures.
Reference
756k+ Instagram Views | 1m+ Impressions | Social Media Manager | I help busy founders create their brands that 10x their company growth
8 个月Great post! Understanding interrupt handling is crucial for efficient embedded programming. Your explanation of the Cortex M Vector Table is clear and concise, and the discussion of setting it up and switching tables is valuable information for ARM Cortex M developers.