本学习笔记参考《手把手教你设计CPU——RISC-V处理器》一书,该书出版日期为2018年,可能部分内容已经过时,仅作为学习用途
RISC-V简介
RISC-V的设计理念就是“简单”(有C语言内味了)
RISC-V通过架构的定义使硬件的实现足够简单,实现“简单就是美”的设计哲学
riscv的架构文档分成“非特权架构文档”(曾经被称为“指令集文档”)和“特权架构文档”,在需求高性能时,核心可以运行在特权+非特权的模式下;在需求高实时性时,核心可以只运行在非特权模式下。
RISCV具有模块化的指令集,可以通过组合不同的指令集来满足不同场景下的使用,核心指令数仅有40多条,加上其他的模块化扩展指令总共有几十条
模块化指令子集
RISCV每个指令集模块用一个英文字母来表示,其中最基本且唯一要求强制实现的指令集是由I字母表示的基本整数指令子集,使用该指令集就可以实现完整的软件编译器,进而使用高级语言进行编程
比较有代表性的模块包括IMAFD,这些模块组合起来被称为通用组合,用G(general)表示,全称是RV32G(32位)或RV64G(64位)。为提高代码密度,RISCV也提供可选的压缩指令集,用C(compress)表示。压缩指令的指令编码长度为16比特,普通的非压缩指令长度为32比特。还有面积更小的“嵌入式架构”,用E(embedded)表示,该架构仅需要支持16个通用整数寄存器,而非嵌入式架构的指令集需要支持32个通用整数寄存器
通用寄存器组
RISCV架构支持32位或64位架构,32位架构用RV32表示,寄存器位宽32bit;64位架构用RV64表示,寄存器位宽64bit。整数通用寄存器组包含32(I架构)或16(E架构)个通用整数寄存器;其中整数寄存器0被预留为常数0,其他31个(I)或15个(E)为普通的通用整数寄存器
如果使用浮点模块(F或D指令子集),则需要另外一个独立的浮点寄存器组,包含32个通用浮点寄存器。如果仅使用F模块(float)的浮点指令子集,则每个通用浮点寄存器的宽度为32bit;如果仅使用了D模块(double)的浮点指令子集,则每个通用浮点寄存器的宽度为64bit
规整的指令编码
RISCV的指令集编码非常规整,指令所需的通用寄存器的索引(Index)被放在固定的位置,指令译码器可以非常便捷地译码出寄存器索引来读取通用寄存器组(Register File,Regfile)
存储器访问指令
RISCV架构使用专用的存储器读指令和存储器写指令进行访问存储器操作,其他普通指令无法访问存储器!
这是RISC-V架构的一个基本策略,体现了简单即是美的设计哲学
存储器访问的基本单位是字节(Byte)
RISCV的存储器读和存储器写指令支持单字32位、半字16位、字节8位,如果是64位架构还可以支持一个双字64位的存储器读写
其他特点如下:
- 推荐使用地址对齐,但也支持地址非对齐,处理器可以选择硬件支持也可以选择软件支持
- RISCV仅支持现在的主流存储器格式,即小端格式little endian(低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节,大端格式big endian与其相反)
- 不支持地址自增或自减模式
- 采用松散存储器模型(Relaxed Memory Model),对于访问不同地址的存储器读写指令的执行顺序不做要求,除非明确使用存储器屏障(Fence)指令将存储器屏蔽
这种存储器访问指令要求简化了硬件电路设计,对于追求高性能的超标量处理器,也可以通过复杂设计的动态硬件调度能力来提高性能
分支跳转指令
RISC-V拥有2条无条件跳转指令,分别称为JAL和JALR指令
JAL即jump and link跳转链接指令,可用于子程序调用(函数调用),同时将子程序返回地址存在链接寄存器(Link Register,由某一个通用整数寄存器担任)中
JALR即JAL加上return,用于从子程序返回(函数返回)
还有6条带条件跳转指令(Conditional Branch),这种带条件的跳转指令和普通运算指令一样使用两个整数操作数,然后对其进行比较,如果比较的条件满足则进行跳转(很多其他的RISC架构处理器使用两条独立的指令,第一条指令进行比较,结果放在状态寄存器中,第二条指令读取状态寄存器进行跳转)
对于没有配备硬件分支预测器的低端CPU,为保证其性能,RISCV架构明确要求采用默认的静态分支预测机制:如果是向后跳转的条件跳转指令则预测为跳;向前跳转的指令则预测为不跳,并且要求编译器也按照这种默认的静态分支预测机制来编译生成汇编代码。同时RISCV架构规定了所有带条件跳转指令跳转目标相对与当前指令地址的偏移量都是有符号数,其符号位被编码在固定的位置,因此这种静态预测机制在硬件上容易实现。对于配备硬件分支预测器的高端CPU,也可以采用更高级的动态分支预测机制
子程序调用
一般RISC架构中程序采用以下方式进行子函数调用:
- 保存现场:用存储器写指令将当前函数上下文(通用寄存器、SP、IR等)保存到系统存储器的堆栈区(上下文压栈)
- 恢复现场:用存储器读指令将之前保存的上下文从系统存储器的堆栈区读入对应寄存器(上下文出栈)
RISCV架构直接放弃使用“一次读/写多个寄存器”指令,让用户可选公用程序库来处理保存现场-恢复现场的指令,省掉在每个子函数调用的过程中都放置数目不等的保存-恢复相关指令
无条件码执行
RISCV中没有带条件码的指令(当xxx条件为真时执行xxx)
对所有条件判断都使用普通的带条件分支跳转指令,大幅简化CPU的硬件设计
无分支延迟槽
分支延迟槽:在每一条分支指令后面紧跟的一条或若干条指令不受分支跳转的影响——不管分支是否跳转,后面的几条指令都一定会被执行
分支延迟槽是为了早期CPU缺少高级硬件动态分支预测器情况下增加CPU效率而使用的,会让CPU硬件设计极其难受
RISCV直接砍掉分支延迟槽来简化电路
RISCV的思路就是“我只要硬件够少、操作够方便,跟高性能有关的东西全部交给分支预测电路和软件”
无零开销硬件循环
零开销硬件循环:通过硬件直接参与,设置某些循环次数寄存器,然后让程序自动地进行循环,每一次循环则循环次数寄存器自动-1,直到循环次数寄存器值为0(相当于软件的for(int i=cnt;i==0;i--)
)
这可以让上述软件循环得到优化,减少加法和条件跳转指令使用
RISCV砍掉了零开销硬件循环来简化电路
RISCV:👴见啥砍啥
运算指令
基本整数指令子集I子集支持下列运算:
- 加法
- 减法
- 移位
- 按位逻辑操作
- 比较操作
整数乘除法指令子集M子集支持有符号或无符号数的乘除法运算,支持两个32位整数相乘得到一个64位的结果;支持两个32位的整数相除得到一个32位的商和一个32位的余数
单精度浮点指令子集F子集和双精度浮点指令子集D子集支持下列运算:
- 浮点加减法
- 浮点乘除法
- 浮点乘累加
- 开平方根
- 浮点数比较
- 整数与浮点数、单精度与双精度浮点之间的格式转换
RISCV对任何的运算指令错误均不产生异常,而是产生一个特殊的默认值,同时设置某些状态寄存器的状态位
推荐以软件方式找到这些错误
压缩指令子集
RISCV规定了可选的压缩指令子集C子集,也可以用RVC表示,用于解决32位编码指令造成的代码体积相对较大的问题
RVC将一部分普通最常用的32位指令中的信息进行压缩重排为16位指令,使得每一条16位长的指令都能找到其对应的原始32位指令
在汇编器阶段可以将程序编译成为压缩指令,优化编译器工具链
特权模式
RISC-V定义了3种工作模式,称为特权模式,分别是:
- 机器模式Machine Mode,:必选模式,简称M Mode
- 监督模式Supervisor Mode:可选模式,简称S Mode
- 用户模式User Mode:可选模式,简称U Mode
通过三种模式的组合可以实现不同的系统
RSC-V也支持几种不同的存储器地址管理机制,包括对物理地址和虚拟地址的管理机制,使其能支RTOS操作物理地址或通用OS操作虚拟地址等各种不同层次的系统操作
CSR寄存器
RISC-V架构定义了控制和状态寄存器(Control and Status Register),即CSR,用于配置或记录运行状态
CSR位于处理器核内部,使用单独的地址编码空间,和存储器寻址的地址区间完全没有关系
使用专用的CSR指令来访问CSR寄存器
例如:CSRRW、CSRRS、CRSRC、CSRRWI、CSRRSI、CSRRCI等
中断和异常
RISCV定义了一套相对简单的中断和异常机制,允许用户进行定制和扩展
矢量指令子集
RISCV使用可变长度的矢量,而不是矢量定长的SIMD指令集,从而灵活地支持不同的实现
低功耗、小面积CPU可以使用长度较短的硬件矢量进行实现,高性能的CPU则可以选择较长的硬件矢量实现,同样的软件代码可以兼容
自定制指令扩展
RISCV允许第三方扩展,用户可以扩展自己的指令子集
RISC-V预留了大量指令编码空间用于用户的自定义扩展,同时定义了4条Custom指令供用户直接使用,每条Custom指令都预留了几个比特位的子编码空间,用户可以使用这四条指令扩展出几十条自定义的指令
特点总结
RISC-V架构的核心在于“小即是美”的设计哲学,专注以下方面:
- 减小核心面积,方便硬件设计人员,排除大部分历史上CPU的弊端
- 删除硬件优化功能,像C语言的理念一样充分信任用户的软件优化
- 模块化设计、拓展性极强,像emacs/vim一样只要自定义到位,什么都能干
RISC-V软件工具链
RISC-V的软件工具由开源社区维护,所有工具链源代码公开
开发工具链源码被维护成一个红橡木,包含了所有相关软件开发工具、仿真器和测试套件等子项目
按照github相关说明下载源代码编译生成或在网络上直接下载已经预先编译好的GNU工具链和Windows IDE开发工具都可以获取工具链
仿真工具
riscv-fesvr用于上位机和CPU之间进行通信的库
riscv-pk提供程序运行环境和最简单的bootloader
riscv-isa-sim是一个基于C/C++开发的指令集模拟器,更通俗和为人所知的名字是“Spike”
三个软件协同可以实现在Spike模拟器上运行完整的RISC-V程序
GNU开发工具链
GNU工程无处不在.jpg
riscv-gnu-toolchain是支持RISC-V的GNU工具链,包括
- riscv-gcc:移植到RISC-V的GCC编译器
- riscv-binutils-gdb:移植到RISC-V的二进制工具(包括链接器、汇编器等)和GDB调试工具等
- riscv-glibc:GNU C标准库实现
其他开发工具
riscv-llvm:基于llvm编译器的框架
riscv-openocd:基于OpenOCD的RISC-V调试器软件
riscv-opcodes:RISCV操作码信息转换脚本
riscv-tests:一组RISCV指令集测试用例
riscv-qemu:支持RISCV的QEMU模拟器
RISC-V的“发行版”
RISC-V是一种开放的指令集架构,并不是具体的处理器
就像Linux是一个操作系统内核,而GNU/Linux的各种发行版才是操作系统一样
RISC-V使用的并不是GPL协议,所以衍生出来的操作系统有些是开源免费的,有些则是商业公司私有开发用于内部项目的
比较著名的基于RISC-V的处理器如下所示
名称 | 自由度 | 特点 |
---|---|---|
Rocket Core | 开源 | 使用伯克利的SoC生成器Rocket-Chip生成;扩展性强;性能强大甚至能跑linux;性能-面积比超过A5;使用Chisel(一种高层次面向对象的硬件描述语言,学习曲线陡峭)开发——很强大但是不好学 |
BOOM Core | 开源 | 全称Berkeley Out-of-Order Machine,面向高性能目标;超标量乱序发射、乱序执行的处理器核;性能-面积比超过A9 |
Freedom SoC | 开源 | 一套中高性能SoC,主频能运行到320MHz以上;成为蜂鸟E200系列的参考蓝本 |
LowRISC SOC | 开源 | 剑桥大学开发;目标是成为“硬件世界的GNU/Linux” |
PULPino Core and SoC | 开源 | PULPino是一款单核MCU SoC平台,配备了多套32位RISC-V核;分别适应高性能高功耗、中性能中功耗、低性能低功耗的使用场景 |
PicoRV32 Core | 开源 | 由著名IC设计师Clifford Wolf开发并开源;专注于面积和频率的优化,面积巨小;但是性能拉跨(也许可以在项目中叠几个填充剩余空间?) |
SCR1 Core | 开源 | 使用System Verilog编写;有一系列针对MCU的处理器核,称为SCRx系列,只开源了SCR1;性能中下 |
ORCA Core | 开源 | 使用VHDL编写;面向FPGA;可选配置为RV32I或RV32M;最初用途是作主控制处理器 |
Andes Core | 商业IP | 由晶心科技(Andes)开发;作为商业CPU出售 |
Micosemi Core | 商业IP | Micosemi公司将其嵌入自家FPGA;发挥开放性、可移植性和灵活性特点;便于开发 |
Codasip Core | 商业IP | 分为多个产品线出售;面向定制、中高性能MCU |
蜂鸟E200 Core and SoC | 开源 | 强化能效比;参考书作为例子讲解的处理器核与配套SoC,可以参考以后的学习笔记或自行阅读《手把手教你设计CPU——RISC-V处理器》一书 |