前言:本文均为作者原创,内容均来自本人的毕业设计。未经授权严禁转载、使用。里面的插图和表格均为作者本人制作,如需转载请联系我并标注引用参考。分享仅供大家学习和交流。
本设计的硬件结构选取芯来公司开源的Hummingbirdv2 E203(简称E203)进行研究,E203内核是一个基于RISC-V指令集架构所设计的开源RISC-V内核,可以配置为 RV32IMAC架构[14],指令存储在ITCM,数据存储在DTCM。E203内核采用可变动的2级流水线结构,可配置对RISC-V自定义指令扩展[6],用于协处理器的协同工作。
E203内核的两级流水线结构如图2-9所示,第一级取指(由取值令单元完成),第二级为均处于同一个时钟周期的几个核心模块组成:译码(由执行单元完成)、执行(由执行单元完成)和内存访问(由内存访问单元完成)。需要的时候会加上写回(由写回寄存器WB Regfile完成)阶段,写回的结果仍然需要通过写回寄存器WB Regfile回到通用寄存器组,故称E203内核是2级变长流水线。


IFU(Instruction Fetch Unit,指令提取单元)进行取指令操作,其工作流程如图2-10所示。

IFU需要一个重要的指令生成的逻辑,PC生成模块用于产生下一个待取指令(PC值),根据Simple-BPU(Branch Processing Unit,分支处理单元)不同的情形处理。处理后的指令会进入地址判断和ICB总线。由于指令绝大多数都存访在指令紧耦合指令内存存储器ITCM,仅有几条指令存放在外部存储器。取回相应地址后,从ift2icb模块传到ifetch,存到指令寄存器(Instruction Register,IR),并进入简单译码模块对指令执行初等程度的解码,取值令阶段的简单译码主要用以判断当前指令是否属于分支和跳转指令,这种预测的形式使得处理器在取指令阶段都能生成下一条待取指令的程序计数器的pc寄存器存PC值,使取指阶段提前知道下一步要取的指令。

