What is Interrupt Priority & Preemption-Embedded Systems Training Institute in Hyderabad

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Example 10 — Source Code Deep-Dive

Interrupt Priority & Preemption

NVIC IPR registers, priority grouping via AIRCR, the preemption chain where TIM2 ISR pends I2C1, and how lower priority-value = higher urgency.

1. Overview

The 010_interrupt_priority project demonstrates interrupt priority and preemption:

  1. Configure TIM2 (IRQ28) with priority 0x80 and I2C1 (IRQ31) with priority 0x70
  2. Pend TIM2 (lower priority = less urgent), enable both IRQs
  3. TIM2 ISR fires, then manually pends I2C1
  4. I2C1 (higher priority = 0x70 < 0x80) preempts TIM2 ISR
  5. I2C1 ISR runs, returns, TIM2 ISR resumes (stuck in while(1) in this example)

2. Source Code — Annotated

#define IRQNO_TIMER2  28   /* TIM2 global interrupt = IRQ28 */
#define IRQNO_I2C1    31   /* I2C1 event interrupt  = IRQ31 */
#include <stdint.h>
#include <stdio.h>

/* NVIC base addresses */
uint32_t *pNVIC_IPRBase  = (uint32_t*)0xE000E400;  /* Priority registers */
uint32_t *pNVIC_ISERBase = (uint32_t*)0xE000E100;  /* Enable registers */
uint32_t *pNVIC_ISPRBase = (uint32_t*)0xE000E200;  /* Pending registers */

/* Set priority for a given IRQ number.
 * priority_value: 8-bit value, but STM32F407 only uses top 4 bits (bits[7:4]).
 * So effective priority levels are 0x00, 0x10, 0x20...0xF0 (16 levels).
 */
void configure_priority_for_irqs(uint8_t irq_no, uint8_t priority_value)
{
    /* 1. Calculate which IPRx register holds this IRQ's priority.
     * Each IPR register holds 4 IRQ priorities (8-bit each).
     * iprx = which IPR register (0–59) */
    uint8_t iprx = irq_no / 4;           /* IPR index */
    uint32_t *ipr = pNVIC_IPRBase + iprx; /* pointer to the IPR register */

    /* 2. Calculate bit position within that register.
     * Each IRQ gets 8 bits: irq%4 * 8 gives the bit offset */
    uint8_t pos = (irq_no % 4) * 8;

    /* 3. Write the priority value to the 8 bits for this IRQ */
    *ipr &= ~(0xFF << pos);         /* clear existing priority */
    *ipr |=  (priority_value << pos); /* set new priority */
}

int main(void)
{
    /* Set TIM2 priority = 0x80 (lower urgency since higher value) */
    configure_priority_for_irqs(IRQNO_TIMER2, 0x80);

    /* Set I2C1 priority = 0x70 (higher urgency since lower value) */
    configure_priority_for_irqs(IRQNO_I2C1, 0x70);

    /* Pend TIM2 — it will fire when enabled */
    *pNVIC_ISPRBase |= (1 << IRQNO_TIMER2);

    /* Enable I2C1 first (not pending yet — nothing happens) */
    *pNVIC_ISERBase |= (1 << IRQNO_I2C1);

    /* Enable TIM2 — already pending → fires immediately */
    *pNVIC_ISERBase |= (1 << IRQNO_TIMER2);
}

void TIM2_IRQHandler(void)
{
    printf("[TIM2_IRQHandler]\n");

    /* Manually pend I2C1 interrupt from within TIM2 ISR.
     * Since I2C1 priority (0x70) < TIM2 priority (0x80)
     * [lower value = higher urgency], I2C1 preempts TIM2 immediately. */
    *pNVIC_ISPRBase |= (1 << IRQNO_I2C1);

    while(1);  /* TIM2 ISR blocks here after I2C1 returns */
}

void I2C1_EV_IRQHandler(void)
{
    /* This preempts TIM2_IRQHandler because 0x70 < 0x80 */
    printf("[I2C1_EV_IRQHandler]\n");
}

3. IPR Registers — Deep Dive

The NVIC has 60 IPR (Interrupt Priority Register) registers (IPR0–IPR59), each 32 bits wide. Each register holds priority values for 4 consecutive IRQs, one 8-bit field per IRQ.

/* IPR Register Layout (one 32-bit register per 4 IRQs): */
/*
 * IPRx [31:24] = Priority of IRQ (x*4)+3
 * IPRx [23:16] = Priority of IRQ (x*4)+2
 * IPRx [15: 8] = Priority of IRQ (x*4)+1
 * IPRx [ 7: 0] = Priority of IRQ (x*4)+0
 *
 * Example: IRQ28 (TIM2) and IRQ31 (I2C1):
 *   IRQ28 → IPR7 [7:0]    (28/4=7, 28%4=0 → bits [7:0])
 *   IRQ29 → IPR7 [15:8]
 *   IRQ30 → IPR7 [23:16]
 *   IRQ31 → IPR7 [31:24]  (31/4=7, 31%4=3 → bits [31:24])
 *
 * STM32F407 only implements the TOP 4 bits of each 8-bit field.
 * Bits [3:0] of each field always read as 0.
 * This gives 16 configurable priority levels: 0x00, 0x10, 0x20...0xF0
 */

