RISC-V学习(一)

RISC-V(RISC five)的目标:成为一个通用的指令级架构:

  • 它要能适应从最袖珍的嵌入式处理器,到最快的高性能计算机等各种规模的处理器。
  • 它应该能兼容各种流行的软件栈和编程语言。
  • 它应该能适应所有现实技术,包括现场可编程门阵列(FGPA)、专用集成电路(ASIC)、全定制芯片,甚至未来的设备技术。
  • 它应该对所有微体系结构样式都有效:微编码或硬连线控制;顺序或乱序执行流水线;单发射或超标量等等。
  • 它应该支持广泛的专业化,成为定制加速器的基础,随着摩尔定律的消退,加速器的重要性日益提高。
  • 它应该是稳定的,基础的指令集架构应该是不变的。它不能像以前的专有指令集架构一样被弃用。

RISC-V诞生于近十年,而大多数指令集诞生于20世纪70年代,并且它是一个开源的指令级架构。与所有的旧架构不同,它的未来不受任何单一公司的浮沉或一时兴起的决定的影响。它属于一个开放的,非盈利性质的基金会。RISC-V基金会的目标是保持RISC-V的稳定性,仅仅出于技术原因缓慢而谨慎地发展它,并试图让它之于硬件如同Linux之于操作系统一样受欢迎。

模块化与增量型ISA

计算机体系结构的传统方法是增量ISA,新处理器不仅必须实现新的ISA扩展,还必须实现过去的所有扩展。目的是为了保持向后的二进制兼容性,这样几十年前程序的二进制版本仍然可以在最新的处理器上正确运行。这一要求与来自同时发布新指令和新处理器的营销上的诱惑共同导致了ISA的体量随时间大幅增长。
在这里插入图片描述
这个传统意味着x86-32(32位地址版本的x86)的每个实现都必须实现过去的扩展中的错误设计,即使它们不再有意义。

RISC-V的不同之处,除了在于它是最近诞生的和开源的以外。它和几乎所有以往的ISA不同,它是模块化的。它的核心是一个名为RV32I的基础ISA,运行一个完整的软件栈。

RV32I是固定的,永远不会改变。这为编译器编写者,操作系统开发人员和汇编语言程序员提供了稳定的目标。模块化来源于可扩选的标准扩展,根据应用程序的需要,硬件可以包含或不包含这些扩展。这种模块化特性使得RISC-V具有了袖珍化、低能耗的特点,而这对于嵌入式应用至关重要。

RISC-V编译器得知当前硬件包含哪些扩展后,便可以生成当前硬件条件下的最佳代码。惯例是把代表扩展的字母附加到指令集名称之后作为指示。例如RV32IMFD将乘法(RV32M)、单精度浮点数(RV32F)和双精度浮点(RV32D)的扩展添加到了基础指令集(RV32I)中。

设计ISA时的基本原则和必须做出的权衡

  • 成本
  • 简洁性
  • 性能
  • 架构和具体实现的分离
  • 提升空间
  • 程序大小
  • 易于编程/编译/链接

成本:处理器通过集成电路实现,通常称为芯片或晶粒。它们叫做晶粒是因为,它们由一些单个的原形晶片被切割成许多单独的片得到。成本对晶粒面积十分敏感。显然,晶粒越小,每个晶圆上能切割出来的晶粒越多。架构师希望保存ISA的简洁性,从而缩小实现ISA的处理器的尺寸。
使用相同大小缓存(16KB)的RISC-V Rocket处理器和采用相同技术的ARM-32 Cortex A5处理器相比较:RISC-V 晶粒的大小是 0.27mm2,而 ARM-32 晶粒的大小是0.53mm2。由于面积大一倍,ARM-32 Cortex A5 的晶粒成本是 RISC-V Rocket 的约 4(22)倍。

简洁性:鉴于成本对于复杂度的敏感性,架构师需要一个简单的ISA来缩小芯片面积。ISA的简洁性还能缩短芯片的设计和验证时间(芯片开发的大部分成本)。还能降低文档成本,让客户更容易了解如何使用这个ISA。

性能:即使一个简单的ISA可能在每个程序执行的指令数方面多余复杂的ISA,但它可以通过更快的时钟频率或更低的平均单条指令周期数(CPI)来弥补。

程序大小:程序越小,存储它所需的芯片面积就越小。

