RISC-V 指令集基本概念

发布时间 2023-11-14 21:06:27作者: 吴建明wujianming

RISC-V 指令集基本概念

介绍 RISC-V(读音“risk-five”)是一个新的指令集体系结构(ISA),它最初用于支持计算机 体系结构研究和教学,但现在 希望它也成为一个对于工业实现来说标准、免费、开放的 体系结构。 定义 RISC-V 的目的包括:  

一个完全开放的 ISA,能够自由地提供给学术界和工业界使用。

 一个真正的 ISA,能够适合直接在硬件上实现,而不仅仅是适用于模拟或者二进制 翻译。  一个避免对某一种微体系结构风格(例如微编码、按序、去耦合、乱序等)或者实 现技术(例如全定制、ASIC、FPGA)“过度体系结构化(over-architecting)”的 ISA,但是也能够非常高效地利用任何一种技术实现。

 包含一个小的基本整数 ISA(可以作为一个定制的加速器的基础或者作为教学用途) 和多个可选的标准扩展的 ISA,可以支持通用的软件开发。

 支持修订的 2008 IEEE-754 浮点标准

 ISA 支持丰富的用户级 ISA 扩展和各种特殊的变种。

 对应用程序、操作系统内核、硬件实现的 32 位、64 位地址空间变种。  

ISA 支持高度并行的多核、众核实现,包括异构多处理器等。

 可选的变长指令,以支持扩展可用的指令编码空间、支持一个可选的密集指令编码, 以提高性能、静态代码大小和能耗效率。

 一个可完全虚拟化的 ISA,以简化虚拟机监督管理器(Hypervisor)的开发。

 ISA 支持新的管理员级(supervisor-level)和虚拟机监督管理级(hypervisor-level) ISA 设计。  

设计考虑,将出现在类似的文本段落内,如果读者只关心规范,则 可以跳过这些段落。 RISC-V 这个名字,代表了 UC Berkeley 大学设计的第五代主要的 RISC ISA(前 四个是 RISC-I[18]、RISC-II[11]、SOAR[27]和 SPUR[14])。罗马数字“V”也暗示了“变种(Variations)”和“向量(Vectors)”,以支持各种体系结构研究,包括各种数据并行加速器,也是这个 ISA 设计的明确目标。  

研发 RISC-V 以满足 自己的科研和教学需求,对如何在真实 硬件上实现一些研究思想特别感兴趣(自从这个规范的第一个版本发布之后,已经完成了 11 块不同的 RISC-V 硅片的制造),在课堂上提供给学生真实 的实现(在 Berkeley,RISC-V 处理器的 RTL 设计代码已经用于多个本科生、研究生的课程)。在 当前的研究中,由于传统晶体管不断变小带来的能耗约 束,对特殊、异构的加速器特别感兴趣。 需要一个高度灵活、高度可 扩展的基本 ISA,在此基础上可以构建 自己的研究。  

总被问及这样一个问题“为什么要开发一个新的 ISA?”。

使用一个已 有的商业化的 ISA,其显而易见最大的优势在于其已经具备了丰富和广泛支持的软件生态系统,包括开发工具和可移植的应用程序,而在研究和教学中,这 些都是可以利用的。其他的好处包括拥有大量的文档和教程示例。

然而,经验证明,在科研和教学中使用商业的指令集,在实际中获得的好处很小,而且掩盖不了它的缺点:  

商业 ISA 都是私有的。除了 SPARC V8(它是一个开放的 IEEE 标准[1]), 绝大多数 ISA 的拥有者非常小心地保护他们的知识产权,并且并不欢 迎自由实现的竞争实现。对于仅仅使用软件模拟器来进行学术研究和 教学来说,这并不是一个问题,但是对于那些希望分享真实硬件实现 的科研小组来说,这就是一个大问题。对于那些被强迫信任仅有的几个商业 ISA 实现,而不允许创建自己的全新实现(clean room implementation)的企业来说,这也是一个大问题。

