蜂鸟E203-执行

448 阅读12分钟

在流水线中取指之后,便要译码和执行。处理器核中执行单元EXU

执行

指令译码:

指令所包含的信息编码在有限长度的指令字中,因此需要译码,将信息从指令字中翻译出来。

常见的信息:

指令所需要的读取的操作数寄存器索引。

指令需要写回的寄存器索引。

指令的其他信息,如指令类型、指令的操作信息等。

在经典的五级流水线中,在译码阶段直接使用译出的读操作数寄存器索引,将操作数从通用寄存器组中读取出来。

指令执行

执行是根据指令的具体操作类型发射给具体的运算单元以进行操作。

算术逻辑运算单元:

整数乘法单元:

整数除法单元:

浮点运算单元:主要负责浮点指令的运算。由于浮点指令种类较多,因此浮点运算单元本身常分为多个不同的运算单元。

包含特殊指令(或者扩展指令)的处理器核会相应地包含特殊的运算单元。

流水线的冲突

除根据指令的具体类型运算之外,指令执行阶段另外一个最重要的职能就是维护并解决流水线冲突。

包括资源冲突和数据冲突。

指令的交付

五级流水线,处理器的流水线分别为取指,译码,执行,访存,写回。没有提及指令的交付,指令的交付是处理器微架构中非常重要的一个功能。

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

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

派遣: 可以按顺序派遣,也可以乱序派遣。

发射:可以按顺序发射,也可以乱序发射。

处理器分为单发射处理器和多发射处理器。单发射处理器是每个时钟周期只能发射一条指令,多发射处理器是指处理器在每个时钟周期能够发射多条指令。

派遣往往表示指令经过译码后被派发到不同的运算单元的等待队列中的过程,而发射往往表示指令从运算单元的等待队列中发射到运算单元并开始执行。

1 顺序发射 顺序执行 顺序写回:

2 顺序发射 乱序执行 顺序写回

3 顺序发射 乱序执行 乱序写回

4 顺序派遣 乱序发射 乱序执行 乱序写回:

区分了派遣和发射功能的处理器往往属于高性能的超标量处理器。 指令经过译码被顺序派遣到不同运算单元的等待队列中,在等待队列中可以有很多条指令,先把解除数据依赖性的指令发射到运算单元中并开始执行。 因此发射是乱序的。 这种高性能处理器往往会配备ROB或者统一的物理寄存器组,因此运算单元的乱序执行和乱序写回没关系。

分支解析

在取指阶段,对于有条件的分支指令,由于其条件的解析需要进行操作数运算, 因此流水线在取指阶段无法得知该指令的条件跳转结果是跳还是不跳。只能进行预测。

在执行阶段,通常需要使用ALU来进行条件判断。ALU判断的结果将用于解析分支指令是否真的需要跳转。 并且和之前预测的跳转结果进行对比。如预测不一致,则意味着之前的预测错误,需要进行流水线冲刷,将预测取值中的所取的指令都舍弃掉。重新按照真实的跳转方向进行取值。

分支预测错误导致流水线冲刷会造成性能损失。流水线越深,流水线冲刷造成的性能损失越大。分支指令解析如果比较发生在比较靠近取值的流水线,则性能损失会相对小一点。

在功能正确且满足时序的情况下,尽量在比较靠前端的流水线进行分支指令解析,是处理器微架构设计经常需要考虑的问题。

执行阶段在于衔接前端取指和后端写回的中枢位置,是决定处理器性能高低的主要部分。

RISC-V架构的特点在于执行的简化

规整的指令编码格式

指令所用的通用寄存器的索引都放在固定的位置,因此指令译码器可以非常便捷地译码出寄存器索引,然后从通用寄存器组中读取出操作数。很容易译码出指令的类型和具体信息。

优雅的16位指令

每一条16位指令都有对应的32位指令,因此译码逻辑可以利用此特点将16位指令展开成对应的32位指令,从而使流水线后续部分看到的都是统一的32位指令。执行阶段无须区分是16位还是32位指令。

精简的指令个数

基本的RISC-V指令仅有40多条,加上其他模块的扩展指令总共有几十条指令。

整数指令的操组数是1或者是2

