What is SCB NVIC Interrupt Configuration-Embedded Systems Course for Freshers in Hyderabad

SCB NVIC Interrupt Configuration

Master the ARM Cortex-M System Control Block (SCB) — its ICSR, SHCSR, and related registers — learn the default state of every system exception, understand the full Cortex-M4 internal block diagram, and walk through the 6-step process to configure any MCU peripheral interrupt from scratch.

Introduction

EmbeddedPathashala — ARM Cortex-M Course Home All Lectures STM32 Projects

System Control Block (SCB): What It Does

The System Control Block (SCB) is a group of registers inside the Cortex-M System Control Space (SCS, base address 0xE000E000). It is the primary firmware interface for system-level processor control — everything from enabling configurable fault handlers to triggering a software reset. The SCB is defined by ARM and is identical across all Cortex-M3 and Cortex-M4 implementations.

SCB provides: system implementation information (CPUID), control of system exceptions, fault status reporting, sleep configuration, interrupt control state, and the vector table offset register. The ARM Cortex-M4 Generic User Guide (document ID DDI0439) is the canonical reference for every SCB register bit-field.

What the SCB registers collectively let you do

  • Enable fault handlers — activate UsageFault, BusFault, and MemManage exceptions (disabled by default; all escalate to HardFault until enabled)
  • Get pending status of fault exceptions — determine which configurable fault is waiting to be handled even if it is currently masked
  • Trap divide-by-zero and unaligned access — enable automatic UsageFault on integer division by zero (CCR.DIV_0_TRP) or any unaligned access (CCR.UNALIGN_TRP)
  • Control sleep and wake-up settings — configure SLEEPDEEP, SLEEPONEXIT, SEVONPEND for power management
  • Configure priority of system exceptions — assign preemption priorities to SVC, PendSV, SysTick, MemManage, BusFault, UsageFault
  • SysTick timer control and status — start/stop the 24-bit countdown, select clock source, enable overflow interrupt

Default System Exception States

After reset, not all system exceptions are active. The following table shows the out-of-reset state of every Cortex-M system exception. Understanding these defaults prevents common bugs where firmware assumes an exception is active when it isn’t, or disables one that is already running.

ExceptionDefault StateEnable mechanismNotes
Hard FaultAlways enabled — can be masked only by FAULTMASK or debuggerCannot be disabled by software (priority –1 fixed)Catch-all for escalated or unhandled faults; fires if configurable faults are disabled
NMIAlways enabled — cannot be masked by any software mechanismHardware signal only (NMIPENDSET in ICSR for software NMI)Priority –2 fixed; only Reset can preempt it
Usage FaultDisabled by defaultSet SHCSR.USGFAULTENA (bit 18)Undefined instruction, invalid state, divide by zero (if CCR.DIV_0_TRP set)
MemManage FaultDisabled by defaultSet SHCSR.MEMFAULTENA (bit 16)MPU access violation; also fires on XN-region execute attempt
Bus FaultDisabled by defaultSet SHCSR.BUSFAULTENA (bit 17)Precise/imprecise memory bus errors; bad peripheral access address
SysTickDisabled by defaultSet SYST_CSR.TICKINT and SYST_CSR.ENABLEFires on SysTick overflow; frequency set by SYST_RVR reload value
SVC (SVCall)Triggers only when the SVC instruction executesNo separate enable — SVC instruction always invokablePriority programmable; RTOS system-call gateway
PendSVDisabled by default (not pending)Write 1 to ICSR.PENDSVSET (bit 28) to pend itUsed by RTOS context switcher; set to lowest priority to allow preemption
Debug MonitorDisabled by defaultDebugger sets DEMCR.MON_ENUsed for software debug when not in halt-mode; rarely used in firmware

Critical default: UsageFault, BusFault, and MemManage Fault are all disabled by default. Any fault that would normally trigger one of these escalates directly to HardFault instead. In production firmware, always enable all three configurable faults explicitly so your fault handlers can diagnose problems precisely rather than receiving a generic HardFault with no information about the root cause.