并不能确保 所有的 RISC-V 实现没有侵犯第三方专利,但是 确保 绝不会 起诉一个 RISC-V 的实现者。

 商业 ISA 仅仅在某个市场领域比较流行。当书写此文档时,最显而易 见的例子就是 ARM 体系结构在服务器领域并没有得到很好的支持, 而 Intel x86 体系结构(或者几乎任何一种其他的体系结构)在移动领 域并没有得到很好的支持,虽然 Intel 和 ARM 正在试图进入对方的市 场领域。

另外一个例子是 ARC 和 Tensilica,它们提供了可扩展的内核,但是只关注嵌入式市场。这种市场的划分,使得支持某种特定商业 ISA 获得的好处大大削弱,因为事实上软件生态系统只存在于某个领 域,到了别的领域,必须重新构建。  

商业 ISA 此起彼伏。以前基于商业 ISA 构建的研究基础设施,并不流 行(SPARC、MIPS),甚至不再生产(Alpha)。这对于一个活跃的软件 生态系统来说是一个大损失,一些围绕 ISA 和支持工具的知识产权问题,也使得感兴趣的第三方难以继续支持这个 ISA。

一个开放的 ISA 也可能失去流行性,但是任何感兴趣的人,都可以继续使用它并研发 相应的生态系统。  

流行的商业 ISA 是复杂的。占统治地位的 ISA(x86 和 ARM)若要支 持常用软件栈和操作系统,那么其硬件实现都非常复杂。

更糟糕的是,几乎所有的复杂性都来自于糟糕的、或者至少是过时的ISA设计考虑,而不是那些真正提高效率的特性。  

仅靠商业 ISA 并不足以运行应用程序。即使 努力实现了一个商业 ISA,对于运行一个现有的应用程序来说,仍然是不够的。绝大多数 应用程序需要一个完整的 ABI(application binary interface)才能运行,而不仅仅是用户级 ISA。绝大多数 ABI 依赖于库(libraries),而库又 依赖于操作系统支持。为了运行一个已有的操作系统,需要实现管理 员级 ISA、OS 需要的设备接口。这些通常并没有很好的规范,而在实 现上比用户级 ISA 具有更大的复杂性。

 流行的商业 ISA 不是为可扩展性设计的。占统治地位的商业 ISA 并没 有为可扩展性而进行特殊的设计,结果就是,随着后续指令集不断地 增长,指令编码的复杂度大幅度增加。而类似 Tensilica(被 Cadence 公司收购)、ARC(被 Synopsys 公司收购)这样的公司,它们围绕 可扩展性构建了 ISA 和工具链(toolchain),但是它们瞄准的是嵌入 式应用而不是通用计算系统。   

一个修改过的商业 ISA 实际上是一个新的 ISA。

一个主目标 是支持体系结构研究,包括主要的 ISA 扩展。即使是很小的扩展,也 减弱了使用标准 ISA 而带来的好处,因为必须修改编译器,而应用程 序必须从源代码进行重新编译,以利用这些扩展。引入了新的体系结 构状态的大一些的扩展,也需要对操作系统进行修改。最终使得一个修改的商业 ISA 变成一个新的 ISA,但是不得不肩负着所有基本 ISA 遗留下来的包袱。  

坚信 ISA 是整个计算系统中最重要的接口,没有理由把这么重要的接 口变成私有的。占统治地位的商业 ISA 都是基于超过 30 年历史的指令集。软 件开发者应当能够定位到一个开放标准的硬件目标机,商业处理器设计者应当在实现质量上进行竞争。  

并不是第一个为了适合硬件实现而提出开放 ISA 设计的。

也考虑 了其他现有的开放 ISA 设计,其中 OpenRISC 体系结构[17]与 目标最为 接近。

由于几个技术原因,并不采用 OpenRISC ISA:  