在逻辑综合后,可以从图2-11中看到,IFU逻辑综合结构主要有两个逻辑模块:右上角是ifetch(Instruction Fetch,提取指令生成)模块,左下角是ift2icb(Fetch to ICB,取指令)模块。ifetch向ift2icb发送ifu_req_pc信号,由于ifetch产生下一条指令的PC地址,然后产生的PC地址被送到ift2icb。
如下是ifetch模块给ift2icb模块发送PC值代码片段:
assign ifu2itcm_icb_cmd_addr = ifu_icb_cmd_addr[`E203_ITCM_ADDR_WIDTH-1:0];
assign ifu2biu_icb_cmd_addr = ifu2biu_icb_cmd_addr_pre; wire [`E203_ADDR_SIZE-1:0] ifu2biu_icb_cmd_addr_pre = ifu_icb_cmd_addr[`E203_ADDR_SIZE-1:0];
assign ifu_icb_cmd_addr =
({`E203_PC_SIZE{icb_addr_sel_1stnxtalgn | icb_addr_sel_2ndnxtalgn}} & icb_algn_nxt_lane_addr)| ({`E203_PC_SIZE{icb_addr_sel_cur}} & ifu_req_pc);
ift2icb有两个输出地址,分别是ifu2itcm_icb_cmd_addr发送到ITCM、ifu2biu_icb_cmd_addr通过BIU发送到外部存储器。这是自定义总线协议ICB中的地址信号,代表读或写的地址。ift2icb再把从BIU或ITCM的信号ifu_rsp_instr返回到ifetch模块输出,位宽32的信号ifu_o_ir,代表指令寄存器中的值,是当前从itcm或biu中读取的32位指令数据,传输到EXU的译码单元中进行解码操作。
本设计是简单的顺序单发射、顺序执行、顺序写回类型的处理器。第二阶段是EXU单元,主要包括译码部分、派遣部分、执行部分、交付部分和写回控制部分,EXU工作流程如图2-12所示。
【ps,这个图本作者整合了现有论文里的插图,根据本人对源代码的理解画出的工作流程图,很好地解释了EXU的运行流程】

译码部分实现指令的译码,派遣部分实现指令的按顺序派遣到执行部分。译码将信息从指令字中翻译出来原始的信息编码。译码部分通过读取指令的相关位存储的寄存器信息,使得处理器获取指定操作数的寄存器的索引(或者进入整数通用寄存器组)、指令类型、指令操作信息等。不同的指令根据指令类型、指令操作信息意味着指令派遣单元需要派遣给不一样的执行单元执行。
代码判断指令是32位指令还是16位指令的代码。操作码的低2位11,且3到5位不为111,则为rv32指令,否则为16位指令。因为本设计支持RV32I(32位指令低2位的编码是“11”)指令子集不支持rv64(64位指令低6位的编码全1),16位指令有可能为00、01、10只需判断第1位,所以用1位信号就可以判断了。
//32位指令判断:3到5位不为111且低2位是11
wire rv32 = (~(i_instr[4:2] == 3'b111)) & opcode_1_0_11;
判断之后,从图2-13中可以看出,RV32I指令子集中的指令长度均为32位,所操作的寄存器索引在每种指令类型都在指定区域内。立即数imm只有12位和20位这两种,funct3和funct7和opcode共同建立完整的操作码用于决定指令的具体功能操作,rs1和rs2作为操作数寄存器索引根据指令格式,可看出在每一条指令中的rs1、rs2 和 rd 字段都保持在一样的位置。指令的低7位是opcode为RISC-V指令的操作码用于判断指令类型,便于加快译码的速度,简化了硬件设计。

除了单周期指令可能还会存在一些长周期指令。单周期指令可以在一个时钟周期内完成,而长周期指令不能在一个时钟周期内处理完,需要使用OITF模块记录以便写回在下一个时钟周期继续执行。
在提取指令的阶段添加了一种可以提前预测中断、异常、分支和跳转指令的操作,可以增强处理器的译码、执行分支跳转指令的局部性能。但是在交付给执行单元的过程中,交付单元实现了完全判断指令是否是产生跳转的中断、异常、分支和跳转指令,如果不是则需要破坏。
分支预测分析单元的功能提前假设了该指令的执行结果,并取取应当执行的指令,之后通过比较器计算实际上该指令该不该跳转。换言之,整个过程分为并行的提取下一条指令和比较器计算该指令的实际是否跳转,预测失败则需要向取指阶段发送流水线刷新请求,进行流水线刷新[17]。
写回单元(Write Back,简称WB)有两个作用,其一,将写回的结果传输回到通用寄存器组,其二,将未完成的长指令移动到下一个时钟周期以便于执行。因为将“交付”和“写回”分为了两个模块进行判断顺序执行,所以一旦存在着正在执行多周期长指令的操作,也会通过OITF记录未完成长指令,然后按照顺序将未完成的长指令发送到协会模块的写回仲裁模块,仍然不会阻塞流水线,可以实现协同合作完成所有长指令写回。
内存访问由LSU单元实现。E203内核的指令和数据分别到不同的ITCM和DTCM进行存储,其中ITCM位宽为64位,DTCM位宽为32位。
这里以到ITCM取值令的过程来看出内存访问的工作流程,如图2-14所示。指令紧耦合指令内存存储器ITCM有3个访问输入接口,分来自取指令单元、内存访问单元和外部。三个访问输入接口如果输入到ITCM内部后,需要先进行进制的转换全部转换为64位,之后再经过ITCM内部的多路选择器的仲裁选择,根据通过访问输入接口输入的地址位置信息来读取指定位置的数据。多路选择器根据访问的优先级进行仲裁选择。IFU大于LSU大于外部。所取数据或指令需要进行尺寸对其检查,如果超出了大小限制,则可以判定在这一周期传输不完全,需要记录下来通过响应通道经过WB模块,以便于下一个时钟周期完成所有访存操作。