操作数(operand),是 计算机指令的一个组成部分,它规定了指令中进行数字运算的量 。

蜂鸟E203处理器的执行实现

其译码 执行 交付和写回处于流水线的第二级。

执行指令列表

所有RISC-V指令集均需EXU进行译码 派遣 和写回。

RAW 是指数据依懒性中的 写后读依赖(read after write) 假设指令j是在指令i之后执行, RAW表示指令i将数据写入到寄存器后,指令j才能从这个寄存器读取数据. 如果指令j 在指令i写入寄存器前尝试读出寄存器的内容,得不到正确的数据;\

与此相关的数据依赖性 WAR WAW (write after read; write after write)
WAR 读后写 假设指令j是在指令i后面执行的指令, WAR 表示只有当指令i读过寄存器之后,指令j才能写这个寄存器;如果指令j 在指令i读出数据之前就写入了这个寄存器,则指令i读出的数据不准确。
WAW 写后写 仍然假设指令j是在指令i之后执行的指令, WAW 表示只有在指令i写入到寄存器之后, 指令j才能写这个寄存器;否则,会导致该寄存器的值不是最新值;

EXU的总体设计思路

将IFU通过IR发送给EXU的指令进行译码和派遣

通过译码出的操作数寄存器索引 读取通用寄存器RD通用寄存器

维护指令的数据相关性OIFT

将指令派遣到不同的运算单元并执行。 ALU 长指令 LSU 以及NICE。 LSU寄存器完成访存。可配置的NICE(协处理器),它可以用来创建用户定义的指令。

将指令交付

将指令运算结果写回通用寄存器组 WB裁

译码

译码模块主要对IR中的指令进行译码

整数通用寄存器组

整数通用寄存器组模块主要用于实现RISC-V架构定义的整数通用寄存器组。

RISC-V的整数指令都是单操作数或者两操作数指令。 整数通用寄存器组模块最多只需要支持两个读端口。 同时蜂鸟E203 处理器的写回策略是按顺序每次写回一条指令。

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

整数通用寄存器组模块的每个读端口都是一个纯粹的并行多路选择器,多路选择器的选择信号即读读操作数的寄存器索引。为了降低功耗,读端口的寄存器索引信号由专用的寄存器寄存,只有在执行需要读操作数的指令时才会加载。 从而降低端口的动态功耗。

整数通用寄存器组模块有两个可配置选项。


