1.RISC-V简介
RISC-V(发音为“risk-five”)是一个基于精简指令集(RISC)原则的开源指令集架构(ISA)。
与大多数指令集相比,RISC-V指令集可以自由地用于任何目的,允许任何人设计、制造和销售RISC-V芯片和软件。虽然这不是第一个开源指令集,但它具有重要意义,因为其设计使其适用于现代计算设备(如仓库规模云计算机、高端移动电话和微小嵌入式系统)。设计者考虑到了这些用途中的性能与功率效率。该指令集还具有众多支持的软件,这解决了新指令集通常的弱点。
RISC-V的优势:
- 完全开源
- 架构简单
- 易于移植操作系统
- 模块化设计
- 完整工具链
2.RISC-V的特点
- 没有立即数减法:RISC-V只提供立即数加法,不提供立即数减法。当需要使用立即数减法时,由编译器将立即数转化为负数,再使用加法。简化了ALU单元的设计。
- x0寄存器简化指令集:RISC-V规定x0寄存器始终为0,引入该寄存器后,很多特殊指令只需要用普通指令加上x0做操作数就能解决,指令的数量大大减少,处理器的解码电路也大大简化。
- 支持32位常量:在ARM处理器中,会将立即数表示不下的常量存在常量池中,再用PC相关的LDR指令加载到寄存器,RISC-V的常量完全使用指令拼接实现,不需要Load指令,节省了访问周期。RISC-V单条指令可以表示12位的有符号常量,超过12位使用lui和addi两条指令合成。
- 只有小于和大于等于:RISC-V的比较跳转指令只有blt和bge,即只有小于和大于等于。当需要大于和小于等于时,将参与比较的两个操作数位置交换来实现。
- 让编译器做更多的工作:编译的次数远小于执行的次数,因此尽量让处理器少做,编译器多做。
3.指令集
RISC-V指令集的命名以RV位前缀,然后是位宽,最后代表的是指令集的字母集合
RV[###][ABC...XYZ]
符号 | 说明 |
RV | RISC-V的缩写 |
[###] | 用于标识处理器位宽,有32,64,128三种 |
[abc...xyz] | 标识处理器支持的指令模块集合 |
3.1RISC-V模块
名称 | 描述 |
基础 | |
RV32I | 基本整数指令集,32寄存器位宽 |
RV32E | 基本整数指令集,32寄存器位宽,16个寄存器,用于嵌入式 |
RV64I | 基本整数指令集,64寄存器位宽 |
RV128I | 基本整数指令集,64寄存器位宽 |
扩展 | |
M | 整数乘法和除法的标准扩展 |
A | 原子指令的标准扩展 |
F | 单精度浮点的标准扩展 |
D | 双精度浮点的标准扩展 |
B | 位操作的标准扩展 |
N | 用户级中断的标准扩展 |
...等 | 更多详见手册 |
3.2通用寄存器模型
寄存器 | ABI名称 | 描述 | 保存者 |
X0 | zero | 硬件0 | - |
X1 | ra | 返回地址 | 调用者 |
X2 | Sp | 栈指针 | 被调用者 |
X3 | gp | 全局指针 | - |
X4 | tp | 线程指针 | - |
X5-7 | t0-2 | 临时变量 | 调用者 |
X8 | s0/sp | 保存的寄存器/帧指针 | 被调用者 |
X9 | s1 | 保存的寄存器 | 被调用者 |
X10-11 | a0-1 | 函数入口参数/返回值 | 调用者 |
X12-17 | a2-7 | 函数入口参数 | 调用者 |
X18-27 | s2-11 | 保存的寄存器 | 被调用者 |
X28-31 | t3-6 | 临时变量 | 调用者 |
f0-7 | ft0-7 | FP临时变量 | 调用者 |
f8-9 | fs0-1 | FP保存的寄存器 | 被调用者 |
f10-11 | fa0-1 | FP参数/返回值 | 调用者 |
f12-17 | fa2-7 | FP参数 | 调用者 |
f18-27 | fs2-11 | FP保存的寄存器 | 被调用者 |
f28-31 | ft8-11 | FP临时变量 | 调用者 |
对于RISC-V的子程序调用,编译器会尽可能使用寄存器进行参数传递,有8个整数寄存器a0-a7和8个浮点寄存器fa0-fa7可以用于参数传递。
只有1个或者2个函数调用的返回值时,浮点返回值通过浮点寄存器fa0、fa1返回,其他值通过整数寄存器a0和a1返回值;更多的返回值时,全部通过存储器堆栈返回:调用者负责分配返回值的存储器区域,并将区域指针作为第一个隐藏参数传递给被调用者。在标准RISC-V调用约定中,栈是向下增长并且栈指针总是对齐到16字节。
除了参数传递寄存器和返回值寄存器之外,7个整数寄存器t0-t6和12个浮点寄存器ft0-ft11是临时寄存器,它们在被调用的子程序中可以被子程序代码覆盖,如果调用者后面还有使用这些寄存器的数值,在调用者中必须自己先保存其值再调用子程序。12个整数寄存器s0-s11和12个浮点寄存器fs0-fs11在子程序退出时,需要保持其值与子程序调用前一样,因此如果被调用者(子程序)需要使用这些寄存器,则被调用者(子程序)必须先保存它们,然后在结束调用(子程序返回)前恢复其原值。