蜂鸟E200的EXU单元
蜂鸟E200系列CPU是两级流水线架构,其译码、执行、交付、写回功能全部处于流水线的第二级
这些功能使用执行单元EXU完成,EXU功能如下
- 将IFU通过IR寄存器发送给EXU的指令进行译码与派遣(会在下面介绍)
- 通过译码得出的操作数寄存器索引读取寄存器组
- 维护指令的数据相关性
- 将指令派遣给不同的运算单元执行
- 交付指令
- 将指令的运算结果写回寄存器组
译码
经典五级流水线结构中,取指-译码-执行分为三个阶段进行,通过译码让CPU获取指令读取/写回的操作数寄存器索引、指令类型、指令操作信息等。目前高性能处理器普遍采用在每个运算单元前配置乱序发射队列的方式,将指令的相关性解除,从发射队列中发射出来时读取通用寄存器组,再送给运算单元进行计算
蜂鸟E203中的译码器
译码器模块保存在core目录下的e203_exu_decode.v文件
完全由组合逻辑编写
可以在某种程度上理解为一个超大型的case语句
1 | module e203_exu_decode( |
整数通用寄存器组
蜂鸟E200中定义了该模块用于实现RISC-V架构中的整数通用寄存器组
由于E200属于单发射、按顺序每次写回一条指令的微架构,所以该模块只需要支持最多两个读端口和一个写端口
模块相关代码保存在e203_exu_regfile.v文件中
可以通过配置config.v更改通用寄存器的位数
端口逻辑
写端口
通过将输入的结果寄存器索引和各自的寄存器号进行比较,产生写使能信号,被使能的通用寄存器即将写数据写入寄存器
读端口
每个读端口都是一个纯粹的并行多路选择器,使用读操作数的寄存器索引作为选择信号,使用专用寄存器读取寄存器索引信号,当且仅当执行读操作数的时候这个专用寄存器才会被调用,可以减少读端口的动态反转功耗
简而言之就是在读端口放了个门卫大爷,只有需要读取的时候大爷才会给寄存器索引数据开门(把寄存器索引存入该专用寄存器)
代码片段如下
1 | module e203_exu_regfile( |
CSR寄存器
RISC-V架构中定义了控制和状态寄存器CSR(Control and Status Register),用于配置或记录一些运行的状态。这些寄存器都位于核心内部,使用自己独立的地址编码空间,与存储器寻址无关,它们可以被看作“内核的外设控制寄存器”
使用专用的CSR读写指令来访问CSR寄存器
相关源代码位于e203_exu_csr.v文件下,严格按照RISC-V架构定义实现了各个CSR寄存器的具体功能
代码片段如下:
1 | module e203_exu_csr( |
执行
五级流水线架构中的执行需要译码之后执行,根据指令的具体操作类型将指令分配给不同的运算单元执行,常见的运算单元如下:
- 算术逻辑运算单元(ALU):负责普通逻辑运算、加减法运算、移位运算等
- 整数乘法单元:主要负责有符号数或无符号整数的乘法
- 整数除法单元:主要负责有符号数或无符号整数的除法
- 浮点运算单元(FPU):比较复杂,通常会分成多个不同的独立运算单元
对于其他具有特殊指令的处理器核,会相应增加特殊的运算单元(比如可以在处理器旁挂载DSP等硬件加速电路)
指令发射顺序
发射(Issue)或者说派遣(Dispatch)并不是经典五级流水线中的常见概念,但多用于各类RISC架构CPU,RISC-V中也使用了这一定义
发射:指令经过译码之后被派发到不同的运算单元执行的过程
发射和派遣可以混用,蜂鸟E200处理器流水线中使用派遣(Dispatch)作为定义
根据每个周期一次能发射的指令个数,可分为单发射和多发射处理器。
特别地,在一些高端的超标量处理器核中,流水线级数很多,使得派遣和发射有不同的含义:派遣表示指令经过译码之后被派发到不同的运算单元的等待队列中这一过程;发射则表示指令从运算单元的等待队列中发射到运算单元开始执行的过程。
根据发射、执行、写回顺序不同,往往分成以下流派:
顺序发射、顺序执行、顺序写回
性能比较低,硬件实现最简单,面积最小
往往在最简单流水线的处理器核中使用
顺序发射、乱序执行、顺序写回
在指令的执行阶段由不同的运算单元同时执行不同的指令,这样规避了不同运算处理时间不同的问题,提高处理性能;最终写回时还是要按照顺序写回,所以很多时候ALU要等待其他指令先写回而将其运算单元本身的流水线停滞
具有比较好的性能,面积稍大一些
顺序发射、乱序执行、乱序写回
在上述乱序执行的基础上让运算单元乱序写回,又分成了几个不同的方法
重排序缓存法
使用Re-Orde Buffer(ROB)重排序缓存将ALU执行的结果暂存,最后由ROB顺序写回寄存器组
存在面积过大、动态功耗较大的问题
但是性能很好,实现方案很典型、成熟
物理寄存器组法
使用一个统一的物理寄存器组动态管理逻辑寄存器组的映射关系,ALU执行完毕后就将结果乱序写回物理寄存器组,物理寄存器组和逻辑寄存器组之间的映射关系可以改变
控制复杂,功耗有所优化
直接乱序写回法
让没有数据相关性的执行结果直接写回寄存器组,有数据相关性的执行结果顺序写回
只对部分程序有优化,需要增设电路
其他方法
顺序派遣、乱序发射、乱序执行、乱序写回
往往在高性能的超标量处理器中使用这种架构。
基本上可以看作上面所有高性能操作的融合体
分支解析
取指阶段的分支预测功能对于带条件的分支指令,由于器条件解析需要进行操作数运算,所以需要在执行阶段进行运算并判断该分支指令是否真的需要跳转,并按照之前规定的分支预测算法进行对比执行,如果预测错误很可能需要进行流水线冲刷、造成性能损失
一般为了减少性能损失,会在比较靠前的流水线位置进行分支解析
蜂鸟E200系列的指令发射派遣
蜂鸟E200系列CPU的发射和派遣实际上指的是同一个行为:即指令经过译码之后被派发到不同的运算单元执行的总过程
该部分使用Dispatch模块和ALU模块共同完成
Dispatch模块负责向ALU模块转发派遣任务
ALU部分负责交付模块和前级的接口
蜂鸟E200系列的派遣特点如下:
- 所有指令必须被派遣给ALU,通过ALU与交付模块接口进行交付;如果是长指令,也需要通过ALU进一步发送至相应的长指令运算单元
- 实际的派遣功能发生在ALU内部。因为译码部分已经根据指令的运算单元进行了初步分组并译码出了其相应的指示信号,可以按照其指示信号将指令派遣给相应的运算单元
- 在派遣模块中处理流水线冲突问题,包括资源冲突和数据相关性冲突,并在某些特殊情况下将流水线的派遣点阻塞
流水线冲突、长指令和OITF处理
流水线冲突包括资源冲突和数据冲突两类,这两种冲突都会导致流水线阻塞。蜂鸟E203采用了两种方法分别处理资源冲突和数据冲突
数据冲突
数据冲突顾名思义,就是由于数据相关性引起的冲突
蜂鸟E203采用巧妙的方法处理数据冲突:将所有指令分成两类,将数据相关性分为三类,通过长指令拼接和流水线冲刷的方式进行处理
详细内容在下面的长指令和OITF处理部分给出
资源冲突
数据冲突的概念在之前已经给出,这里介绍一下资源冲突
资源冲突通常发生在指令派遣给不同的执行单元进行执行的过程中,当一个指令被执行时耗费的时钟周期较长,此后又有其他指令被派发给同一个硬件模块进行处理的情况下便会出现资源冲突的情况——后续的指令需要等待前一个指令完成操作后将硬件模块释放出来后才能得到执行。
蜂鸟E203的接口实现采用了严谨的valid-ready握手接口,一旦某个模块出现了资源冲突,它便会输出ready=0的信号,即使另一侧valid=1,也无法完成握手,所以前一级模块无法进行分配指令,将会进入等待状态,直到ready=1
长指令和OITF处理
蜂鸟E203将所有需要执行的指令分为两类:
单周期执行指令
蜂鸟E203的交付和写回功能均处于流水线的第二级,单周期执行指令在这一级就完成了交付和写回
多周期执行指令
这种指令通常需要多个时钟周期才能完成执行并写回,因此也称为“后交付长流水线指令”,简称为长指令
长指令的执行过程比较特殊
为了在很多时钟周期后交付长指令,需要先检测出数据相关性,蜂鸟E203采用了一个称为OITF(Outstanding Instructions Track FIFO长指令追踪队列)的模块检测与长指令相关的RAW和WAW相关性
之所以不检测WAR相关性,是因为E203是按序派遣、按序写回的微架构,在派遣时就已经从寄存器组中读取了源操作数,所以写回Regfile操作不会发生在读取Regfile源操作数之前。
言归正传,OITF本质上是一个普通的FIFO(废话),它的源码在rtl/e203/core/e203_exu_oitf.v中可以查看
在派遣点,每派遣一个长指令,便会在OITF中分配一个表项,在这个表项中会存储该长指令的源操作数寄存器索引和结果寄存器索引
在写回点,每次按序写回一个长指令后,就会将此指令在OITF中的表象去除——他就从FIFO中退出了
综上所述,OITF本质上保存了已经被派遣但是尚未写回的长指令信息
为简单起见,这里就不附录相关代码了,感兴趣的读者可以自行翻阅源码
资源冲突的解决思路
蜂鸟E203采用了阻塞流水线的解决思路,并没有将长指令的结果直接快速旁路给后续的待派遣指令来解决数据冲突,也没有增加更多硬件模块处理资源冲突,这是因为蜂鸟E203的设计思路秉承“小面积”,放弃了更高的性能,转而实现较高的性能-面积比。如果设计高性能的CPU,则显然不能简单地使用这种思路
ALU模块
蜂鸟E203的ALU单元位于EXU之下,主要包括5个子模块,它们共性同一份实际的运算数据通路,因此主要数据通路的面积开销只有一份
- 普通ALU:主要负责逻辑运算、加减法、移位运算等通用的ALU指令
- 访存地址生成:主要负责Load、Store和“A”扩展指令的地址生成、“A”扩展指令的微操作拆分和执行
- 分支预测解析:主要负责Branch和Jump指令的结果解析和执行
- CSR读写控制:主要负责CSR读写指令的执行
- 多周期乘除法器:主要负责乘法和除法指令的执行
普通ALU
位于rtl/e203/core/e203_exu_alu_rglr.v
该模块完全由组合逻辑电路构成(也就是说这玩意在FPGA里可以只占用一点点LUT),它本身并没有运算数据通路,其主要逻辑根据普通ALU的指令类型发起对共享运算数据通路的操作请求,并从共享的运算数据通路中取回运算结果
访存地址生成
该模块简称AGU(Adress Generation Unit),位于rtl/e203/core/e203_exu_alu_lsuagu.v
相关内容会在存储器架构部分详细介绍
分支预测解析
位于rtl/e203/core/e203_exu_alu_bjp.v
BJP(Branch and Jump resolve)模块是分支跳转指令进行交付的主要依据,可以查看交付部分进行了解
CSR读写控制
该模块主要负责CSR读写指令的执行,位于rtl/e203/core/e203_exu_alu_csrctrl.v
这个模块也是完全由组合逻辑组成,其根据CSR读写指令的类型产生读写CSR寄存器模块的控制信号
代码片段如下:
1 |
|
多周期乘除法器
蜂鸟E200系列使用了两种乘除法解决方案
对于蜂鸟E203,它配置了低性能小面积的多周期乘除法器,而对于其他性能较高的设备,则使用了高性能的单周期乘法器和独立的除法器
常用的多周期乘除法器和除法器实现,一般采用下面的理论实现:
- 有符号整数乘法:使用常用的Booth编码产生部分积,然后使用迭代的方法,每个周期使用加法器对部分积进行累加,经过多个周期的迭代后得到最终的乘积,从而实现多周期乘法器
- 有符号整数除法:使用常用的加减交替法,然后使用迭代的方法,每个周期使用加法器产生部分余数,经过多个周期的迭代后得到最终商和玉树,从而实现多周期除法器
两个模块的理论内容可参考数电教材或相关书籍
因为两个模块都以加法器为核心并使用一组寄存器保存部分积或部分余数,所以在蜂鸟E203中使用了资源复用——将多周期乘除法器合并作为ALU的一个子单元,二者共享数据通路中的加法器,经过多个周期完成乘法或者除法操作
多周期乘除法器MDV模块位于rtl/e203/core/e203_exu_alu_muldiv.v
同时蜂鸟E203对乘除法进行了以下优化:
- 乘法操作中,为了减少所需周期数,采用了基4(Radix-4)的Booth编码,并对无符号乘法进行一位符号扩展后统一当作有符号数进行运算,所以需要17个迭代周期
- 除法操作中,使用了普通的加减交替法,同样对于无符号乘法统一进行一位符号扩展后当作有符号数进行运算,需要33个迭代周期。此外,由于加减交替法所得结果存在1比特精度的问题,还需要额外的1个时钟周期判断是否需要进行商和余数的矫正,还有额外2个周期的商和余数矫正,最终才能得到准确的除法结果
- MDV模块只进行运算控制,没有自己的加法器,加法器与其他ALU子单元复用共享的运算数据通路
- MDV模块也没有自己的寄存器,寄存器与AGU单元复用
综上所述,MDV实际上只是一个状态机,其乘法实现需要17个迭代周期,除法实现需要最多36个周期,采用了典型的“速度换面积”思想
运算数据通路
事实上ALU真正用于计算的模块是数据通路,位于rtl/e203/core/e203_exu_alu_dpath.v
它被动接受其他ALU子单元的请求来进行具体运算,然后将计算结果返回非其他子单元运算数据通路
可以说ALU的其他子单元只是一套针对不同指令选择不同逻辑的状态机(控制系统),而数据主要经过的运算数据通路才是ALU的运算核心,整个ALU是类似“众星捧月”的结构——占据面积最大的运算数据通路在中间,数据流经过时会被周边的状态机挑选,或单次通过(普通ALU)或反复通过并输出不同结果到寄存器(多周期乘除法器),这就使得ALU面积大大缩小
高性能乘除法运算
除了小面积的多周期乘除法器外,其他型号的蜂鸟E200还配备了高性能的单周期乘法器和独立的除法器
高性能乘法器会被部署在流水线第二级,除法器则仍然使用多周期除法器,但不再与ALU复用共享的运算数据通路,而是作为长指令拥有单独的除法器单元,同样部署在流水线第二级
浮点单元
蜂鸟E200系列支持RISC-V的“F”和“D”扩展子集,可以处理单精度和双精度浮点指令
浮点指令由FPU支持,如果配置了FPU,则FPU作为长指令拥有独立的运算单元,并且FPU还具有独立的通用浮点寄存器组。包含F和D扩展子集的模块要求包含32个通用浮点寄存器,其中如果仅包含F的话,浮点指令子集通用浮点寄存器的宽度为32位,仅包含D的浮点指令子集通用浮点寄存器的宽度为64位
蜂鸟E200系列的FPU支持以下功能
- 独立的时钟门控
- 独立电源域
- 单双精度浮点指令复用数据通路
但是开源的蜂鸟E203并没有配备FPU(悲)
总结
这部分简要介绍了蜂鸟E203的执行单元中译码和执行两个环节
写回、交付还有其他的一些单元因为涉及到篇幅会在之后的写回、交付、存储器相关博文中介绍
EXU部分是蜂鸟E203的核心环节,代码量也很多,所以需要反复理解