本篇继续看BOOM得运算功能模块并注释。

该模块主要在于以下3个文件中,本次按照相反的顺序来梳理代码。

$ ls -la execut* function*

-rw-r--r-- 1 chipcamp 197609 28023 Sep 10 19:02 functional_unit.scala
-rw-r--r-- 1 chipcamp 197609 28240 Sep 10 17:19 execute.scala
-rw-r--r-- 1 chipcamp 197609  5572 Sep 10 17:14 execution_units.scala

除此之外,本文还会下探到imul.scala、fpu.scala、fdiv.scala这三运算单元的实现中,简单扫一遍代码,这样BOOM-v1.0的代码扫描阅读覆盖就再进一步。但对于Mem和Branch的执行,则仍然不列入本文的扫描阅读和注释的范围。

$ ll imul.scala fpu.scala fdiv.scala
-rw-r--r-- 1 chipcamp 197609 8479 Sep 11 10:53 fdiv.scala
-rw-r--r-- 1 chipcamp 197609 9309 Sep 11 10:34 fpu.scala
-rw-r--r-- 1 chipcamp 197609 2074 Aug 30 19:15 imul.scala

图0、先对运算功能模块在整个CPU Core中的位置和功能有个印象。

----最右侧的ALU、iDiv、iMul、I2F、F2I、FDV等就是典型的“运算功能”,它属于执行阶段的一部分。执行阶段还包括LS指令的执行,这个需要单独说,不在本文的范围内。

----比较特殊的地方是,ALU到底是单独放一个,还是由ALU+iDiv+iMul进行组合,无论哪一种,它们都是从Physical INT Register里进行读取,涉及到RegFile和RegisterRead两个源文件(regfile.scala和registerread.scala)。

图1、看看execution_units.scala安排多少个执行单元!

----当ISSUE_WIDTH等于1的时候,整个exe_units只加了一个ALUMemExeUnit单元,而这个单元中is_branch_unit、has_mul、has_div都设置为TRUE,也就是说这个ALU包含了iMUL、iDiv以及BranchUnit。但是否由fpu及fdiv则要看usingFPU变量的值。而这个ALUMemExeUnit单元从命名上来说就包含了Memory访问的LS指令的处理!

----当ISSUE_WIDTH等于4的时候,不再采用ALUMemExeUnit,而是分开的ALUExeUnit和MemExeUnit!而且ALUExeUnit有进一步分为三个,这三个分别专用于Brnch_unit、iDiv_unit和ALU_iMul三种!

----当ISSUE_WIDTH等于2的时候,ALU_BRANCH_iMUL组合 + MEM_iDIV组合!看起来比较均衡。

----当ISSUE_WIDTH等于3的时候,ALU_BRANCH_iMUL组合+iDIV单独+MEM单独!也是比较均衡的划分。

----可见这些运算功能到底是分还是合,主要取决于issue的宽度。如果issue的宽度为1,那就没什么说的“全合在一起”。如果宽度大于1,就要考虑模块拆封的均衡性了。

图2:再来看看execute.scala中的模块定义之AluExeUnit:

----再次明确这是ALUExeUnit!但它不仅仅包含那Alu运算!

----160行:AluUnit用来进行整数运算,包括加减ADD/SUB、位运算AND/OR/XOR、移位运算。

----172行:Branch处理。

----187行:iMul运算。

----206行:FPU运算。

----233行:FDivSqrt运算!特别是235行,FDivSqrtUnit()所实例化的电路单元,下面会看到。

----261行:iDiv运算!

----283行:开始收尾、往io.resp.xxx赋值(写数据),这就到了类(也就是电路模块)的输出收尾阶段了。

----可以看到,这个ALUExeUnit是没有Memory相关的指令处理,也就是LS指令的处理的。这将再下面ALUMemExeUnit以及MemExeUnit的对比中看出差异。

图3、

----FDivSqrtExeUnit类(代表电路模块)的处理是原子化的,只处理一个FDivSqrt运算功能。

----315行:功能的实现是使用FDivSqrtUnit实现的,前面AluExeUnit也是调用这个FDivSqrtUnit。后面ALUMemExeUnit也是调用这个FDivSqrtUnit。而MemExeUnit则不会调用这个!马上来看。

图4、

----这个MemExeUnit所负责的事情,就是LS指令。

----其中哦343~347行,io.dmem中的dmem代表的似乎只包括DMem而不包括IMem,而362行和368行看起来是有Load的。因此整个函数的功能一时半会还看不清楚,先搁置起来,本文只明确一点,这个MemExeUnit不包含任何整数、浮点数运算!

图5、ALUMemExeUnit

----最后看ALUMemExeUnit,除了预期的各种ALU、FPU、FDivSqrt以外(这些都不再重复放再上图里占版面了),是包含了Mem指令处理的,具体参看上面586~627行。

图6、看一下fpu.scala所定义的FPU模块!

----FP浮点数指令,包括IntToFP、FPToInt、FPToFP。但都是基于Rocket项目的tile.xxx模块!

----176行:这些浮点数运算指令的缩写!IFPU,FPIU,FPMU,SFMA,DFMA!前3个不用说了,代码可以看出来。后两个查了以下大概是这样的,SFMA 单精度融合乘加指令,DFMA  双精度融合乘加指令 。也就是说,浮点数FPU主要是乘法和加法以及整数浮点数转换!不包括除法!除法统统放在FDivSqrtUnit里面(在fdiv.scala里定义)----见下!

图7、看一下fdiv.scala里定义的FDivSqrtUnit。

----95行:用到了hardfloat.RecFNtoRecFN。这是另一个开源项目hardfloat。这个开源项目和Rocket项目都是并列存在的。浮点数除法,还是有些复杂的。

----128行:再次使用hardfloat.DivSqrtRecF64。

----168行:再次使用hardfloat.RecFNToRecFN。

图8、还有一个文件imul.scala显然是做整数乘法的:

----这个文件貌似挺简单但又没看太懂,基本的印象先留一个,那就是整数乘法运算看起来是一个拼接的活。

至此,BOOM-v1.0的运算功能模块扫描式快读完一遍,总结一下:

ALU的实现电路是rocket.ALU,FPU的实现电路是tile.XXX,而FDivSqrtUnit的实现电路则是hardfloat.XXX。BOOM的代码所作的工作,就是把这些电路整合起来,将BOOM-v1.0中的解码后的信号对接到ALU/FPU/FDivSqrtU模块的信号上去!

这些运算功能模块的输入,对接BOOM的Execution_Units框架模块中的具体模块(ALU/FPU/FDivSqrtU)的IO端口,其中主要的INPUT端口是io.req.bits.uop.xxx,包括操作数(rs1、rs2)和操作符(加减乘除等),而主要的OUTPUT端口则是io.resp.bits.xxx。

<<<<<<<< END >>>>>>>>

Logo

更多推荐