中断是指处理器内核在顺序执行程序指令流的过程中突然被别的请求打断而中止执行当前的程序,转而去处理别的事情,待其处理完了别的事情,然后重新回到之前程序中断的点继续执行之前的程序指令流。 芯来科技N级别内核支持内部中断和外部中断,其中内部中断包括计时器中断和软件中断。这里我们以Nuclei SDK中demo_timer为例,简单讲解内部中断的非向量处理模式。 系统环境 Windows 10-64bit 软件平台 Nuclei Studio IDE 202102版 硬件需求 RV-STAR开发板
RV-STAR
中断初始化配置介绍
为了方便进一步讲解,我们先打开Github上的demo timer工程main.c函数源码:
https://github.com/Nuclei-Software/nuclei-sdk/blob/master/application/baremetal/demo_timer/main.c
其中,main函数调用的“ECLIC_Register_IRQ”函数就是中断配置函数。这里调用了两次,分别配置了计时器中断和软件中断,我们详细介绍计时器中断配置时各参数的作用,这样软件中断可参考对应参数来理解。
截取计时器中断初始化函数代码如下:
returnCode = ECLIC_Register_IRQ(SysTimer_IRQn, ECLIC_NON_VECTOR_INTERRUPT, ECLIC_LEVEL_TRIGGER, 1, 0,mtimer_irq_handler); /* register system timer interrupt */
这里一共有六个参数
接下来我们将一一讲解其作用
1
SysTimer_IRQn
第一个参数设置要配置的是中断号。我们首先配置的是Timer中断,所以这里是Timer中断的中断号。所有可用的中断号都在“IRQn_Type”枚举当中,在gd32vf103.h文件当中可以查看RV-STAR的所有中断号,如果配置不同的中断,需要修改此参数为对应的中断号。
中断号与中断向量表一一对应,关于中断向量表我们会在后文详解。
2
ECLIC_NON_VECTOR_INTERRUPT
第二个参数配置的是中断的处理模式。中断包含向量处理模式和非向量处理模式,配置成为非向量处理模式,该中断被处理器内核响应后,处理器会直接跳入到所有非向量中断共享的入口地址,该入口地址可以通过软件进行设置。在进入所有非向量中断共享的入口地址之后,处理器会开始执行一段共有的软件代码,包含一系列的入栈操作,也就是保存上下文的功能。之后跳转至中断对应的中断处理函数中,待中断处理函数执行完毕,程序执行一系列出栈操作,来恢复上下文,使得程序可以恢复至中断之前程序执行的位置。
关于中断的非向量处理模式与向量处理模式之间的区别,后期我们会进行详细讲解,如有兴趣,读者可阅读Nuclei 指令集架构手册 (https://www.rvmcu.com/quickstart-doc-u-nuclei_n_isa.html) 进行了解。
3
ECLIC_LEVEL_TRIGGER
第三个参数配置的是中断触发模式。中断触发模式支持上升沿触发,下降沿触发和电平触发这三种模式。这里配置的是电平触发模式,其他触发模式可以在core_feature_eclic.h文件的ECLIC_TRIGGER枚举中查看。
4
1
5
0
我们把第四个参数和第五个参数合并在一起介绍。第四个参数配置的是中断的级别,第五个参数配置的是中断的优先级。这两个参数配置的是此中断的仲裁等级,这两个数值越高,对应的中断仲裁等级越高,也会被优先处理。这里我们demo_timer例程没有涉及到中断仲裁,所以此处不过多介绍。
想了解更多这部分知识,可以阅读RV-STAR开发板中断快速入门(https://www.rvmcu.com/quickstart-doc-u-nuclei_interrupt_quickstart.html)进行学习。
6
mtimer_irq_handler
第六个参数配置的是中断处理函数。在讲解此参数前,需要一些中断向量表的知识。
中断向量表是指在存储器里面开辟的一段连续的地址空间,该地址空间的每个字(Word)用于存储ECLIC每个中断源对应的中断服务程序(Interrupt Service Routine,ISR)函数的PC地址。中断向量表的起始地址由CSR寄存器mtvt指定,通常可以将mtvt寄存器设置为整个代码段的起始位置。中断向量表的作用非常重要,当处理器响应某个中断源后,无论中断是向量处理模式还是非向量处理模式,硬件最终都将通过查询中断向量表中存储的PC地址跳转到其对应的中断服务程序函数中去。
由此可知,这个参数配置的是中断跳转的中断服务程序地址。需要注意的是,RV-STAR的中断向量表是存储在flash当中的,这就意味着运行时不能直接修改其中的数据,所以在不修改源码的情况下,RV-STAR不能通过这个参数修改中断向量表。
RV-STAR虽然不能通过设置此参数来修改使用其他中断处理函数,但是可以实现默认的中断处理函数。在main.c文件开头位置有如下两个宏定义:
#define mtimer_irq_handler eclic_mtip_handler
#define mtimer_sw_irq_handler eclic_msip_handler
后面的“eclic_mtip_handler”和“eclic_msip_handler”分别就是timer中断和软件中断的默认中断处理函数。详细的中断向量表可以在startup_gd32vf103.S文件开头部分查看,这里就不一一列举。
RV-STAR
中断处理函数介绍
有了以上知识,我们回到main.c文件中,会找到“mtimer_irq_handler”函数和“mtimer_sw_irq_handler”函数的具体实现,这两个函数就是对应的中断处理函数。
因为使用的是非向量处理模式,所以在执行中断处理函数前会跳转到非向量中断统一的中断入口,保存上下文入栈,再跳转至对应的中断处理函数中执行里面的指令。当中断处理函数执行结束时,会跳转至非向量中断统一的结束位置,恢复上下文,这样程序会恢复到中断之前执行的位置和状态。
进入和退出中断过程可以参考以下两图:
在Nuclei Studio中新建一个demo_timer工程。芯来科技官网的文档与工具页面可以下载Nuclei Studio,下载后解压缩,在Nuclei Studio解压缩的目录下双击NucleiStudio.exe即可启动IDE。
第一次启动Nuclei Studio将会弹出对话框要求设置Workspace目录路径,该目录将用于存放后续创建的项目工程文件。设置好Workspace路径,再单击“Launch”启动Nuclei Studio。
启动后推荐打开Launch Bar功能,方便快速编译和调试。打开菜单栏“Window -> Preferences”,搜索“bar”,勾选第一个选项“Enable the Launch Bar”即可启用Launch Bar功能。
在菜单栏中,选择“File-> New -> C/C++ Project”开始新建工程,在弹窗中双击选择“C Managed Build”。
新的页面中“Project name”填写“timer”,“Project type” 选择“Nuclei SDK Project For GD32VF103 SoC”和“RISC-V Cross GCC”,如下图,点击“Next”。新的页面不用修改,直接点击“Next”即可。
在选择模板工程页面修改“Project Example”选项为“baremetal_demo_timer”,后续页面不需要修改,点击“Next”直到最后一页,点击“Finish”完成新建demo_timer工程。
例子main函数代码和对照介绍如下:
int main(void)
{
uint32_t returnCode;
returnCode = ECLIC_Register_IRQ(
SysTimer_IRQn, ECLIC_NON_VECTOR_INTERRUPT, ECLIC_LEVEL_TRIGGER, 1, 0,mtimer_irq_handler); /* 配置timer中断*/
__enable_irq(); /* 使能全局中断 */
setup_timer(); /* timer初始化 */
while (int0_cnt < 10); /* while循环,用来等timer中断。每当timer中断执行一次,int0_cnt会加一,直到timer中断被触发10次,退出while循环 */
ECLIC_DisableIRQ(SysTimer_IRQn); /* 关闭timer中断 */
returnCode = ECLIC_Register_IRQ(
SysTimerSW_IRQn, ECLIC_NON_VECTOR_INTERRUPT,
ECLIC_POSTIVE_EDGE_TRIGGER, 2, 0,
mtimer_sw_irq_handler); /* 配置软件中断 */
do {
if (msip_trig_flag == 1) {
msip_trig_flag = 0;
SysTimer_SetSWIRQ(); /* 触发软件中断 */
wait_seconds(1); /* 等待一秒 */
}
} while (int1_cnt < 10); /* while循环,用来循环触发软件中断。每当软件中断执行一次,int1_cnt会加一,直到软件中断被触发10次,退出while循环 */
printf("MTimer msip and mtip interrupt test finish and passrn");
if (returnCode != 0) {
return -1;
}
while (1);
return 0;
}
工程新建完毕,需要在Launch bar工具中切换使用openocd的debug配置,如下图:
参考运行结果如下:
更多实验例程 www.rvmcu.com