2.2.2、寄存器集
RISC-V 有 32 个整数寄存器 (在嵌入式版本则是 16 个)。当浮点延伸集被实现的时候,还有 32 个浮点寄存器。除了“存储器访问指令”之外,一般指令“只能”寻址寄存器。
RISC-V 的通用寄存器中包含一个“零寄存器”(其值写入无效、读为零)。使用零寄存器可以让指令集设计更简单,减少硬件的指令形式。比方说,把“寄存器 X 复制到寄存器 Y” (MOV Y, X),可以使用“将寄存器 X 与 0 相加后,复制到寄存器 Y” (ADD Y, X, r0) 实现。
RISC-V 有提供“控制寄存器”及“状态寄存器”,但是 user-mode 程序只能访问用来“量测性能”及“浮点管理”的部分。
RISC-V 并没有指令可以存储和恢复多个寄存器。这些设计在 RISC-V 当中,被认为是不必要的,过于复杂的,可能过慢的设计(核心原因是这种设计会增加流水线的复杂度)。
2.2.3、存储器访问
RISC-V 只有 load 与 store 指令可以访问存储器。Load 与 Store 支持的数据长度从 8-bit 到架构的位宽(比如32bit或64bit)。访问并不需要对齐,不过如果有对齐的话可以增加性能。
Load 和 Store 指令可以直接访问常量、堆栈中的本地变量或是数据结构中的内容。寻址的方式是使用基底寄存器与 12-bit 的 signed 相对地址 (+- 2KB)。
内存顺序方面(Memory Order)RISC-V 不保证访问的顺序 --- 除非有像 FENCE 这样的指令出现。这意味着底层软件人员编写多核相关代码时需要十分小心。
RISC-V 固定是 little-endian。这稍稍降低了硬件的复杂度与成本,因为所有位宽的数据读取,都遵循一样的顺序。举例来说,RISC-V 的指令集都是从最低的那个 byte 开始解码(所有RISC-V指令的编码格式都将opcode放在了最低7个bit,因此硬件取指时总是先获取到opcode,方便流水线设计)。
2.2.4、立即数
RISC-V 读取 32-bit 常量与地址是透过设置 upper 20-bit 的指令达到的。LUI 指令 (Load Upper Immediate) 把(指令中的)20-bit 读取到寄存器的 31~12 bits 当中。与LUI搭配一条包含 12-bit 位移的 Load 与 Store 指令或是ADDI指令,便可以完成一个32bit立即数的获取。
另一个 AUIPC 指令,读取 upper 20-bit并加上 PC (Program Counter) ,之后存放到指定寄存器。同样搭配ADDI指令,可以方便的让 PIC 程序(Position-Independent Code)能够支持“相对于代码位置的 32-bit 地址”。
在 64-bit 架构下,LUI 与 AUIPC 运行的结果会被比特扩展至 64-bit (sign-extent)。
有些高速的 CPU 会把一些指令“融合”成一个指令。比如说:上述的 LUI 与 AUIPC 就很适合和 Load/Save 指令一起融合。
2.2.5、函数调用
RISC-V 的函数调用 JAL (Jump and Link) 把回传地址放在一个寄存器当中。它省下了一次访问推叠存储器,所以对性能友好。JAL 伴随一个 20-bit signed 位移。这个位移会被乘上 2 之后,加到 PC 当中。如果没有对齐到 32-bit 地址,CPU 会触发一个例外。
RISC-V 的 JALR (Jump and Link Register) 指令和 JAL 很像,但是他是把一个 12-bit 的相对位移,和某一个寄存器相加。(而 JAL 是用 20-bit 相加)
JALR 的指令格式有点像使用寄存器的 load/store 指令。搭配另一个设置高 20-bit 的基底寄存器,可以组成一个 32-bit 的地址(可以是绝对地址,例如 LUI; 或是相对于 PC 的地址,例如 AUIPC)。(使用零寄存器当基底寄存器,则是可以跳到 0 +- 2KB 的绝对地址)
2.2.6、分支预测
RISC-V 要求 CPU 实现“默认分支预测” (default branch prediction)。如果是往回跳跃 (例如: do {...} while (expr) 中的 expr 判断式),CPU 要预测跳跃会发生,也就是预测 expr “会”成立。如果是向前跳跃(例如:if (expr) {...} else {...} 中的 else 部分),CPU 预测这个跳跃会发生,也就是预测 expr “不会”成立。CPU 判断往回或向前的方法,是看指令中相对地址的最高比特,也就是有号数的部分 (signed bit):如果是 1,表示是负数,要往回跳跃。如果是 0,表示是正数,要向前跳跃。当然,复杂的 CPU 实现也可以加入更多的分支预测。
2.2.7、运算与逻辑
RISC-V 把数**算指令归类到一个很小的 I 子集当中,包括:加法,减法,位移,位操作,及比较跳跃。这些可以使用软件的方式去模拟其他大部分的 RISC-V 指令(atomic 运算是值得一提的例外)。RISC-V 目前没有“数开头有几个零”以及一些用来加速软件浮点运算的位操作。
整数乘法子集(M 子集)包括:有号数与无号数的乘法与除法。浮点子集包括单精度运算,以及类似于整数的“比较跳跃”。
2.2.8、原子内存操作
RISC-V 支持多个 CPU 与 thread。其标准存储器同步模式是“释放一致”原则。也就是说,读取和写入顺序可以重排,但是有些读取可以被设置成“获取”运算,会在其后的访问之前被运行,有些写入可以被当作“释放”运算,必须在其之前的访问的后面运行。
基本指令当中包含了最少的支持,使用 fence 指令来保证存储器访问顺序。尽管这很简单(fence r/rw 提供“获取”,fence rw/w 提供“释放”),组合起来还是可以很有效率。
3、RISC-V架构在物联网领域有哪些优势?
3.1、技术层面
结合物联网碎片化的特点,会发现RISC-V的很多设计思想十分贴合物联网灵活的需求。下面展开说明为什么说RSIC-V是非常适合物联网的CPU架构。
3.1.1、成本与简洁性
从前文的"架构特色"章节中可以看到大量的简洁化设计,比如没有复杂的多寄存器读写指令、合理的12+20bit立即数设计、去除条件执行指令、数**算归类到极小集等等。这些简化设计最终体现在芯片生产过程中,缩小了实现 RISC-V架构处理器的尺寸,进而压缩了成本。即使晶粒的大小只缩小 10%,成本也将以 1.2(1.1 2)倍的比例缩小。
鉴于成本对于复杂度的敏感性,RISC-V间接性带来更小的芯片面积之外,还能缩短芯片的设计和验证时间,而它们可能构成了芯片开发的大部分成本。这些成本必须算到芯片的成本当中。简洁性还能降低文档成本,让客户更容易了解如何使用RISC-V架构。
3.1.2、性能
评估一个CPU架构的性能,需要看做同样的事需要多少条指令以及CPU的频率。即使一个简单的CPU架构可能在每个程序执行的指令数方面多于复杂的CPU架构,但它可以通过 更快的时钟频率或更低的平均单条指令周期数(CPI)来弥补。
RISC-V从一开始就考虑到方便硬件实现更加高效的流水线,同时指令集的选择也更加高效。举例说明,运行 CoreMark 测试程序[Gal-On, Levy 2012](100000 次迭代)后,得到 ARM-32 Cortex-A9 的性能为18.15秒,相对应RISC-V的芯片则为14.26秒。
体现RISC-V性能优势的另一点是其支持32个通用寄存器,ARM-32 有 16 个寄存器,而 x86-32 只有 8 个。CPU架构领域寄存器可以被理解为第0级Cache,有了更多的寄存器可供选择,编译器编译出的指令会较少很多内存操作。
综合性能与成本可以发现,简洁如RISC-V这样的架构能催生出更小的芯片,使其具备很高的性价比。
3.1.3、可扩展性
随着摩尔定律(Moore’s law)的终结,对CPU性能进行重大改进的唯一途径是为特 定领域(例如深度学习,增强现实,组合优化,图形等)添加自定义指令。而RISC-V架构原生保留了大量的操作码空间,为厂商实现自定义的指令集留出了空间。
与之相对,ARM或者X86由于历史悠久,都需要兼容大量的历史遗留指令,导致指令码空间是否有限。x86由于指令长度可变,所以增加了很多很长的指令,为处理器的指令解析、cache、分支预测等带来沉重的负担。而ARM采用了同一个架构支持多个指令模式的做法(Thumb、Thumb-2、ARM),带来架构设计的复杂性。
|