易于编程/编译/链接:大多数现代ISA(包括RISC-V)都有32个整型寄存器。有了更多的寄存器,编译器和汇编程序员工作会更轻松。一般每一条RISC-V指令最多用一个时钟周期时间。

RISC-V基础整数指令集

RV32I指令图示。把带下换线的字母从左到右连接就组成了RV32I指令。花括号{}表示集合中垂直方向上的每个项目都是指令的不同变体。
图2.1是RV32I的基础指令集的一页图形表示。对于每幅图,将有下划线的字母从左到右连接起来,便可组成完整的RV32I指令集。对于每一个图,集合标志{}内列举了指令的所有变体,变体用加下划线的字母或下划线字符_表示。
在这里插入图片描述
这张图表示了四个RV32I指令:slt、slti、sltu、sltiu。

在这里插入图片描述
图2.2显示了六种基本指令格式。

  • 用于寄存器-寄存器操作的R类型指令。
  • 用于短立即数和访存load操作的I型指令。
  • 用于访存store的S型指令。
  • 用于条件跳转操作的B类型指令。
  • 用于长立即数的U型指令。
  • 用于无条件跳转的J型指令。

即使是指令格式也能从一些方面说明RISC-V更简洁的ISA设计设计能提高性能功耗比。

  1. 首先,指令只有六种格式,并且所有的指令都是32位长,简化了指令解码。
  2. RISC-V提供了三个寄存器操作数。
  3. RISC-V中对于所有指令,要读写的寄存器的标识符总是在同一位置,意味着在解码指令之前,就可以先开始访问寄存器。
  4. 这些格式的立即数字段总是符号扩展,符号位总是在指令中最高位。

为了帮助程序员,所有位全部是0是非法的RV32I指令。因此,试图跳转到被清零的内存区域的错误跳转将会立即触发异常,这也可以帮助调试。类似地,所有位全是1的指令也是非法指令,它将捕获其它常见的错误,例如未编程的非易失性内存设备,断开连接的内存总线或者坏掉的内存芯片。

简单的算术指令(add,sub)、逻辑指令(and,or,xor),以及图2.1中的移位指令(sll,srl,sra)和其它ISA差不多。他们从寄存器读取两个32位的值,并将32位结果写入目标寄存器。RV32I还提供了这些指令的立即数版本。

程序可以根据比较结果生成布尔值。为了应对这种使用场景下,RV32I提供了一个当小于时置位的指令。若第一个操作数小于第二个操作数,它将目标寄存器设置为1。对于这个指令,有一个有符号版本(slt)和无符号版本(sltu),分别用于处理有符号和无符号的整数比较。相应的,上述两条指令也有立即数版本的(slti,sltiu)。

图2.1剩下的两条整数计算指令主要用于构造大的常量数值和链接。加载立即数到高位(lui)将20位常量加载到寄存器的高20位,接着便可以使用标准的立即指令来创建32位常量。这样子,仅使用2条32位RV32I指令,便可构造一个32位常量。向PC高位加上立即数(auipc),让我们仅用两条指令,便可以在基于当前PC以任意偏移量转移控制流或者访问数据。

  • 将auipc中的20位立即数与jalr中12位立即数的组合,我们可以将执行流转移到任何32位PC相对地址。
  • 将auipc加上普通加载或存储指令中的12位立即数偏移量,我们可以访问任何32位PC相对地址的数据。

RISC-V中没有字节或半字宽度的整数计算操作。操作始终是以完整的寄存器宽度。内存访问需要的能量比算数运算高几个数量级。因此底宽度的数据访问可以节省大量的能量,但底宽度的运算不会。

RV32I中不包含乘法和除法,它们包含在可选的RV32M扩展中。

RV32I的Load和Store
除了提供32位字(lw,sw)的加载和存储外,RV32I支持加载有符号和无符号字节和半字(lb,lbu,lh,lhu)和存储字节和半字(sb、sh)。

有符号字节和半字符号扩展为32位再写入目的寄存器。即使是自然数据类型更窄,低位宽数据也是被扩展后再处理,这使得后续的整数计算指令能正确处理所有的32位。在文本和无符号整数中常用的无符号字节和半字,在写入目标寄存器之前都被无符号扩展到32位。


版权声明:本文为Caramel_biscuit原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。