hal::nvic

Nested Vectored Interrupt Controller - ARM Cortex-M

~380 satır ~18 function ARM Cortex-M

Overview

NVIC modülü, ARM Cortex-M serisi işlemcilerde interrupt yönetimini sağlar. 240+ interrupt kaynağını 16 öncelik seviyesi ile yönetir, nested interrupts destekler ve kritik bölgeleri koruma mekanizmaları sunar. Gerçek zamanlı embedded sistemlerin omurgasıdır.

Key Features

Quick Start

import hal::nvic

// Simple interrupt enable
nvic.enable_irq(nvic.USART1_IRQn, do
    priority: 5  // 0-15 range
end)

// Priority grouping (preemption vs sub-priority)
nvic.set_priority_grouping(nvic.PRIGROUP_4_4)  // 4 bit preempt, 4 bit sub

// Preemption priority 2, sub-priority 1
nvic.set_priority(nvic.TIM2_IRQn, do
    preempt: 2,
    sub: 1
end)

// Disable interrupt
nvic.disable_irq(nvic.USART1_IRQn)

// Software trigger interrupt
nvic.set_pending_irq(nvic.EXTI0_IRQn)

Types and Enums'lar

// Priority Grouping
enum PriorityGroup {
    PRIGROUP_0_4,  // 0 bit preempt, 4 bit sub
    PRIGROUP_1_3,  // 1 bit preempt, 3 bit sub
    PRIGROUP_2_2,  // 2 bit preempt, 2 bit sub
    PRIGROUP_3_1,  // 3 bit preempt, 1 bit sub
    PRIGROUP_4_0   // 4 bit preempt, 0 bit sub (default)
}

// IRQ Numbers (platform-specific)
enum IRQn {
    // Cortex-M core exceptions
    NMI_IRQn = -14,
    HardFault_IRQn = -13,
    MemManage_IRQn = -12,
    BusFault_IRQn = -11,
    UsageFault_IRQn = -10,
    SVCall_IRQn = -5,
    PendSV_IRQn = -2,
    SysTick_IRQn = -1,
    
    // STM32-specific interrupts (0+)
    WWDG_IRQn = 0,
    PVD_IRQn = 1,
    TAMP_STAMP_IRQn = 2,
    RTC_WKUP_IRQn = 3,
    FLASH_IRQn = 4,
    RCC_IRQn = 5,
    EXTI0_IRQn = 6,
    EXTI1_IRQn = 7,
    // ... 80+ more
    USART1_IRQn = 37,
    TIM2_IRQn = 28,
    DMA1_Stream0_IRQn = 11
}

struct NVICConfig do
    irq: IRQn,
    priority: int,        // Combined or preemption priority
    sub_priority: int = 0, // Sub-priority (if grouping allows)
    enabled: bool = true
end

Example 1: Priority Grouping & Preemption

Problem: USART ve Timer interrupt'larının öncelik yönetimi

import hal::nvic, hal::usart, hal::timer

function interrupt_sistemi_init() do
    // Set priority grouping: 3-bit preempt, 1-bit sub
    nvic.set_priority_grouping(nvic.PRIGROUP_3_1)
    
    // USART1 (high priority, preempt=1, sub=0)
    nvic.enable_irq(nvic.USART1_IRQn, do
        priority: 1,      // Preemption priority
        sub_priority: 0
    end)
    
    // Timer2 (medium priority, preempt=3, sub=0)
    nvic.enable_irq(nvic.TIM2_IRQn, do
        priority: 3,
        sub_priority: 0
    end)
    
    // ADC (low priority, preempt=5, sub=1)
    nvic.enable_irq(nvic.ADC_IRQn, do
        priority: 5,
        sub_priority: 1
    end)
end

// USART handler (can preempt TIM2 and ADC)
function USART1_IRQHandler() do
    if usart.get_flag(usart.USART1, usart.FLAG_RXNE) do
        let data = usart.receive_data(usart.USART1)
        buffer_yaz(data)
        usart.clear_flag(usart.USART1, usart.FLAG_RXNE)
    end
end

// Timer handler (can preempt ADC, but NOT USART)
function TIM2_IRQHandler() do
    if timer.get_flag(timer.TIM2, timer.FLAG_UPDATE) do
        // Timer tick processing
        systick_emulate()
        timer.clear_flag(timer.TIM2, timer.FLAG_UPDATE)
    end
end

// ADC handler (lowest priority, cannot preempt anyone)
function ADC_IRQHandler() do
    if adc.get_flag(adc.ADC1, adc.FLAG_EOC) do
        let value = adc.read_value(adc.ADC1)
        sensor_buffer[buffer_idx] = value
        buffer_idx = (buffer_idx + 1) % 32
        adc.clear_flag(adc.ADC1, adc.FLAG_EOC)
    end
end

Çıktı: USART interrupt'ı TIM2'ye girebilir (preempt), ama TIM2 USART'a giremez. ADC her ikisi tarafından preempt edilebilir.

Example 2: Critical Section (Disable All Interrupts)

Problem: Paylaşımlı leti koruma (thread-safe erişim)

import hal::nvic

let paylasilanOlan sayac: int = 0

function kritik_islem() do
    // Disable all interrupts (PRIMASK = 1)
    nvic.disable_all_interrupts()
    
    // Critical section (atomic operation)
    sayac = sayac + 1
    let local_sayac = sayac
    
    // Re-enable interrupts
    nvic.enable_all_interrupts()
    
    return local_sayac
end

// Alternative: BASEPRI (disable interrupts with priority >= threshold)
function basepri_koruma() do
    // Disable interrupts with priority >= 3 (only allow 0-2)
    nvic.set_basepri(3)
    
    // Critical section
    paylasilanOlan veri = veri_oku()
    veri_isle(veri)
    
    // Restore (allow all)
    nvic.set_basepri(0)
end

Çıktı: sayac leti interrupt'lardan korunur. Race condition önlenir.

Example 3: Vector Table Relocation (Bootloader)

Problem: Bootloader'dan application'a geçiş

import hal::nvic, hal::flash

const BOOTLOADER_BASE: int = 0x0800_0000
const APP_BASE: int = 0x0800_8000  // 32 KB offset

function jump_to_application() do
    // Read application's stack pointer
    let app_sp = *(APP_BASE cast int*)
    
    // Read application's reset handler
    let app_reset = *((APP_BASE + 4) cast int*)
    
    io.yazdir_satır('Jumping to application at 0x' + APP_BASE.onaltılıya())
    
    // Disable all interrupts
    nvic.disable_all_interrupts()
    
    // Relocate vector table to application address
    nvic.set_vector_table(APP_BASE)
    
    // Set stack pointer
    asm do
        'MSR MSP, r0' // Set Main Stack Pointer
    end with app_sp
    
    // Jump to application reset handler
    let app_func = app_reset cast function()
    app_func()
end

// Bootloader mode check
function bootloader_mode() -> bool do
    // Check if user button pressed during reset
    gpio.pin_init(gpio.PORT_A, 0, do mode: gpio.MODE_INPUT end)
    
    let buton = gpio.pin_read(gpio.PORT_A, 0)
    
    if (buton == 1) do
        io.yazdir_satır('Bootloader mode (button pressed)')
        return true
    end
    
    // Check if application is valid (stack pointer in RAM)
    let app_sp = *(APP_BASE cast int*)
    if (app_sp >= 0x2000_0000 ve app_sp < 0x2002_0000) do
        io.yazdir_satır('Valid application found')
        return false  // Jump to app
    end
    
    io.yazdir_satır('No valid application, staying in bootloader')
    return true
end

Çıktı: Bootloader 0x0800_0000'den çalışır, uygulama 0x0800_8000'e yüklenir. Reset endrası true vector table kullanılır.

API Reference

Core Functions

// Interrupt enable/disable
function enable_irq(irq: IRQn, config: NVICConfig)
function disable_irq(irq: IRQn)

// Priority configuration
function set_priority_grouping(group: PriorityGroup)
function set_priority(irq: IRQn, preempt: int, sub: int)
function get_priority(irq: IRQn) -> (int, int)

// Pending management
function set_pending_irq(irq: IRQn)
function clear_pending_irq(irq: IRQn)
function is_pending_irq(irq: IRQn) -> bool

// Active status
function is_active_irq(irq: IRQn) -> bool

// Global interrupt control
function disable_all_interrupts()  // PRIMASK = 1
function enable_all_interrupts()   // PRIMASK = 0
function set_basepri(priority: int)  // Disable pri >= threshold
function get_basepri() -> int

// Vector table relocation
function set_vector_table(offset: int)
function get_vector_table() -> int

// System reset
function system_reset()
Important Notes:
  • Priority Inversion: Düşük öncelikli interrupt uzun sürerse, yüksek öncelikli bekler
  • Shared Peripherals: Aynı periferali kullanan interrupt'ların önceliği dikkatli ayarlanmalı
  • Stack Overflow: Nested interrupt çok derinleşirse stack overflow olabilir
  • Vector Table: RAM'e relocation doarken alignment (0x200) gerekli

Platform Support

Platform Core IRQ Count Priority Bits
STM32F1 Cortex-M3 60 4 bit (16 levels)
STM32F4 Cortex-M4F 82 4 bit
STM32F7 Cortex-M7 97 4 bit
STM32H7 Cortex-M7 150 4 bit
GD32VF103 RISC-V 87 (ECLIC) 4 bit

Best Practices

Related Modules

HAL Modules | Turkish