/* Example: Read priority of TIM2 (IRQ28) */
uint32_t *ipr7 = (uint32_t*)0xE000E400 + 7;  /* IPR7 */
uint8_t tim2_priority = (*ipr7 >> 0) & 0xFF;  /* bits [7:0] */
/* On STM32F407, only bits [7:4] are meaningful */

STM32F407 Priority Levels

STM32F407 implements 4 priority bits (out of 8 available in the ARM spec). Only bits [7:4] of each IPR byte are used; bits [3:0] always read 0. This gives 16 priority levels: 0 (highest urgency) through 15 (lowest urgency), stored in the nibble at bits [7:4].

Priority values: 0x00=highest, 0x10, 0x20, 0x30…0xF0=lowest
Priority Value (full byte)Effective Priority (top nibble)Urgency
0x000Highest — preempts everything
0x101Very high
0x707Medium (I2C1 in this example)
0x808Medium-low (TIM2 in this example)
0xF015Lowest

4. Priority Grouping — AIRCR

ARM Cortex-M4 supports dividing the 4 priority bits into preemption priority and sub-priority using the PRIGROUP field in AIRCR (Application Interrupt and Reset Control Register).

PRIGROUPPreempt Priority BitsSub-Priority BitsPreempt LevelsSub Levels
0 (default)4 bits [7:4]0 bits161
43 bits [7:5]1 bit [4]82
52 bits [7:6]2 bits [5:4]44
61 bit [7]3 bits [6:4]28
70 bits4 bits [7:4]116
Preemption vs Sub-priority: Only preemption priority causes interrupt nesting (higher-preemption-priority ISR interrupts lower). Sub-priority is used as a tie-breaker when two interrupts with the same preemption priority become pending simultaneously — the one with lower sub-priority number is taken first. Sub-priority does NOT cause preemption.
/* Setting PRIGROUP in AIRCR */
/* AIRCR = Application Interrupt and Reset Control Register */
#define AIRCR  (*(volatile uint32_t*)0xE000ED0C)

/* AIRCR write key: must write 0x05FA to bits [31:16] */
/* PRIGROUP is in bits [10:8] */

void NVIC_SetPriorityGrouping(uint8_t prigroup)
{
    uint32_t reg = AIRCR;
    reg &= ~(0xFFFF0000 | (0x7 << 8)); /* clear write key and PRIGROUP */
    reg |=  (0x05FA0000 | ((prigroup & 7) << 8));
    AIRCR = reg;
}

/* Example: Use 3 preemption bits, 1 sub-priority bit */
NVIC_SetPriorityGrouping(4);

5. Preemption — What Happens Step by Step

/* Timeline of the example:
 *
 * t=0: main() runs in Thread Mode
 *      TIM2 priority = 0x80, I2C1 priority = 0x70
 *
 * t=1: TIM2 pending bit set (ISPR bit28)
 *
 * t=2: I2C1 enabled (ISER bit31) — not pending, nothing happens
 *
 * t=3: TIM2 enabled (ISER bit28) → immediately taken (was pending)
 *      Hardware saves {R0-R3,R12,LR,PC,xPSR} to MSP (Thread mode was on MSP)
 *      IPSR = 28+16 = 44 (TIM2 exception number)
 *      TIM2_IRQHandler begins executing
 *
 * t=4: TIM2_IRQHandler pends I2C1 (ISPR bit31)
 *      NVIC checks: I2C1 priority (0x70) < TIM2 priority (0x80)
 *      → I2C1 PREEMPTS TIM2 immediately!
 *      Hardware saves a NEW stack frame to MSP on top of TIM2's frame
 *      IPSR = 31+16 = 47 (I2C1 exception number)
 *
 * t=5: I2C1_EV_IRQHandler runs
 *      Prints "[I2C1_EV_IRQHandler]"
 *
 * t=6: I2C1_EV_IRQHandler returns (BX LR with EXC_RETURN 0xFFFFFFF1)
 *      Hardware pops I2C1 stack frame from MSP
 *      IPSR = 44 (back to TIM2)
 *      TIM2_IRQHandler resumes at the while(1) line
 *
 * t=7: TIM2_IRQHandler loops forever in while(1) */

6. Priority Configuration Calculator

IPR Register Calculator

28
0x80
IRQ28 priority configuration: IPR register index: IPR7 IPR register address: 0xE000E41C Bit position in register: [7:0] Priority value written: 0x80 Effective priority (STM32F407 top 4 bits): 8 Code: *(volatile uint32_t*)0xE000E41C = (0x80 << 0);

