volatile int timer_n = 0;
void timer_handler (void) {
timer_n++;
}
timer_handler:
lui a0, %hi(timer_n)
lw a1, %lo(timer_n)(a0)
addi a1, a1, 1
sw a1, %lo(timer_n)(a0)
ret
__attribute__((interrupt))
void timer_handler (void) {
timer_n++;
}
timer_handler:
addi sp, sp, -16
sw a0, 12(sp) # 4-byte Folded Spill
sw a1, 8(sp) # 4-byte Folded Spill
lui a0, %hi(timer_n)
lw a1, %lo(timer_n)(a0)
addi a1, a1, 1
sw a1, %lo(timer_n)(a0)
lw a0, 12(sp) # 4-byte Folded Reload
lw a1, 8(sp) # 4-byte Folded Reload
addi sp, sp, 16
mret
对比发现,添加 __attribute__((interrupt)) 修饰后,编译器给用到的寄存器添加保存、恢复代码,并将 ret 替换为 mret.
既然 ISR 对自己使用的寄存器都会提前保存、返回时恢复,,那么发生中断嵌套就不会破坏 ISR 的上下文环境,,可以允许中断嵌套的发生
但是在中断返回时 CPU 需要 mepc 和 mstatus 中的信息确定返回地址和返回特权级,,这些寄存器编译器没有给我们保存、恢复,,因此还需要手动添加代码处理
__attribute__((interrupt))
void timer_handler (void) {
int mepc, mstatus;
asm("csrr %0, mepc\n\t"
"csrr %1, mstatus\n\t"
"csrsi mstatus, 8" // mstatus.MIE = 1, interrupt enable
: "=r" (mepc), "=r" (mstatus));
// ISR code
asm("csrci mstatus, 8\n\t" // mstatus.MIE = 0, interrupt disable
"csrw mepc, %0\n\t"
"csrw mstatus, %1"
: : "r" (mepc), "r" (mstatus));
}
实现中断嵌套的关键是在重新使能中断之前保存 mepc、mstatus,并在执行 mret 之前恢复它们
但在 CLINT 下实现中断嵌套没有意义,因为 CLINT 下没有中断优先级,中断嵌套只有配合可配置的中断优先级才有意义。所以一般 MCU 厂商会使用更高级的中断控制器替代 CLINT,而不会直接使用官方简陋的 CLINT,,比如沁恒 MCU 使用了自己设计的 PFIC 中断控制器,,添加了中断优先级等功能。。
最近编辑记录 XIVN1987 (2024-04-15 12:40:43)
在线