课前准备

定时器中断的触发是由硬件来检查和处理的。在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();
}

putao
8 声望1 粉丝

推动世界向前发展,改善民生。