//使用二维数组定义通用寄存器组
  wire [`E203_XLEN-1:0] rf_r [`E203_RFREG_NUM-1:0];
  wire [`E203_RFREG_NUM-1:0] rf_wen;
  
  `ifdef E203_REGFILE_LATCH_BASED //{
  
  
  // Use DFF to buffer the write-port
  
  //如果使用锁存器实现通用寄存器,需要将写端口使用DFF打一拍,以防止锁存器带来的写端口至读端口之间锁存器的锁存器穿通效应。
  
  wire [`E203_XLEN-1:0] wbck_dest_dat_r;
  sirv_gnrl_dffl #(`E203_XLEN) wbck_dat_dffl (wbck_dest_wen, wbck_dest_dat, wbck_dest_dat_r, clk);
  wire [`E203_RFREG_NUM-1:0] clk_rf_ltch;
  `endif//}

  
  genvar i;
  
  // 通过使用参数化的generate语法生成 通用寄存器组的逻辑。
  generate //{
  
      for (i=0; i<`E203_RFREG_NUM; i=i+1) begin:regfile//{
  
        if(i==0) begin: rf0
            // x0 cannot be wrote since it is constant-zeros
            assign rf_wen[i] = 1'b0;
            assign rf_r[i] = `E203_XLEN'b0;
          `ifdef E203_REGFILE_LATCH_BASED //{
            assign clk_rf_ltch[i] = 1'b0;
          `endif//}
        end
        else begin: rfno0
            assign rf_wen[i] = wbck_dest_wen & (wbck_dest_idx == i) ;
          `ifdef E203_REGFILE_LATCH_BASED //{
            e203_clkgate u_e203_clkgate(
              .clk_in  (clk  ),
              .test_mode(test_mode),
              .clock_en(rf_wen[i]),
              .clk_out (clk_rf_ltch[i])
            );
                //from write-enable to clk_rf_ltch to rf_ltch
            sirv_gnrl_ltch #(`E203_XLEN) rf_ltch (clk_rf_ltch[i], wbck_dest_dat_r, rf_r[i]);
          `else//}{
            sirv_gnrl_dffl #(`E203_XLEN) rf_dffl (rf_wen[i], wbck_dest_dat, rf_r[i], clk);
          `endif//}
        end
  
      end//}
  endgenerate//}
  
  assign read_src1_dat = rf_r[read_src1_idx];
  assign read_src2_dat = rf_r[read_src2_idx];
CSR 控制与状态寄存器

RISC-V架构中定义了一些控制和状态寄存器,用于配置或记录一些运行的状态。 CSR是处理器核内部的寄存器,使用其自己的地址编码空间,与存储器寻址的地址区间完全无关系。

CSR的访问采用专用的CSR读写指令。

蜂鸟E203处理器的EXU中的CSR模块主要用于实现蜂鸟E203处理器所支持的CSDR功能。

在ALU模块中的CSR读写控制模块会产生CSR读写控制信号,而CSR模块则严格按照RISC-V架构的定义实现各个CSR的具体功能。

指令发射、派遣

即表示指令经过译码且从寄存器组中读取操作数之后被派发到不同的运算单元并执行的过程。 派遣功能由派遣模块和ALU联合完成。

派遣机制:

其所有的指令都派遣给ALU,并且通过ALU与交付模块接口进行交付。

如果指令是长指令。则需要ALU进一步将其发送至相应的长指令运算单元。属于长指令的load/store指令便通过ALU的AGU模块(地址生成单元),进一步发送至LSU并执行。

由于所有指令都需要通过ALU,因此实际派遣功能发生在ALU内部,蜂鸟E203在进行译码时,已经根据执行指令的运算单元进行了分组,并且译码出了其相应的指示信号。 所以可以根据其指示信号将指令派遣给相应的运算单元。

流水线冲突、长指令和OITF

资源冲突:

资源冲突通常发生在指令派遣给不同的执行单元并执行的过程中。 例如将指令派遣给除法单元并进行运算,但是除法单元需要数十个周期才能完成此指令。 后续的除法指令如果也需要派遣给除法单元并执行,便需要等待前一个指令并完成操作。并将除法单元释放出来。 蜂鸟E203的接口均采用vaild-ready握手信号,因此一旦某个模块当前不能使用,它就会使ready信号变为低电平。从而无法完成握手。

数据冲突:

对于数据相关性引起的数据冲突

首先将所需执行的指令分为两类:

单周期执行的指令,单周期执行的指令在流水线的第二级便完成了交付,同时将结果写回了通用寄存器组。

多周期执行的指令,这种指令通常需要多个周期才能完成执行并写回。称为后交付长流水线指令。简称为长指令。

多周期执行的指令的写回操作要在多大个周期后才能完成,而此指令的交付操作已经在流水线第二级完成,因此写回和交付在不同周期内完成,写回在交付完成后,后交付写回。

蜂鸟E203是简单的按顺序 发射派遣的微架构。在每条指令被派遣时,需要检查它是否和之前派遣,执行,尚未写回的指令存在数据相关性。

WAR RAW WAW

WAR: 因为是按顺序派遣,按顺序写回的。因此在派遣指令时,就已经从通用寄存器组中读取了源操作数。后续执行的写回通用寄存器组操作 不可能发生在前序执行的读操作数之前。 不存在WAR数据冲突。

WAW:由于存在长指令所以。

RAW 由于长指令所以。

综上正在派遣的指令只可能与尚未执行完毕的长指令之间产生RAW WAW相关性。

为了检测出长指令的RAW和WAW 相关性,使用了一个滞外指令追踪FIFO模块。

OITF本质是一个FIFO缓存。

在流水线的派遣点,在派遣每条指令时发现了数据相关性,则会将流水线的派遣点阻塞,直到长指令执行完毕,并解除相关性才会继续进行派遣。
对于数据相关性造成的冲突,只采取了阻塞流水线的方法,并没有将长指令的结果直接快速旁路给后续待派遣指令。