meidoufu

导航

E203执行

一、执行概述
1、指令译码

2、指令执行(运算)
算术逻辑运算单元(ALU)
整数乘法单元
整数除法单元
浮点运算单元
3、流水线的冲突

4、指令发射、派遣、执行、写回的顺序

5、分支解析
二、RISC-V架构对于执行的简化

三、蜂鸟E203的执行实现
1、EXU总体设计思路
将IFU通过IR寄存器发送给EXU的指令进行译码与派遣(会在下面介绍)
通过译码得出的操作数寄存器索引读取寄存器组
维护指令的数据相关性
将指令派遣给不同的运算单元执行
交付指令
将指令的运算结果写回寄存器组

2、译码:对IR中的指令进行译码
译码部分通过读取指令的相关位存储的寄存器信息,使得处理器获取指定操作数的寄存器的索引(或者进入整数通用寄存器组)、指令类型指令操作信息等。不同的指令根据指令类型、指令操作信息意味着指令派遣单元需要派遣给不一样的执行单元执行。

算术逻辑单元(Arithmetic and Logic Unit,简称ALU)根据指令译码的Funct7、Funct3和opcode相关指令段来识别需要进行怎样的运算操作。如果
细分功能,ALU包含了5个子功能模块单元。分别是ALU、MMD、AGU、BJP、CSR-CTRL。

  1. 普通算术逻辑运算单元(Regular arithmetic and logic unit,简称ALU):主要执行加减运算和移位等运算操作。
  2. 多周期乘除法器(Multicycle Multiplication and Division,简称MMD):主要执行RV32M指令子集的乘、除法运算。
  3. 地址生成单元(Address Generation Unit,简称AGU):主要负责存取操作(属于长指令,需要通过AGU截取后再发送至LSU单元执行存取)以及地址的产生。
  4. 分支预测解析(Branch and Jump Resolve,简称BJP):主要执行分支和跳转指令,包含比较操作判断分支预测。
  5. 控制状态寄存器读写控制模块(Control Status Register-Control,简称CSR-CTRL):主要执行CSR寄存器读写模式的指令。

e203\core\e203_exu_decode.v
完全由组合逻辑编写
译码模块的输入信号来自于IFU模块,包括以下信号:

点击查看代码
input  [`E203_INSTR_SIZE-1:0] i_instr, //来自IFU的32位指令
input  [`E203_PC_SIZE-1:0] i_pc,       //来自IFU的当前指令PC
input  i_prdt_taken,           //预测需要跳转,来自IFU模块的分支预测单元,对分支跳转指令有用,如果当前指令为分支跳转指令,该信号会被传递到相应的info_bus
 input  i_misalgn,              // 表示当前指令遇到了分支跳转异常
 input  i_buserr,               // 表面当前指令遭遇了取指存储器访问错误
 input  i_muldiv_b2b,           // 前后临接的mul和div指令时置1,如果当前指令为乘法除法指令,该信号传递到相应的info_bus

input  dbg_mode,   //debug模式下,忽视dret指令异常的情况

译码模块的输出包括以下信号:

点击查看代码
output dec_rs1x0, // 该指令源操作数1为x0
 output dec_rs2x0, // 该指令源操作数2为x0
 output dec_rs1en, //该指令需要读取源操作数1
 output dec_rs2en, //该指令需要读取源操作数2
 output 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, //该指令的其它信息,被打包成一组宽信号,称之为信息总线(info bus).
 output [`E203_XLEN-1:0] dec_imm, //该指令使用的立即数值。
 output [`E203_PC_SIZE-1:0] dec_pc, // 该指令的pc值,等于输入的i_pc
 output dec_misalgn, //指令遇到了分支跳转异常,等于输入的misalgn
 output dec_buserr,  //指令遭遇了取指存储器访问错误,等于输入的i_buserr
 output dec_ilegl, //解码后,发现该指令是非法指令


 output dec_mulhsu, //指令为 mulh或mulhsu或mulhu,这些乘法指令都把结果的高32位放到目的寄存器
 output dec_mul   , //指令为乘法指令
 output dec_div   , //指令为除法指令
 output dec_rem   ,//指数为取余数指令
 output dec_divu  ,//指令为无符号数除法指令
 output dec_remu  ,//指令为无符号数取余数指令

output dec_rv32, //该指令为32位指令,为0的话则是16位的压缩指令
 output dec_bjp, //该指令为跳转指令,jal 或者jalr或者bxx指令
 output dec_jal,  //该指令为jal指令
 output dec_jalr, //该指令为jalr指令
 output dec_bxx, //该指令为bxx

