课前准备
定时器中断的触发是由硬件来检查和处理的。在RISC-V架构中,CLINT(Core Local Interruptor)负责管理定时器中断。具体来说,硬件会不断比较 mtime 寄存器和每个硬件线程的 mtimecmp 寄存器的值,当 mtime 的值达到或超过 mtimecmp 的值时,硬件会触发一个定时器中断。
CLINT(Core Local Interruptor)寄存器的布局。
+---------------------------+
| CLINT_BASE |
+---------------------------+
| MSIP[0] |
+---------------------------+
| MSIP[1] |
+---------------------------+
| ... |
+---------------------------+
| MSIP[N-1] |
+---------------------------+
| RESERVED |
+---------------------------+
| MTIMECMP[0] |
+---------------------------+
| MTIMECMP[1] |
+---------------------------+
| ... |
+---------------------------+
| MTIMECMP[N-1] |
+---------------------------+
| RESERVED |
+---------------------------+
| MTIME |
+---------------------------+
详细说明
基地址(CLINT_BASE):
CLINT寄存器的基地址,通常定义为 0x2000000L。
MSIP 寄存器(Machine Software Interrupt Pending Registers):
每个硬件线程(hart)都有一个MSIP寄存器,用于触发软件中断。
地址从 CLINT_BASE 开始,每个寄存器占用4字节。
例如,MSIP[0] 的地址是 CLINT_BASE + 0 4,MSIP[1] 的地址是 CLINT_BASE + 1 4,以此类推。
MTIMECMP 寄存器(Machine Timer Compare Registers):
每个硬件线程都有一个MTIMECMP寄存器,用于设置定时器中断的触发时间。
地址从 CLINT_BASE + 0x4000 开始,每个寄存器占用8字节。
例如,MTIMECMP[0] 的地址是 CLINT_BASE + 0x4000 + 0 8,MTIMECMP[1] 的地址是 CLINT_BASE + 0x4000 + 1 8,以此类推。
MTIME 寄存器(Machine Timer Register):
这是一个全局寄存器,记录自系统启动以来的时钟周期数。
地址是 CLINT_BASE + 0xBFF8,占用8字节。
硬件定时器中断
系统启动时候, 不会初始化MTIMECMP, 需要自己手动的去设置,
#define CLINT_BASE 0x2000000L
#define CLINT_MTIME (CLINT_BASE + 0xBFF8)
void timer_load(int interval)
{
int id = r_mhartid();
//从MTIME读取最新的值,加上一个固定的触发间隔,进行初始化
*(uint64_t*)CLINT_MTIMECMP(id) = *(uint64_t*)CLINT_MTIME + interval;
}
陷入入口, 触发时间中断处理
reg_t trap_handler(reg_t epc, reg_t cause)
{
reg_t return_pc = epc;
reg_t cause_code = cause & MCAUSE_MASK_ECODE;
if (cause & MCAUSE_MASK_INTERRUPT) {
switch (cause_code) {
case 7:
uart_puts("timer interruption!\n");
timer_handler();
break;
} else {
}
return return_pc;
}
timer_handler 处理时候,还要触发一个时间中断。 循环往复,如此,酸爽。
_tick 是一个全局变量, 所有hart共享。怎么保证线程安全呢?
static uint32_t _tick = 0;
void timer_handler()
{
_tick++;
printf("tick: %d\n", _tick);
timer_load(TIMER_INTERVAL);
}
软件定时器中断
硬件:精度高, 资源少, 不灵活
软件:资源多, 灵活,精度稍微差一些
void timer_handler()
{
_tick++;
printf("tick: %d\n", _tick);
timer_check(); // 在触发之前加上了一个任务多检查,去执行用户的任务,这一步有时间消耗
timer_load(TIMER_INTERVAL);
schedule();
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。