RV32I:RISC-V 基础整数指令集

目录

  • RV32I指令
  • 指令格式
  • RV32I寄存器
  • RV32I 整数计算
  • RV32I load 和 Store
  • 条件分支
  • 无条件跳转
  • RV32I 杂项
  • RV32I特性
  • 一个例子
  • 参考

提升计算性能并且让用户能切实享受到性能提升的唯一方法是同时设计编译器和计算机。这样软件用不 到的特性将不会被实现在硬件上…
——Frances Elizabeth “Fran” Allen, 1981

RV32I指令

  图 2.1 是 RV32I 基础指令集的一页图形表示。对于每幅图,将有下划线的字母从左到 右连接起来,即可组成完整的 RV32I 指令集。对于每一个图,集合标志{}内列举了指令的 所有变体,变体用加下划线的字母或下划线字符_表示。特别的,下划线字符_表示对于此 指令变体不需用字符表示。。例如,下图表示了这四个 RV32I 指令:slt,slti,sltu,sltiu:
在这里插入图片描述

RV32I:RISC-V 基础整数指令集_第1张图片

指令格式

  六种基本指令格式,分别是:

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

  即使是指令格式也能从一些方面说明 RISC-V 更简洁的 ISA 设计能提高性能功耗比。 首先,指令只有六种格式,并且所有的指令都是 32 位长,这简化了指令解码。ARM-32, 还有更典型的 x86-32 都有许多不同的指令格式,使得解码部件在低端实现中偏昂贵,在中高端处理器设计中容易带来性能挑战。第二,RISC-V 指令提供三个寄存器操作数,而不是 像 x86-32 一样,让源操作数和目的操作数共享一个字段。当一个操作天然就需要有三个不 同的操作数,但是 ISA 只提供了两个操作数时,编译器或者汇编程序程序员就需要多使用 一条 move(搬运)指令,来保存目的寄存器的值。第三,在 RISC-V 中对于所有指令,要读写的寄存器的标识符总是在同一位置,意味着在解码指令之前,就可以先开始访问寄存 器。在许多其他的 ISA 中,某些指令字段在部分指令中被重用作为源目的地,在其他指令 中又被作为目的操作数(例如,ARM-32 和 MIPS-32)。因此,为了取出正确的指令字 段,我们需要时序本就可能紧张的解码路径上添加额外的解码逻辑,使得解码路径的时序 更为紧张。第四,这些格式的立即数字段总是符号扩展,符号位总是在指令中最高位。这 意味着可能成为关键路径的立即数符号扩展,可以在指令解码之前进行。

  为了给 ISA 扩展留出足够的空间,最基础的 RV32I 指令集只使用了 32 位指令字中的 编码空间的不到八分之一。架构师们也仔细挑选了 RV32I 操作码,使拥有共同数据通路的 指令的操作码位有尽可能多的位的值是一样的,这简化了控制逻辑。最后,当我们看到,B 和 J 格式的分支和跳转地址必须向左移动 1 位以将地址乘以 2,从而给予分支和跳转指令 更大的跳转范围。RISC-V 将立即数中的位从自然排布进行了一些移位轮换,将指令信号的 扇出和立即数多路复用的成本降低了近两倍,这也简化了低端实现中的数据通路逻辑。

RV32I寄存器

  图 4 列出了 RV32I 寄存器以及由 RISC-V 应用程序二进制接口(ABI)所定义的寄存 器名称。为了满足汇编 语言程序员和编译器编写者,RV32I 有 31 寄存器加上一个值恒为 0 的 x0 寄存器。为常量 0 单独分配一个寄存器是 RISC-V ISA 能如此简单的一个很 大的因素。

RV32I 整数计算

  简单的算术指令(add, sub)、逻辑指令(and, or, xor),以及图 2.1 中的移位指令 (sll, srl, sra)和其他 ISA 差不多。他们从寄存器读取两个 32 位的值,并将 32 位结果写 入目标寄存器。RV32I 还提供了这些指令的立即数版本。和 ARM-32 不同,立即数总是进 行符号扩展,这样子如果需要,我们可以用立即数表示负数,正因为如此,我们并不需要 一个立即数版本的 sub。

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

  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)的加载和存储外,图 2.1 中说明,RV32I 支持加载有符 号和无符号字节和半字(lb,lbu,lh,lhu)和存储字节和半字(sb,sh)。有符号字节 和半字符号扩展为 32 位再写入目的寄存器。在文本和无符号整数 中常用的无符号字节和半字,在写入目标寄存器之前都被无符号扩展到 32 位。

  加载和存储的支持的唯一寻址模式是符号扩展 12 位立即数到基地址寄存器,这在 x86-32 中被称为位偏移寻址模式。

  RV32I 省略了 ARM-32 和 x86-32 的复杂寻址模式。另外,ARM-32 提供的寻址模式并非适用于所有数据类型,但 RV32I 寻址不会歧视任何数据类型。RISCV 可以模仿某些 x86 寻址模式。例如,将立即数字段设置为 0 即与 x86 中的寄存器间接寻 址效果相同。与 x86-32 不同,RISC-V 没有特殊的堆栈指令。将 31 个寄存器中的某一个作 为堆栈指针(见图 2.4),标准寻址模式使用起来和压栈(push)和出栈(pop)类似,并 且不增加 ISA 的复杂性。与 MIPS-32 不同,RISC-V 不支持延迟加载(delayed load)。

  虽然 ARM-32 和 MIPS-32 要求存储在内存中的数据,要按照数据的自然大小进行边界 对齐,但是 RISC-V 没有这个要求。