/* Enable all three configurable faults at startup */
void enable_configurable_faults(void)
{
    SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk   /* bit 18: UsageFault  */
               |  SCB_SHCSR_BUSFAULTENA_Msk    /* bit 17: BusFault    */
               |  SCB_SHCSR_MEMFAULTENA_Msk;   /* bit 16: MemManage   */
}

/* Enable divide-by-zero and unaligned trapping */
void enable_usage_traps(void)
{
    SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk    /* bit 4: trap div/0 as UsageFault */
             |  SCB_CCR_UNALIGN_TRP_Msk; /* bit 3: trap unaligned access     */
}

Cortex-M4 Processor Internal Block Diagram

Understanding the internal structure of the Cortex-M4 processor shows how the NVIC, SCB, bus interfaces, and debug components connect. Every block communicates through the Bus Matrix — the central crossbar that allows multiple masters to simultaneously access different slaves.

Cortex-M4 Processor Structure
NVIC
Nested Vectored
Interrupt Controller
Cortex-M4 Core
ARMv7-M ISA
+ FPU (single-prec)
ETM
Embedded Trace
Macrocell
FPB
Flash Patch &
Breakpoint
MPU
Memory Protection
Unit — 8 regions
DWT
Data Watchpoint
& Trace
WIC
Wake-up Interrupt
Controller
BUS MATRIX
Central crossbar — allows CPU + DMA to simultaneously access Flash, SRAM, and APB peripherals
ITM
Instrumentation
Trace Macrocell
SW-DP / SWJ-DP
SWD / JTAG
Debug Port
AHB-AP
AHB Access
Port
TPIU
Trace Port
Interface Unit
CoreSight ROM
Debug component
discovery table
I-Code
AHB-Lite instruction
D-Code
AHB-Lite data
System
AHB-Lite system
PPB APB
Debug system bus
The Bus Matrix arbitrates between the CPU (via I-Code, D-Code, System buses) and debug (via AHB-AP). All internal peripherals (NVIC, SCB, SysTick, MPU, FPB, DWT, ITM) sit on the PPB APB debug bus.

ICSR — Interrupt Control and State Register

The Interrupt Control and State Register (ICSR) at address 0xE000ED04 is the primary software interface for querying and controlling the exception state machine. It exposes which exception is currently active, which is pending, and provides the software write-bits to trigger PendSV and NMI via firmware.

ICSR Bit-Field Map (0xE000ED04)
Bit: 31 28 27 26 25 23 12 8 0
NMIPENDSET
[31]
res
[30]
DBG
[29]
PENDSVSET
[28]
PENDSVCLR
[27]
PENDSTSET
[26]
PENDSTCLR
[25]
res
[24]
ISRPENDING
[22]
RETTOBASE
[11]
VECTPENDING[12:3]
res
[9:8]
VECTACTIVE[8:0]
Bit(s)NameR/WMeaning
31NMIPENDSETR/WWrite 1 to pend an NMI via software. Read 1 = NMI is pending. Hardware-edge triggered NMI also sets this bit.
28PENDSVSETR/WWrite 1 to pend the PendSV exception. Read 1 = PendSV is pending. This is the only software mechanism to set PendSV. Used by RTOS context switchers.
27PENDSVCLRWWrite 1 to clear a pending PendSV. Write-only — always reads 0.
26PENDSTSETR/WWrite 1 to pend SysTick exception. Read 1 = SysTick pending.
25PENDSTCLRWWrite 1 to clear pending SysTick. Write-only.
22ISRPENDINGRRead 1 = at least one external IRQ is pending (excluding NMI and faults). Useful in critical section exit code.
21RETTOBASERRead 1 = when current exception returns, it will return to Thread mode (no preempted exceptions remain). Read 0 = another exception is still active.
20:12VECTPENDINGRException number of the highest-priority pending exception. 0 = nothing pending.
8:0VECTACTIVERException number of the currently executing exception. 0 = Thread mode (no active exception). Same as IPSR bits 8:0.

Triggering PendSV from an ISR (RTOS context switch)

/* In FreeRTOS, the SysTick handler triggers a PendSV to perform context switching.
   PendSV is set to the lowest priority so it runs after all other ISRs complete. */

