1. riscv中断框架回顾
在riscv中,中断被分为CLINT和PLIC两类,CLINT负责架构定时器和核间中断管理,PLIC负责外设中断的管理。由于本系列教程我们的定时器没有选用架构的定时器,所以CLINT功能我们用不到,我们只需要关心PLIC外设中断就行了。 在后续的s模式系统全局中断寄存器设置上我们只会去使能和处理外设控制器中断,这样就简化了中断的设置和处理。
2. 全志PLIC外设中断控制器简介
在全志的手册中我们查找到PLIC章节,寄存器其实也不多,比较简单的中断控制器:
我们主要关心图中红框中的寄存器,其他的寄存器大部分都需要在m模式下访问,s模式下无权限去访问这些寄存器:
- PLIC_PRIO_REGn:优先级寄存器,如果为0表示此中断无效,初始化时我们需要将所有中断的优先级设置为1。
- PLIC_SIE_ REGn:s模式中断使能寄存器,每个bit位代表对应一个中断号,设置1表示使能。
- PLIC_STH_REG:s模式中断优先级阈值寄存器,优先级大于阈值的中断才会被处理,初始化时我们会将阈值设置为0,表示处理所有中断。
- PLIC_SCLAIM_REG:中断号和ACK寄存器,读操作表示当前产生中断的外设中断号,写操作表示对应的中断处理完成了。
从上面的分析可以看出全志的PLIC中断控制器还是比较简单的,因为全志D1为单核处理器,所以这里也不需要考虑多核中断的处理。
3. 全志PLIC中断控制器接口封装
3.1 plicInit
VOID plicInit (VOID) { INT i; // 关闭 S 模式下所有中断 write_csr("sie", 0); // 清除 S 模式下所有中断 write_csr("sip", 0); // 使能 S 模式下外部控制器中断 set_csr("sie", 1 << IRQ_S_EXT); // 关闭控制器所有中断 for (i = 0; i < 9; i++) { writel(0, PLIC_BASE + 0x2080 + 4 * i); } // 处理所有优先级中断 for (i = 0; i < 256; i++) { writel(1, PLIC_BASE + 4 * i); } writel(0, PLIC_BASE + PLIC_STH_REG); }
首先关闭和清除s模式系统中断寄存器,然后只使能外部控制器中断,接着关闭PLIC所有外设的中断,并设置优先级和阈值。
3.2 plicVectorEnable
VOID plicVectorEnable (UINT32 ulVector) { UINT32 uiRegAddr = PLIC_SIE_REG(ulVector); UINT32 uiVal = PLIC_SIE_POS(ulVector); writel(readl(uiRegAddr) | uiVal, uiRegAddr); }
原理很简单,根据中断号计算出要操作的寄存器位置,然后设置为1表示使能。
3.3 plicVectorDisable
VOID plicVectorDisable (UINT32 ulVector) { UINT32 uiRegAddr = PLIC_SIE_REG(ulVector); UINT32 uiVal = PLIC_SIE_POS(ulVector); writel(readl(uiRegAddr) & ~uiVal, uiRegAddr); }
和使能原理类似,只是将对应的位置清零,表示关闭中断。
3.4 plicVectorGet
UINT32 plicVectorGet (VOID) { // 清除 S 模式下外部控制器中断 clear_csr("sip", 1 << IRQ_S_EXT); return (readl(PLIC_BASE + PLIC_SCLAIM_REG)); }
这个接口只会在产生中断的时候调用,所以我们首先要清除s模式系统中断状态寄存器中的外设控制器中断状态,然后返回当前产生中断的硬件中断号。
3.5 plicVectorAckDone
VOID plicVectorAckDone (UINT32 ulVector) { writel(ulVector, PLIC_BASE + PLIC_SCLAIM_REG); }
不解释,是个人都能看懂吧。
3.6 plicVectorIsEnable
BOOL plicVectorIsEnable (UINT32 ulVector) { UINT32 uiRegAddr = PLIC_SIE_REG(ulVector); UINT32 uiVal = PLIC_SIE_POS(ulVector); return (readl(uiRegAddr) & uiVal) ? TRUE : FALSE; }
读取中断号对应的寄存器来检测中断是否使能。
4. bsp修改
首先需要将封装好的6个接口填入bspLib.c中的bspIntxxx对应的接口中,如下所示:
然后需要在bspMsp.h中添加PLIC的寄存器地址映射,如下所示。
接着需要根据手册将uart.h和timer.h中的串口中断号和定时器中断号修改为手册的值,如下所示。
#define UART_VECTOR(x) ((x) + 18) #define TIMER_VECTOR(x) ((x) + 75)
最后需要将bspTickInit中定时器中断号初始化为正确的值,如下所示。
5. 系统启动
设置完后,编译bsp重启开发板引导,如果一切正常就可以在串口输入命令了,同时通过ints命令查看定时器产生的中断数来确定定时器中断是否工作正常了,如下所示。
6. 小结
到此为止我们已经将全志D1的SylixOS最小系统给适配完成了,得益于之前为全志R16适配的驱动,我们只需要做简单的修改就能复用代码了。后续会讲解sd卡驱动和网络驱动的开发,最小系统的开发我们只用了四个章节快速带过,如果中间有看不明白的请先回顾开始提到的4篇专题文章或者在文章下留言。
最小系统的BSP源码可以在https://pan.baidu.com/s/15qXCrqqjzpFz-FvNat5tkQ?pwd=koex下载。
2022年8月20日 14:42 1F
博主你好,能重新更新下源码链接不,这个过期了
2022年9月2日 21:56 B1
@ zz 已经更新了