The Cortex-M4 processor from ARM includes an advanced interrupt controller called the Nested Vectored Interrupt Controller (NVIC). The NVIC allows assigning priorities to interrupts, which determines the order in which pending interrupts will be handled by the processor. Proper configuration of NVIC priorities is crucial for designing real-time embedded systems using Cortex-M4.
What is NVIC?
The NVIC is the interrupt controller present in Cortex-M series processors from ARM. It handles all the interrupts generated within the processor as well as external interrupts from peripherals. The key features of NVIC include:
- Supports up to 240 external interrupt sources
- 8 priority levels for each interrupt
- Priority grouping into preemption priority and subpriority
- Nested interrupt handling
The NVIC receives interrupt requests from various sources, determines the priority of the requests, and signals the processor to handle the highest priority pending interrupt. It maintains the state of interrupt requests and in-service flags for each interrupt.
NVIC Priority Levels
The Cortex-M4 NVIC allows assigning a priority from 0 to 255 to each interrupt source. Lower value means higher priority. So priority 0 is the highest priority and 255 is the lowest priority.
These 256 priority levels are further grouped into preemption priority and subpriority bits. This grouping is configured through the Application Interrupt and Reset Control Register (AIRCR). For example, a grouping of 2 preemption bits and 2 subpriority bits provides the following priority levels:
- Preemption Priority 0, Subpriority 0: Highest priority
- Preemption Priority 0, Subpriority 1
- Preemption Priority 0, Subpriority 2
- Preemption Priority 0, Subpriority 3
- Preemption Priority 1, Subpriority 0
- Preemption Priority 1, Subpriority 1
- Preemption Priority 1, Subpriority 2
- Preemption Priority 1, Subpriority 3
- Preemption Priority 2, Subpriority 0
- Preemption Priority 2, Subpriority 1
- Preemption Priority 2, Subpriority 2
- Preemption Priority 2, Subpriority 3
- Preemption Priority 3, Subpriority 0: Lowest priority
The preemption priority determines if a higher priority interrupt can preempt a lower priority interrupt. The subpriority is used to prioritize interrupts with same preemption priority.
NVIC Priority Configuration
The NVIC priorities need to be configured properly during firmware development for Cortex-M4 based microcontrollers. There are several aspects to priority configuration:
Grouping of Priority Bits
The first step is choosing the split between preemption priority and subpriority bits in the AIRCR register. Example groupings are:
- 8 preemption priority, 0 subpriority
- 4 preemption priority, 4 subpriority
- 2 preemption priority, 6 subpriority
- 0 preemption priority, 8 subpriority
Higher number of preemption priority bits allows more interrupts to preempt each other. Subpriority bits help create priority hierarchy for same preemption level interrupts.
The next step is assigning numeric priority values to interrupts. Higher priority should be given to time critical interrupts like timers, ADC sampling complete, etc. Lower priority can be set for interrupts that are less time sensitive like UART, I2C, etc.
Subpriority levels can be used to create further order between interrupts. For example, ADCs sampling multiple analog channels can have different subpriorities if samples need to be read out in certain order.
Some Cortex-M4 microcontrollers allow “priority boosting” for critical interrupts. For example, SysTick timer can have its priority boosted to 0 temporarily. This ensures the SysTick ISR is not blocked by any other interrupt when executing.
Another configuration option is the base priority levels in the PRIGROUP field of AIRCR register. This determines the starting preemption priority for all interrupts. For example, if base priority is 4, then all interrupts will have preemption priority between 4 to 7.
Using NVIC Priorities
Properly configured NVIC priorities are crucial for real-time applications using Cortex-M4 MCUs. Here are some guidelines for using priorities:
- Time critical interrupts should have highest priorities
- Priorities should be assigned according to requirements of interrupt response time
- Lower priority handlers should have lower preemption levels
- Use priority boosting for ultra-critical interrupts if supported
- Subpriorities allow creating hierarchy within same preemption level
- Tune priorities during development to meet response time needs
Inappropriate priority configurations can lead to unexpected blocking of high priority interrupts or create priority inversion conditions. So NVIC priorities should be carefully validated along with the rest of the firmware.
NVIC Priority Related Issues
Some common issues related to NVIC priority configurations are:
Priority inversion happens when a high priority interrupt is blocked by a lower priority handler due to improper priority grouping or assignment. This can make the application miss deadlines for time critical events.
If a lower priority interrupt is not getting CPU time, it indicates the priorities are not configured properly. For example, if a UART interrupt is starved for CPU, it can drop received bytes.
In some cases, improper NVIC priorities combined with incorrect application firmware can lead to deadlock conditions where interrupts are perpetually blocked from execution. The system hangs in such scenarios.
When higher priority interrupts corrupt memory being used by lower priority ones due to incomplete execution, it indicates improper subpriority levels assigned.
Tools for Analyzing NVIC Behavior
To analyze NVIC priority issues, the following tools and techniques can be used during Cortex-M4 firmware development:
- OS-Aware Debuggers – Allow debugging RTOS applications and visualize thread priorities and preemptions.
- Logic Analyzers – Capture time-stamped interrupt execution traces.
- Runtime Priority Viewers – Get snapshot of active priorities within firmware using APIs.
- Stack Overflow Checking – Detect stack corruption to analyze memory conflicts.
- Profilers – Measure exact interrupt latencies and execution times.
These tools help validate that NVIC priorities are working as intended in the firmware application.
Modifying NVIC Priorities
NVIC priorities are typically configured statically during firmware initialization. But the Cortex-M4 also allows modifying priorities dynamically at runtime.
The NVIC_SetPriority() function can be used to change the priority of any interrupt at runtime. This takes the interrupt number and priority value as arguments.
Dynamically changing priorities can be useful in certain cases like –
- Moving peripheral interrupts between core and communication peripherals.
- Boosting priority of timer interrupts temporarily for low power mode entry.
- Reducing UART priority to prevent blocking during firmware upgrade.
So NVIC priorities can be tuned both statically during initialization and dynamically at runtime if needed.
In summary, the Cortex-M4 NVIC enables assigning priorities to interrupts in a very flexible manner using preemption priority and subpriority bits. Proper priority configuration, analysis, and tuning is essential for developing robust real-time embedded applications. Using the various priority fields correctly ensures time critical ISRs get CPU cycles when needed.
NVIC priorities require careful validation to avoid issues like priority inversion, unresponsive interrupts, deadlocks, etc. Tools like debuggers, analyzers, and profilers help analyze NVIC behavior. Both static and dynamic priority tuning can be done to optimize the priority scheme.
Understanding Cortex-M4 NVIC priorities will help embedded developers build responsive real-time systems using this popular ARM processor.