本章涉及到CLINT寄存器。如何通过该寄存器的值控制定时器实现中断。
一、CLINT寄存器
CLINT(基地址:0x02000000)是用于处理器本地中断的控制器,它主要管理定时器中断和软件中断,并将这些中断分发到RISC-V处理器内核。
关于定时器中断主要涉及两个寄存器,mtime(偏移地址:0xBFF8)和mtimecmp(偏移地址:0x4000)。其中mtime用于存储自开机以来经过的时钟数。 mtimecmp中的值用来和mtime中的值进行对比,若mtimecmp中的值小于等于mtime中的值则会触发定时器中断。
同样的,想要使能定时器中断,需要把mie寄存器中的第7位MTIE值置为1。
void timer_load(int interval) { int id = 0; *(uint64_t *)CLINT_MTIMECMP(id) = *(uint64_t *)CLINT_MTIME + interval; } void timer_init() { timer_load(TIMER_INTERVAL); w_mie(MIE_MTIE); }
二、处理定时器中断
可以通过mcause的值来区分是不是定时器中断。想要实现循环中断,只需要在中断处理时,更新mtimecmp的值位mtime的值加上一个固定间隔的数即可。
void timer_load(int interval)
{
int id = 0;
*(uint64_t *)CLINT_MTIMECMP(id) = *(uint64_t *)CLINT_MTIME + interval;
}
void timer_handler()
{
_tick++;
printf("tick: %d\n", _tick);
timer_load(TIMER_INTERVAL);
}
reg_t trap_handler(reg_t epc,reg_t cause){
reg_t return_pc = epc;
reg_t cause_code = cause & MCAUSE_MASK_ECODE;
Uart_puts("traps!\n");
if(cause & MCAUSE_MASK_INTERRUPT){
switch (cause_code)
{
case 3:
Uart_puts("software interruption!\n");
break;
case 7:
Uart_puts("timer interruption!\n");
timer_handler();
break;
case 11:
Uart_puts("external interruption!\n");
external_interrupt_handler();
break;
default:
printf("Unknown async exception! Code = %ld\n", cause_code);
break;
}
}else {
/* Synchronous trap - exception */
printf("Sync exceptions! Code = %ld\n", cause_code);
panic("OOPS! What can I do!");
return_pc += 4;
}
return return_pc;
}