Picorv32处理器中断的实现
Picorv32
是一个非常精简的RISCV核心,实现了RISC-V RV32IMC指令集。源代码开源且核心只有一个不到3000行的Verilog文件,FPGA实现非常简单,可以使用开源的iverilog
软件进行仿真,非常适合FPGA学习和性能要求不高的一些FPGA应用场合。实际上Picorv32
已经有很多应用,甚至有ASIC上的应用,这里作为学习目的,我也用这个小核心做了一个非常简单的SOC,在这个过程中也遇到了一些问题,这里做一些简单的记录。
这篇文章我们就来讨论一下这个小核心的中断系统的实现,中断系统对于嵌入式应用来说是必不可少的,所以即便是一个非常小的SOC,我们也需要实现中断系统,在Picorv32
中,实际已经内嵌了一个自定义的中断系统,而标准的中断系统对于这个小核心来说过于复杂,所以只是采用了一个硬件简单,软件复杂的中断系统。这个自定义的中断系统中使用了几条自定义的指令用于中断屏蔽和等待中断事件等操作,所以在这个核心中对于中断寄存器的操作只能通过插入原始的机器码来实现,这里也刚好学习了怎么实现在C语言中插入原始的机器码。
Picorv32
中引入了4个自定义的寄存器来实现自定义的中断操作,q0用于保存中断的返回地址,q1用于保存所有pending的中断,每个bit代表一个中断,用户需要对这个寄存器的值进行遍历,将所有的中断进行处理然后返回。q2和q3用户可以自由使用。一下是几条自定义指令的格式和具体含义:
getq rd, qs
: 读取Q寄存器到通用寄存器
f7 | rs2 | qs | f3 | rd | opcode |
---|---|---|---|---|---|
31-25 | 24-20 | 19-15 | 14-12 | 11-7 | 6-0 |
0000000 | ----- | 000XX | 000 | XXXXX | 0001011 |
setq qd, rs
: 读取通用寄存器到Q寄存器
f7 | rs2 | rs | f3 | qd | opcode |
---|---|---|---|---|---|
31-25 | 24-20 | 19-15 | 14-12 | 11-7 | 6-0 |
0000001 | ----- | XXXXX | 000 | 000XX | 0001011 |
retirq
: 中断返回,CPU从q0寄存器读取返回地址设置PC并重新使能中断
f7 | rs2 | rs | f3 | rd | opcode |
---|---|---|---|---|---|
31-25 | 24-20 | 19-15 | 14-12 | 11-7 | 6-0 |
0000010 | ----- | 00000 | --- | 00000 | 0001011 |
maskirq
: 中断屏蔽寄存器,从通用寄存器中读取值到中断屏蔽寄存器,并将之前的值读回,中断屏蔽寄存器为1表示屏蔽中断。
f7 | rs2 | rs | f3 | rd | opcode |
---|---|---|---|---|---|
31-25 | 24-20 | 19-15 | 14-12 | 11-7 | 6-0 |
0000011 | ----- | XXXXX | --- | XXXXX | 0001011 |
waitirq
: 等待中断,相当于WFE指令,CPU会停止执行指令,RD寄存器是等待的中断mask。
f7 | rs2 | rs | f3 | rd | opcode |
---|---|---|---|---|---|
31-25 | 24-20 | 19-15 | 14-12 | 11-7 | 6-0 |
0000100 | ----- | 00000 | --- | XXXXX | 0001011 |
timer
: 设置CPU定时器装载值,定时器向下计数并在等于0的时候触发中断。RS写入值,RD读回定时器的值,写0表示禁用定时器。
f7 | rs2 | rs | f3 | rd | opcode |
---|---|---|---|---|---|
31-25 | 24-20 | 19-15 | 14-12 | 11-7 | 6-0 |
0000101 | ----- | XXXXX | --- | XXXXX | 0001011 |
以上就是自定义中断模块相关的中断寄存器,可以看到基本包含了常用的中断操作,并且还提供了一个简单的定时器,所谓麻雀虽小,五脏俱全。
中断执行的具体流程
Picorv32
遇到中断的时候会直接跳转到SOC中定义的中断地址执行,跳转到中断的时候并不会保存现场,所以用户需要自己保存相关的寄存器,执行完中断的时候也需要自己进行现场恢复。另外所有的中断都会跳转到同一个地址,当多个中断同时发生的时候也只有一次跳转,用户需要在中断中对中断状态进行逐一判断,执行所有中断,当然,如果有必要引入优先级,也是软件自己定义优先级。所以这里只是一个简单的,实时性并不高的一个中断实现。
因此当用户编写中断程序时候需要自己编写相应的现场保存和恢复代码,这部分代码官方已经提供了一个简单的实例:https://github.com/cliffordwolf/picorv32/blob/master/firmware/start.S我们可以直接用里面的irq_vec
作为中断入口函数,汇编代码中也提供了现场保存和恢复的代码,并且中间会跳转到irq
函数,并传递寄存器和中断状态寄存器的值(可以读取具体中断号),我们可以将irq_vec
通过link脚本链接到一个和SOC匹配的地址,不过这里为了方便,我直接将link的结果地址放在SOC里面,毕竟我的SOC也是可以随便改的呀。
以上我们就实现了自定义中断,虽然中断执行过程稍微繁琐,并且现场保存和恢复都是软件实现,实时性稍微有点差,但是总归是满足了我们对于中断的需求。我的学习又可以继续向下进行了。
最后更新于 2020-02-25 02:18:10 并被添加「FPGA Verilog RISCV Picorv32 SOC」标签,已有 8239 位童鞋阅读过。
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
请问有Start.S可以参考下吗?我用firmware文件夹下的Start.S,每次返回中断都不正常,偶尔也会造成CPU卡死。