条件分支

  RV32I 可以比较两个寄存器并根据比较结果上进行分支跳转。比较可以是:相等 (beq),不相等 (bne),大于等于(bge),或小于(blt)。最后两种比较有符号比 较,RV32I 也提供相应的无符号版本比较的:bgeu 和 bltu。剩下的两个比较关系(大于和 小于等于)可以通过简单地交换两个操作数,即可完成比较。

  由于 RISC-V 指令长度必须是两个字节的倍数。分支指令的寻址方式是 12 位的立即数乘以 2,符号扩展它,然后将得到值加到 PC 上作为分支的跳转地址。PC 相对寻址可用于位置无关的代码,简化了链接器和加载器 的工作。

  对于条件分支,它还没有像 ARM-32 和 x86-32 那样使用条件 码。条件码的存在使得大多数指令都需要隐式设置一些额外状态,这使乱序执行的依赖计 算复杂化。最后,它省略了 x86-32 中的循环指令:loop,loope,loopz,loopne, loopnz。

无条件跳转

  图 2.1 中的跳转并链接指令(jal)具有双重功能。若将下一条指令 PC + 4 的地址保存 到目标寄存器中,通常是返回地址寄存器 ra(见图 2.4),便可以用它来实现过程调用。 如果使用零寄存器(x0)替换 ra 作为目标寄存器,则可以实现无条件跳转,因为 x0 不能 更改。像分支一样,jal 将其 20 位分支地址乘以 2,进行符号扩展后再添加到 PC 上,便得 到了跳转地址。

  跳转和链接指令的寄存器版本(jalr)同样是多用途的。它可以调用地址是动态计算 出来的函数,或者也可以实现调用返回(只需 ra 作为源寄存器,零寄存器(x0)作为目的 寄存器)。Switch 和 case 语句的地址跳转,也可以使用 jalr 指令,目的寄存器设为 x0。

  RV32I 避开了错综复杂的程序调用指令。

RV32I 杂项

  图 2.1 中的控制状态寄存器指令 (csrrc、csrrs、csrrw、csrrci、csrrsi、csrrwi), 使我们可以轻松地访问一些程序性能计数器。对于这些 64 位计数器, 我们一次可以读取 32 位。这些计数器包括了系统时间, 时钟周期以及执行的指令数目。

  在 RISC-V 指令集中,ecall 指令用于向运行时环境发出请求,例如系统调用。调试器 使用 ebreak 指令将控制转移到调试环境。

  fence 指令对外部可见的访存请求,如设备 I / O 和内存访问等进行串行化。外部可见 指对处理器的其他核心、线程,外部设备或协处理器可见。fence.i 指令同步指令和数据 流。在执行 fence.i 指令之前,对于同一个硬件线程,RISC-V 不保证用存储指令写到内存 指令区的数据可以被取指令取到。

 &ems;RISC-V 使用内存映射 I / O 。为支持字符串处理,RISC-V 实现了 字节存取。

RV32I特性

  图 2.7 使用七个 ISA 设计指标来组织前面提到的一些过去的指令集中学习 到的经验教训,并说明了这些经验教训对 RV32I 设计的积极影响。

  • 32 位字节可寻址的地址空间
  • 所有指令均为 32 位长
  • 31 个寄存器,全部 32 位宽,寄存器 0 硬连线为零
  • 所有操作都在寄存器之间(没有寄存器到内存的操作)
  • 加载/存储字加上有符号和无符号加载/存储字节和半字
  • 所有算术,逻辑和移位指令都有立即数版本的指令
  • 立即数总是符号扩展
  • 仅提供一种数据寻址模式(寄存器+立即数)和 PC 相对分支
  • 无乘法或除法指令
  • 一个指令,用于将大立即数加载到寄存器的高位,这样加载 32 位常量到寄存器只需要两条指令

一个例子

  冒泡排序算法的C实现如下:
RV32I:RISC-V 基础整数指令集_第2张图片
  编译成RV32I汇编语言之后如下:
RV32I:RISC-V 基础整数指令集_第3张图片

参考

RISC-V 手册 第二章

你可能感兴趣的:(笔记,risc)