简单BPU模块的主要功能是根据简单译码模块的结果来进行预测,预测下一个指令的PC,E203实现了一个简单的静态分支预测功能,即对于条件分支跳转指令,如果是向后跳转,则预测为跳,向前跳转预测为不跳。详细的预测逻辑如下:
对于无条件跳转指令jar,总是预测为跳,跳转的PC是当前的PC与偏置值offset之和。
对于无条件跳转连接指令jalr,需要判断源操作数1的寄存器索引,如果为x0,则总是判断为跳转;如果为x1,则需要检查x1与当前正在执行的指令是否有RAW数据依赖性;如果为xn(不是x1以及x0),除了要检查数据依赖性之外还需要检查寄存器的读端口是否被占用(因为E203对xn的读取需要使用一个寄存器读端口)。
判断是否有RAW数据依赖性是通过判断下面两个条件来进行的:
1.判断OITF是否为空,如果OTIF不为空,证明有长指令正在执行,这条长指令可能会写入当前指令的源操作数1寄存器,E203对于这种情况保守地预测为不跳。
2.判断IR寄存器中的指令的目的寄存器是否是本条跳转指令的源操作数1寄存器。如果是,则有RAW数据依赖性。
如果存在数据依赖性,IFU会挂起直到数据依赖性被消除。
/*
Copyright 2018-2020 Nuclei System Technology, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
//=====================================================================
// Designer : Bob Hu
//
// Description:
// The Lite-BPU module to handle very simple branch predication at IFU
//
// ====================================================================
`include "e203_defines.v"
module e203_ifu_litebpu(
// Current PC
input [`E203_PC_SIZE-1:0] pc,
// The mini-decoded info
input dec_jal, //是否属于jal指令
input dec_jalr, //是否属于jalr指令
input dec_bxx, //是否属于bxx指令
input [`E203_XLEN-1:0] dec_bjp_imm, //用立即数表示的偏移量
input [`E203_RFIDX_WIDTH-1:0] dec_jalr_rs1idx,
// The IR index and OITF status to be used for checking dependency
input oitf_empty, //指示OITF是否为空
input ir_empty, //指示IR寄存器是否为空
input ir_rs1en, //指示IR寄存器中的指令的源操作数1是否使能
input jalr_rs1idx_cam_irrdidx, //
// The add op to next-pc adder
output bpu_wait,
output prdt_taken, //分支预测方向
output [`E203_PC_SIZE-1:0] prdt_pc_add_op1, //生成PC的加法运算的操作数1
output [`E203_PC_SIZE-1:0] prdt_pc_add_op2, //生成PC的加法运算的操作数2
input dec_i_valid,
// The RS1 to read regfile
output bpu2rf_rs1_ena,
input ir_valid_clr,
input [`E203_XLEN-1:0] rf2bpu_x1,
input [`E203_XLEN-1:0] rf2bpu_rs1,
input clk,
input rst_n
);
// BPU of E201 utilize very simple static branch prediction logics
// * JAL: The target address of JAL is calculated based on current PC value
// and offset, and JAL is unconditionally always jump
// * JALR with rs1 == x0: The target address of JALR is calculated based on
// x0+offset, and JALR is unconditionally always jump
// * JALR with rs1 = x1: The x1 register value is directly wired from regfile
// when the x1 have no dependency with ongoing instructions by checking
// two conditions:
// ** (1) The OTIF in EXU must be empty OITF:查询是否有长指令正在执行
// ** (2) The instruction in IR have no x1 as destination register
// * If there is dependency, then hold up IFU until the dependency is cleared
// * JALR with rs1 != x0 or x1: The target address of JALR need to be resolved
// at EXU stage, hence have to be forced halted, wait the EXU to be
// empty and then read the regfile to grab the value of xN.
// This will exert 1 cycle performance lost for JALR instruction
// * Bxxx: Conditional branch is always predicted as taken if it is backward
// jump, and not-taken if it is forward jump. The target address of JAL
// is calculated based on current PC value and offset
// The JAL and JALR is always jump, bxxx backward is predicted as taken
assign prdt_taken = (dec_jal | dec_jalr | (dec_bxx & dec_bjp_imm[`E203_XLEN-1]));
// The JALR with rs1 == x1 have dependency or xN have dependency
//判断jalr指令的源操作数是否是x0或者x1、xn
wire dec_jalr_rs1x0 = (dec_jalr_rs1idx == `E203_RFIDX_WIDTH'd0);
wire dec_jalr_rs1x1 = (dec_jalr_rs1idx == `E203_RFIDX_WIDTH'd1);
wire dec_jalr_rs1xn = (~dec_jalr_rs1x0) & (~dec_jalr_rs1x1);
//如果RS1是x1,则判断OITF是否为空以及IR中是否有指令使用X1作为目标寄存器
wire jalr_rs1x1_dep = dec_i_valid & dec_jalr & dec_jalr_rs1x1 & ((~oitf_empty) | (jalr_rs1idx_cam_irrdidx));
//如果RS1是xn,判断OITF是否为空,IR是否为空
wire jalr_rs1xn_dep = dec_i_valid & dec_jalr & dec_jalr_rs1xn & ((~oitf_empty) | (~ir_empty));
// If only depend to IR stage (OITF is empty), then if IR is under clearing, or
// it does not use RS1 index, then we can also treat it as non-dependency
//如果IR不为空的话,判断IR中的指令RS1是否在被使用
wire jalr_rs1xn_dep_ir_clr = (jalr_rs1xn_dep & oitf_empty & (~ir_empty)) & (ir_valid_clr | (~ir_rs1en));
wire rs1xn_rdrf_r;
//判断第一个读端口是否空闲,对xn的读取是否有资源冲突
wire rs1xn_rdrf_set = (~rs1xn_rdrf_r) & dec_i_valid & dec_jalr & dec_jalr_rs1xn & ((~jalr_rs1xn_dep) | jalr_rs1xn_dep_ir_clr);
wire rs1xn_rdrf_clr = rs1xn_rdrf_r;
wire rs1xn_rdrf_ena = rs1xn_rdrf_set | rs1xn_rdrf_clr;
wire rs1xn_rdrf_nxt = rs1xn_rdrf_set | (~rs1xn_rdrf_clr);
//使用DFF模块例化生成寄存器
//使用标准的DFF模块例化的好处包括以下内容:
//便于全局替换寄存器类型;
//便于在寄存器中全局插入延迟;
//明确的load-enable使能信号(如下例的flg_ena)方便综合工具自动插入寄存器级别的门控时钟以降低动态功耗;
//便于规避Verilog语法if-else不能传播不定态的问题。
sirv_gnrl_dfflr #(1) rs1xn_rdrf_dfflrs(rs1xn_rdrf_ena, rs1xn_rdrf_nxt, rs1xn_rdrf_r, clk, rst_n);
assign bpu2rf_rs1_ena = rs1xn_rdrf_set;
//不满足消除数据依赖性,则置位bpu_wait
assign bpu_wait = jalr_rs1x1_dep | jalr_rs1xn_dep | rs1xn_rdrf_set;
//下一条PC地址的操作数1
assign prdt_pc_add_op1 = (dec_bxx | dec_jal) ? pc[`E203_PC_SIZE-1:0]
: (dec_jalr & dec_jalr_rs1x0) ? `E203_PC_SIZE'b0
: (dec_jalr & dec_jalr_rs1x1) ? rf2bpu_x1[`E203_PC_SIZE-1:0]
: rf2bpu_rs1[`E203_PC_SIZE-1:0];
//下一条PC地址的操作数2
assign prdt_pc_add_op2 = dec_bjp_imm[`E203_PC_SIZE-1:0];
endmodule