棋子 · 2021年07月07日

处理器设计 -- 顺序与乱序

作者简介:刁同学身为后浪,毅然投身祖国的IC大业,虽毕业未久,但是在处理器设计领域颇有心得,尤其精通riscv。

现在的处理器执行方式分为两种,顺序执行和乱序执行。根据不同的产品定位,不同的应用场景,所使用的微架构也是不相同的。

1. 顺序执行解析

顺序执行的处理器一般用于低功耗类型的处理器,比如ARM公司的cortex-M系列都是顺序执行的,如果要稍微性能和功耗兼顾的产品,也是使用的顺序执行的方式。

何为顺序执行,顾名思义,亦就是按照PC的取指顺序,一条一条的执行。遇到数据相关性就停下等待,当然,可以进行数据旁路的就进行数据旁路加快一下效率。

相比较之下,顺序执行所需要的硬件开销是比较小的。这是因为顺序执行不需要考虑WAW冲突和WAR冲突。即寄存器重命名部件可考虑不使用,仅仅只需要考虑RAW冲突,而RAW冲突是无可避免的,只能通过数据旁路去优化,减少冲突所带来的气泡。

因为相关性的问题,顺序执行一般最多使用双发射结构,再多就浪费了,硬件开销所收获的性能就不成正比。同时,顺序执行不需要额外的监管部件,大家都排好队去执行的,最后按照顺序提交即可。

在此解析一下cortex A53的流水线。ARM公司的cortex A53个人感觉是顺序流水线的巅峰之作了。

1.png

Cortex A53是一个顺序双发射8级流水处理器。上图看到最多是十级,那是因为支持NEON浮点运算单元,浮点运算单元时间会比较久。严格在算处理器流水线时,以标量运算为基准。A53在取指时花费三个时钟周期,因为在这一阶段,要做的事情还是比较多的,需要进行分支预测,而且由于是双发的结构,为了提高效率,每次取回一个package的指令是4条,所以取完指令后,会将指令存入Instruction Queue(指令队列)中,避免后面执行指令没那么快,导致指令丢失。

指令译码会有两个译码单元同时进行译码,提高译码效率。译码完成后送入发射队列中,在发射队列里,会判断指令功能,将指令分发到不同的执行单元中。例如,此时准备好的是一条浮点指令,便将浮点指令送入F0模块中进行运算。是一条标量指令,则把该指令送入ALU运算单元中进行运算。

Cortex A53的coremark性能测试跑分大概在3.1分左右,不算高,但是它将性能和面积之间平衡做得是非常好的。在RISC-V指令集阵营中,不少公司都有产品对标A53处理器,性能是能达到,但是面积或多或少都会比之差一点。当然,ARM公司经过这么多年的经营,其生态是目前的RISC-V无法媲美的,RISC-V在嵌入式领域与ARM竞争目前是不占优势的。

2. 乱序执行解析

前文有说过,顺序执行会受到RAW冲突,会造成一定的资源浪费,白白等待很长的时间。为了避免等待,那没有数据相关性的指令是否就能提前去执行呢?那肯定是可以的,所以这就叫乱序执行了。乱序执行的处理器,只是在执行阶段是乱的,在执行阶段以前都是顺序的,按顺序去取指令,按顺序去译码。到了分发段,准备好的指令,没有相关性的指令就可以先行发射,去执行。但是要注意的是,乱序只是执行的时候是乱序,但是在最后提交指令的时候还是按照顺序去提交。

就比如一个公司,有很多订单,一个员工需要上一个员工的给他交接任务才能做,那么在等待的时候,这个员工先做其他的任务,等上一个员工的任务交接给他,他又继续做该任务。最后公司提交这些订单是按照时间顺序提交给客户的,毕竟先来后到。

因此,乱序执行需要一个监管部件,我们称之为ROB(Re-order Buffer),重排序缓存。

2.png