output [`E203_RFIDX_WIDTH-1:0] dec_jalr_rs1idx, //jalr指令中rs1寄存器索引
 output [`E203_XLEN-1:0] dec_bjp_imm  //bjp指令中的立即数

代码判断指令是32位指令还是16位指令的代码。操作码的低2位11,且3到5位不为111,则为rv32指令,否则为16位指令。因为本设计支持RV32I(32位指令低2位的编码是“11”)指令子集不支持rv64(64位指令低6位的编码全1),16位指令有可能为00、01、10只需判断第1位,所以用1位信号就可以判断了。

点击查看代码
wire [32-1:0] rv32_instr = i_instr;
wire [16-1:0] rv16_instr = i_instr[15:0];
//指令的低7位为操作码
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);
 //为32位指令或者16位指令
 wire rv32 = (~(i_instr[4:2] == 3'b111)) & opcode_1_0_11;

取出32位指令和16位指令的关键编码段:

点击查看代码
wire [4:0]  rv32_rd     = rv32_instr[11:7]; // 目的寄存器索引
 wire [2:0]  rv32_func3  = rv32_instr[14:12];//func3段
 wire [4:0]  rv32_rs1    = rv32_instr[19:15];//源操作数1寄存器索引
 wire [4:0]  rv32_rs2    = rv32_instr[24:20];//源操作数2寄存器索引
 wire [6:0]  rv32_func7  = rv32_instr[31:25];//func7段
 //CR  15-12 func4, 11-7 rd/rs1, 6-2 rs2, 0-1 opcode
 //CI  15-13 func3, 12 imm,11-7 rd/rs1,6-2 imm, 0-1 opcode
 wire [4:0]  rv16_rd     = rv32_rd;
 wire [4:0]  rv16_rs1    = rv16_rd;
 wire [4:0]  rv16_rs2    = rv32_instr[6:2];
 //CIW,CL,CS,CA,CB,CJ
 //rdd=rd', rss1=rs1', rss2=rs2' short register, x8-x15, f8-f15
 wire [4:0]  rv16_rdd    = {2'b01,rv32_instr[4:2]};
 wire [4:0]  rv16_rss1   = {2'b01,rv32_instr[9:7]};
 wire [4:0]  rv16_rss2   = rv16_rdd;

wire [2:0]  rv16_func3  = rv32_instr[15:13]

下面的代码产生指令中的关键信息判断,用于后面的代码复用。

点击查看代码
// We generate the signals and reused them as much as possible to save gatecounts
  wire opcode_4_2_000 = (opcode[4:2] == 3'b000);
  wire opcode_4_2_001 = (opcode[4:2] == 3'b001);
   wire opcode_4_2_010 = (opcode[4:2] == 3'b010);
   wire opcode_4_2_011 = (opcode[4:2] == 3'b011);
   wire opcode_4_2_100 = (opcode[4:2] == 3'b100);
   wire opcode_4_2_101 = (opcode[4:2] == 3'b101);
   wire opcode_4_2_110 = (opcode[4:2] == 3'b110);
   wire opcode_4_2_111 = (opcode[4:2] == 3'b111);
   wire opcode_6_5_00  = (opcode[6:5] == 2'b00);
   wire opcode_6_5_01  = (opcode[6:5] == 2'b01);
   wire opcode_6_5_10  = (opcode[6:5] == 2'b10);
   wire opcode_6_5_11  = (opcode[6:5] == 2'b11);

  wire rv32_func3_000 = (rv32_func3 == 3'b000);
   wire rv32_func3_001 = (rv32_func3 == 3'b001);
   wire rv32_func3_010 = (rv32_func3 == 3'b010);
   wire rv32_func3_011 = (rv32_func3 == 3'b011);
   wire rv32_func3_100 = (rv32_func3 == 3'b100);
   wire rv32_func3_101 = (rv32_func3 == 3'b101);
   wire rv32_func3_110 = (rv32_func3 == 3'b110);
   wire rv32_func3_111 = (rv32_func3 == 3'b111);

  wire rv16_func3_000 = (rv16_func3 == 3'b000);
   wire rv16_func3_001 = (rv16_func3 == 3'b001);
   wire rv16_func3_010 = (rv16_func3 == 3'b010);
   wire rv16_func3_011 = (rv16_func3 == 3'b011);
   wire rv16_func3_100 = (rv16_func3 == 3'b100);
   wire rv16_func3_101 = (rv16_func3 == 3'b101);
   wire rv16_func3_110 = (rv16_func3 == 3'b110);
   wire rv16_func3_111 = (rv16_func3 == 3'b111);

  wire rv32_func7_0000000 = (rv32_func7 == 7'b0000000);
   wire rv32_func7_0100000 = (rv32_func7 == 7'b0100000);
   wire rv32_func7_0000001 = (rv32_func7 == 7'b0000001);
   wire rv32_func7_0000101 = (rv32_func7 == 7'b0000101);
   wire rv32_func7_0001001 = (rv32_func7 == 7'b0001001);
   wire rv32_func7_0001101 = (rv32_func7 == 7'b0001101);
   wire rv32_func7_0010101 = (rv32_func7 == 7'b0010101);
   wire rv32_func7_0100001 = (rv32_func7 == 7'b0100001);
   wire rv32_func7_0010001 = (rv32_func7 == 7'b0010001);
   wire rv32_func7_0101101 = (rv32_func7 == 7'b0101101);
   wire rv32_func7_1111111 = (rv32_func7 == 7'b1111111);
   wire rv32_func7_0000100 = (rv32_func7 == 7'b0000100);
   wire rv32_func7_0001000 = (rv32_func7 == 7'b0001000);
   wire rv32_func7_0001100 = (rv32_func7 == 7'b0001100);
   wire rv32_func7_0101100 = (rv32_func7 == 7'b0101100);
   wire rv32_func7_0010000 = (rv32_func7 == 7'b0010000);
   wire rv32_func7_0010100 = (rv32_func7 == 7'b0010100);
   wire rv32_func7_1100000 = (rv32_func7 == 7'b1100000);
   wire rv32_func7_1110000 = (rv32_func7 == 7'b1110000);
   wire rv32_func7_1010000 = (rv32_func7 == 7'b1010000);
   wire rv32_func7_1101000 = (rv32_func7 == 7'b1101000);
   wire rv32_func7_1111000 = (rv32_func7 == 7'b1111000);
   wire rv32_func7_1010001 = (rv32_func7 == 7'b1010001);
   wire rv32_func7_1110001 = (rv32_func7 == 7'b1110001);
   wire rv32_func7_1100001 = (rv32_func7 == 7'b1100001);
   wire rv32_func7_1101001 = (rv32_func7 == 7'b1101001);

  wire rv32_rs1_x0 = (rv32_rs1 == 5'b00000);
   wire rv32_rs2_x0 = (rv32_rs2 == 5'b00000);
   wire rv32_rs2_x1 = (rv32_rs2 == 5'b00001);
   wire rv32_rd_x0  = (rv32_rd  == 5'b00000);
   wire rv32_rd_x2  = (rv32_rd  == 5'b00010);

  wire rv16_rs1_x0 = (rv16_rs1 == 5'b00000);
   wire rv16_rs2_x0 = (rv16_rs2 == 5'b00000);
   wire rv16_rd_x0  = (rv16_rd  == 5'b00000);
   wire rv16_rd_x2  = (rv16_rd  == 5'b00010);

  wire rv32_rs1_x31 = (rv32_rs1 == 5'b11111);
   wire rv32_rs2_x31 = (rv32_rs2 == 5'b11111);
   wire rv32_rd_x31  = (rv32_rd  == 5'b11111);

RVC的编码比较复杂,有些指令寄存器为3位表示,有些为5位表示,立即数通常都有旋转,旋转格式还很多。具体参见https://www.cnblogs.com/mikewolf2002/p/9884789.html

对32位指令和16位指令进行译码:

点击查看代码
//rv32 load指令,opcode是0000011,不同load指令通过func3区分
wire rv32_load     = opcode_6_5_00 & opcode_4_2_000 & opcode_1_0_11;

//rv32 store指令,opcode是0100011,不同store指令通过func3区分
 wire rv32_store    = opcode_6_5_01 & opcode_4_2_000 & opcode_1_0_11;

//madd指令 opcode=1000011,乘加指令

//madd指令是浮点指令的一部分,fmadd.s, fmadd.d, fmadd.q,e203 不支持
// wire rv32_madd     = opcode_6_5_10 & opcode_4_2_000 & opcode_1_0_11;

//branch指令opcode=1100011, bxx指令jalr,jal操作码不同,单独判断
 wire rv32_branch   = opcode_6_5_11 & opcode_4_2_000 & opcode_1_0_11;

//浮点数load指令,opcode=0000111,包括fsw,fsd,fsq
//wire rv32_load_fp  = opcode_6_5_00 & opcode_4_2_001 & opcode_1_0_11;

//浮点数store指令,opcode=0100111,包括flw,fld,flq
// wire rv32_store_fp = opcode_6_5_01 & opcode_4_2_001 & opcode_1_0_11;

//msub指令 opcode=1000111,乘减指令
//msub指令是浮点指令的一部分,fmsub.s, fmsub.d, fmsub.q

wire rv32_msub     = opcode_6_5_10 & opcode_4_2_001 & opcode_1_0_11;

//jalr指令的opcode=1100111
 wire rv32_jalr     = opcode_6_5_11 & opcode_4_2_001 & opcode_1_0_11;

//opcode为0001011为第一类的定制指令,架构设计者可以实现自己定义的指令
//opcode为0101011为第二类的定制指令,还有第三和第四类的定制指令。

//wire rv32_custom0  = opcode_6_5_00 & opcode_4_2_010 & opcode_1_0_11;
//wire rv32_custom1  = opcode_6_5_01 & opcode_4_2_010 & opcode_1_0_11;

//nmsub指令opcode=1001011, 负乘减操作x[rd]=-rs1*rs2+rs3
//wire rv32_nmsub    = opcode_6_5_10 & opcode_4_2_010 & opcode_1_0_11;

//保留的指令,为了将来扩展用 opcode=1101011
//wire rv32_resved0  = opcode_6_5_11 & opcode_4_2_010 & opcode_1_0_11;

//存储器屏障指令, fence,fence.i, opcode=0001111

wire rv32_miscmem  = opcode_6_5_00 & opcode_4_2_011 & opcode_1_0_11;

//原子指令, opcode=0101111,不同的原子指令,通过func3和func7区分
 `ifdef E203_SUPPORT_AMO//{
 wire rv32_amo      = opcode_6_5_01 & opcode_4_2_011 & opcode_1_0_11;
 `endif//E203_SUPPORT_AMO}
 `ifndef E203_SUPPORT_AMO//{
 wire rv32_amo      = 1'b0;
 `endif//}

//nmadd指令是浮点指令的一部分,fnmadd.s, fnmadd.d, fnmadd.q
 wire rv32_nmadd    = opcode_6_5_10 & opcode_4_2_011 & opcode_1_0_11;

//jal指令的opcode=1101111
 wire rv32_jal      = opcode_6_5_11 & opcode_4_2_011 & opcode_1_0_11;

//i型的运算指令,opcode=0010011,比如addi

wire rv32_op_imm   = opcode_6_5_00 & opcode_4_2_100 & opcode_1_0_11;

//浮点R型的运算指令, opcode=1010011, 比如add
 wire rv32_op       = opcode_6_5_01 & opcode_4_2_100 & opcode_1_0_11;

//R型的运算指令, opcode=0110011, 比如fadd.s
 wire rv32_op_fp    = opcode_6_5_10 & opcode_4_2_100 & opcode_1_0_11;

//一些csr指令, opcode=1110011,比如csrrw
 wire rv32_system   = opcode_6_5_11 & opcode_4_2_100 & opcode_1_0_11;

//auipc指令,opcode=0010111

wire rv32_auipc    = opcode_6_5_00 & opcode_4_2_101 & opcode_1_0_11;

//lui指令,opcode=0110111
 wire rv32_lui      = opcode_6_5_01 & opcode_4_2_101 & opcode_1_0_11;

//第二、三类保留指令
// wire rv32_resved1  = opcode_6_5_10 & opcode_4_2_101 & opcode_1_0_11;
// wire rv32_resved2  = opcode_6_5_11 & opcode_4_2_101 & opcode_1_0_11;

//slliw,srliw,addiw, sraiw等指令的操作码,opcode=0011011,这些都是64位指令

//wire rv32_op_imm_32= opcode_6_5_00 & opcode_4_2_110 & opcode_1_0_11;

//addw,subw,…等64位算术运算指令
// wire rv32_op_32    = opcode_6_5_01 & opcode_4_2_110 & opcode_1_0_11;

//第三,四类定制指令
// wire rv32_custom2  = opcode_6_5_10 & opcode_4_2_110 & opcode_1_0_11;
// wire rv32_custom3  = opcode_6_5_11 & opcode_4_2_110 & opcode_1_0_11;

生成BJP单元所需的信息总线

生成普通ALU所需的信息总线

生成CSR单元所需的信息总线

生成乘除法单元所需的信息总线

生成AGU所需的信息总线,AGU是ALU的一个子单元,用于处理amo load store指令

并行多路选择器:将上述生成的不同模块的信息总线通过并行多路选择的方式复用到统一的输出信号dec_info上

判断是否需要读取寄存器操作数1、寄存器操作数2,是否需要写回结果寄存器

立即数译码

并行多路选择器:根据立即数的类型,生成32位/16位指令最终的立即数

生成最终的操作数寄存器索引

译码出不同的非法指令类型

3、整数通用寄存器组
通用寄存器在功能上,主要承担译码和执行部分的临时数据存储

支持两个读端口(单发射、整数指令都是单操作数or两操作数)、一个写端口(写回策略:按顺序每次写回一条指令)
模块相关代码保存在e203_exu_regfile.v文件中
可以通过配置config.v更改通用寄存器的位数

写端口
通过将输入的结果寄存器索引和各自的寄存器号进行比较,产生写使能信号,被使能的通用寄存器即将写数据写入寄存器

读端口
每个读端口都是一个纯粹的并行多路选择器,使用读操作数的寄存器索引作为选择信号,使用专用寄存器读取寄存器索引信号,当且仅当执行读操作数的时候这个专用寄存器才会被调用,可以减少读端口的动态反转功耗

简而言之就是在读端口放了个门卫大爷,只有需要读取的时候大爷才会给寄存器索引数据开门(把寄存器索引存入该专用寄存器)
4、CSR
RISC-V架构中定义了控制和状态寄存器CSR(Control and Status Register),用于配置或记录一些运行的状态。这些寄存器都位于核心内部,使用自己独立的地址编码空间,与存储器寻址无关,它们可以被看作“内核的外设控制寄存器”

使用专用的CSR读写指令来访问CSR寄存器

相关源代码位于e203_exu_csr.v文件下,严格按照RISC-V架构定义实现了各个CSR寄存器的具体功能

点击查看代码
module e203_exu_csr(
	input csr_ena,//CSR使能信号,来自ALU
  	input csr_wr_en,//CSR写操作标志位
 	input csr_rd_en,//CSR读操作标志位
    input [12-1:0] csr_idx,//CSR寄存器地址索引
	......
    
    output [`E203_XLEN-1:0] read_csr_dat,//读出数据
    input  [`E203_XLEN-1:0] wbck_csr_dat,//写入数据
	......
);
    
    ......
    //以MTVEC寄存器为例
    wire sel_mtvec = (csr_idx == 12'h305);///对CSR寄存器索引进行**译码判断**是否选中mtvec
	wire rd_mtvec = csr_rd_en & sel_mtvec;
`ifdef E203_SUPPORT_MTVEC //{
	wire wr_mtvec = sel_mtvec & csr_wr_en;
    wire mtvec_ena = (wr_mtvec & wbck_csr_wen);//mtvec使能信号
	wire [`E203_XLEN-1:0] mtvec_r;
	wire [`E203_XLEN-1:0] mtvec_nxt = wbck_csr_dat;
    //例化生成寄存器DFF
	sirv_gnrl_dfflr #(`E203_XLEN) mtvec_dfflr (mtvec_ena, mtvec_nxt, mtvec_r, clk, rst_n);
	wire [`E203_XLEN-1:0] csr_mtvec = mtvec_r;
`else//}{
  	//向量表基地址是可配置的参数,不支持软件写入
	wire [`E203_XLEN-1:0] csr_mtvec = `E203_MTVEC_TRAP_BASE;
`endif//}
	//对于读地址不存在的CSR寄存器,返回数据0;写地址不存在的CSR寄存器,忽略此写操作
    //这是为了对应RISC-V要求的不产生异常
    assign csr_mtvec_r = csr_mtvec;
    
    ......
    
    //生成CSR读操作所需的读数据,本质上该逻辑是使用与-或方式实现的并行多路选择器
    assign read_csr_dat = `E203_XLEN'b0 
               //| ({`E203_XLEN{rd_ustatus  }} & csr_ustatus  )
               | ({`E203_XLEN{rd_mstatus  }} & csr_mstatus  )
               | ({`E203_XLEN{rd_mie      }} & csr_mie      )
               | ({`E203_XLEN{rd_mtvec    }} & csr_mtvec    )
               | ({`E203_XLEN{rd_mepc     }} & csr_mepc     )
               | ({`E203_XLEN{rd_mscratch }} & csr_mscratch )
               | ({`E203_XLEN{rd_mcause   }} & csr_mcause   )
               | ({`E203_XLEN{rd_mbadaddr }} & csr_mbadaddr )
               | ({`E203_XLEN{rd_mip      }} & csr_mip      )
               | ({`E203_XLEN{rd_misa     }} & csr_misa      )
               | ({`E203_XLEN{rd_mvendorid}} & csr_mvendorid)
               | ({`E203_XLEN{rd_marchid  }} & csr_marchid  )
               | ({`E203_XLEN{rd_mimpid   }} & csr_mimpid   )
               | ({`E203_XLEN{rd_mhartid  }} & csr_mhartid  )
               | ({`E203_XLEN{rd_mcycle   }} & csr_mcycle   )
               | ({`E203_XLEN{rd_mcycleh  }} & csr_mcycleh  )
               | ({`E203_XLEN{rd_minstret }} & csr_minstret )
               | ({`E203_XLEN{rd_minstreth}} & csr_minstreth)
               | ({`E203_XLEN{rd_counterstop}} & csr_counterstop)// Self-defined
               | ({`E203_XLEN{rd_mcgstop}} & csr_mcgstop)// Self-defined
               | ({`E203_XLEN{rd_itcmnohold}} & csr_itcmnohold)// Self-defined
               | ({`E203_XLEN{rd_mdvnob2b}} & csr_mdvnob2b)// Self-defined
               | ({`E203_XLEN{rd_dcsr     }} & csr_dcsr    )
               | ({`E203_XLEN{rd_dpc      }} & csr_dpc     )
               | ({`E203_XLEN{rd_dscratch }} & csr_dscratch)
               ;
    
endmodule

5、指令发射、派遣
蜂鸟E200系列CPU的发射和派遣实际上指的是同一个行为:即指令经过译码之后被派发到不同的运算单元执行的总过程

该部分使用Dispatch模块和ALU模块共同完成

Dispatch模块负责向ALU模块转发派遣任务

ALU部分负责交付模块和前级的接口

蜂鸟E200系列的派遣特点如下:

所有指令必须被派遣给ALU,通过ALU与交付模块接口进行交付;如果是长指令,也需要通过ALU进一步发送至相应的长指令运算单元
实际的派遣功能发生在ALU内部。因为译码部分已经根据指令的运算单元进行了初步分组并译码出了其相应的指示信号,可以按照其指示信号将指令派遣给相应的运算单元
在派遣模块中处理流水线冲突问题,包括资源冲突和数据相关性冲突,并在某些特殊情况下将流水线的派遣点阻塞

dispatch模块代码在e203_exu_disp.v
ALU模块代码在e203_exu_alu.v
dispatch模块和ALU模块之间的接口采用valid_ready模式的握手协议

6、流水线冲突、长指令和OITF
(1)资源冲突
资源冲突通常发生在指令派遣给不同的执行单元进行执行的过程中,当一个指令被执行时耗费的时钟周期较长,此后又有其他指令被派发给同一个硬件模块进行处理的情况下便会出现资源冲突的情况——后续的指令需要等待前一个指令完成操作后将硬件模块释放出来后才能得到执行。

蜂鸟E203的接口实现采用了严谨的valid-ready握手接口,一旦某个模块出现了资源冲突,它便会输出ready=0的信号,即使另一侧valid=1,也无法完成握手,所以前一级模块无法进行分配指令,将会进入等待状态,直到ready=1

(2)数据冲突
对于数据相关性造成的冲突,蜂鸟E203只采用了阻塞流水线的方法,而没有将长指令的结果直接快速旁路给后续的待派遣指令
蜂鸟E203将所有需要执行的指令分为两类:
单周期执行指令:蜂鸟E203的交付和写回功能均处于流水线的第二级,单周期执行指令在这一级就完成了交付和写回
多周期执行指令:这种指令通常需要多个时钟周期才能完成执行并写回,因此也称为“后交付长流水线指令”,简称为长指令
长指令的执行过程比较特殊,为了在很多时钟周期后交付长指令,需要先检测出数据相关性,蜂鸟E203采用了一个称为OITF(Outstanding Instructions Track FIFO长指令追踪队列)的模块检测与长指令相关的RAW和WAW相关性。

之所以不检测WAR相关性,是因为E203是按序派遣、按序写回的微架构,在派遣时就已经从寄存器组中读取了源操作数,所以写回Regfile操作不会发生在读取Regfile源操作数之前。

在派遣点,每派遣一个长指令,便会在OITF中分配一个表项,在这个表项中会存储该长指令的源操作数寄存器索引和结果寄存器索引
在写回点,每次按序写回一个长指令后,就会将此指令在OITF中的表象去除——他就从FIFO中退出了
综上所述,OITF本质上保存了已经被派遣但是尚未写回的长指令信息
在流水线的派遣点,派遣每条指令时如果发现了数据相关性,会阻塞派遣点,直到相关长指令执行完毕解除相关性后才会继续派遣

言归正传,OITF本质上是一个普通的FIFO(废话),它的源码在rtl/e203/core/e203_exu_oitf.v中可以查看

点击查看代码
module e203_exu_oitf (
  output dis_ready,

  input  dis_ena,//派遣一条长指令的使能信号,该信号将用于分配一个OITF表项
  input  ret_ena,//写回一条长指令的使能信号,该信号将用于移除一个OITF表项

  output [`E203_ITAG_WIDTH-1:0] dis_ptr,
  output [`E203_ITAG_WIDTH-1:0] ret_ptr,

  output [`E203_RFIDX_WIDTH-1:0] ret_rdidx,
  output ret_rdwen,
  output ret_rdfpu,
  output [`E203_PC_SIZE-1:0] ret_pc,
  //以下为派遣的长指令相关信息,有的会存储于OITF的表项中,有的会用于进行RAW和WAW判断
  input  disp_i_rs1en,
  input  disp_i_rs2en,
  input  disp_i_rs3en,
  input  disp_i_rdwen,
  input  disp_i_rs1fpu,
  input  disp_i_rs2fpu,
  input  disp_i_rs3fpu,
  input  disp_i_rdfpu,
  input  [`E203_RFIDX_WIDTH-1:0] disp_i_rs1idx,
  input  [`E203_RFIDX_WIDTH-1:0] disp_i_rs2idx,
  input  [`E203_RFIDX_WIDTH-1:0] disp_i_rs3idx,
  input  [`E203_RFIDX_WIDTH-1:0] disp_i_rdidx,
  input  [`E203_PC_SIZE    -1:0] disp_i_pc,

  output oitfrd_match_disprs1,//派遣的指令的第一个源操作数和OITF任意表项中的结果寄存器相同
  output oitfrd_match_disprs2,//派遣的指令的第二个源操作数和OITF任意表项中的结果寄存器相同
  output oitfrd_match_disprs3,//派遣的指令的第三个源操作数和OITF任意表项中的结果寄存器相同
  output oitfrd_match_disprd,//派遣的指令的结果寄存器和OITF任意表项中的结果寄存器相同

  output oitf_empty,
  input  clk,
  input  rst_n
);

7、ALU
蜂鸟E203的ALU单元位于EXU之下,主要包括5个子模块,它们共性同一份实际的运算数据通路,因此主要数据通路的面积开销只有一份
** 普通ALU(e203_exu_alu_rglr.v)**:主要负责逻辑运算、加减法、移位运算等通用的ALU指令
访存地址生成AGU(e203_exu_alu_lsuagu.v):主要负责Load、Store和“A”扩展指令的地址生成、“A”扩展指令的微操作拆分和执行
相关内容会在存储器架构部分详细介绍
分支预测解析BJP(e203_exu_alu_bjp.v):主要负责Branch和Jump指令的结果解析和执行
BJP(Branch and Jump resolve)模块是分支跳转指令进行交付的主要依据,可以查看交付部分进行了解
CSR读写控制(e203_exu_alu_csrctrl.v):主要负责CSR读写指令的执行
该模块主要负责CSR读写指令的执行,这个模块也是完全由组合逻辑组成,其根据CSR读写指令的类型产生读写CSR寄存器模块的控制信号
代码片段如下:

点击查看代码
`include "e203_defines.v"

module e203_exu_alu_csrctrl(
  //握手接口
  input  csr_i_valid, // valid信号
  output csr_i_ready, // ready信号

  input  [`E203_XLEN-1:0] csr_i_rs1,
  input  [`E203_DECINFO_CSR_WIDTH-1:0] csr_i_info,
  input  csr_i_rdwen,   

  output csr_ena, // CSR读写使能信号
  output csr_wr_en, // CSR写操作指示信号
  output csr_rd_en, // CSR读操作指示信号
  output [12-1:0] csr_idx, // CSR寄存器的地址索引

  input  csr_access_ilgl,
  input  [`E203_XLEN-1:0] read_csr_dat, // 读操作从CSR寄存器模块读出的数据
  output [`E203_XLEN-1:0] wbck_csr_dat, // 写操作写入CSR寄存器模块的数据

  `ifdef E203_HAS_CSR_NICE//{
  input          nice_xs_off,
  output         csr_sel_nice,
  output         nice_csr_valid,
  input          nice_csr_ready,
  output  [31:0] nice_csr_addr,
  output         nice_csr_wr,
  output  [31:0] nice_csr_wdata,
  input   [31:0] nice_csr_rdata,
  `endif//}

  //CSR写回/交付接口
  output csr_o_valid, // valid信号
  input  csr_o_ready, // ready信号
  // 为了非对齐lst和AMO指令使用的特殊写回接口
  output [`E203_XLEN-1:0] csr_o_wbck_wdat,
  output csr_o_wbck_err,   

  input  clk,
  input  rst_n
  );

  `ifdef E203_HAS_CSR_NICE//{
      // If accessed the NICE CSR range then we need to check if the NICE CSR is ready
  assign csr_sel_nice        = (csr_idx[11:8] == 4'hE);
  wire sel_nice            = csr_sel_nice & (~nice_xs_off);
  wire addi_condi         = sel_nice ? nice_csr_ready : 1'b1; 

  assign csr_o_valid      = csr_i_valid
                            & addi_condi; // Need to make sure the nice_csr-ready is ready to make sure
                                          //  it can be sent to NICE and O interface same cycle
  assign nice_csr_valid    = sel_nice & csr_i_valid & 
                            csr_o_ready;// Need to make sure the o-ready is ready to make sure
                                        //  it can be sent to NICE and O interface same cycle

  assign csr_i_ready      = sel_nice ? (nice_csr_ready & csr_o_ready) : csr_o_ready; 

  assign csr_o_wbck_err   = csr_access_ilgl;
  assign csr_o_wbck_wdat  = sel_nice ? nice_csr_rdata : read_csr_dat;

  assign nice_csr_addr = csr_idx;
  assign nice_csr_wr   = csr_wr_en;
  assign nice_csr_wdata = wbck_csr_dat;
  `else//}{
  wire   sel_nice      = 1'b0;
  assign csr_o_valid      = csr_i_valid;
  assign csr_i_ready      = csr_o_ready;
  assign csr_o_wbck_err   = csr_access_ilgl;
  assign csr_o_wbck_wdat  = read_csr_dat;
  `endif//}

  //从Info Bus中取出相关信息
  wire        csrrw  = csr_i_info[`E203_DECINFO_CSR_CSRRW ];
  wire        csrrs  = csr_i_info[`E203_DECINFO_CSR_CSRRS ];
  wire        csrrc  = csr_i_info[`E203_DECINFO_CSR_CSRRC ];
  wire        rs1imm = csr_i_info[`E203_DECINFO_CSR_RS1IMM];
  wire        rs1is0 = csr_i_info[`E203_DECINFO_CSR_RS1IS0];
  wire [4:0]  zimm   = csr_i_info[`E203_DECINFO_CSR_ZIMMM ];
  wire [11:0] csridx = csr_i_info[`E203_DECINFO_CSR_CSRIDX];
  //生成操作数1,如果使用立即数则选择立即数,否则选择源寄存器1
  wire [`E203_XLEN-1:0] csr_op1 = rs1imm ? {27'b0,zimm} : csr_i_rs1;
  //根据指令的信息生成读操作指示信号
  assign csr_rd_en = csr_i_valid & 
    (
      (csrrw ? csr_i_rdwen : 1'b0) // the CSRRW only read when the destination reg need to be writen
      | csrrs | csrrc // The set and clear operation always need to read CSR
     );
  //根据指令的信息生成写操作指示信号
  assign csr_wr_en = csr_i_valid & (
                csrrw // CSRRW always write the original RS1 value into the CSR
               | ((csrrs | csrrc) & (~rs1is0)) // for CSRRS/RC, if the RS is x0, then should not really write
            );                                                                           
  //生成访问CSR寄存器的地址索引
  assign csr_idx = csridx;
  //生成送到CSR寄存器模块的CSR读写使能信号
  assign csr_ena = csr_o_valid & csr_o_ready & (~sel_nice);
  //生成写操作写入CSR寄存器模块的数据
  assign wbck_csr_dat = 
              ({`E203_XLEN{csrrw}} & csr_op1)
            | ({`E203_XLEN{csrrs}} & (  csr_op1  | read_csr_dat))
            | ({`E203_XLEN{csrrc}} & ((~csr_op1) & read_csr_dat));
endmodule
** 多周期乘除法器MDV(e203_exu_alu_muldiv.v)**:主要负责乘法和除法指令的执行 常用的多周期乘除法器和除法器实现,一般采用下面的理论实现: 有符号整数乘法:使用常用的Booth编码产生部分积,然后使用迭代的方法,每个周期使用加法器对部分积进行累加,经过多个周期的迭代后得到最终的乘积,从而实现多周期乘法器 有符号整数除法:使用常用的加减交替法,然后使用迭代的方法,每个周期使用加法器产生部分余数,经过多个周期的迭代后得到最终商和玉树,从而实现多周期除法器 多周期乘法器和除法器的结构类似,都使用加法作为主要的运算通路,使用一组寄存器保存部分积或者部分余数,因此二者进行复用;MDV模块只使用一些状态机进行运算控制和选择,加法器和其他ALU子单元复用共享的运算数据通路;没有保存部分积或者部分余数的寄存器,复用AGU的寄存器

数据运算通路e203_exu_alu_dpath.v
8、交付、写回、协处理器扩展

posted on 2025-02-20 11:09  霉豆腐  阅读(124)  评论(0)    收藏  举报