RISC-V开放架构设计之道
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.3 ISA设计导论

在介绍RISC-V ISA前,了解计算机架构师在设计ISA时需要遵守的基本原则和做出的权衡,将有助于你理解RISC-V的设计。下面列出7个评价指标。后续章节介绍RISC-V如何决策时,我们将在页边放置相应的图标予以强调。

成本

简洁

性能

架构和实现分离

提升空间

代码大小

易于编程/编译/链接

成本

• 成本(美元硬币)

• 简洁(车轮)

• 性能(速度计)

• 架构和实现分离(分开的两个半圆)

• 提升空间(手风琴)

• 代码大小(挤压线条的两个相向箭头)

• 易于编程/编译/链接(儿童积木,“像ABC一样简单”)

为阐述我们的想法,我们将在本节中展示过去ISA做出的一些选择。今天看来这些选择并不明智,而RISC-V通常能做出更好的决策。

成本。处理器以集成电路的形式实现,通常称为芯片(chip)或晶粒(die)。称它们为晶粒的原因是,它们是由一个圆形晶片切割(dice)得到的许多单独的块。图1.4展示了RISC-V处理器的晶圆。成本对晶粒面积十分敏感:

成本≈f(晶粒面积2

显然,晶粒越小,每个晶圆能切割出的晶粒越多,而晶粒成本主要来自生产晶圆本身。不太直观的是,晶粒越小,良率(可用晶粒占晶粒总数的比例)越高。这是因为硅制造过程导致晶圆上分布着一些很小的制造缺陷,故晶粒越小,有缺陷的晶粒比例越低。

架构师希望保持ISA的简洁性,从而缩小相应处理器的尺寸。我们将在后续章节中看到,RISC-V ISA比ARM-32 ISA简洁得多。为展示简洁的重要性,我们将大小缓存(16KiB)和制造工艺(TSMC40GPLUS)均相同的RISC-V Rocket处理器与ARM-32 Cortex-A5处理器进行对比。RISC-V晶粒的大小是0.27mm2,而ARM-32晶粒的大小是0.53mm2。由于面积大将近1倍,ARM-32 Cortex-A5的晶粒成本大约是RISC-V Rocket的4(22)倍。即使晶粒大小只减小10%,成本也会降低为81%(0.92)。

高端处理器可通过组合简单指令来提升性能,而不会因更大、更复杂的ISA给所有低端处理器的实现带来负担。这种技术被称为宏融合(macrofusion),因为它将“宏”指令融合在一起。

图1.4 由SiFive设计的直径为8英寸的RISC-V芯片晶圆

此晶圆包含两类RISC-V芯片,均使用较旧、较大的工艺制程。其中FE310芯片的尺寸为2.65mm×2.72mm,SiFive测试芯片的尺寸为2.89mm×2.72mm。该晶圆包含总计3712颗芯片,前者1846颗,后者1866颗。

简洁。鉴于成本对复杂性十分敏感,架构师需要一款简洁的ISA来减小晶粒面积。简洁的ISA也能节省芯片设计和验证的时间,而这可能占芯片开发成本的主要部分。这些都是芯片成本的一部分,最终根据芯片的发货量均摊到单颗芯片上。简洁性还能降低文档的开销,让客户更容易了解如何使用这款ISA。

简洁

简单处理器有助于嵌入式应用程序,因为其执行时间更容易预测。微控制器的汇编语言程序员通常希望维护精确时序,因此他们依赖可通过手动计算来预测所需时钟周期数的代码。

以下是ARM-32 ISA复杂性的一个鲜明例子:

该指令表示LoaD Multiple,Increment-Address,on EQual。它会在EQ条件码置位时从内存读入5次数据,并写入6个寄存器。此外,它还将结果写入PC,从而执行条件分支。做的事情真多!

讽刺的是,简单指令通常比复杂指令更常用。例如,x86-32的enter本来作为进入过程后执行的第一条指令以创建栈帧(见第3章),但大多数编译器用两条简单的x86-32指令代替它:

性能。除了那些用于嵌入式应用的微型芯片,架构师通常关注芯片的性能和成本。性能可分解为以下三个因子:

性能

最后一项因子是时钟频率的倒数,因此1GHz时钟频率意味着每个时钟周期的时间为1ns(1/109)。

平均时钟周期数可小于1,因为A9和BOOM(Celio et al.2015)是所谓的超标量处理器,每个时钟周期执行多条指令。

即使在每个程序中简洁ISA需要执行的指令比复杂ISA的多,但前者能通过更高的时钟频率或更小的每指令平均周期数(Cycles Per Instruction,CPI)来弥补。

例如,ARM-32 Cortex-A9运行CoreMark基准测试(Gal-on et al.2012)(100000次迭代)的性能为

对于RISC-V的BOOM实现,其性能为

本例中,ARM处理器执行的指令并不比RISC-V处理器的少。我们将看到,简单的指令也是最常用的指令,因此ISA的简洁性在所有指标中尤为重要。对于上述程序,RISC-V处理器的三个因子分别提升近10%,总体性能提升近30%。如果更简洁的ISA还能设计出更小的芯片,那么其性价比将非常出色。

架构和实现分离。架构和实现之间的最初区别可追溯到20世纪60年代,即:架构是机器语言程序员为了编写正确的程序所需了解的知识,而不是为了提升程序性能。对于架构师,一项诱人的方案是在ISA中添加指令来优化特定时期某个实现的性能和成本,但这会给其他不同或将来的实现带来负担。

架构和实现分离

今天的流水线处理器使用硬件预测器预测分支结果,准确度超过90%,且适用于任意长度的流水线,只需一种机制在预测错误时刷新并重启流水线。

MIPS-32 ISA的延迟分支是一个令人遗憾的例子。考虑流水线执行的场景,处理器希望下一条待执行指令已位于流水线中,但条件分支指令无法提前确定后续执行的是顺序的下一条指令(若分支不跳转),还是分支目标地址的指令(若分支跳转)。对于第一个5级流水线的微处理器,上述性质导致流水线阻塞一个时钟周期。为解决该问题,MIPS-32将分支指令的跳转重新定义为在后续一条指令之后才发生,因此其后续一条指令总会被执行。程序员或编译器开发者需要将一些有用的指令放入延迟槽。

遗憾的是,这种“解决方案”对后续有着更多流水级(在计算分支结果前取了更多指令)的MIPS-32处理器毫无帮助,且由于增量型ISA需要向过去兼容(见1.2节),这反而让MIPS-32程序员、编译器开发者和处理器设计者的工作变得更加困难,也让MIPS-32的代码变得更难理解(见第32页图2.9)。

除了不应加入那些仅有助于一个实现的功能,架构师也不应加入阻碍某些实现的功能。如前文所述,ARM-32和其他一些ISA提供取多字(Load Multiple)指令。这些指令能提升单发射流水线设计的性能,但会给多发射流水线带来负面影响。原因在于简单实现无法将该指令的单个取数操作与其他指令并行调度,从而降低这些处理器的指令吞吐。

提升空间。随着摩尔定律(Moore’s Law)终结,大幅提高性价比的唯一途径是为特定领域(如深度学习、增强现实、组合优化、图形等)添加自定义指令。这意味着如今的ISA必须为将来的扩展预留操作码空间。

提升空间

前文提到的ARM-32指令ldmiaeq甚至更复杂,因为当它跳转时,还能在ARM-32和Thumb/Thumb-2之间切换指令集模式。

在摩尔定律如日中天的20世纪70年代和80年代,很少有人考虑为将来的加速器节省操作码空间。相反,架构师认为更长的地址和立即数字段更有价值,它们能减少每个程序执行的指令数,这也是前文性能等式的第一个因子。

操作码空间不足的一个反面例子是,ARM-32架构师后来试图通过向以前统一的32位ISA中添加16位指令来缩减代码大小,但发现无可用空间。因此,唯一的解决方案是先设计一款16位指令的新ISA(Thumb),后来又设计了一款同时支持16位和32位指令的新ISA(Thumb-2),并通过一个模式位在它们和ARM ISA之间切换。为切换模式,程序员或编译器通过分支跳转到一个最低位为1的地址,这种切换方案可行是因为该位在16位和32位指令中均为0。

代码大小。程序越小,程序存储器所需芯片面积越小,这对嵌入式设备是一项巨大的成本。实际上,这启发了ARM架构师在Thumb ISA和Thumb-2 ISA中追加一些更短的指令。更小的程序还能减少指令缓存的缺失次数,从而降低功耗(访问片外DRAM的能耗远高于访问片上SRAM)并提升性能。让代码更短是ISA架构师的目标之一。

代码大小

15字节x86-32指令的一个例子是

它汇编成十六进制形式得到:67 66 f0 3e 81 84 8e 78 56 34 12 89 ab cd ef。后8字节是2个地址,前7字节分别表示原子内存操作、加操作、32位数据、数据段寄存器、2个地址寄存器和比例变址寻址模式。1字节指令的一个例子是汇编成40的inc eax。

x86-32 ISA的指令可短至1字节,也可长达15字节。你可能认为,与ARM-32和RISC-V这些32位定长ISA相比,使用变长指令的x86必定能生成更小的程序。从逻辑上看,以8位为单元的变长指令也应该比那些仅提供16位和32位指令的ISA(如Thumb-2和使用RV32C扩展的RISC-V,见第7章)的指令更短。图1.5显示,对于32位指令,ARM-32和RISC-V的代码比x86-32的长6%~9%,而令人惊讶的是,x86-32的代码比同时提供16位和32位指令的压缩版本(RV32C和Thumb-2)的长26%。

图1.5 RV32G、ARM-32、x86-32、RV32C和Thumb-2程序的相对大小

最后两个ISA正是以短代码为目标的。测试程序为采用GCC编译器的SPEC CPU2006基准测试。与RV32C相比,Thumb-2代码更短的原因是它在过程入口处使用多字存取(Load and Store Multiple)指令来减小代码大小。RV32C为维护与RV32G的一一映射而未加入此类指令,后者则为降低高端处理器的实现复杂性而未加入多字存取指令。第7章将介绍RV32C。RV32G表示常用的RISC-V扩展组合(RV32M、RV32F、RV32D和RV32A),全称为RV32IMAFD(Waterman,2016)。

虽然与RV32C和Thumb-2相比,使用变长指令的新ISA能生成更短的代码,但20世纪70年代设计第一版x86的架构师并不关心此问题。此外,考虑到增量型ISA(见1.2节)需要保持向过去的二进制兼容性,数百条新增的x86-32指令比预期的要长,因为将它们塞进原始x86有限的空闲操作码空间时,需要增加1~2字节的前缀。

易于编程/编译/链接。由于访问寄存器比访问内存快得多,因此编译器必须做好寄存器分配工作,这在寄存器数量较多的时候更简单。在这方面,ARM-32有16个寄存器,而x86-32只有8个。大多数现代ISA(包括RISC-V)都有相对较多的32个整数寄存器。更多寄存器显然能让编译器和汇编语言程序员的工作更轻松。

易于编程/编译/链接

编译器和汇编语言程序员面临的另一个问题是估计代码序列的执行速度。我们将看到,每条RISC-V指令通常最多只需要一个时钟周期(忽略缓存缺失)。但正如前文所述,即使所需内容已在缓存中,ARM-32和x86-32有些指令的执行仍需多个时钟周期。此外,与ARM-32和RISC-V不同,x86-32算术指令的操作数可位于内存中,而不要求都在寄存器中。复杂指令和内存操作数使处理器设计者难以实现性能的可预测性。

ISA支持位置无关代码(Position Independent Code,PIC),有助于支持动态链接(见3.5节),因为共享库代码在不同的程序中可位于不同的地址。PC相对分支和数据寻址是PIC的福音。虽然几乎所有的ISA都提供PC相对寻址的分支指令,但x86-32和MIPS-32缺少PC相对数据寻址。

补充说明:ARM-32、MIPS-32和x86-32

补充说明是一些选读内容,如果读者对某个主题感兴趣,则可深入阅读,但它们对理解本书其余部分并非必要。例如,我们未采用官方的ISA名称。32位地址的ARM ISA有许多版本,第一版诞生于1986年,最新版本是2005年的ARMv7。ARM-32一般指ARMv7 ISA。MIPS也有许多32位版本,但我们指的是初版MIPS I(“MIPS32”是一款后续的ISA,和我们所称的MIPS-32不同)。Intel的首个16位地址架构是1978年的8086,1985年的80386 ISA扩展到32位地址。我们采用的x86-32记号一般指IA-32,它是x86 ISA的32位地址版本。和这些ISA的众多变体相比,我们采用的非标准术语反而最容易区分。