所有的指令都会存在这个单元中,进行备份,并且相应的状态也一并保存。当指令执行完成时,ROB中该指令的状态会被更新,就会将该指令去提交,并移出ROB中。可以把ROB模块理解为一个FIFO,指令在分发段会按照顺序把指令送入保留站,以及ROB模块中,最后会根据指针去进行提交,后面的指令哪怕是执行完成,可以提交了,但是还未到它位置,就不会去提交它。

乱序所需要的硬件开销不止增加ROB模块一个而已,因为乱序执行打破了原有的规则,所以接踵而来的是会有WAW冲突和WAR冲突,这两类冲突需要额外花费相应的硬件去解决掉。比如解决掉WAR冲突就需要使用寄存器重命名的方式去解决。寄存器重命名操作的本质是扩大可用寄存器,在上一篇文章有讲,指令集定义的寄存器组是固定个数的,大家都会去使用,那么将这些寄存器的数量扩大后,可用的寄存器就多了。

比如在一个公司里,大家都需要把资料给到一位老总或者从这位老总手里拿资料,这位老总现在被一帮美女员工所占用,导致其他长得丑的员工没机会去给资料或者拿资料。当然,老板爱美女,更爱江山,为了提高效率,早日开上宝马,就请了一位秘书,说大家把资料给他也行,后面秘书把资料再转交给老板就行。

以上是寄存器重命名操作的举例。读后写(WAR)冲突,情况就是前一条指令需要读一个寄存器,后面一条指令需要对该寄存器写数据进去。如果后一条指令先执行完成,把数据写到该寄存器了,那么前一条指令就极有可能读到一个错误的数据。那先将后一条指令写到另外一个寄存器,也就是上述说的招的秘书,等指令都执行完,再找机会把该寄存器的数据写回到目标寄存器中。

而写后写(WAW)冲突有很多解决办法。前一条指令会对某一个寄存器写数据,后一条指令也是对该寄存器写操作,那么最新的数据就是后一条指令所写的数据,前一条指令的数据我们也就不怎么关心了。所以直接限制前一条指令不写该寄存器了,直接把数据丢掉,让后一条指令写入。但是这样的操作只适用于两条连续的指令,若中间存在RAW冲突,就不太适用了。所以使用寄存器重命名的方式也可以解决此类冲突。

既然使用乱序执行方式了,那么处理器的执行效率也就提上来了,若此时运算单元跟不上,造成结构冲突,那就是一件很丢脸的事情。所以乱序执行的硬件开销更多的都来源于此。浮点运算单元、ALU、访存单元都有多个。并且发射通道也有很多,例如中科院刚刚发布的香山处理器,就是一个6发射的乱序处理器。伯克利大学RISC-V开源处理器Boom最大支持5发射。

3.png

3. 总结

顺序执行方式和乱序执行方式所应用的场景不同,所使用的架构也会不同。对于嵌入式应用产品(手机除外),一般使用顺序执行的架构即可满足。若面向于一些需要高性能的场景,如手机、电脑、服务器等,一般会采用乱序执行。但是手机这类产品不会单纯的用乱序执行的大核,一般会大小核一起用,即乱序和顺序的处理器都会使用。

顺序执行方式的优势在于硬件开销小,功耗比较低,用于做控制,完成一些简单运算绰绰有余。乱序执行方式的优势在于运算快,对于跑稍微大一点的操作系统或者稍微大一点的运算优势更大一点。在产品上这两者之间如何结合,就需要经验丰富的架构师和产品经理去衡量了。

【后续更精彩】

祝大家周末愉快!

作者:刁炸天
来源:https://mp.weixin.qq.com/s/fWnIwiCqN3pV4992HZzIqw
微信公众号:
老秦.jpg

相关文章推荐

更多IC设计技术干货请关注IC设计技术专栏。
推荐阅读
关注数
20187
内容数
1307
主要交流IC以及SoC设计流程相关的技术和知识
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息