1. RISC-V基础回顾:指令集架构概览、特权架构与模式、Hart概念与CSR寄存器
好,咱们正式开始。这一节算是整个课程的基石,说白了就是先把RISC-V的底子打牢。我见过不少工程师,上来就撸多核调度代码,结果连Hart和CSR都没搞明白,最后调试到怀疑人生。嗯,咱们不干那事。
1.1 指令集架构概览:为什么RISC-V能火?
RISC-V是个精简指令集,但它不是那种「为了精简而精简」的设计。我个人觉得,它最聪明的地方在于——模块化。
你想想看,ARM的指令集是固定的,你要用就得全盘接受。RISC-V不一样,它把指令集拆成了两部分:
- 基础指令集(Base ISA):比如RV32I、RV64I,这是必选项。加减乘除、访存、分支,都在这里。
- 扩展指令集(Standard Extensions):比如M(乘除法)、F/D(单/双精度浮点)、A(原子操作)、C(压缩指令)。这些都是可选的,按需选配。
我在一个IoT项目里就只用RV32IMC,连浮点都不要,省面积省功耗。换做ARM?你得买一整套Cortex-M核,里面一堆你用不上的东西还得照单付费。
核心要点:RISC-V的指令集是「乐高式」的。基础块固定,扩展块自由组合。这给硬件设计者留了极大的发挥空间。
另外,RISC-V的指令编码非常规整。比如所有寄存器操作指令,源寄存器位置都是固定的(rs1在[19:15],rs2在[24:20])。这有什么好处?硬件解码器可以做得更简单、更快。我当年做乱序执行引擎时,就特别喜欢这种规整性——少了很多绕来绕去的解码逻辑。
1.2 特权架构与模式:你的CPU有几种「权限」?
好,指令集讲完了,咱们聊聊更关键的东西——特权架构。
为什么需要特权架构?说白了,就是为了隔离。操作系统不能让你随便访问硬件寄存器,否则一个用户程序就能把整个系统搞崩。RISC-V定义了三种特权模式:
| 模式 | 编码 | 名称 | 典型用途 |
|---|---|---|---|
| M | 11 | Machine Mode | 最底层固件、安全监控、异常处理 |
| S | 01 | Supervisor Mode | 操作系统内核 |
| U | 00 | User Mode | 普通应用程序 |
注意,M模式是必须实现的。S和U是可选的。但如果你要做多核调度、跑Linux,那S模式是标配。
我曾经踩过一个坑:在设计一个双核RISC-V处理器时,我默认所有核都跑在M模式下,结果发现两个核都能直接操作中断控制器,互相抢资源,调度逻辑乱成一锅粥。后来老老实实引入S模式,让一个核跑S模式做调度,另一个核跑U模式做计算,问题迎刃而解。
避坑指南:多核系统中,务必明确每个Hart的特权模式。不要让所有核都跑在M模式下,否则中断和资源管理会变成噩梦。
模式之间通过异常(Exception)和中断(Interrupt)来切换。比如用户程序执行了ecall指令,就会陷入S模式或M模式,由操作系统或固件来处理。这个机制,就是多核调度中「上下文切换」的基础。
1.3 Hart概念:别再说「核心」了,咱们说「硬件线程」
RISC-V里有个独特的概念——Hart(Hardware Thread)。
你可能会问:「这不就是CPU核心吗?」嗯,不完全一样。一个物理核心可以包含多个Hart。比如一个核心支持SMT(同步多线程),那它就有两个Hart。每个Hart都有自己的程序计数器(PC)、寄存器文件和CSR寄存器。
我习惯这么理解:Hart是调度的最小单位。操作系统看到的不是「核心」,而是「Hart」。每个Hart独立执行指令流,共享核心的执行单元(比如ALU、浮点单元)。
个人经验:在做多核调度策略时,我建议把Hart当作「虚拟CPU」来看待。调度器只需要关心有多少个Hart可用,而不必关心它们是否共享物理核心。这样设计更干净。
每个Hart都有一个唯一的Hart ID,存储在mhartid这个CSR寄存器中。多核启动时,每个Hart读取自己的mhartid,就知道自己该干什么。比如Hart 0负责初始化,Hart 1~N负责跑应用。
1.4 CSR寄存器:控制你的Hart
CSR(Control and Status Register)是RISC-V里控制Hart行为的「开关面板」。每个CSR都有12位的地址编码,通过csrrw、csrrs、csrrc等指令来读写。
常用的CSR寄存器我列一下:
| CSR地址 | 名称 | 作用 |
|---|---|---|
| 0x301 | misa | 机器模式ISA控制,标识支持的扩展 |
| 0x341 | mepc | 异常返回地址,保存触发异常的指令PC |
| 0x342 | mcause | 异常原因,告诉你发生了什么异常 |
| 0x344 | mie | 机器模式中断使能,控制哪些中断可以触发 |
| 0x348 | mip | 机器模式中断挂起,显示哪些中断正在等待 |
| 0xF14 | mhartid | Hart ID,每个Hart唯一 |
举个例子,多核调度中,一个Hart要进入低功耗模式等待任务,可以这样操作:
# 关闭该Hart的中断使能
csrci mie, 0x1 # 清除mie中的全局中断使能位
# 执行WFI指令等待中断
wfi
# 被唤醒后,恢复中断使能
csrsi mie, 0x1
这段代码看起来简单,但我在实际项目中吃过亏——WFI指令在某些实现中可能不会立即返回。如果你用的是模拟器或FPGA原型,WFI的行为可能和真实芯片不一样。我建议在硬件验证阶段,多测几种WFI唤醒场景。
关键点:CSR寄存器是Hart的「私有财产」。每个Hart都有自己的CSR副本,互不干扰。这也是多核调度能独立控制每个Hart的基础。
1.5 小结:这一节你该记住什么?
好,咱们回顾一下。这一节我讲了四个东西:
- 指令集架构:模块化设计,基础指令集+扩展,按需选配。
- 特权模式:M/S/U三级,多核调度至少需要S模式。
- Hart概念:硬件线程,调度的最小单位,每个Hart有独立ID。
- CSR寄存器:控制Hart行为的开关,每个Hart私有。
这些东西看着基础,但后面讲多核调度策略时,你会反复用到它们。尤其是CSR和特权模式,几乎每个调度算法都绕不开。嗯,下一节咱们就进入正题——多核启动与同步。