OpenRISC 有条件码(condition code)和分支延迟槽(branch delay slot),这对于更高性能的实现来说,变得更为复杂。

 OpenRISC 使用了 32 位定长指令编码和 16 位立即数,阻碍了更密集 的指令编码,并对后续 ISA 扩展限制了空间。

 OpenRISC 并不支持 2008 修订的 IEEE-754 浮点标准。  

在开始的时候,64 位 OpenRISC 设计并没有完成。

从零开始,可以设计一个符合 所有需求的 ISA,当然,这花了比  在开始时预期多得多的努力。现在 在构建 RISC-V ISA 基础设施上投入了大量的精力,包括文档、编译器工具链、操作系统移植、参考 ISA 模拟器、FPGA 实现、高效的 ASIC 实现、体系结构测试套件、教学材料等。自本文档的上一个版本以来,在学术界和工业界对此 RISC-V ISA 都有大量的吸收(uptake),也创建了非盈利的 RISC-V 基金会来保护和推进这个标准。

RISC-V 基金会的网址在 http://riscv.org,包含了基金会成员最新的信息和各种各样使用 RISC-V 的开源项目。

RISC-V 手册被分为两卷。本卷涵盖了用户级 ISA 设计,包括可选的 ISA 扩展。

在这个用户级手册中, 目标是移除所有与特定微体系结构特性或者 管特权体系结构相关的细节。这样做,主要是为了清晰,并最大程度地允许其 他实现的灵活性。

1.1 RISC-V ISA

概述 RISC-V ISA 被定义为一个基本的整数 ISA,必须在任何实现中存在,另外可以包含基于基 本 ISA 的其他扩展。这个基本的整数 ISA 与早期的 RISC 处理器非常相似,除了没有分支延迟 槽(delay slot),另外支持可选的变长指令编码。这个基本核心被小心地限制具有最少的指 令,足够支持一个合理的目标机,以便编译器、汇编器、链接器、操作系统(包含额外的管理员级操作)可以在之上运行,这样就可以提供一个方便的 ISA 和软件工具链“骨架”,围 绕它可以构建更为定制化的处理器 ISA。

每一个基本整数指令集,被整数寄存器宽度和相应的用户地址空间大小进行分类。有两 种主要的基本整数变种,RV32I 和 RV64I,分别提供了 32 位 和 64 位用户级地址空间。硬件实现和操作系统可以提供给用户程序使用 RV32I 或者 RV64I 中的一种或者两种。

描述了 RV32I 基本指令集的子集变种 RV32E,它被加入以支持小 的微控制器。描述了未来支持 128 位用户地址空间的 RV128I 变种基本整数指令集。

虽然对于更大的系统来说,64 位地址空间是必须的,但 相信在未来 的数十年中,对于许多嵌入式应用和客户端设备来说,32 位地址空间是足够 的,而且适合更低的存储器传输和能耗消耗。另外,32 位地址空间对于教育 来说足够了。更大的 128 位地址空间也许最终也是必须的,于是确保在 RISC-V ISA 框架中也包含它。

一个硬件实现可以只实现基本整数 ISA 的一个子集,但是在更高特权层必须实现操作码 自陷(trap)和软件仿真,用于实现那些硬件没有提供的功能。 基本整数 ISA 的一个子集对于教学目的来说,也许是有用的。但是基本核 心被定义得非常简单,使得真实硬件实现没理由只实现它的一个子集,除了省略对非对齐存储器访问支持、以及将所有 SYSTEM 指令作为自陷实现之外。

RISC-V 被设计成可以支持丰富的定制化和特殊化。基本整数 ISA 可被一个或者多个可选 指令集扩展进行增强,但是基本整数指令集不能被重新定义。 将 RISC-V 指令集扩展分 为标准扩展和非标准扩展。标准扩展一般都是有用的,并且与其它的标准扩展并不冲突。非 标准扩展是高度特殊化的,并可能与其它的标准扩展或者非标准扩展冲突。指令集扩展根据 基本整数指令集宽度不同,可能有轻微的功能差异。第 10 章描述了各种用来扩展 RISC-V ISA 不同的方法。

