终于有⼈把RISC-V讲明⽩了
0 RISC-V和其他开放架构有何不同
如果仅从“免费”或“开放”这两点来评判,RISC-V架构并不是第⼀个做到免费或开放的处理器架构。
在开始之前,我们先通过论述⼏个具有代表性的开放架构,来分析RISC-V架构的不同之处以及为什么其他开放架构没能取得⾜够的成功。
0.1 平民英雄——OpenRISC
OpenRISC是OpenCores组织提供的基于GPL协议的开放源代码RISC处理器。
OpenRISC具有以下特点:
采⽤免费开放的32/64位 RISC架构。
⽤Verilog HDL(硬件描述语⾔)实现了基于该架构的处理器源代码。
具有完整的⼯具链。
OpenRISC被应⽤到很多公司的项⽬之中。可以说,OpenRISC是应⽤⾮常⼴泛的⼀种开源处理器实现。
OpenRISC的不⾜之处在于其侧重于实现⼀种开源的CPU Core,⽽⾮⽴⾜于定义⼀种开放的指令集架构,因此其架构的发展不够完整,指令集的定义也不具备上节中提到的RISC-V架构的优点,更加没有上升到成⽴专门的基⾦会组织的⾼度。OpenRISC更多的时候被认为是⼀个开源的Core,⽽⾮⼀种优美的指令集架构。此外,OpenRISC的许可证为GPL,这意味着所有的指令集改动都必须开源(⽽RISC-V则⽆此约束)。
0.2 豪门显贵——SPARC
SPARC架构作为经典的RISC微处理器架构之⼀,SPARC最早于1985年由Sun电脑所设计。SPARC也是SPARC国际公司的注册商标之⼀,这家公司于1989年成⽴,⽬的是向外界推⼴SPARC架构以及为该架构进⾏兼容性测试。该公司为了推⼴SPARC的⽣态系
统,SPARC国际公司将标准开放,并授权予多家⽣产商采⽤,包括德州仪器、Cypress半导体和富⼠通等。由于SPARC架构也对外完全开放,因此,也出现了完全开放源码的LEON处理器。不仅如此,Sun公司还于1994年推动SPARC v8架构成为IEEE标准(IEEE Standard 1754-1994)。
由于SPARC架构的初衷是⾯向服务器领域⽽设计,其最⼤的特点是拥有⼀个⼤型的寄存器窗⼝,符合SPARC架构的处理器需要实现从72到640个之多的通⽤寄存器,每个寄存器宽度为64bits,组成⼀系列的寄存器组,称之为寄存器窗⼝。
这种寄存器窗⼝的架构,由于可以切换不同的寄存器组快速地响应函数调⽤与返回,因此,能够产⽣⾮常⾼的性能,但是这种架构由于功耗⾯积代价太⼤,⽽并不适⽤于PC与嵌⼊式领域处理器。⽽SPARC架构也不具备模块化的特点,使得⽤户⽆法裁剪和选择。很难作为⼀种通⽤的处理器架构对商⽤的x86和ARM架构形成替代。
设计这种超⼤服务器CPU芯⽚⼜⾮普通公司与个⼈所能涉⾜,⽽有能⼒设计这种⼤型CPU的公司也没有必要投⼊巨⼤的成本来挑战x86的统治地位。随着Sun公司的衰弱,SPARC架构现在基本上退出了⼈们的视野。感兴趣的读者请在⽹络上⾃⾏搜索⽂章《再见SPARC处理器,再见Sun》
0.3 名校优⽣——RISC-V
关于RISC-V在伯克利⼤学诞⽣的经历,本节在此不做重复赘述。
因为多年来在CPU领域已经出现过多个免费或开放的架构,很多⾼校也在科研项⽬中推出过多种指令集架构。因此,当笔者第⼀次听说RISC-V之时,以为⼜是⼀个玩具,或纯粹学术性质的科研项⽬⽽不以为意。
直到笔者亲⾃通读了⼀遍RISC-V的架构⽂档,不禁为其先进的设计理念所折服。同时,RISC-V架构的各种优点也得到了众多专业⼈⼠的青睐好评和众多商业公司的相继加盟。并且2016年RISC-V基⾦会的正式启动在业界引起了不⼩的影响。如此种种,使得RISC-V成为⾄今为⽌最具备⾰命性意义的开放处理器架构。
1 简单就是美——RISC-V架构的设计哲学
RISC-V架构作为⼀种指令集架构,在介绍细节之前,让我们先了解设计的哲学。所谓设计的“哲学”便是其推崇的⼀种策略,譬如说我们熟知的⽇本车的设计哲学是经济省油,美国车的设计哲学是霸⽓外漏等。RISC-V架构的设计哲学是什么呢?是“⼤道⾄简”。
笔者最为推崇的⼀种设计原则便是:简单就是美,简单便意味着可靠。⽆数的实际案例已经佐证了“简单即意味着可靠的”真理,反之越复杂的机器越则越容易出错。
所谓⼤道⾄简,在IC设计的实际⼯作中,笔者曾见过最简洁的设计实现安全可靠,也曾见过最繁复的设计长时间⽆法稳定收敛。最简洁的设计往往是最可靠的,在⼤多数的项⽬实践中⼀次次的得到检验。
IC设计的⼯作性质⾮常特殊,其最终的产出是芯⽚,⽽⼀款芯⽚的设计和制造周期均很长,⽆法像软
件代码那样轻易的升级和打补丁,每⼀次芯⽚的改版到交付都需要⼏个⽉的周期。不仅如此,芯⽚的⼀次制造成本费⽤⾼昂,从⼏⼗万美⾦到百千万美⾦不等。这些特性都决定了IC设计的试错成本极为⾼昂,因此能够有效的降低错误的发⽣就显得⾮常的重要。
现代的芯⽚设计规模越来越⼤,复杂度越来越⾼,并不是说要求设计者⼀味的逃避使⽤复杂的技术,⽽是应该将好钢⽤在⼑刃上,将最复杂的设计⽤在最为关键的场景,在⼤多数有选择的情况下,尽量选择简洁的实现⽅案。
笔者在第⼀次阅读了RISC-V架构⽂档之时,不禁击节赞叹,拍案惊奇,因为RISC-V架构在其⽂档中不断地明确强调,其设计哲学是“⼤道⾄简”,⼒图通过架构的定义使得硬件的实现⾜够简单。其简单就是美的哲学,可以从⼏个⽅⾯容易看出,后续⼩节将⼀⼀加以论述。
1.1 ⽆病⼀⾝轻——架构的篇幅
在处理器领域,⽬前主流的架构为x86与ARM架构,笔者曾经参与设计ARM架构的应⽤处理器,因此需要阅读ARM的架构⽂档,如果对其熟悉的读者应该了解其篇幅。经过⼏⼗年的发展,现代的x86与ARM架构的架构⽂档长达⼏百数千页。打印出来能有半个桌⼦⾼,可真
是“著作等⾝”。
之所以现代x86与ARM架构的⽂档长达数千页,且版本众多,⼀个主要的原因是因为其架构的发展的过程也伴随了现代处理器架构技术的不断发展成熟。
并且作为商⽤的架构,为了能够保持架构的向后兼容性,其不得不保留许多过时的定义,或者在定义新的架构部分时为了能够将就已经存在的技术部分⽽显得⾮常的别扭。久⽽久之就变得极为冗长。
那么现代成熟的架构是否能够选择重新开始,重新定义⼀个简洁的架构呢,可以说是⼏乎不可能。其中⼀个重要的原因便是其⽆法向前兼容,从⽽⽆法得到⽤户的接受。试想⼀下如果我们买了⼀款新的搭配新的处理器的电脑或者⼿机回家,之前所有的软件都⽆法运⾏⽽变砖,那肯定是⽆法让⼈接受的。
⽽现在才推出的RISC-V架构,则具备了后发优势,由于计算机体系结构经过多年的发展已经成为⽐较成熟的技术,多年来在不断成熟的过程中暴露的问题都已经被研究透彻,因此新的RISC-V架构能够加以规避,并且没有背负向后兼容的历史包袱,可以说是⽆病⼀⾝轻。
⽬前的“RISC-V架构⽂档”分为“指令集⽂档”(riscv-spec-v2.2.pdf)和“特权架构⽂档”(riscv-privileged-v1.10.pdf)。“指令集⽂档”的篇幅为145页,⽽“特权架构⽂档”的篇幅也仅为91页。熟悉体系结构的⼯程师仅需⼀⾄两天便可将其通读,虽然“RISC-V的架构⽂档”还在不断地丰富,但是相⽐“x86的架构⽂档”与“ARM的架构⽂档”,RISC-V的篇幅可以说是极其短⼩精悍。
图1 RISC-V基⾦会⽹站上的架构⽂档
1.2 能屈能伸——模块化的指令集
RISC-V架构相⽐其他成熟的商业架构的最⼤⼀个不同还在于它是⼀个模块化的架构。因此,RISC-V架构不仅短⼩精悍,⽽且其不同的部分还能以模块化的⽅式组织在⼀起,从⽽试图通过⼀套统⼀的架
构满⾜各种不同的应⽤。
这种模块化是x86与ARM架构所不具备的。以ARM的架构为例,ARM的架构分为A、R和M三个系列,分别针对于Application(应⽤操作系统)、Real-Time(实时)和Embedded(嵌⼊式)三个领域,彼此之间并不兼容。
但是模块化的RISC-V架构能够使得⽤户能够灵活选择不同的模块组合,以满⾜不同的应⽤场景,可以说是“⽼少咸宜”。譬如针对于⼩⾯积低功耗嵌⼊式场景,⽤户可以选择RV32IC组合的指令集,仅使⽤Machine Mode(机器模式);⽽⾼性能应⽤操作系统场景则可以选择譬如RV32IMFDC的指令集,使⽤Machine Mode(机器模式)与User Mode(⽤户模式)两种模式。⽽他们共同的部分则可以相互兼容。
1.3 浓缩的都是精华——指令的数量
短⼩精悍的架构以及模块化的哲学,使得RISC-V架构的指令数⽬⾮常的简洁。基本的RISC-V指令数⽬仅有40多条,加上其他的模块化扩展指令总共⼏⼗条指令。
2 RISC-V指令集架构简介
本章将对RISC-V的指令集架构多⽅⾯的特性进⾏简要介绍。
2.1 模块化的指令⼦集
RISC-V的指令集使⽤模块化的⽅式进⾏组织,每⼀个模块使⽤⼀个英⽂字母来表⽰。RISC-V最基本也是唯⼀强制要求实现的指令集部分是由I字母表⽰的基本整数指令⼦集,使⽤该整数指令⼦集,便能够实现完整的软件编译器。其他的指令⼦集部分均为可选的模块,具有代表性的模块包括M/A/F/D/C,如表1所⽰。
表1 RISC-V的模块化指令集
为了提⾼代码密度,RISC-V架构也提供可选的“压缩”指令⼦集,由英⽂字母C表⽰。压缩指令的指令编码长度为16⽐特,⽽普通的⾮压缩指令的长度为32⽐特。以上这些模块的⼀个特定组合“IMAFD”,也被称为“通⽤”组合,由英⽂字母G表⽰。因此RV32G表⽰
RV32IMAFD,同理RV64G表⽰RV64IMAFD。
为了进⼀步减少⾯积,RISC-V架构还提供⼀种“嵌⼊式”架构,由英⽂字母E表⽰。该架构主要⽤于追求极低⾯积与功耗的深嵌⼊式场景。该架构仅需要⽀持16个通⽤整数寄存器,⽽⾮嵌⼊式的普通架构则需要⽀持32个通⽤整数寄存器。
通过以上的模块化指令集,能够选择不同的组合来满⾜不同的应⽤。譬如,追求⼩⾯积低功耗的嵌⼊式场景可以选择使⽤RV32EC架构;⽽⼤型的64位架构则可以选择RV64G。
除了上述的模块,还有若⼲的模块包括L、B、P、V和T等。这些扩展⽬前⼤多数还在不断完善和定义中,尚未最终确定,因此本⽂在此不做详细论述。
2.2 可配置的通⽤寄存器组
RISC-V架构⽀持32位或者64位的架构,32位架构由RV32表⽰,其每个通⽤寄存器的宽度为32⽐特;64位架构由RV64表⽰,其每个通⽤寄存器的宽度为64⽐特。
RISC-V架构的整数通⽤寄存器组,包含32个(I架构)或者16个(E架构)通⽤整数寄存器,其中整数寄存器0被预留为常数0,其他的31个(I架构)或者15个(E架构)为普通的通⽤整数寄存器。
如果使⽤了浮点模块(F或者D),则需要另外⼀个独⽴的浮点寄存器组,包含32个通⽤浮点寄存器。如果仅使⽤F模块的浮点指令⼦集,则每个通⽤浮点寄存器的宽度为32⽐特;如果使⽤了D模块的浮点
指令⼦集,则每个通⽤浮点寄存器的宽度为64⽐特。
2.3 规整的指令编码
在流⽔线中能够尽早尽快的读取通⽤寄存器组,往往是处理器流⽔线设计的期望之⼀,这样可以提⾼处理器性能和优化时序。这个看似简单的道理在很多现存的商⽤RISC架构中都难以实现,因为经过多年反复修改不断添加新指令后,其指令编码中的寄存器索引位置变得⾮常的凌乱,给译码器造成了负担。
得益于后发优势和总结了多年来处理器发展的教训,RISC-V的指令集编码⾮常的规整,指令所需的通⽤寄存器的索引(Index)都被放在固定的位置,如图2所⽰。因此指令译码器(Instruction Decoder)可以⾮常便捷的译码出寄存器索引然后读取通⽤寄存器组(Register File,Regfile)。
图2 RV32I规整的指令编码格式
2.4 简洁的存储器访问指令
与所有的RISC处理器架构⼀样,RISC-V架构使⽤专⽤的存储器读(Load)指令和存储器写(Store)指令访问存储器(Memory),其他的普通指令⽆法访问存储器,这种架构是RISC架构的常⽤的⼀个基本策略,这种策略使得处理器核的硬件设计变得简单。
存储器访问的基本单位是字节(Byte)。RISC-V的存储器读和存储器写指令⽀持⼀个字节(8位),半字(16位),单字(32位)为单位的存储器读写操作,如果是64位架构还可以⽀持⼀个双字(64位)为单位的存储器读写操作。
RISC-V架构的存储器访问指令还有如下显著特点:
为了提⾼存储器读写的性能,RISC-V架构推荐使⽤地址对齐的存储器读写操作,但是地址⾮对齐的存储器操作RISC-V架构也⽀持,处理器可以选择⽤硬件来⽀持,也可以选择⽤软件来⽀持。
由于现在的主流应⽤是⼩端格式(Little-Endian),RISC-V架构仅⽀持⼩端格式。有关⼩端格式和⼤端格式的定义和区别,本⽂在此不做过多介绍,若对此不甚了解的初学者可以⾃⾏查阅学习。
很多的RISC处理器都⽀持地址⾃增或者⾃减模式,这种⾃增或者⾃减的模式虽然能够提⾼处理器访问连续存储器地址区间的性能,但是也增加了设计处理器的难度。RISC-V架构的存储器读和存储器写指令不⽀持地址⾃增⾃减的模式。
RISC-V架构采⽤松散存储器模型(Relaxed Memory Model),松散存储器模型对于访问不同地址的存储器读写指令的执⾏顺序不作要求,除⾮使⽤明确的存储器屏障(Fence)指令加以屏蔽。
这些选择都清楚地反映了RISC-V架构⼒图简化基本指令集,从⽽简化硬件设计的哲学。RISC-V架构
如此定义⾮常合理,能够达到能屈能伸的效果。譬如:对于低功耗的简单CPU,可以使⽤⾮常简单的硬件电路即可完成设计;⽽对于追求⾼性能的超标量处理器则可以通过复杂设计的动态硬件调度能⼒来提⾼性能。
2.5 ⾼效的分⽀跳转指令
RISC-V架构有两条⽆条件跳转指令(Unconditional Jump),jal与jalr指令。跳转链接(Jump and Link)指令jal可⽤于进⾏⼦程序调⽤,同时将⼦程序返回地址存在链接寄存器(Link Register:由某⼀个通⽤整数寄存器担任)中。跳转链接寄存器(Jump and Link-Register)指令jalr指令能够⽤于⼦程序返回指令,通过将jal指令(跳转进⼊⼦程序)保存的链接寄存器⽤于jalr指令的基地址寄存器,则可以从⼦程序返回。
RISC-V架构有6条带条件跳转指令(Conditional Branch),这种带条件的跳转指令跟普通的运算指令⼀样直接使⽤2个整数操作数,然后对其进⾏⽐较,如果⽐较的条件满⾜时,则进⾏跳转。因此,此类指令将⽐较与跳转两个操作放到了⼀条指令⾥完成。
作为⽐较,很多的其他RISC架构的处理器需要使⽤两条独⽴的指令。第⼀条指令先使⽤⽐较指令,⽐较的结果被保存到状态寄存器之中;第⼆条指令使⽤跳转指令,判断前⼀条指令保存在状态寄存器当中的⽐较结果为真时则进⾏跳转。相⽐⽽⾔RISC-V的这种带条件跳转指令不仅减少了指令的条数,同
时硬件设计上更加简单。
对于没有配备硬件分⽀预测器的低端CPU,为了保证其性能,RISC-V的架构明确要求其采⽤默认的静态分⽀预测机制,即:如果是向后跳转的条件跳转指令,则预测为“跳”;如果是向前跳转的条件跳转指令,则预测为“不跳”,并且RISC-V架构要求编译器也按照这种默认的静态分⽀预测机制来编译⽣成汇编代码,从⽽让低端的CPU也能得到不错的性能。
为了使硬件设计尽量简单,RISC-V架构特地定义了所有的带条件跳转指令跳转⽬标的偏移量(相对于当前指令的地址)都是有符号数,并且其符号位被编码在固定的位置。因此,这种静态预测机制在硬件上⾮常容易实现,硬件译码器可以轻松的到这个固定的位置,并判断其是0还是1来判断其是正数还是负数,如果是负数则表⽰跳转的⽬标地址为当前地址减去偏移量,也就是向后跳转,则预测为“跳”。当然对于配备有硬件分⽀预测器的⾼端CPU,则可以采⽤⾼级的动态分⽀预测机制来保证性能。
2.6 简洁的⼦程序调⽤
为了理解此节,需先对⼀般RISC架构中程序调⽤⼦函数的过程予以介绍,其过程如下:
进⼊⼦函数之后需要⽤存储器写(Store)指令来将当前的上下⽂(通⽤寄存器等的值)保存到系统存储器的堆栈区内,这个过程通常称为“保存现场”。
在退出⼦程序之时,需要⽤存储器读(Load)指令来将之前保存的上下⽂(通⽤寄存器等的值)从系统存储器的堆栈区读出来,这个过程通常称为“恢复现场”。
“保存现场”和“恢复现场”的过程通常由编译器编译⽣成的指令来完成,使⽤⾼层语⾔(譬如C或者C++)开发的开发者对此可以不⽤太关⼼。⾼层语⾔的程序中直接写上⼀个⼦函数调⽤即可,但是这个底层发⽣的“保存现场”和“恢复现场”的过程却是实实在在地发⽣着(可以从编译出的汇编语⾔⾥⾯看到那些“保存现场”和“恢复现场”的汇编指令),并且还需要消耗若⼲的CPU执⾏时间。
为了加速这个“保存现场”和“恢复现场”的过程,有的RISC架构发明了⼀次写多个寄存器到存储器中(Store Multiple),或者⼀次从存储器中读多个寄存器出来(Load Multiple)的指令,此类指令的好处是⼀条指令就可以完成很多事情,从⽽减少汇编指令的代码量,节省代码的空间⼤⼩。但是此种“Load Multiple”和“Store Multiple”的弊端是会让CPU的硬件设计变得复杂,增加硬件的开销,也可能损伤时序使得CPU的主频⽆法提⾼,笔者在曾经设计此类处理器时便深受其苦。
RISC-V架构则放弃使⽤这种“Load Multiple”和“Store Multiple”指令。并解释,如果有的场合⽐较介意这种“保存现场”和“恢复现场”的指令条数,那么可以使⽤公⽤的程序库(专门⽤于保存和恢复现场)来进⾏,这样就可以省掉在每个⼦函数调⽤的过程中都放置数⽬不等的“保存现场”和“恢复现场”的指令。
此选择再次印证了RISC-V追求硬件简单的哲学,因为放弃“Load Multiple”和“Store Multiple”指令可以
⼤幅简化CPU的硬件设计,对于低功耗⼩⾯积的CPU可以选择⾮常简单的电路进⾏实现,⽽⾼性能超标量处理器由于硬件动态调度能⼒很强,可以有强⼤的分⽀预测电路保证CPU能够快速的跳转执⾏,从⽽可以选择使⽤公⽤的程序库(专门⽤于保存和恢复现场)的⽅式减少代码量,但是同时达到⾼性能。
2.7 ⽆条件码执⾏
很多早期的RISC架构发明了带条件码的指令,譬如在指令编码的头⼏位表⽰的是条件码(Conditional Code),只有该条件码对应的条件为真时,该指令才被真正执⾏。
这种将条件码编码到指令中的形式可以使得编译器将短⼩的循环编译成带条件码的指令,⽽不⽤编译成分⽀跳转指令。这样便减少了分⽀跳转的出现,⼀⽅⾯减少了指令的数⽬;另⼀⽅⾯也避免了分⽀跳转带来的性能损失。然⽽,这种“条件码”指令的弊端同样会使得CPU的硬件设计变得复杂,增加硬件的开销,也可能损伤时序使得CPU的主频⽆法提⾼,笔者在曾经设计此类处理器时便深受其苦。
RISC-V架构则放弃使⽤这种带“条件码”指令的⽅式,对于任何的条件判断都使⽤普通的带条件分⽀跳转指令。此选择再次印证了RISC-V 追求硬件简单的哲学,因为放弃带“条件码”指令的⽅式可以⼤幅简化CPU的硬件设计,对于低功耗⼩⾯积的CPU可以选择⾮常简单的电路进⾏实现,⽽⾼性能超标量处理器由于硬件动态调度能⼒很强,可以有强⼤的分⽀预测电路保证CPU能够快速的跳转执⾏达到⾼性
能。
2.8 ⽆分⽀延迟槽
很多早期的RISC架构均使⽤了“分⽀延迟槽(Delay Slot)”,最具有代表性的便是MIPS架构,在很多经典的计算机体系结构教材中,均使⽤MIPS对分⽀延迟槽进⾏过介绍。分⽀延迟槽就是指在每⼀条分⽀指令后⾯紧跟的⼀条或者若⼲条指令不受分⽀跳转的影响,不管分⽀是否跳转,这后⾯的⼏条指令都⼀定会被执⾏。
早期的RISC架构很多采⽤了分⽀延迟槽诞⽣的原因主要是因为当时的处理器流⽔线⽐较简单,没有使⽤⾼级的硬件动态分⽀预测器,所以使⽤分⽀延迟槽能够取得可观的性能效果。然⽽,这种分⽀延迟槽使得CPU的硬件设计变得极为的别扭,CPU设计⼈员对此往往苦不堪⾔。
RISC-V架构则放弃了分⽀延迟槽,再次印证了RISC-V⼒图简化硬件的哲学,因为现代的⾼性能处理器的分⽀预测算法精度已经⾮常⾼,可以有强⼤的分⽀预测电路保证CPU能够准确的预测跳转执⾏达到⾼性能。⽽对于低功耗⼩⾯积的CPU,由于⽆需⽀持分⽀延迟槽,硬件得到极⼤简化,也能进⼀步减少功耗和提⾼时序。
x86架构和arm架构区别2.9 ⽆零开销硬件循环
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论