蜂鸟E203学习笔记(五)——执行
创始人
2024-03-27 01:49:57
0

1.1 执行概述

1.1.1 指令译码

指令所包含的信息编码在有限长度的指令字中,信息如下:

  • 指令所需要读取的操作数寄存器索引
  • 指令需要写回的寄存器索引
  • 指令的其他信息如指令类型、指令的操作信息等

顺便注意:并非所有的处理器流水线都会在译码阶段读取操作数。在目前众多高性能处理器中,普遍采用在每个运算单元前配置乱序发射队列的方式,待指令的相关性解除之后并从发射队列中发射出来时读取通用寄存器组,然后送给运算单元开始计算

1.1.2 指令执行

常见的运算单元有以下几种:

  • 算术逻辑运算单元(ALU),主要负责普通逻辑运算、加减法运算、和移位运算等基本运算。
  • 整数乘法单元,主要负责有符号或无符号数中整数的乘法
  • 整数除法单元
  • 浮点运算单元,常常分为不同的运算单元

1.1.3 流水线的冲突

除了根据指令的具体类型运算之外,指令执行阶段另外一个最重要的只能就是维护并解决流水线的冲突,包括资源冲突和数据冲突(WAR,WAW等)

1.1.4 指令的交付

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

将指令发射给运算单元,由运算单元执行,然后写回的相对顺序,是执行阶段需要解决的重要问题。

  • 派遣(dipatch):可以按顺序派遣,也可以乱序派遣。
  • 发射(issue):可以按顺序发射,也可以乱序发射。

以上两个定义很容易被混淆,在简单的处理器中两者属于同一概念,都是指令经过译码之后被派发到不同的运算单元并执行的过程。
根据每个时钟周期一次能够发射的指令数,处理器可以分为单发射处理器和多发射处理器。根据各种顺序,处理器可以分为很多种流派:

  1. 顺序发射,顺序执行,顺序写回
    如经典的5级流水线。这种方式的性能比较低,但是硬件实现最简单面积最小
  2. 顺序发射,乱序执行,顺序写回
    执行不同指令所需的时钟周期不同,如除法指令往往要耗费几十个时钟周期,而最简单的逻辑运算仅需要一个时钟周期。乱序执行是指在指令的执行阶段由不同的运算单元同时执行不同的指令从而提高性能。但是在最终的写回阶段仍要要严格地按照顺序写回,因此很多时候运算单元需要等待其他的先写回,从而导致停滞。
  3. 乱序写回
    有些处理器会配备重排序缓冲器(ROB),但是这种方案会占用过大的空间,并且会增加功耗。还有一些别的方法。
  4. 顺序派遣,乱序发射
    存在于高性能超标量处理器

1.1.6 分支解析

主要是对于带条件的分支指令
在执行阶段需要使用ALU对指令进行条件判断(如大小)。计算结果与之前的预测结果进行比较如果结果不一致则代表之前的预测是错误的,需要进行流水线冲刷。需要放在流水线比较前端的位置。

1.2 蜂鸟E203处理器的执行实现

在这里插入图片描述

1.3 译码模块

译码模块完全由组合逻辑组成。其主要逻辑即根据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类无条件间接跳转指令

1.4 整数通用寄存器组

整数通用寄存器组(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;   //控制打开哪个寄存器进行写入数据

1.5 CSR(控制和状态寄存器)

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特性的指示信号

1.6 指令发射派遣

表示指令经过译码且从寄存器组中读取操作数之后被派发到不同的运算单元并执行的过程

1.7 流水线冲突、长指令和OITF

资源冲突
比如将指令派遣给除法单元并进行计算,但是除法单元需要数十个时钟周期才可以完成此指令。后续的指令派遣就需要等待时防。因此采用了严谨的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)模块。

1.8 ALU

一共分为5个部分
在这里插入图片描述

  1. 普通ALU模块,主要负责普通的ALU指令(逻辑运算、加减法和移位等指令)的执行。
  2. 地址生成单元(AGU)模块,主要负责load、Store和A拓展指令的地址生成。
  3. 分支预测(BJP)模块 ,主要负责跳转和分支指令的分析和执行。
  4. CSR读写控制模块,主要负责分支与跳转指令的结果分析和执行
  5. 多周期乘除法模块,对于有符号的乘法采用常用的Booth编码算法计算部分积,然后使用迭代的方法对部分积进行累加得到最终的乘积。;对于有符号的整数除法采用常用的加减交替法,然后使用迭代的方法进行计算。

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...