指令所包含的信息编码在有限长度的指令字中,信息如下:
顺便注意:并非所有的处理器流水线都会在译码阶段读取操作数。在目前众多高性能处理器中,普遍采用在每个运算单元前配置乱序发射队列的方式,待指令的相关性解除之后并从发射队列中发射出来时读取通用寄存器组,然后送给运算单元开始计算
常见的运算单元有以下几种:
除了根据指令的具体类型运算之外,指令执行阶段另外一个最重要的只能就是维护并解决流水线的冲突,包括资源冲突和数据冲突(WAR,WAW等)
将指令发射给运算单元,由运算单元执行,然后写回的相对顺序,是执行阶段需要解决的重要问题。
以上两个定义很容易被混淆,在简单的处理器中两者属于同一概念,都是指令经过译码之后被派发到不同的运算单元并执行的过程。
根据每个时钟周期一次能够发射的指令数,处理器可以分为单发射处理器和多发射处理器。根据各种顺序,处理器可以分为很多种流派:
主要是对于带条件的分支指令
在执行阶段需要使用ALU对指令进行条件判断(如大小)。计算结果与之前的预测结果进行比较如果结果不一致则代表之前的预测是错误的,需要进行流水线冲刷。需要放在流水线比较前端的位置。
译码模块完全由组合逻辑组成。其主要逻辑即根据RISC-V架构的指令编码规则进行译码,产生不同的指令类型信息、操作数寄存器索引等。相关源代码片段如下所示。
RISC-V指令标准参考
module e203_exu_decode(//// The IR stage to Decoderinput [`E203_INSTR_SIZE-1:0] i_instr, //指令input [`E203_PC_SIZE-1:0] i_pc, //该指令的pc值input i_prdt_taken, //预测为需要跳转input i_misalgn, // The fetch misalign //产生取址非对齐异常input i_buserr, // The fetch bus error //取址存储其访问错误标志input i_muldiv_b2b, // The back2back case for mul/div //暂时不知道input dbg_mode, //来自外部//// The Decoded Info-Busoutput dec_rs1x0, //该指令原操作数1的寄存器索引为x0output dec_rs2x0, //该指令原操作数2的寄存器索引为x0output dec_rs1en, //需要读取原操作数1output dec_rs2en, //需要读取原操作数2output dec_rdwen, //该指令需要写结果操作数output [`E203_RFIDX_WIDTH-1:0] dec_rs1idx, //该指令原操作数1的寄存器索引output [`E203_RFIDX_WIDTH-1:0] dec_rs2idx, //该指令原操作数2的寄存器索引output [`E203_RFIDX_WIDTH-1:0] dec_rdidx, //该指令结果寄存器索引output [`E203_DECINFO_WIDTH-1:0] dec_info, //该指令的其他信息output [`E203_XLEN-1:0] dec_imm, //该指令使用的立即数值output [`E203_PC_SIZE-1:0] dec_pc, //该指令的pc值output dec_misalgn, //产生取址非对齐异常output dec_buserr, //取址存储其访问错误标志output dec_ilegl, //该指令是一个非法指令`ifdef E203_HAS_NICE//{////nice decodeinput nice_xs_off, //接1‘b0output dec_nice, //接1‘b0output nice_cmt_off_ilgl_o, //接1‘b0/`endif//}output dec_mulhsu, //指令是mulshu指令output dec_mul ,//指令是乘法指令output dec_div , //指令是除法指令output dec_rem , //指令是取余指令output dec_divu , //指令是无符号除法指令output dec_remu , //指令是无符号取余指令output dec_rv32, //指令是32位指令还是16位指令output dec_bjp,//指令是普通指令还是分支指令(跳转指令)output dec_jal, //指令是无条件直接跳转指令output dec_jalr, //指令是无条件间接跳转指令output dec_bxx,//指令是带条件直接跳转指令output [`E203_RFIDX_WIDTH-1:0] dec_jalr_rs1idx,//无条件间接跳转指令rs1的索引 5bitoutput [`E203_XLEN-1:0] dec_bjp_imm //分支指令中的立即数 32bit);
对于32位指令的译码比较直接,因为RISC-V的32位指令较规整
wire [32-1:0] rv32_instr = i_instr;wire [16-1:0] rv16_instr = i_instr[15:0];wire [6:0] opcode = rv32_instr[6:0];wire opcode_1_0_00 = (opcode[1:0] == 2'b00);wire opcode_1_0_01 = (opcode[1:0] == 2'b01);wire opcode_1_0_10 = (opcode[1:0] == 2'b10);wire opcode_1_0_11 = (opcode[1:0] == 2'b11);wire rv32 = (~(i_instr[4:2] == 3'b111)) & opcode_1_0_11; //该指令是一个32位指令 指令的234位不全是1且指令的最低两位全是1wire [4:0] rv32_rd = rv32_instr[11:7]; //把32位指令码分段表示不同的含义wire [2:0] rv32_func3 = rv32_instr[14:12];wire [4:0] rv32_rs1 = rv32_instr[19:15];wire [4:0] rv32_rs2 = rv32_instr[24:20];wire [6:0] rv32_func7 = rv32_instr[31:25];
指令译码:
wire rv32_load = opcode_6_5_00 & opcode_4_2_000 & opcode_1_0_11; //opcode==0000011 属于I类load指令wire rv32_store = opcode_6_5_01 & opcode_4_2_000 & opcode_1_0_11; //opcode==0100011 属于S类存储指令wire rv32_madd = opcode_6_5_10 & opcode_4_2_000 & opcode_1_0_11; wire rv32_branch = opcode_6_5_11 & opcode_4_2_000 & opcode_1_0_11; //opcode==1100011 属于32位分支指令wire rv32_load_fp = opcode_6_5_00 & opcode_4_2_001 & opcode_1_0_11; wire rv32_store_fp = opcode_6_5_01 & opcode_4_2_001 & opcode_1_0_11; wire rv32_msub = opcode_6_5_10 & opcode_4_2_001 & opcode_1_0_11; wire rv32_jalr = opcode_6_5_11 & opcode_4_2_001 & opcode_1_0_11; //opcode==1100111 属于I类无条件间接跳转指令
整数通用寄存器组(Inter Register File, IRF)主要用于实现RISC-V架构定义的整数通用寄存器组。整数指令最多两个操作数,而且蜂鸟是单发射,则只需要两个读端口,一个写端口。结构如下图所示。
写端口通过比较输入的结果寄存器索引和通用寄存器号来产生写使能信号。读端口是纯粹的并行多路选择器。选择信号就是读操作数的寄存器索引,放到专用的寄存器寄存来降低功耗。
E203_exu_regfile.v
module e203_exu_regfile(input [`E203_RFIDX_WIDTH-1:0] read_src1_idx, // 从minidecode的指令源操作数rs1的索引 在读数据时候用input [`E203_RFIDX_WIDTH-1:0] read_src2_idx, // 从minidecode的指令源操作数rs2的索引 在读数据时候用output [`E203_XLEN-1:0] read_src1_dat, // 取出的源操作数的值output [`E203_XLEN-1:0] read_src2_dat, // 取出的源操作数的值input wbck_dest_wen, //写回操作的写使能,由wbck模块给出input [`E203_RFIDX_WIDTH-1:0] wbck_dest_idx, // 写回操作的寄存器地址,由wbck模块给出input [`E203_XLEN-1:0] wbck_dest_dat, // 写回操作的待写入数据,由wbck模块给出output [`E203_XLEN-1:0] x1_r, // 由于x1常用于link寄存器用于函数的跳转返回,这里直接接出来的x1的值做加速,读x1不需要读端口,但是需要检查数据相关性input test_mode,input clk,input rst_n);wire [`E203_XLEN-1:0] rf_r [`E203_RFREG_NUM-1:0]; //32个32位通用寄存器wire [`E203_RFREG_NUM-1:0] rf_wen; //控制打开哪个寄存器进行写入数据
RISC-V架构中国定义了一些控制和状态寄存器用于配置或者记录一些运行的状态。CSR是处理器核内部的寄存器,使用其自己的地址编码空间,与存储器寻址的地址空间完全没有关系。
CSR的访问采用专用的CSR读写指令。包括csrrw、csrrs、csrrc、csrrwi等
e203_exu_csr.v
就是用来实现E203处理器所支持的CSR功能。
端口定义代码如下所示
module e203_exu_csr(////用于配置或记录一些运行状态////input nonflush_cmt_ena, //这个信号只有输入,但啥也没干,来自commit
`ifdef E203_HAS_NICEoutput nice_xs_off, //跟写处理器相关,接了0
`endifinput csr_ena, //来自alu的csr读写使能信号 来自alu的csrctrlinput csr_wr_en, //csr的写使能信号 来自alu的csrctrlinput csr_rd_en, //csr的读使能信号 来自alu的csrctrlinput [12-1:0] csr_idx,//csr寄存器的地址索引 来自alu的csrctrloutput csr_access_ilgl, //固定接1'b0output tm_stop, //自定义的配置寄存器的配置信息相应位段的输出,停止timer计数指示信号 output core_cgstop, //停止cpu core逻辑的clk gatingoutput tcm_cgstop, //停止TCM clk gatingoutput itcm_nohold, //itcm 不hold数据的指示信号output mdv_nob2b, //mul/div 不采用back2back特性的指示信号
表示指令经过译码且从寄存器组中读取操作数之后被派发到不同的运算单元并执行的过程
资源冲突
比如将指令派遣给除法单元并进行计算,但是除法单元需要数十个时钟周期才可以完成此指令。后续的指令派遣就需要等待时防。因此采用了严谨的valid-ready握手接口。一旦出现资源冲突ready信号就会拉低从而无法完成握手。
假设指令需要派遣到AGU子单元并执行,那么agu_op为高,但是AGU在占用,那么她的agu_i_ready为低这样i_ready就为低。反馈给上游模块的话就无法完成握手。
assign i_ready = (agu_i_ready & agu_op)`ifdef E203_SUPPORT_SHARE_MULDIV //{| (mdv_i_ready & mdv_op)`endif//E203_SUPPORT_SHARE_MULDIV}| (alu_i_ready & alu_op)| (ifu_excp_i_ready & ifu_excp_op)| (bjp_i_ready & bjp_op)| (csr_i_ready & csr_op)`ifdef E203_HAS_NICE//{| (nice_i_ready & nice_op)`endif//}
数据冲突
E20将所有需要执行的指令分为两类
由于E203是按顺序派遣和顺序写回的架构,因此不会发生WAR相关性。
对于RAW。如果前序指令为多周期指令则会发生这种相关性。
对于WAW。同RAW
为了能够检测出与长指令的RAW和WAW相关性,蜂鸟E203设计了一个滞外指令追踪FIFO(OITF)模块。
一共分为5个部分