void SysTick_Handler(void)
{
    /* Increment tick count */
    xTaskIncrementTick();

    /* Request context switch by pending PendSV.
       PendSV will fire after this ISR exits (tail-chaining or after all higher-prio ISRs). */
    SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;   /* write 1 to bit 28 */

    /* Direct register access equivalent: */
    /* *((volatile uint32_t *)0xE000ED04) |= (1U << 28); */
}

/* PendSV_Handler performs the actual context switch */
__attribute__((naked)) void PendSV_Handler(void)
{
    __asm volatile (
        "MRS   R0, PSP          \n\t"   /* get current task's PSP */
        "STMDB R0!, {R4-R11}    \n\t"  /* save callee-saved regs */
        /* ... save R0 to current TCB, load next TCB, restore regs ... */
        "MSR   PSP, R0          \n\t"   /* restore next task's PSP */
        "BX    LR               \n\t"   /* exception return */
    );
}

Reading which exception is active

/* Read VECTACTIVE to determine current exception context */
uint32_t current_exception(void)
{
    return SCB->ICSR & 0x1FFU;   /* bits 8:0 = VECTACTIVE */
}

bool in_thread_mode(void)
{
    return current_exception() == 0;
}

bool in_handler_mode(void)
{
    return current_exception() != 0;
}

/* Example: find the highest-priority pending exception number */
uint32_t pending_exception(void)
{
    return (SCB->ICSR >> 12) & 0x1FFU;  /* VECTPENDING bits 20:12 */
}

SHCSR — System Handler Control and State Register

The System Handler Control and State Register (SHCSR) at 0xE000ED24 serves a dual role: it enables the three configurable fault exceptions (MemManage, BusFault, UsageFault) and exposes real-time active/pending status for all system exception handlers.

SHCSR Bit-Field Map (0xE000ED24)
Bit 18
USGFAULTENA — R/W. Write 1 to enable the UsageFault exception. When 0, all usage faults escalate to HardFault.
Bit 17
BUSFAULTENA — R/W. Write 1 to enable BusFault. When 0, all bus faults escalate to HardFault.
Bit 16
MEMFAULTENA — R/W. Write 1 to enable MemManage fault. Required when using the MPU.
Bit 15
SVCALLPENDED — R/W. Reads 1 if SVCall is pending. Write 1 to force-pend SVC (unusual; normally SVC fires via instruction).
Bit 14
BUSFAULTPENDED — R/W. Read 1 = BusFault is pending. Write 1 to force-pend it.
Bit 13
MEMFAULTPENDED — R/W. Read 1 = MemManage fault is pending.
Bit 12
USGFAULTPENDED — R/W. Read 1 = UsageFault is pending.
Bit 11
SYSTICKACT — R. Read 1 = SysTick handler is currently active (executing).
Bit 10
PENDSVACT — R. Read 1 = PendSV handler is currently active.
Bit 8
MONITORACT — R. Read 1 = Debug Monitor handler is active.
Bit 7
SVCALLACT — R. Read 1 = SVCall handler is active.
Bit 3
USGFAULTACT — R. Read 1 = UsageFault handler is active.
Bit 1
BUSFAULTACT — R. Read 1 = BusFault handler is active.
Bit 0
MEMFAULTACT — R. Read 1 = MemManage handler is active.

Using SHCSR in a fault diagnostic handler

/* Read SHCSR at the start of HardFault_Handler to understand the escalation source */
void HardFault_Handler(void)
{
    uint32_t shcsr = SCB->SHCSR;
    uint32_t hfsr  = SCB->HFSR;
    uint32_t cfsr  = SCB->CFSR;   /* combined MMFSR + BFSR + UFSR */

    if (hfsr & SCB_HFSR_FORCED_Msk) {
        /* Configurable fault escalated to HardFault */
        if (cfsr & 0xFFFF0000U) {
            /* Usage fault bits in CFSR[31:16] */
            if (cfsr & SCB_CFSR_DIVBYZERO_Msk) { /* handle div-by-zero */ }
            if (cfsr & SCB_CFSR_UNDEFINSTR_Msk) { /* undefined instruction */ }
        }
        if (cfsr & 0x0000FF00U) {
            /* Bus fault bits in CFSR[15:8] */
            if (cfsr & SCB_CFSR_BFARVALID_Msk) {
                volatile uint32_t bfar = SCB->BFAR;  /* address of faulting access */
                (void)bfar;
            }
        }
        if (cfsr & 0x000000FFU) {
            /* MemManage bits in CFSR[7:0] */
            if (cfsr & SCB_CFSR_MMARVALID_Msk) {
                volatile uint32_t mmfar = SCB->MMFAR;  /* violation address */
                (void)mmfar;
            }
        }
    }

    if (hfsr & SCB_HFSR_VECTTBL_Msk) {
        /* Vector table read error during exception entry */
    }

    while (1);   /* spin — or trigger a system reset */
}