7. Interview Questions & Answers

Q1. How are interrupt priorities configured in ARM Cortex-M4 NVIC?
Priorities are configured using the IPR (Interrupt Priority Registers) at base address 0xE000E400. Each IRQ has an 8-bit priority field within a 32-bit IPR register (4 IRQs per register). Lower numerical value = higher urgency (higher priority).

STM32F407 implements only 4 priority bits (top nibble: bits [7:4]). Effective values are 0x00 (highest), 0x10, 0x20…0xF0 (lowest) — 16 levels.

Formula: IPR_register_index = irq / 4, bit_position = (irq % 4) × 8.
Write: IPR[irq/4] = (priority_value << ((irq%4)*8)).
Q2. Why does lower priority VALUE mean higher urgency?
ARM’s priority model is “lower number = higher priority.” This is designed so that priority 0 is the most urgent (the “boss”), and higher numbers are progressively less urgent. The analogy: a Category 5 hurricane (priority 0) is more urgent than a light rain (priority 15).

Preemption occurs when a pending interrupt has a numerically smaller priority value than the currently executing interrupt. In the example: I2C1 at 0x70 preempts TIM2 at 0x80 because 0x70 < 0x80.

This convention is consistent across ARM Cortex-M3/M4/M7, FreeRTOS configuration, and most ARM-based RTOS systems. In FreeRTOS specifically, configMAX_SYSCALL_INTERRUPT_PRIORITY sets the boundary — higher priority (lower value) ISRs cannot use FreeRTOS API.
Q3. What is priority grouping and how does it affect interrupt behaviour?
Priority grouping (PRIGROUP in AIRCR, address 0xE000ED0C) divides each IRQ’s priority bits into preemption priority and sub-priority:

• Preemption priority: Determines if this interrupt can preempt a running ISR. An interrupt with higher preemption priority (lower value) WILL preempt a lower preemption priority ISR.
• Sub-priority: When two interrupts with the same preemption priority are pending simultaneously, the one with lower sub-priority value is taken first. Sub-priority does NOT cause preemption.

Default (PRIGROUP=0): All 4 bits are preemption priority, no sub-priority. Most embedded systems leave PRIGROUP=0 for simplicity.
Q4. What are the system exception priorities (HardFault, SysTick etc.)?
System exceptions have fixed or configurable priorities:

Fixed priority (not configurable):
• Reset: -3 (highest possible)
• NMI: -2
• HardFault: -1 (always takes exceptions escalated from lower priority faults)

Configurable (use SCB registers, not NVIC IPR):
• MemManage: SCB_SHPR1[7:0]
• BusFault: SCB_SHPR1[15:8]
• UsageFault: SCB_SHPR1[23:16]
• SVCall: SCB_SHPR2[31:24]
• PendSV: SCB_SHPR3[23:16] — typically set to lowest priority (0xF0) in RTOS
• SysTick: SCB_SHPR3[31:24] — typically low priority in RTOS

Base addresses: SCB_SHPR1=0xE000ED18, SCB_SHPR2=0xE000ED1C, SCB_SHPR3=0xE000ED20
Q5. What is priority inversion and how does it relate to interrupt priorities?
Priority inversion occurs when a high-priority task/interrupt is blocked waiting for a resource held by a low-priority entity. In interrupt systems:

Example: High-priority ISR A needs a shared resource (e.g., a buffer). Low-priority ISR B holds that resource. A medium-priority ISR C preempts B (since C > B). Now A waits for B, but B can’t run because C is running. A (highest) is stuck behind C (medium), inverting their intended priorities.

Solutions include:
• Priority inheritance: temporarily boost B’s priority to A’s level while B holds the resource
• Priority ceiling: assign a “ceiling priority” to the resource equal to the highest-priority user
• Avoiding shared resources between ISRs of different priorities

In embedded systems, this is most relevant in RTOS task scheduling, but the concept applies to any preemptive interrupt system.
Q6. In this example, TIM2 priority=0x80 and I2C1 priority=0x70. On STM32F407 which only uses top 4 bits, what are the effective priorities?
STM32F407 uses 4 priority bits stored in bits [7:4] of the IPR byte:

Priority 0x80 = 0b10000000 → top 4 bits = 0b1000 = decimal 8 → effective priority = 8
Priority 0x70 = 0b01110000 → top 4 bits = 0b0111 = decimal 7 → effective priority = 7

Since 7 < 8 (lower number = higher urgency), I2C1 (priority 7) CAN preempt TIM2 (priority 8). The difference is just 1 priority level on the effective 16-level scale.

If both had priority 0x80 (effective 8), no preemption would occur — same preemption priority level. If they had the same preemption priority and both pended simultaneously, sub-priority would decide order (but in default PRIGROUP=0 there are no sub-priority bits, so IRQ number order decides).

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *