ECLIC Core Configuration: Vector Table, Interrupt Enablement, and Priority

Chapter 4: Interrupts and Exceptions: Enhanced Interrupt Controller (ECLIC)

4.2 ECLIC Core Configuration: Vector Table, Interrupt Enablement, and Priority

4.2.1 Overall Configuration Process of ECLIC

Configuration Architecture Overview

The complete configuration of ECLIC is a systematic project that requires configuring various functional modules in a specific order. The correct configuration sequence ensures system stability and interrupt response performance.

ECLIC Configuration Flowchart

┌─────────────────┐│  Start ECLIC Configuration │└─────────┬───────┘          │          ▼┌─────────────────┐│ 1. Global ECLIC Enable │ ← Activate ECLIC Controller└─────────┬───────┘          │          ▼┌─────────────────┐│ 2. Priority Bit Configuration │ ← Set Priority Granularity└─────────┬───────┘          │          ▼┌─────────────────┐│ 3. Vector Table Base Address Setting │ ← Establish Interrupt Jump Table└─────────┬───────┘          │          ▼┌─────────────────┐│ 4. Machine Mode Threshold Setting │ ← Configure Interrupt Filtering└─────────┬───────┘          │          ▼┌─────────────────┐│ 5. Interrupt Attribute Configuration │ ← Configure Each Interrupt Source└─────────┬───────┘          │          ▼┌─────────────────┐│ 6. Global Interrupt Enable │ ← Enable Interrupt System└─────────┬───────┘          │          ▼┌─────────────────┐│ ECLIC Configuration Complete │└─────────────────┘

