ChipCamp探索系列 -- 7J. 开源CPU之BOOM的运算功能模块注释2
本文对BOOM-v1.0处理器的运算功能模块进行了代码分析,重点梳理了ALU、FPU和FDivSqrt等运算单元的实现架构。主要内容包括:1)运算功能模块在处理器中的位置及作用;2)execution_units.scala中根据不同ISSUE_WIDTH配置运算单元的方式;3)各运算单元的具体实现,包括ALUExeUnit、MemExeUnit、ALUMemExeUnit等模块的功能划分;4)F
本篇继续看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 >>>>>>>>
更多推荐
所有评论(0)