Exercise: Enabling and Pending USART3 Interrupt

This exercise walks through the complete path from a peripheral event to ISR execution, using USART3 on STM32F411 as the example. USART3 is connected to the NVIC on IRQ line 39.

USART3 → NVIC IRQ39 → CPU Flow
USART3
Peripheral
(in Microcontroller)
→
NVIC
IRQ39 slot
(in Processor)
ISER[1] bit 7
↔
CPU Core
Executes
USART3_IRQHandler
USART3 generates a signal on IRQ line 39 whenever the configured event occurs (RXNE, TXE, etc.). The NVIC accepts it, checks priority, and signals the CPU to run the handler.

Finding the IRQ number for USART3

IRQ numbers are vendor-specific — defined by the MCU manufacturer, not by ARM. You find them in the device header file or the reference manual’s vector table section. For STM32F4 series:

/* From stm32f411xe.h (CMSIS device header) */
typedef enum {
    /* Cortex-M system exceptions (negative numbers) */
    NonMaskableInt_IRQn     = -14,
    HardFault_IRQn          = -13,
    MemoryManagement_IRQn   = -12,
    BusFault_IRQn           = -11,
    UsageFault_IRQn         = -10,
    SVCall_IRQn             = -5,
    DebugMonitor_IRQn       = -4,
    PendSV_IRQn             = -2,
    SysTick_IRQn            = -1,

    /* STM32F411 peripheral IRQs */
    WWDG_IRQn               = 0,
    /* ... */
    USART1_IRQn             = 37,
    USART2_IRQn             = 38,
    USART3_IRQn             = 39,   /* ← USART3 is IRQ 39 */
    /* ... */
} IRQn_Type;

Calculating the NVIC register bit for IRQ 39

NVIC ISER/ICER/ISPR/ICPR registers are 32-bit wide. Each register covers 32 IRQs: ISER[0] covers IRQ0–31, ISER[1] covers IRQ32–63, etc.

/* IRQ 39 → register index = 39 / 32 = 1 → ISER[1]
              bit position  = 39 % 32 = 7 → bit 7 of ISER[1] */

/* Direct register access (without CMSIS): */
#define NVIC_ISER1   (*(volatile uint32_t *)0xE000E104U)
#define NVIC_ISPR1   (*(volatile uint32_t *)0xE000E204U)
#define NVIC_IPR9    (*(volatile uint32_t *)0xE000E424U)  /* IPR byte for IRQ39 */

/* Enable IRQ39 */
NVIC_ISER1 = (1U << 7);        /* bit 7 of ISER[1] */

/* Set priority = 5 (using top 4 bits → value = 5 << 4 = 0x50) */
/* IPR registers: 4 IRQs per 32-bit word; IRQ39 is byte 3 of IPR9 */
NVIC_IPR9 = (NVIC_IPR9 & ~(0xFFU << 24)) | (0x50U << 24);

/* Software-pend IRQ39 (simulate USART3 interrupt firing) */
NVIC_ISPR1 = (1U << 7);

/* Using CMSIS (preferred): */
NVIC_SetPriority(USART3_IRQn, 5);
NVIC_EnableIRQ(USART3_IRQn);

6 Steps to Configure Any MCU Peripheral Interrupt

This process applies to every peripheral interrupt on every Cortex-M based MCU. The steps always follow this order — changing the order (especially enabling the peripheral before the NVIC) can cause interrupts to fire before the handler is ready.