为 RISC-V 基本指令和指令集扩展开发了一个命名规则,为了支持更一般的软件开发,定义了一组标准扩展,提供乘法/除法、原子操作以及单 精度、双精度浮点算术。基本整数 ISA 被命名为“I”(依据整数寄存器宽度不同,前缀 RV32 或者 RV64),其中包含了整数计算指令、整数 load、整数 store 和控制流指令,并且在所有 RISC-V 实现中,都是必须的。标准整数乘法和除法扩展被命名为“M”,其中增加了对保存 在整数寄存器中的值进行乘法和除法的指令。

标准原子指令扩展被命名为“A”,其中增加 了对存储器进行原子的读、修改和写操作的指令,以支持处理器间的同步。标准单精度浮点 扩展,被命名为“F”,增加了浮点寄存器、单精度计算指令、单精度 load 和 store 指令。标 准双精度浮点扩展,被命名为“D”,扩展了浮点寄存器,并增加了双精度计算指令、load 和 store 指令。一个基本整数内核加上这四个标准扩展(“IMAFD”),被缩写为“G”,它提供 了一个通用的标量指令集。RV32G 和 RV64G 现在是 编译器工具链的缺省目标机器。后 续章节描述了这些扩展以及其他计划中的标准 RISC-V 扩展。

除了基本整数 ISA 和标准扩展之外,很少有一条新指令对所有应用程序巨大的好处,虽 然它可能在某些领域中非常有用。由于能耗效率要求更为特殊化, 相信对于一个 ISA 规 范中的必须部分的简化是很重要的。鉴于其他的体系结构通常将它们的 ISA 作为一个单一的 整体,它们会随着时间推移,当加入新指令的时候,就变化到一个新的版本。然而 RISC-V 尝试随着时间的推移,保持基本内核和每一个标准扩展不变,相反的,将新指令作为可选的   5 扩展。例如,基本整数 ISA 将成为一个被持续支持的独立 ISA,而不管任何随后而来的扩展。

随着用户 ISA 规范的 2.0 版本发布,尝试在未来的开发中,保持 “RV32IMAFD”、“RV64IMAFD”基本内核和标准扩展(也就是“RV32G”和 “RV64G”)保持不变。

1.2 指令长度编码

基本 RISC-V ISA 具有 32 位固定长度指令,并且必须在 32 位边界对齐。然而,标准 RISC-V 编码模式被设计成支持变长指令的扩展,在这个扩展中,每条指令长度可以是 16 位指令包 裹(parcel)长度的整数倍,并且这些指令包裹必须在 16 位边界对齐。描述的标准压缩 ISA 扩展,通过提供压缩的 16 位指令,减少了代码大小,并放松了对齐要求,允许 所有指令(16 位和 32 位)对齐到任意 16 位边界,以提高代码密度。

图 1.1 展示了标准 RISC-V 指令长度编码约定。所有基本 ISA 中的 32 位指令的最低 2 位 被设置为 11。可选的压缩 16 位指令集扩展中的指令,最低 2 位被设置为 00、01 或者 10。 超过 32 位的标准指令集扩展,在低位有额外的位被设置为 1,48 位、64 位长度约定如图 1.1 所示。指令长度在 80 位到 176 位之间的长度信息,被编码到一个 3 位的字段[14:12]中,给 出了 16 位字的数量,加上最开始的 5×16 位字。

 

由于压缩格式的代码大小和能耗节约, 想将对压缩格式内建支持加入 到 ISA 编码模式中,而不是事后再来添加。但是为了简化实现,也不希望 压缩格式支持是必须的。

也希望可选地支持更长的指令,用于实验和更大 的指令集扩展。虽然 编码约定需要一个对核心 RISC-V ISA 更为紧凑的编 码,但是这样做有几个好处。

一个支持标准 G 的 ISA 实现,只需要在指令缓存(instruction cache)保存 指令的最高 30 位(带来 6.25%的节约)。当重新填充指令缓存时,任何最低两 位有一位为零的指令,应当在被保存到缓存之前,被重新编码为 30 位非法指 令,以确保出现非法指令异常行为。