Functions and Principles of Each Configuration Step

  1. Global ECLIC Enable:

  • Function:Activate the ECLIC controller to start its operation

  • Principle:ECLIC is disabled by default after reset and needs to be explicitly enabled

  • Key Register<span>ECLIC->CFG</span> enable bit

  • Priority Bit Configuration

    • Function:Determine the granularity of interrupt priority in the system

    • Principle:8-bit priority can be subdivided into 256 levels, while 3-bit priority has only 8 levels

    • Trade-off:More levels provide finer scheduling but increase arbitration complexity

  • Vector Table Base Address Setting

    • Function:Establish a hardware interrupt vector jump table

    • Principle:When an interrupt occurs, the hardware automatically calculates the vector address and jumps

    • Advantage:Significantly reduces interrupt latency compared to software table lookup

  • Machine Mode Threshold Setting

    • Function:Filter low-priority interrupts to reduce unnecessary context switching

    • Principle:Only interrupts with priority higher than the threshold will be processed

    • Application:Protection of critical system tasks

  • Interrupt Attribute Configuration

    • Function:Configure specific behavioral characteristics for each interrupt source

    • Includes:Priority, trigger mode, vector mode, etc.

    • Importance:Determines the real-time performance and reliability of interrupts

  • Global Interrupt Enable

    • Function:The final step, enabling the entire interrupt system

    • Principle:RISC-V controls global interrupts through<span>mstatus.MIE</span> bit

    • Best Practice:Always enable interrupts after configuration is complete

    ECLIC Overall Configuration Pseudocode

    // Complete ECLIC Configuration Pseudocode - Based on GD32VF103 Practiceeclic_config_result_t eclic_complete_configuration(void) {    printf("Starting ECLIC Complete Configuration...\n");    // === Stage 1: Basic ECLIC Configuration ===    // 1.1 Global ECLIC Enable    // Key Register: ECLIC-&gt;CFG    ECLIC-&gt;CFG = (0x1 &lt;&lt; 16);  // Bit16: ECLIC Enable Bit    if (!(ECLIC-&gt;CFG &amp; (0x1 &lt;&lt; 16))) {        printf("ERROR: ECLIC enable failed\n");        return ECLIC_CONFIG_FAILED;    }    printf("✓ ECLIC globally enabled\n");    // 1.2 Priority Bit Configuration      // Key Register: ECLIC-&gt;CFG[4:0] - nlbits field    // Set 3-bit priority (2^3 = 8 priority levels)    uint32_t nlbits = 0x3;  // 3-bit priority    ECLIC-&gt;CFG = (ECLIC-&gt;CFG &amp; ~0x1F) | (nlbits &amp; 0x1F);    printf("✓ Priority bits configured: %lu bits (%lu levels)\n",            nlbits, (1UL &lt;&lt; nlbits));    // === Stage 2: Vector System Configuration ===    // 2.1 Vector Table Base Address Setting    // Key Register: mtvec (Machine Trap Vector)    uint32_t vector_base = (uint32_t)&amp;interrupt_vector_table;    // Set vector mode: mtvec[1:0] = 0x1    uint32_t mtvec_value = (vector_base &amp; ~0x3) | 0x1;    __asm volatile ("csrw mtvec, %0" : : "r" (mtvec_value));    // Verify mtvec setting    uint32_t read_mtvec;    __asm volatile ("csrr %0, mtvec" : "=r" (read_mtvec));    if ((read_mtvec &amp; 0x3) != 0x1) {        printf("ERROR: mtvec vector mode setup failed\n");        return ECLIC_CONFIG_FAILED;    }    printf("✓ Vector table configured at 0x%08lX\n", vector_base);    // === Stage 3: Interrupt Filtering Configuration ===    // 3.1 Machine Mode Threshold Setting    // Key Register: ECLIC-&gt;MTH (Machine Threshold)    // Set threshold to 0, accept all priority interrupts    ECLIC-&gt;MTH = 0x00;    printf("✓ Machine threshold set to 0x%02X\n", ECLIC-&gt;MTH);    // === Stage 4: Detailed Interrupt Source Configuration ===    // 4.1 Configure Specific Interrupt Sources    if (!configure_all_interrupt_sources()) {        printf("ERROR: Interrupt source configuration failed\n");        return ECLIC_CONFIG_FAILED;    }    // === Stage 5: Final Enable ===    // 5.1 Global Interrupt Enable    // Key Register: mstatus.MIE (Machine Interrupt Enable)    __asm volatile ("csrs mstatus, %0" : : "r" (0x8));  // Set MIE bit    // Verify Global Interrupt Enable    uint32_t mstatus;    __asm volatile ("csrr %0, mstatus" : "=r" (mstatus));    if (!(mstatus &amp; 0x8)) {        printf("ERROR: Global interrupt enable failed\n");        return ECLIC_CONFIG_FAILED;    }    printf("✓ Global interrupts enabled\n");    // === Configuration Verification ===    if (!verify_eclic_configuration()) {        printf("WARNING: ECLIC configuration verification issues\n");        return ECLIC_CONFIG_WARNING;    }    printf("ECLIC configuration completed successfully\n");    return ECLIC_CONFIG_SUCCESS;} // Pseudocode for configuring all interrupt sourcesbool configure_all_interrupt_sources(void) {    printf("Configuring interrupt sources...\n");    // Configure system critical interrupts (high priority)    if (!configure_system_critical_interrupts()) {        return false;    }    // Configure peripheral interrupts (medium priority)    if (!configure_peripheral_interrupts()) {        return false;    }    // Configure GPIO interrupts (low priority)    if (!configure_gpio_interrupts()) {        return false;    }    printf("✓ All interrupt sources configured\n");    return true;}
    
    
    4.2.2 Detailed Vector Table Configuration

    Vector Table Design Principles

    The vector table is the core of ECLIC hardware vectorization, achieving rapid interrupt response through a predefined jump table:

    • Hardware Automatic Jump:When an interrupt occurs, the hardware automatically calculates the vector address

    • Fixed Spacing:Each interrupt vector has a fixed address offset

    • Flexible Layout:The vector table can be placed at any aligned memory location

    Vector Table Configuration Steps

    // Complete Vector Table Configuration Pseudocodevoid setup_vector_table_system(void) {    printf("Setting up vector table system...\n");    // Step 1: Define Vector Table Structure    // The vector table is an array of function pointers, each element corresponds to an interrupt handler    void (*vector_table[ECLIC_MAX_INTERRUPTS])(void) = {0};    // Step 2: Populate Vector Table    populate_vector_table(vector_table);    // Step 3: Set Vector Table Base Address to mtvec    uint32_t vector_base = (uint32_t)vector_table;    // Ensure vector table is correctly aligned (at least 4-byte aligned)    if (vector_base &amp; 0x3) {        printf("WARNING: Vector table not 4-byte aligned, adjusting...\n");        vector_base = (vector_base + 3) &amp; ~0x3;    }    // Set mtvec to vector mode (mode = 1)    uint32_t mtvec_value = vector_base | 0x1;    __asm volatile ("csrw mtvec, %0" : : "r" (mtvec_value));    // Step 4: Configure ECLIC Vector Related Settings    configure_eclic_vector_settings();    printf("✓ Vector table system ready at 0x%08lX\n", vector_base);} // Implementation to populate vector tablevoid populate_vector_table(void (*vector_table[])(void)) {    // Set default interrupt handler    for (int i = 0; i &lt; ECLIC_MAX_INTERRUPTS; i++) {        vector_table[i] = &amp;default_interrupt_handler;    }    // Configure specific interrupt handlers - based on GD32VF103 practice    vector_table[ECLIC_IRQ_USART0] = &amp;usart0_interrupt_handler;    vector_table[ECLIC_IRQ_USART1] = &amp;usart1_interrupt_handler;    vector_table[ECLIC_IRQ_TIMER0] = &amp;timer0_interrupt_handler;    vector_table[ECLIC_IRQ_TIMER1] = &amp;timer1_interrupt_handler;    vector_table[ECLIC_IRQ_EXTI0] = &amp;exti0_interrupt_handler;    vector_table[ECLIC_IRQ_EXTI1] = &amp;exti1_interrupt_handler;    // GPIO interrupt vector configuration    for (int i = ECLIC_IRQ_GPIO_BASE; i &lt; ECLIC_IRQ_GPIO_BASE + 16; i++) {        vector_table[i] = &amp;gpio_interrupt_handler;    }    printf("✓ Vector table populated with %d handlers\n", ECLIC_MAX_INTERRUPTS);} // Vector Interrupt Handler Templatevoid __attribute__((interrupt)) default_interrupt_handler(void) {    uint32_t mcause, interrupt_id;    // Read interrupt cause    __asm volatile ("csrr %0, mcause" : "=r" (mcause));    interrupt_id = mcause &amp; 0x7FFFFFFF;    printf("Default handler: Unhandled interrupt %lu\n", interrupt_id);    // Safety measure: Disable unhandled interrupts    if (interrupt_id &lt; ECLIC_MAX_INTERRUPTS) {        ECLIC-&gt;interrupts[interrupt_id].INTIE = 0x0;    }} // GD32VF103 Specific Peripheral Interrupt Handlervoid __attribute__((interrupt)) usart0_interrupt_handler(void) {    uint32_t status = USART0-&gt;STAT;    // Handle receive interrupt    if (status &amp; USART_STAT_RBNE) {        uint8_t data = USART0-&gt;DATA;        handle_usart0_rx_data(data);        // Clear receive interrupt flag        USART0-&gt;STAT &amp;= ~USART_STAT_RBNE;    }    // Handle transmit interrupt    if (status &amp; USART_STAT_TBE) {        handle_usart0_tx_ready();        // Note: Transmit complete interrupt needs software control to clear    }    // Handle error interrupt    if (status &amp; (USART_STAT_PERR | USART_STAT_FERR)) {        handle_usart0_error(status);        // Error flags need to be read from STAT register to clear    }}
    
    
    4.2.3 Detailed Interrupt Enable Configuration

    Interrupt Enable Hierarchy

    The interrupt enablement in RISC-V+ECLIC adopts a three-layer control:

    ┌─────────────────┐│  Global Interrupt Enable │ ← mstatus.MIE (Highest Layer)│  (mstatus.MIE)  │└─────────┬───────┘          │          ▼┌─────────────────┐│  Controller Interrupt Enable │ ← ECLIC Global Enable│  (ECLIC-&gt;CFG)   │└─────────┬───────┘          │          ▼┌─────────────────┐│  Individual Interrupt Enable │ ← ECLIC-&gt;INTIE[n] (Finest Granularity)│ (ECLIC-&gt;INTIE[]) │└─────────────────┘
    
    

    Interrupt Enable Configuration Code

    // Complete Interrupt Enable Management Pseudocodevoid manage_interrupt_enables(void) {    printf("Managing interrupt enable states...\n");    // === Individual Interrupt Enable Configuration ===    // Configure USART0 interrupt enable    ECLIC-&gt;interrupts[ECLIC_IRQ_USART0].INTIE = 0x1;    printf("✓ USART0 interrupt enabled\n");    // Configure TIMER0 interrupt enable    ECLIC-&gt;interrupts[ECLIC_IRQ_TIMER0].INTIE = 0x1;    printf("✓ TIMER0 interrupt enabled\n");    // Batch configure GPIO interrupt enable    for (int i = 0; i &lt; 8; i++) {  // Only enable the first 8 GPIO interrupts        ECLIC-&gt;interrupts[ECLIC_IRQ_GPIO_BASE + i].INTIE = 0x1;    }    printf("✓ GPIO interrupts 0-7 enabled\n");    // === Safety Interrupt Disable Example ===    // Disable unused system interrupts    ECLIC-&gt;interrupts[ECLIC_IRQ_RESERVED_1].INTIE = 0x0;    ECLIC-&gt;interrupts[ECLIC_IRQ_RESERVED_2].INTIE = 0x0;    printf("✓ Reserved interrupts disabled for safety\n");} // Dynamic Interrupt Enable Control Functioninterrupt_control_result_t control_interrupt_enable(uint32_t interrupt_id, bool enable) {    if (interrupt_id &gt;= ECLIC_MAX_INTERRUPTS) {        return INTERRUPT_ID_INVALID;    }    // Atomic operation: first disable global interrupts, modify enable state, then restore    uint32_t old_mstatus;    __asm volatile ("csrr %0, mstatus" : "=r" (old_mstatus));    __asm volatile ("csrc mstatus, %0" : : "r" (0x8));  // Clear MIE    if (enable) {        ECLIC-&gt;interrupts[interrupt_id].INTIE = 0x1;        printf("Interrupt %lu enabled\n", interrupt_id);    } else {        ECLIC-&gt;interrupts[interrupt_id].INTIE = 0x0;        printf("Interrupt %lu disabled\n", interrupt_id);    }    // Restore original global interrupt state    if (old_mstatus &amp; 0x8) {        __asm volatile ("csrs mstatus, %0" : : "r" (0x8));    }    return INTERRUPT_CONTROL_SUCCESS;} // Interrupt Enable State Checkvoid check_interrupt_enable_states(void) {    printf("Checking interrupt enable states...\n");    uint32_t enabled_count = 0;    for (int i = 0; i &lt; ECLIC_MAX_INTERRUPTS; i++) {        if (ECLIC-&gt;interrupts[i].INTIE) {            enabled_count++;            // Check for handlers that are enabled but not configured            if (vector_table[i] == &amp;default_interrupt_handler) {                printf("WARNING: Interrupt %d enabled but using default handler\n", i);            }        }    }    printf("✓ %lu interrupts currently enabled\n", enabled_count);    if (enabled_count == 0) {        printf("WARNING: No interrupts are enabled\n");    }}
    
    
    4.2.4 Detailed Priority Configuration

    Priority System Design Principles

    The ECLIC priority system is based on a fixed priority preemption model:

    • Lower values indicate higher priority:0 is the highest priority, 255 is the lowest

    • Preemption Mechanism:High-priority interrupts can preempt low-priority interrupts

    • Threshold Filtering:MTH register filters interrupts below the threshold

    • Support for Nesting:Supports interrupt nesting to improve real-time performance

    Priority Configuration Code

    // Complete Priority Configuration Pseudocodevoid configure_interrupt_priorities(void) {    printf("Configuring interrupt priorities...\n");    // === System Critical Interrupts - Highest Priority ===    // Watchdog Interrupt - Highest Priority (0)    ECLIC-&gt;interrupts[ECLIC_IRQ_WWDG].INTCTL = 0x00;    printf("✓ Watchdog interrupt: priority 0 (highest)\n");    // System Exception Interrupt - High Priority (16)    ECLIC-&gt;interrupts[ECLIC_IRQ_SYSTICK].INTCTL = 0x10;    printf("✓ SysTick interrupt: priority 16\n");    // === Real-time Peripheral Interrupts - Medium Priority ===    // Communication Interface Interrupts - Medium Priority (32-48)    ECLIC-&gt;interrupts[ECLIC_IRQ_USART0].INTCTL = 0x20;    ECLIC-&gt;interrupts[ECLIC_IRQ_USART1].INTCTL = 0x28;    ECLIC-&gt;interrupts[ECLIC_IRQ_SPI0].INTCTL = 0x30;    printf("✓ Communication interrupts: priorities 32-48\n");    // Timer Interrupts - Medium Priority (40-56)    ECLIC-&gt;interrupts[ECLIC_IRQ_TIMER0].INTCTL = 0x28;    ECLIC-&gt;interrupts[ECLIC_IRQ_TIMER1].INTCTL = 0x30;    ECLIC-&gt;interrupts[ECLIC_IRQ_TIMER2].INTCTL = 0x38;    printf("✓ Timer interrupts: priorities 40-56\n");    // === General Peripheral Interrupts - Low Priority ===    // GPIO Interrupts - Low Priority (64+)    for (int i = 0; i &lt; 16; i++) {        ECLIC-&gt;interrupts[ECLIC_IRQ_GPIO_BASE + i].INTCTL = 0x40 + (i * 2);    }    printf("✓ GPIO interrupts: priorities 64-94\n");    // ADC/DAC Interrupts - Low Priority (80+)    ECLIC-&gt;interrupts[ECLIC_IRQ_ADC0].INTCTL = 0x50;    ECLIC-&gt;interrupts[ECLIC_IRQ_DAC].INTCTL = 0x58;    printf("✓ Analog interrupts: priorities 80-88\n");} // Dynamic Priority Adjustment Functionvoid adjust_interrupt_priority(uint32_t interrupt_id, uint8_t new_priority) {    if (interrupt_id &gt;= ECLIC_MAX_INTERRUPTS) {        printf("ERROR: Invalid interrupt ID %lu\n", interrupt_id);        return;    }    uint8_t old_priority = ECLIC-&gt;interrupts[interrupt_id].INTCTL &amp; 0xFF;    // Safety check: prevent setting reserved highest priority    if (new_priority == 0x00) {        printf("WARNING: Setting highest priority (0) for interrupt %lu\n", interrupt_id);    }    // Atomic operation to modify priority    uint32_t old_mstatus;    __asm volatile ("csrr %0, mstatus" : "=r" (old_mstatus));    __asm volatile ("csrc mstatus, %0" : : "r" (0x8));  // Disable global interrupts    ECLIC-&gt;interrupts[interrupt_id].INTCTL = new_priority;    // Restore global interrupt state    if (old_mstatus &amp; 0x8) {        __asm volatile ("csrs mstatus, %0" : : "r" (0x8));    }    printf("Interrupt %lu priority changed: 0x%02X → 0x%02X\n",            interrupt_id, old_priority, new_priority);} // Priority Conflict Detection and Resolutionvoid detect_priority_conflicts(void) {    printf("Checking for priority conflicts...\n");    uint8_t priority_usage[256] = {0};    bool conflicts_detected = false;    // Count usage of each priority    for (int i = 0; i &lt; ECLIC_MAX_INTERRUPTS; i++) {        if (ECLIC-&gt;interrupts[i].INTIE) {  // Only check enabled interrupts            uint8_t priority = ECLIC-&gt;interrupts[i].INTCTL &amp; 0xFF;            priority_usage[priority]++;            if (priority_usage[priority] &gt; 1) {                printf("CONFLICT: Priority 0x%02X used by multiple interrupts\n", priority);                conflicts_detected = true;            }        }    }    // Check highest priority usage    if (priority_usage[0] &gt; 1) {        printf("WARNING: Multiple interrupts using highest priority (0)\n");        conflicts_detected = true;    }    if (!conflicts_detected) {        printf("✓ No priority conflicts detected\n");    }    // Output priority usage statistics    printf("Priority usage statistics:\n");    for (int i = 0; i &lt; 16; i++) {  // Only display the first 16 priorities        if (priority_usage[i] &gt; 0) {            printf("  Priority 0x%02X: %d interrupts\n", i, priority_usage[i]);        }    }} // Recommended Priority Allocation Strategy for GD32VF103void apply_gd32vf103_priority_policy(void) {    printf("Applying GD32VF103 recommended priority policy...\n");    // Policy Principles:    // 1. System critical functions use high priority (0-31)    // 2. Real-time communication uses medium-high priority (32-95)    // 3. General peripherals use medium-low priority (96-191)    // 4. Background tasks use low priority (192-255)    // System Critical Interrupts    ECLIC-&gt;interrupts[ECLIC_IRQ_SYSTICK].INTCTL = 0x10;    // System Timer    ECLIC-&gt;interrupts[ECLIC_IRQ_WWDG].INTCTL = 0x00;       // Watchdog    // Real-time Communication Interrupts    ECLIC-&gt;interrupts[ECLIC_IRQ_USART0].INTCTL = 0x20;     // Main Serial Port    ECLIC-&gt;interrupts[ECLIC_IRQ_USART1].INTCTL = 0x28;     // Auxiliary Serial Port    ECLIC-&gt;interrupts[ECLIC_IRQ_SPI0].INTCTL = 0x30;       // SPI Interface    // Timer Interrupts    ECLIC-&gt;interrupts[ECLIC_IRQ_TIMER0].INTCTL = 0x40;     // Main Timer    ECLIC-&gt;interrupts[ECLIC_IRQ_TIMER1].INTCTL = 0x48;     // General Timer    // GPIO and General Peripherals    ECLIC-&gt;interrupts[ECLIC_IRQ_EXTI0].INTCTL = 0x60;      // External Interrupt 0    ECLIC-&gt;interrupts[ECLIC_IRQ_ADC0].INTCTL = 0x70;       // ADC Conversion    printf("✓ GD32VF103 priority policy applied\n");}
    
    
    4.2.5 Summary

    Through the detailed configuration instructions and code implementations in this section, we have established a complete ECLIC configuration system:

    1. Systematic Configuration Process:Strictly configure in the order of global enablement → vector table → priority → interrupt enablement

    2. Efficiency of Vector Table:Hardware vectorization significantly reduces interrupt latency and improves system real-time performance

    3. Reasonableness of Priority:The layered priority strategy based on GD32VF103 practice ensures critical task responsiveness

    4. Safety of Enablement Control:Three-layer enablement control provides fine-grained interrupt management capabilities

    Key Best Practices

    • Always enable global interrupts after configuration is complete
    • Set appropriate priorities for all interrupts to avoid conflicts
    • Use hardware vector mode for optimal performance
    • Regularly check interrupt configuration status to ensure system stability

    Practices with domestic chips like GD32VF103 demonstrate that correct ECLIC configuration can provide reliable and efficient interrupt handling capabilities for embedded systems, meeting various needs from simple control to complex real-time systems.

    Leave a Comment