1

Identify the IRQ number (vendor-specific)

Look up the peripheral’s IRQ number in the MCU’s device header file or the vector table section of the reference manual. This is not standardised by ARM — USART3 is IRQ39 on STM32F4, but a completely different number on NXP or TI parts. Never hardcode IRQ numbers; always use the enumeration from the CMSIS device header.

2

Enable the IRQ in the NVIC and set priority

Write to NVIC_ISER to enable the IRQ line. Optionally set the priority in NVIC_IPR. If no priority is set, the default is 0 (highest). Always set priorities before enabling — setting priority after enabling creates a brief window where the IRQ can fire at the wrong priority.

NVIC_SetPriority(USART3_IRQn, 5);   /* set first */
NVIC_EnableIRQ(USART3_IRQn);         /* then enable */

3

Configure the peripheral to generate the interrupt

Set the interrupt-enable bit(s) in the peripheral’s own control register. For USART3: set USART_CR1.RXNEIE (receive buffer not empty interrupt enable) to trigger an interrupt whenever a byte is received. Until this step, the NVIC line is armed but the peripheral never fires it.

/* Enable USART3 receive interrupt */
USART3->CR1 |= USART_CR1_RXNEIE;   /* bit 5 in CR1 */
USART3->CR1 |= USART_CR1_UE;       /* enable USART3 */

4

Interrupt fires → NVIC pending register is set first

When USART3 issues a signal on IRQ39, the NVIC immediately sets the pending bit in NVIC_ISPR[1] bit 7. The interrupt does not immediately run the handler — it enters the pending state. This pending state persists even if the IRQ is currently disabled in the NVIC (step 2 not done yet), meaning events are not lost while the IRQ is temporarily masked.

5

NVIC runs the handler only if priority qualifies

The NVIC allows the pending IRQ to activate only if its preemption priority is numerically lower (higher priority) than the currently executing exception (or if no exception is active). If a higher-priority ISR is already running, the new IRQ stays in pending state until the higher-priority handler finishes. This is the “nested” part of NVIC.

/* Example priority check logic (hardware does this automatically):
   Currently active IRQ priority = 3
   New USART3 priority = 5 (numerically higher = lower priority)
   → USART3 stays pending until active IRQ exits */

   /* If USART3 priority = 2 (numerically lower = higher priority):
   → USART3 preempts the current ISR (tail-chain or preempt) */

6

Pending events survive IRQ disable — fire immediately on re-enable

If the peripheral generates an interrupt when the IRQ is disabled in the NVIC (ISER bit = 0), the event is still recorded in the NVIC pending register. As soon as the IRQ is re-enabled (ISER bit set to 1), the NVIC immediately evaluates the pending bit. If the priority qualifies, the ISR fires right away — without waiting for another peripheral event. This ensures no interrupts are silently dropped during critical sections.

NVIC_DisableIRQ(USART3_IRQn);
/* USART3 byte arrives here → pending bit set, but ISR does not fire */
critical_section_work();
NVIC_EnableIRQ(USART3_IRQn);
/* ↑ USART3_IRQHandler fires here immediately if priority qualifies */

Complete USART3 interrupt setup example

#include "stm32f4xx.h"

/* Step 1–3: Full USART3 receive interrupt configuration */
void usart3_irq_init(void)
{
    /* Enable clocks */
    RCC->APB1ENR |= RCC_APB1ENR_USART3EN;   /* USART3 clock */
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;    /* GPIOB for TX/RX pins */

    /* Configure PB10 (TX) and PB11 (RX) as AF7 (USART3) */
    GPIOB->MODER  |=  (2U << 20) | (2U << 22);   /* alternate function mode */
    GPIOB->AFR[1] |=  (7U <<  8) | (7U << 12);   /* AF7 = USART3 */

    /* Configure USART3: 115200 baud at PCLK1 = 50 MHz */
    /* BRR = 50,000,000 / 115200 ≈ 434 = 0x1B2 */
    USART3->BRR = 0x1B2U;
    USART3->CR1 = USART_CR1_TE | USART_CR1_RE;  /* TX + RX enable, no IRQ yet */

    /* Step 1: IRQ number = USART3_IRQn = 39 (from device header) */
    /* Step 2: Configure NVIC */
    NVIC_SetPriority(USART3_IRQn, 5);
    NVIC_EnableIRQ(USART3_IRQn);

    /* Step 3: Enable RXNE interrupt in USART3, then enable USART */
    USART3->CR1 |= USART_CR1_RXNEIE | USART_CR1_UE;
    /* From this point, any received byte triggers USART3_IRQHandler */
}

