3.1 RISC-V的历史
RISC-V 最早源自 2010 年夏天美国加州大学伯克利分校 Krste Asanović 教授主持的一个关于开源计算机系统的研究项目。该项目得到了美国国防高级研究计划局(Defense Advanced Research Projects Agency,DARPA)的资助,后来成为 RISC-V 的前身[ 这里顺便提一句,国际互联网Internet 的前身ARPANET(Advanced Research Projects Agency Network,高级研究计划局网络)也是由 DARPA 资助的。
RISC-V 中的字母 V 表示第五代的意思,所以发音时应该发作“RISC-Five”,表示它师承于伯克利分校之前开发的一系列 RISC 指令集。根据 RISC-V 的族谱,RISC-V 之前四代指令集都产生于 20 世纪 80 年代。当然,RISC-V 在其形成过程中,也从其他各种流行的指令集(MIPS、SPARC、ARM 等)中吸取了经验教训。
在 RISC-V 问世之际,移动计算主要由 ARM 处理器把持,而 Intel 公司的 x86处理器则占据了大部分的桌面计算市场,RISC-V 的出现给这两大巨头带来了挑战。与这两大巨头的指令集不同的是,RISC-V 是一个自由和开放的指令集,它的标准化工作由 RISC-V 基金会主持,该组织目前有超过 100 个会员,并在不断扩大之中。对任何想要用 RISC-V 设计实现处理器的公司与个人,他们都不会受到来自RISC-V 基金会的限制,也无须向 RISC-V 基金会支付授权费用。基金会各会员公司也承诺不会就 RISC-V 的基本议题向其他成员发起诉讼。
由于 RISC-V 没有上面提到的这些限制,因此很快得到了开源社区的大力拥护。面对 RISC-V 的攻城略地,ARM 也开始予以反击。2018 年夏,ARM 上线了一个名为 riscv-basics.com 的网站,对 RISC-V 发起舆论战。但是这种做法很快受到了来自各方的诟病,甚至连 ARM 自己的员工都对此做法表示不满。迫于各方压力,ARM 很快就关闭了该网站。
另外,为了促进 RISC-V 的产业化,RISC-V 的主要开发成员还于 2015 年成立了一家叫 SiFive 的初创公司,向市场提供各类 RISC-V 的处理器内核,以及相关的软件工具和开发套件。
3.2 8051的CISC指令集与RISC-V的比较
提到 RISC(Reduced Instruction Set Computer,精简指令集计算机),就必然也会提到 CISC(Complex Instruction Set Computer,复杂指令集计算机)。在许多嵌入式系统中得到广泛使用的 8051 单片机,便是 CISC 指令集的典型代表。笔者在开始设计 RISC-V 处理器之前,也曾做过一款 1T(单时钟周期)8051 处理器的设计,所以对这两类不同类型的指令集都有深入了解。这里笔者愿意将这些体会做个总结,并由此来反映 RISC-V 在技术设计上的优势。
可能有许多读者对 8051 单片机早已熟悉,该单片机是由美国 Intel 公司于 20世纪 80 年代推出的一款 8 位单片机。由于该单片机方便易用,许多公司都推出了第三方的兼容设计。直到今天,8051 单片机依然被许多嵌入式系统所选用。
然而在 20 世纪 80 年代该单片机刚刚问世时,半导体的制造工艺还只能达到μm 级,处理器所能达到的时钟频率偏低。而且当时硬件设计语言还处于起步阶段,也缺乏自动设计的工具,软件多以手工汇编编程为主。这就导致流水线设计的优势无法得到发挥,并且每条指令需要多个时钟周期才能完成。由于上述原因,当时的指令集设计往往具有以下特点:
(1)尽量在每条指令中实现更多的功能。例如 8051 的 CJNE 指令,就需要在一条指令中依次实现:
① 与累加器做减法。
② 修改进位标示。
③ 将结果做相等比较。
④ 根据比较结果决定是否跳转。
(2)指令集庞大,以实现更多的复杂功能。例如 8051 虽然是 8 位单片机,其指令集却包含高达 255 种不同的指令和格式。
(3)由于以上两点,导致变长指令的出现,以提高内存利用率。8051 的指令就有单字节、双字节与三字节三种不同的种类,而且除了对指令解码以外,没有其他的手段帮助判定指令长度。
(4)寻址方式众多。例如在8051 指令集中,对数值的操作包括如下方式:
① 立即数寻址。将常数包含在指令中。
② 直接寻址。将内存地址包含在指令中。
③ 间接寻址。将内存地址放入寄存器中,然后将寄存器地址包含在指令中。
④ 寄存器寻址。将操作数放入寄存器中,然后将寄存器地址包含在指令中。
由于众多的寻址方式,同一个功能在指令集中就可能对应多种指令格式。例如在 8051 指令集中,光是一个加法指令就有 12 种不同格式。类似地,跳转指令也存在多种的寻址方式和指令格式。
8051 指令集的特点,很大程度上也代表了当时众多 CISC 指令集的共同特点。这种特点是与当时半导体制造水平和软件发展水平相匹配的。随着半导体加工工艺的不断进步和软件开发水平的提高,流水线和高时钟频率的设计开始在处理器设计中流行,汇编语言也开始C/C++ 这类高级编程语言所替代。尽管 8051 是一个非常长寿的指令集,自问世近 40 年,依然被业界广泛采用,但是今天市面上出现的8051 处理器,却早已和它们的祖先大不一样了。8051 的第一代产品,其时钟频率只有 12 MHz,每个指令需要 12 个时钟周期才能完成。而今天我们所使用的 8051 处理器,都是增强型处理器,除了有更丰富的外围设备外,其增强之处主要表现在:
(1)时钟频率大幅提高。
(2)指令的吞吐率大幅提高,对大部分的指令,都可以做到在单个时钟周期内完成即我们通常说的 1T 8051。
(3)在软件上,支持 C 语言的开发环境。
换句话说,今天的增强型 8051 处理器,虽然其指令集还是 40 年前的那个指令集,但是其内部实现却早已经在原型基础上进行了 RISC 改造(实际上,类似的 RISC 改造也同样发生在 Intel 的 x86 处理器上)。
说明:由于指令集设计的缺陷,这种对 CISC 指令集的 RISC 实现不可避免地要在硬件上付出一定的代价。下面就以笔者主持设计的 PulseRain FP51-1T MCU 为例,对此具体加以说明。
3.2.2 8051指令集对处理器设计的负面影响
PulseRain FP51-1T MCU 是美国 PulseRain Technology 公司推出的一款针对FPGA 的 8 位微控制器,其内部的处理器内核是一个增强型 8051,可以对大部分的 8051 指令实现 1T 吞吐率,并且在 FPGA 上可以实现很高的时钟频率(在 Intel MAX10 C8 级器件上主频可以达到 100 MHz)。
8051 的流水线实现如图 3-1 所示,该处理器的内部有一个 5 级流水线,包含指令读取、指令解码(一)、数据内存读取、指令解码(二)和指令执行。尽管该处理器在 FPGA 上有优秀的性能表现,然而由于 8051 指令集本身的缺陷,使得设计者不得不以额外的逻辑资源为代价来换取更高的性能。最后的结果就是与同样时钟频率的 RISC-V 处理器相比,8 位 8051 内核居然比 32 位 RISC-V 内核消耗更多的逻辑资源,占用更大的芯片面积,而更大的芯片面积意味着更加耗电。对 FPGA器件来说,这些还不是一个太大的问题,但是对专用芯片(ASIC),特别是移动设备的专用芯片来说,更多的耗电往往意味着更短的电池寿命(Battery Life),这可能也是 Intel x86 处理器始终无法在移动设备市场上打开局面的原因之一。
![]()
图3-1 8051 的流水线实现
具体来说,8051 指令集的特点会对处理器的 RISC 实现产生如下负面影响:
1)尽量在每条指令中实现更多的功能
为了在实现这些复杂功能的同时保持高吞吐率,流水线的设计者不得不花更多的时间规划流水线的各级。即便如此,有些指令依然无法实现单周期吞吐,例如上文提到的 CJNE 指令,就需要两个时钟周期。
另外,现代的 8051 处理器开发,早已经采用C语言代替了早期的汇编语言。而高级语言的编译器往往很难把这类复杂、多功能机器指令的威力全部发挥出来,有违当初指令集的设计初衷。
当然,指令集复杂这个特点也并非一无是处。由于 CISC 指令集的指令复杂,也使得其代码密度(Code Density)一般要优于同等字宽的 RISC 处理器。
2)庞大的指令集
庞大的指令集必然导致指令的解码阶段变得更为复杂,需要耗费更多的逻辑资源。读者可能已经注意到,在图 3-1 所示的 5 级流水线中,指令集被分为两部分,对它们各自的解码分别占用了流水线的一级。这样设计的原因之一就是为了在庞大指令集下实现高吞吐率、高时钟频率,而不得不做出的妥协。同样时钟频率的RISC-V 处理器,由于指令集比较精简,就无需做这样的妥协,从而大大节省了逻辑资源,简化了流水线设计。
3)由于以上两点,导致变长指令的出现,以提高内存利用率
8051 的指令有单字节、双字节和三字节三种不同的种类,除解码(Decode)外,没有其他的手段帮助判定指令长度。这种变长的指令结构,导致指令之间的边界很难判定,甚至有可能导致内存的非对齐读取(Unaligned Memory Access),从而对流水线的取指器(Instruction Fetch)设计带来挑战。
幸运的是,8051 的内存架构是哈佛架构,其代码与数据在不同的地址空间中分开存放。这就使得代码存储部分可以单独做一些优化设计。在图 3-1 中左边部分的片上代码内存,实际上被分成 4 个 8 位宽的存储体,这样对代码内存的一次读取就可以得到 4 字节,从而保证至少可以有一条完整的指令。然而即便如此,由于8051 指令集没有其他辅助手段来帮助判定指令长度,为了确定指令的边界,8051的取指器不得不为此花费比 RISC-V 更多的逻辑资源。
4)众多的寻址方式
由于 8051 存在众多的寻址方式,使得指令集中的许多指令都可以访问内存。这导致流水线的数据冲突(Data Hazard)很难判断,有时不得不通过硬件自动插入空操作(Null Operation,NOP)来保持数据的正确和完整。这样既消耗了逻辑资源,又降低了流水线的效率,从而对功耗和性能造成双重打击。
说明:虽然 8051 指令集有其历史局限性,但是 8051 处理器却由于其短小精悍、性价比高,一直为笔者所钟爱。其虽历四十载,依然廉颇未老,不乏拥趸。
3.2.3 RISC-V 指令集对处理器设计的正面影响
8051 指令集的缺陷,在 RISC-V 中都得到了避免,具体说明如下。
1. 引入指令长度编码
8051 指令集除了对指令解码以外,没有其他的辅助手段帮助判定指令长度,而 RISC-V 则可以通过指令的低位部分来判断指令的长度,被称为指令长度编码(Instruction Length Encoding)。图 3-2 展示了 16 ~ 64 位指令的编码方式。64 位以上的编码方式,可以在 RISC-V 官方标准中找到。
![]()
图3-2 RISC-V指令长度编码
指令长度编码的引入,大大简化了流水线取指器的设计,在取指时,硬件只需要集中优化边界对齐的内存读取就可以了。而对非对齐的访问,则可以通过产生异常,让软件处理器来处理。这样既节省了逻辑资源,又不影响处理器的性能。
2. 指令集规模较小,指令格式规整尽管不是 8 位指令集,RISC-V 的指令集规模却比 8051 这样的 8 位指令集要小许多。RISC-V 的 32 位基础整数指令集只有 47 条指令,即使算上 8 条乘除法扩展指令,其指令总数也不到 8051 指令集规模的 1/4。指令集的小巧使得指令的解码器变得简单,更无须像图 3-1 中那样将指令集分成两部分来分别解码。同时,RISC-V 的指令格式也非常规整,除了指令长度编码总是处在指令低位以外,在不同指令格式之间,操作码、源寄存器和目标寄存器总是位于相同的位置上。例如在 RISC-V 32 位基础整数指令集中(RV32I),操作码总是占用低 7 位,而源寄存器 1 和 2(rs1,rs2)则分别占据 15-19 位与 20-24 位。目标寄存器(rd)则占用 7-11 位(位索引以 0 为参考起点)。这种规整的指令格式进一步简化了指令解码器和指令执行器的设计。
3. 每条指令实现单个功能与 CISC 指令集的设计思想截然相反,RISC-V 指令集中的每条指令只集中于优化实现单个的功能,这种将复杂任务通过多个单功能的指令来实现的做法也一直是 RISC 指令集的指导思想。因为这样可以简化流水线的设计,从而能实现更高的时钟主频,最终可以让 RISC 获得比 CISC 更佳的总体性能。
4. 内存访问只能通过 LOAD/STORE
与 8051 指令集中,具有众多的寻址方式不同,在 RISC-V 指令集中,对内存的读写只能通过 LOAD 指令和 STORE 指令实现。而其他的指令,都只能以寄存器为操作对象。没有了复杂的内存寻址方式,使得流水线对数据冲突(Data Hazard)可以及早做出正确的判断,并通过流水线各级之间的转送加以处理,而不需要插入空操作(NOP),极大提高了代码的执行效率。当然,这一特点也是 RISC 指令集的共有特点之一。
至此我们可以看到,CISC 指令集的那些历史局限性,在 RISC-V 指令集中都得到了突破。下面的章节会将 RISC-V 与其他的主流 RISC 指令集做对比,并展示其设计上的考量与取舍。