可能更为重要的是,通过浓缩 基本 ISA 为一个 32 位指令字的子集,留给定制扩展更多的空间。特别地,基本 RV32I ISA 在 32 位指令字中,使 用了少于 1/8 的编码空间。

一个不需要支持标准压缩指令扩展的实现,可以将3 个额外的 30位指令空间映射到 32位固定长度格式中,同时确保对大于 32 位指令集扩展的支持。更进一步,如果该实现并不需要支 持超过 32 位长度的指令,它还可以恢复 4 个主要的操作码区域。  

认为这是一个特性,即任何长度的指令,如果它的所有位都是零,那么这是一条非法的指令,因此这将快速地自陷错误跳转到填满零的存储器区域 (as this quickly traps erroneous jumps into zeroed memory regions)。类似的,也将指令编码全是 1 保留为一条非法指令,以捕获另外一种当断开存储器总 线或损坏的存储器设备时常见的情形。

基本RISC-V ISA具有一个小端存储器系统,但是非标准变种可以提供大端或者双端存储 器系统。指令被保存在存储器中,每个16位包裹以实现的端字节顺序,被保存到一个存储器 半字中。包含一条指令的包裹,被保存到递增的半字地址,其中最低寻址的包裹保存着指令 规范中最低位的二进制值,也就是说,指令总是按照一系列包裹的小端顺序保存的,而不管 存储器的端字节顺序。

图 1.2中的代码序列,将把一条32位指令正确地保存到存储器中,而 不管存储器的端字节顺序。

// 将x2中的32位指令,保存到x3指向的存储器 sh x2, 0(x3)

// 将指令的低半部分保存到第一个包裹中 srli x2, x2, 16

// 将高位移动到低位,覆盖x2 sh x2, 2(x3)

// 将高位保存到第二个包裹中

图 1.2:将 32 位指令从寄存器保存到存储器的推荐代码序列。

在大端、小端存储器系统中 都能正确工作,当使用变长指令集扩展时,可避免出现非对齐访问。  

为 RISC-V 存储器系统选择小端字节顺序,是因为当前小端系统占据 商业应用的统治地位(所有的 x86 系统;iOS,Android,Windows for ARM)。

一个小问题就是, 发现小端存储器系统对于硬件设计者来说,更为自然。 然而,某些应用领域,例如 IP 网络,在大端数据结构上进行操作,因此  留给非标准大端或者双端系统一些机会。  

必须将指令包裹在存储器中保存的顺序固定下来,而与存储器系统的 端字节顺序无关,来确保指令的长度编码位总是出现在半字地址的最前面。这 就允许取指单元通过一次取指,读取第一个 16 位指令包裹的最低几位,就可 以确定变长指令的长度。一旦确定了小端存储器系统和指令包裹顺序,自 然导致 将指令长度编码放到指令格式的 LSB 位置,以避免破坏操作码字段。

1.3 异常、自陷和中断  

将术语异常(exception)认为是在运行时出现了一个与当前RISC-V线程中的一条指 令相关的非正常的情况。

将术语自陷(trap)认为是在一个RISC-V线程中出现了一个异 常的情况,导致将控制同步传输到自陷处理函数。自陷处理函数通常是在一个更高特权环境 中执行的。  

将术语中断(interrupt)认为是在当前RISC-V线程外异步出现了一个事件。如果出现了一个必须处理的中断,将会选择某条指令来接收中断异常,然后顺序地产生一个自陷。

后续指令描述了在执行时产生异常的条件。这些异常是否和如何转变为自陷 的,依赖于执行环境,虽然预期是绝大多数环境在一个异常被触发时(signaled),采取一个精确的自陷(除了浮点异常,在标准浮点扩展中,并不会产生自陷)。  

“异常”和“自陷”术语的使用,与 IEEE-754 浮点标准是一致的。