Interview questions & answers
Q1. What is an interrupt and how does the CPU handle it on ARM Cortex-M?
An interrupt is a hardware or software signal that causes the CPU to suspend the current program and execute a specific handler routine called an ISR. On ARM Cortex-M4, when an interrupt fires the hardware automatically saves R0–R3, R12, LR, PC, and xPSR onto the current stack, switches to the handler stack if configured, and jumps to the ISR address from the vector table. This automatic context save is called exception entry stacking and it is what allows the ISR to be a normal C function without manual register saving.
Follow-up: What happens after the ISR returns — how does the CPU restore state?
Q2. What is the NVIC and what are its main functions?
The NVIC (Nested Vectored Interrupt Controller) is the interrupt management unit inside every ARM Cortex-M processor that handles priority, enabling, pending, and nesting of up to 240 external interrupts plus 16 system exceptions. On an STM32F4, you configure NVIC_SetPriority(USART1_IRQn, 5) and NVIC_EnableIRQ(USART1_IRQn) to arm the UART interrupt before enabling it at the peripheral. The NVIC evaluates all pending interrupts each cycle and always presents the highest-priority one to the CPU, enabling true interrupt nesting.
Follow-up: What is the difference between interrupt priority and sub-priority in the NVIC?
Q3. What is interrupt priority grouping in Cortex-M and how does it work?
Cortex-M implements interrupt priority as an 8-bit field split into a preemption priority and a sub-priority by the PRIGROUP bits in the Application Interrupt and Reset Control Register (AIRCR). If PRIGROUP = 5 on an STM32 with 4 implemented priority bits, you get 4 levels of preemption priority and 2 levels of sub-priority — a higher preemption priority interrupt can nest inside a lower one, but sub-priority only breaks ties among same-preemption-priority pending interrupts. FreeRTOS requires all interrupts that use FromISR API calls to have a numerical priority value greater than or equal to configMAX_SYSCALL_INTERRUPT_PRIORITY.
Follow-up: What happens if you call a FreeRTOS API from an ISR with too high a priority?
Q4. What is interrupt latency and what factors affect it on a Cortex-M MCU?
Interrupt latency is the time from when the interrupt signal is asserted to when the first instruction of the ISR executes — on Cortex-M4 the minimum latency is 12 cycles for the hardware stacking plus pipeline flush. On an STM32F407 at 168 MHz, this is about 70 ns in the best case with zero wait-state flash, but if the CPU is executing a multi-cycle instruction or if the flash prefetch buffer misses, latency increases to 30+ cycles. Cache misses, DMA bus contention, and priority masking from BASEPRI or PRIMASK are the most common causes of latency jitter in production firmware.
Follow-up: How does tail-chaining reduce interrupt latency when multiple IRQs are pending?
Q5. What is tail-chaining in ARM Cortex-M interrupt handling?
Tail-chaining is a Cortex-M optimization where, if another interrupt is pending when an ISR finishes, the CPU skips the full unstacking and restacking sequence and jumps directly to the next ISR, saving 12 cycles. Without tail-chaining, two back-to-back interrupts would waste 24 cycles for one unstacking and one restacking that accomplish nothing. In a motor drive application processing phase current samples at 20 kHz, tail-chaining between the ADC interrupt and a DMA-complete interrupt prevents cumulative timing jitter.
Follow-up: What is late-arrival optimization in Cortex-M interrupts?
Q6. What are PRIMASK, FAULTMASK, and BASEPRI registers and when do you use each?
PRIMASK disables all exceptions except NMI and HardFault when set to 1, FAULTMASK additionally disables HardFault and is only writable in privileged mode, and BASEPRI masks all interrupts with a numerical priority equal to or lower than the value written to it. In FreeRTOS, taskENTER_CRITICAL() on Cortex-M sets BASEPRI to configMAX_SYSCALL_INTERRUPT_PRIORITY rather than setting PRIMASK, so safety-critical interrupts like a motor overcurrent trip remain active during critical sections. Using PRIMASK for critical sections in a motor controller would be dangerous because it blocks the overcurrent ISR.
Follow-up: What is the difference between a critical section and disabling all interrupts?
Q7. How do you share data safely between an ISR and a task in embedded C?
Variables shared between an ISR and a task must be declared volatile and accesses to multi-byte variables must be protected by disabling the interrupt or using atomic operations, because the compiler does not know the ISR can modify the variable between two C statements. For a 32-bit variable on Cortex-M4, a single LDR instruction is atomic so volatile alone is enough; for a 64-bit timestamp, you must disable the IRQ with __disable_irq() before reading both words and re-enable after. Failing to declare shared variables volatile causes the compiler to cache the value in a register and the task never sees the ISR update.
Follow-up: Is volatile sufficient for synchronization between two tasks on a dual-core processor?
Q8. What is a vector table and where is it located in an STM32?
The vector table is an array of 32-bit exception handler addresses stored starting at the reset vector; on STM32 it defaults to address 0x08000000 in flash and can be relocated at runtime by writing the new address to the VTOR register in the SCB. The first word is the initial Main Stack Pointer value, the second is the Reset handler address, and subsequent entries are NMI, HardFault, and peripheral IRQs. Bootloaders that jump to application firmware must set VTOR to the application's vector table address before enabling interrupts, otherwise all ISRs point to the bootloader's handlers.
Follow-up: Why does a FreeRTOS application on STM32 not usually need to modify VTOR?
Q9. What causes a HardFault on Cortex-M and how do you debug it?
A HardFault is a catch-all exception triggered by illegal memory access, unaligned access with strict alignment enabled, execution of an undefined instruction, or escalation of another fault. In the HardFault handler, reading the SCB->HFSR register identifies the fault type, and if it is a forced fault from another handler, SCB->CFSR gives the precise cause — for example bit 7 (MMARVALID) in MMFSR with SCB->MMFAR holding the faulting address indicates a memory protection violation. The most practical debug technique is to read the stacked PC from the exception frame to identify the exact instruction that faulted.
Follow-up: How do you extract the stacked PC from a HardFault handler in C?
Q10. What is the difference between an IRQ and an exception in Cortex-M terminology?
In Cortex-M terminology, an exception is any event that causes the processor to change mode and vector to a handler, including system exceptions like Reset, NMI, SysTick, and SVCall; an IRQ (interrupt request) specifically refers to the peripheral-generated external interrupts numbered IRQ0 through IRQ239. SysTick is exception number 15 and is used by FreeRTOS as its tick source, but it is not an IRQ — it has no entry in the NVIC's external interrupt registers. This distinction matters when using FreeRTOS priority masking because BASEPRI only masks IRQs, not the NMI or HardFault exceptions.
Follow-up: Can the SysTick exception priority be set via NVIC_SetPriority?
Q11. What is an NMI (Non-Maskable Interrupt) and when is it used?
The NMI is the highest-priority exception on Cortex-M that cannot be masked by PRIMASK or BASEPRI, making it suitable only for events that must always be handled — typically catastrophic hardware faults like a clock failure or watchdog pre-warning. On STM32, the NMI is connected to the CSS (Clock Security System) so that a crystal oscillator failure immediately triggers the NMI handler which can switch the clock to the internal RC oscillator and log the fault. Using NMI for routine peripherals is wrong because once inside the NMI handler there is no way to mask further NMI events and re-entrancy will corrupt the stack.
Follow-up: Can you return from an NMI handler normally?
Q12. What is interrupt debouncing and how is it implemented in firmware?
Interrupt debouncing is the technique of ignoring spurious re-triggers of a mechanical switch interrupt that occur as the contacts bounce for 1–50 ms after a press. A robust firmware approach is to trigger on the falling edge, immediately disable the GPIO interrupt in the ISR, start a 20 ms timer, and in the timer callback re-enable the interrupt after confirming the pin is still low. Simply checking GPIO state inside the ISR without disabling re-triggering causes multiple counts per button press, which breaks UI state machines in consumer electronics.
Follow-up: What is the difference between hardware debouncing with an RC filter and software debouncing?
Q13. What is the significance of the EXC_RETURN value when an ISR returns?
EXC_RETURN is a special value loaded into the Link Register during exception entry by the Cortex-M hardware; its bit pattern tells the CPU which stack (MSP or PSP), which mode (Thread or Handler), and whether to restore FPU registers when returning from the ISR. A value of 0xFFFFFFFD means return to Thread mode using the PSP, which is what FreeRTOS tasks use, while 0xFFFFFFF9 means return to Thread mode using the MSP. Corrupting LR inside an ISR by calling a function that overwrites it without saving it first is a common cause of system crashes that appear as HardFaults on the EXC_RETURN instruction.
Follow-up: What happens if a function call inside an ISR overwrites LR before EXC_RETURN is executed?
Q14. How do you measure interrupt latency on an STM32 using GPIO?
Toggle a GPIO pin at the very first line of the ISR and again at the last line; connect a logic analyzer or oscilloscope to that pin and to the interrupt source signal, then measure the time from the source edge to the first pin toggle. On an STM32F4 at 168 MHz with AHB-connected GPIO, you can see the minimum 12-cycle hardware stacking latency as approximately 71 ns, plus any additional cycles from flash wait states. This GPIO-toggle measurement technique is standard practice in Bosch and Continental firmware validation to certify that ISR latencies meet AUTOSAR timing requirements.
Follow-up: What is the best way to minimize flash wait-state-induced ISR latency on Cortex-M?
Q15. What is the difference between level-triggered and edge-triggered interrupts?
A level-triggered interrupt asserts as long as the signal remains at the active level, so the ISR must clear the source before returning or the interrupt immediately re-fires; an edge-triggered interrupt fires once per transition and does not re-fire if the ISR is slow. STM32 EXTI lines are edge-triggered (rising, falling, or both), so a button press gives exactly one interrupt event, but a UART RX interrupt on the NVIC peripheral side is level-triggered — the interrupt stays asserted until the data register is read. Misunderstanding this distinction causes UART ISRs to execute in an infinite loop until the receive register is drained.
Follow-up: How does the EXTI controller on STM32 handle both rising and falling edge detection simultaneously?
Common misconceptions
Misconception: Setting interrupt priority to 0 in STM32 HAL makes it the lowest priority.
Correct: In Cortex-M, lower numerical priority values mean higher priority, so priority 0 is the highest possible priority and should be reserved for the most critical real-time events.
Misconception: volatile keyword ensures thread-safe access to shared variables between an ISR and a task.
Correct: volatile only prevents compiler register caching; it does not prevent read-modify-write races on multi-byte variables, which require interrupt disabling or atomic operations.
Misconception: PRIMASK disables all interrupts including NMI and HardFault.
Correct: PRIMASK disables all configurable priority exceptions but NMI and HardFault remain active; only FAULTMASK additionally masks HardFault.
Misconception: The NVIC is a separate chip that must be interfaced externally on Cortex-M MCUs.
Correct: The NVIC is an integral part of the ARM Cortex-M processor core, not an external chip, and is present in every Cortex-M0 through M7 device.