当时钟中断初始化完成后,每隔固定的时间,定时器会产生一次时钟中断。在本节中,以每隔1秒在显示器上显示字符串“hello,world.”为例,详细介绍MaQueOS在收到时钟中断后的处理过程。
在定时器配置和使能完成,并使能定时器局部中断和全局中断后,定时器根据设定的初始值进行自减,当自减为0时,硬件将ESTAT寄存器中IS字段的第11位(定时器中断位)设置为1。ESTAT寄存器的部分字段如图C.4所示,该寄存器用于记录例外 的状态信息。其中,IS字段中的13个位用于记录LoongArch架构支持的13个中断的状态,若某个中断对应的位被硬件设置为1,则表示产生了该中断。当定时器产生中断,硬件将ESTAT寄存器的IS字段中的第11位设置为1后,若ECFG寄存器的LIE字段中的第11位为1,且CRMD寄存器中的IE字段同时为1,则硬件会进行如下操作:
1)首先将CRMD寄存器中的IE字段保存在PRMD寄存器的PIE字段中,并将CRMD寄存器中的IE字段设置为0,表示禁止中断。然后,将CRMD寄存器的PLV字段保存在PRMD寄存器的PPLV字段中,并将CRMD寄存器中的PLV字段设置为0,PLV字段的详细介绍参见第3章。PRMD寄存器的部分字段如图C.2所示,如前所述,该寄存器在中断产生时,用于保存CRMD寄存器中的IE和PLV字段;在中断返回时,将这2个字段的值恢复到CRMD寄存器中。
2)将产生中断时下一条运行指令的地址保存到ERA寄存器中。当时钟中断初始化完成,并开启系统中断后,系统进入死循环(详见代码清单2.1的第7~8行),等待时钟中断产生。当时钟中断产生后,ERA寄存器中保存的是死循环中的指令的地址。
3)跳转到EENTRY寄存器中存放的中断处理函数exception_handler的入口地址处,具体存放过程详见代码清单2.2的第15行。
进入中断处理函数exception_handler后,首先进行中断现场的保存与恢复的操作,exception_handler函数的实现详见代码清单2.4。
代码清单2.4 exception_handler函数(第1版)
下面对代码清单2.4进行说明。
·第36行: 使用addi.d汇编指令 ,将sp寄存器的值减去0xf0,即在内核初始化栈上预留0xf0字节大小的空间,用于保存中断现场。sp寄存器的初始化在_start函数中完成,start函数的实现详见代码清单2.5。
·第37行: LoongArch架构中共有32个通用寄存器,中断现场由除r0和sp寄存器之外的30个寄存器的值组成。为了提升代码的简洁度,将30个寄存器的保存与恢复定义为store_load_regs宏(定义详见第1~32行)。通过调用store_load_regs宏,将30个寄存器组成的中断现场保存到上一行预留的内核初始化栈上。
·第38行: 中断现场保存结束后,调用do_exception函数进行中断处理,处理过程详见2.2.3节。
·第39行: 中断处理结束后,调用store_load_regs宏,将在第37行保存到内核初始化栈上的中断现场恢复到30个对应的寄存器中。
·第40行: 中断现场恢复后,将预留的内核初始化栈中的空间释放。
·第41行: 使用ertn汇编指令进行中断返回,返回过程详见2.2.4节。
代码清单2.5 start函数(第1版)
下面对代码清单2.5进行说明。
·第3行: 使用la汇编指令,将第6~7行申请的内核初始化栈的栈顶地址赋值给sp寄存器。
·第4行: 设置好内核初始化栈后,进入main函数运行。
·第6~7行: 为内核初始化栈申请4KB地址空间,kernel_init_stack变量指向栈顶位置。
保存好中断现场后,调用do_exception函数对中断进行处理,do_exception函数的实现详见代码清单2.6。do_exception函数通过判断中断的类型,调用相应的中断处理程序进行中断处理。
代码清单2.6 do_exception函数(第1版)
下面对代码清单2.6进行说明。
·第10行: 调用read_csr_32库函数,读取ESTAT寄存器的值。
·第11~15行: 如2.2.1节所述,通过查看ESTAT寄存器中IS字段的第11位是否为1,来判断该中断是否为定时器中断,若是,则调用timer_interrupt函数处理时钟中断(见第13行)。timer_interrupt函数的实现详见代码清单2.7。在 第14行 中,通过向TICLR寄存器的CLR字段写1(CSR_TICLR_CLR),清除定时器中断标记。CLR字段在TICLR寄存器中的位置如图C.6所示。
代码清单2.7 timer_interrupt函数(第1版)
在 第3行 中,调用printk函数,实现在显示器上显示“hello,world.”字符串。
当timer_interrupt函数完成时钟中断处理之后,会返回到exception_handler函数中的调用中断处理函数do_exception的指令的下一条指令,即代码清单2.4中第39行的代码。如代码清单2.4的第39~41行所示,将中断现场恢复后,使用ertn汇编指令进行中断返回。硬件会进行如下操作:
1)将PRMD寄存器中的PPLV和PIE字段的值分别恢复到CRMD寄存器的PLV和IE字段中。
2)将ERA寄存器中保存的代码清单2.1的第7~8行死循环中指令的地址加载到PC寄存器 中,因此,时钟中断处理结束后,系统继续回到死循环状态,等待下一次时钟中断产生。
之后,定时器每隔1秒会产生一次时钟中断信号,在显示器上显示字符串“hello,world.”。