/* Step 4–6 happen automatically in hardware */

/* The ISR: */
void USART3_IRQHandler(void)
{
    if (USART3->SR & USART_SR_RXNE) {
        uint8_t data = (uint8_t)(USART3->DR & 0xFFU);
        /* process received byte */
        (void)data;
    }
    /* Clear RXNE by reading DR (done above).
       If using DMA, clear DMAR bit in CR3 instead. */
}

Frequently Asked Questions

Why are UsageFault, BusFault, and MemManage disabled by default?

The Cortex-M architects chose HardFault as the universal catch-all so that minimal systems (no OS, simple bare-metal) have a single fault handler to implement. Enabling the configurable faults requires the programmer to deliberately enable them via SHCSR, signalling that the system has fault handlers capable of classifying and recovering from specific fault types. This prevents beginners from accidentally creating unhandled exception loops.

What is the difference between setting PENDSVSET and calling NVIC_SetPendingIRQ for PendSV?

PendSV is a system exception (exception number 14), not an external IRQ. NVIC_SetPendingIRQ only works for external IRQs (numbers ≥ 16 / IRQ0+). To pend PendSV, you must write directly to ICSR.PENDSVSET (bit 28 of 0xE000ED04). CMSIS provides this via SCB->ICSR = SCB_ICSR_PENDSVSET_Msk.

What happens if I enable a peripheral interrupt before enabling it in the NVIC?

The peripheral starts signalling on its IRQ line, and the NVIC records the event as pending in its ISPR register. The ISR does not execute yet (because the IRQ is not enabled in ISER). When you later enable the IRQ via ISER, the NVIC sees the pending bit and fires the ISR immediately. This is useful in cases where you want to ensure no events are missed during initialisation, but be aware the ISR will fire right away once enabled.

How do I read which fault caused a HardFault escalation?

Read SCB->HFSR first: if FORCED bit is set, a configurable fault escalated. Then read SCB->CFSR which combines MMFSR (bits 7:0), BFSR (bits 15:8), and UFSR (bits 31:16). The BFARVALID and MMARVALID bits in CFSR tell you whether SCB->BFAR and SCB->MMFAR contain valid fault addresses. Read those addresses before clearing CFSR, as clearing CFSR invalidates BFAR/MMFAR.

Can I use ICSR.RETTOBASE to detect nested ISR exits?

Yes. Inside any ISR, reading ICSR.RETTOBASE tells you whether this is the last active exception. If RETTOBASE = 1, the processor will return to Thread mode after this handler exits. If RETTOBASE = 0, it will return to a preempted lower-priority handler. FreeRTOS uses this pattern to decide whether to trigger a context switch (PendSV) from inside an ISR — a context switch is only safe when RETTOBASE = 1.

Is there a maximum number of pending interrupts the NVIC can hold?

The NVIC can hold one pending bit per IRQ — one 32-bit ISPR register per 32 IRQs, up to 240 IRQs. So theoretically all 240 IRQs can be simultaneously pending. However, an IRQ’s pending bit is automatically cleared by the hardware when the ISR starts executing. If the peripheral generates another event before the ISR clears the peripheral’s flag, the pending bit is re-set immediately — effectively queuing one event.

SCB Registers ICSR PENDSVSET SHCSR Fault Enable NVIC Configuration System Exceptions USART3 IRQ39 Interrupt Priority PendSV RTOS HardFault Diagnosis STM32F411

© EmbeddedPathashala — Free Embedded Systems Course — ARM Cortex-M3/M4 Series

EmbeddedPathashala — Embedded Systems Programming on ARM Cortex-M3/M4

2 Comments

Leave a Reply

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