中断/异常/NMI处理
中断处理
SPV1x SDK中以中断向量表的方式进行中断处理,支持中断抢占,支持8个抢占优先级。 为了提高中断响应速度并增强用户程序的灵活性,SPV1x SDK将中断向量表放置于IRAM代码空间下, 支持实时配置(attach)向量表中的外设中断服务函数入口。
外设中断服务函数统一以 void xxx_irq_entry(void) 形式定义,其中xxx为外设名称,如dma、gpio和uart0等, 与上图中断源名称一一对应。函数声明形式如下:
__attribute__((interrupt, aligned(4))) void xxx_irq_entry(void)
{
nested_irq_enter();
xxx_irq_handler();
nested_irq_exit();
}
__attribute__((interrupt, aligned(4))) 用于区分中断服务函数与普通用户函数,其通用寄存器现场保护和返回机制上存在明显差异。
nested_irq_enter/exit() 为支持中断嵌套和抢占所必须的步骤。
xxx_irq_handler()为外设xxx的用户定义中断处理流程,包括清除外设xxx自身的中断Pending位(PD)等必要操作。为了提升代码跳转性能,我们鼓励用户直接在xxx_irq_entry()中以非调用子函数的方式进行用户中断处理流程的书写,比如直接操作宏定义的外设寄存器。
综上所述,以PWM为例,常规的外设中断完整配置流程如下:
配置PWM外设的控制寄存器,开启PWM的中断使能位(IE)
配置PWM于中断控制器ECLIC中相关寄存器:
通过
eclic_irq_init()配置PWM中断在向量表模式下的抢占优先级。通过
eclic_irq_enable()开启ECLIC上PWM中断请求信号的输入通路。
准备外设中断处理函数
pwm_irq_entry(),并以静态编译或动态挂载的方式接入中断向量表。开启全局中断使能
global_irq_enable()。
备注
SDK提供
cpu_enter_critical()和cpu_exit_critical()用于用户程序对共享资源执行安全访问。
异常处理
SPV1x用户程序流程中的同步异常和Non-maskable Interrupt(NMI)均执行统一的处理函数: trap_entry()。
trap_entry()首先对 mepc,mcause,mtval等关键CSR寄存器现场进行保存,然后判断 mcause.exccode != 0xFFF成立,
则确认发生同步异常,并分支进入对应的处理流程。反之,则确认发生NMI。
__attribute__((interrupt, aligned(64))) void trap_entry(void)
{
uint32_t mcause;
uint32_t mepc;
uint32_t mtval;
mepc = __RV_CSR_READ(CSR_MEPC);
mcause = __RV_CSR_READ(CSR_MCAUSE);
mtval = __RV_CSR_READ(CSR_MTVAL);
if((mcause & 0xFFF) == 0xFFF) {
/* NMI handling process here */
} else {
/* All exceptions except for NMI are handled here */
}
while(1);
}
NMI处理
如上文所述,Non-maskable Interrupt(NMI)发生时,CPU PC跳转至 trap_entry() 入口执行, 通过判断 mcause.exccode == 0xFFF 确认NMI发生,并分支进入对应的处理流程。
可以触发NMI的事件源(外设中断信号)通过DEV_INT->NMI_MASK0寄存器进行配置,各个比特位对应含义与WFE唤醒机制中使用的 DEV_INT->MASK0寄存器一一对应。
API说明
-
enum IRQn_Type
外设中断源在ECLIC中的入口通道号。
IRQ_Source_MSIP:软件中断源
IRQ_Source_MTIP:CPU核内Machine Timer中断源
IRQ_Source_UART1:UART1中断源
IRQ_Source_TIMER1:TIMER1中断源
IRQ_Source_CAPTURE1:CAPTURE1中断源
IRQ_Source_SPI1:SPI1中断源
IRQ_Source_I2C0:I2C0中断源
IRQ_Source_PMU:PMU中断源
IRQ_Source_DMA:DMA中断源
IRQ_Source_GPIO:GPIO中断源
IRQ_Source_UART0:UART0中断源
IRQ_Source_TIMER0:TIMER0中断源
IRQ_Source_CAPTURE0:CAPTURE0中断源
IRQ_Source_SPI0:SPI0中断源
IRQ_Source_PWM:PWM中断源
IRQ_Source_ADC:ADC中断源
IRQ_Source_DSM:DSM中断源
IRQ_Source_WDT:WDT中断源
-
enum eclic_irq_priority_t
中断抢占优先级。0-7优先级依次递增。
IRQ_Priority_0: 抢占优先级P0,为最低优先级
IRQ_Priority_1: 抢占优先级P1
IRQ_Priority_2: 抢占优先级P2
IRQ_Priority_3: 抢占优先级P3
IRQ_Priority_4: 抢占优先级P4
IRQ_Priority_5: 抢占优先级P5
IRQ_Priority_6: 抢占优先级P6
IRQ_Priority_7: 抢占优先级P7,为最高优先级
-
void eclic_irq_init(IRQn_Type source, eclic_irq_priority_t priority)
对ECLIC指定外设中断通道进行配置,设置为向量模式和电平触发模式。
- 参数
source – 配置外设中断源,通过枚举定义
IRQn_Type选择。priority – 该中断的抢占优先级,通过枚举定义
eclic_irq_priority_t选择。
- 返回
无
-
void eclic_irq_enable(IRQn_Type source)
使能ECLIC指定外设中断通道。
- 参数
source – 配置外设中断源,通过枚举定义
IRQn_Type选择。
- 返回
无
-
void eclic_irq_disable(IRQn_Type source)
失能ECLIC指定外设中断通道。
- 参数
source – 配置外设中断源,通过枚举定义
IRQn_Type选择。
- 返回
无
-
void global_irq_enable()
使能全局中断响应。
- 返回
无
-
void global_irq_disable()
失能全局中断响应。
- 返回
无