这个文章记录了我学习RISC-V蜂鸟E203处理器的学习历程
这是我正式阅读代码学习的第3个源代码文件
针对代码的学习,我结合自己的理解对每个module的接口,以及内部关键信号做了详细的注释说明
原创不易,请保护版权,转载联系作者,并请注明出处,标出原始链接,谢谢~~~
e203_ifu_litebpu.v
/*
Copyright 2017 Silicon Integrated Microelectronics, 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.
*/
//=====================================================================
//-- _______ ___
//-- ( ____/ /__/
//-- \ \ __
//-- ____\ \ / /
//-- /_______\ /_/ MICROELECTRONICS
//--
//=====================================================================
// Designer : Bob Hu
//
// Description:
// The Lite-BPU module to handle very simple branch predication at IFU
//
// ====================================================================
// +++++++++++++++++++++++++++ 我的阅读笔记:
// 这个模块用于对指令微译码的结果进行进一步处理,用来预测是否需要跳转
// 分支预测采用最简单的静态分支预测,对于BXX等带条件跳转向后跳转为需要跳,向前跳转为不需要跳
// 对于jal和jalr等无需跳转的,不用进行预测,一定是跳转的,直接去跳转的地址取指令就行
// 对于PC和IR,IR是已经成功取到的指令,也就是EXU要执行的指令,PC是该IR中的指令
// 对应的地址
// +++++++++++++++++++++++++++++++++++++++++
`include "e203_defines.v"
module e203_ifu_litebpu(
// Current PC
input [`E203_PC_SIZE-1:0] pc, //被译码的指令(也就是当前EXU正在执行的指令)对应的pc指针值
// The mini-decoded info
input dec_jal, //从mini-decode模块送过来的,具体的指令译码结果信息,这个信号指示是直接跳转
input dec_jalr, // 这个信号指示是需要从寄存器中算地址的间接跳转
input dec_bxx, // 这个信号指示是需要根据条件判断结果才能进行跳转的条件跳转指令
input [`E203_XLEN-1:0] dec_bjp_imm, //跳转指令的立即数
input [`E203_RFIDX_WIDTH-1:0] dec_jalr_rs1idx, //跳转指令的rs1源操作数的cpu内部地址
// The IR index and OITF status to be used for checking dependency
input oitf_empty, //oitf模块为空的标志,用于判断跳转指令跟之前的指令的相关性
input ir_empty, //当前IR寄存器为空
input ir_rs1en, //这个信号源自指令预取模块入口的dec2ifu_rs1en模块,表示的是当前正在执行的指令,其rs1操作数是否有效
input jalr_rs1idx_cam_irrdidx, //jalr指令的rs1的index 和 IR寄存器中rd的index比较,如果相等则为1
//这里是针对jalr指令如果其源操作数rs1和目的操作数rd是同一个寄存器,这样会导致分支预测这里,这条指令本身具有相关性???
// The add op to next-pc adder
output bpu_wait, // branch predict unit
output prdt_taken, // 表示分支预测单元的预测结果是否进行跳转
output [`E203_PC_SIZE-1:0] prdt_pc_add_op1, //用于计算PC值的操作数
output [`E203_PC_SIZE-1:0] prdt_pc_add_op2,
input dec_i_valid, // 当前正在执行的指令经解码后发现指令是有效的
// The RS1 to read regfile
output bpu2rf_rs1_ena,// 生成用第一个读端口的使能信号,该信号将加载和IR寄存器位于同一级的rs1索引(index)寄存器,从而读取REGFile
input ir_valid_clr, // 表示当前ir中的内容被clear(// The ir valid is cleared when it is accepted by EXU stage *or* the flush happening )
input [`E203_XLEN-1:0] rf2bpu_x1, // 从regfile直接拉过来的同IR处于同一级的x1的内容
input [`E203_XLEN-1:0] rf2bpu_rs1, // 从regfile的读端口取出来的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 // JAL指令是无条件的不预测为总是需要跳转
// * JALR with rs1 == x0: The target address of JALR is calculated based on
// x0+offset, and JALR is unconditionally always jump // JALR指令的源操作数为x0的时候,由于x0固定为0,所以JALR等同于JAL
// * 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: //当JALR指令的源操作数为x1寄存器时,如果当前满足下面这两个条件,那么不论当前执行的指令是什么,都采用直接从reg file里接过来的x1
// ** (1) The OTIF in EXU must be empty
// ** (2) The instruction in IR have no x1 as destination register // 这里我暂时理解为如果jalr的rd为x1,那么jalr执行结果会更改x1,这个时候不能直接拉x1的值
// * If there is dependency, then hold up IFU until the dependency is cleared //如果存在相关性,那么需要保持IFU直到相关性解除 //因为这个时候x1的值不是立即数,而是返回的rd,具体要后面理一下时序???
// * 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])); //解码出来的指令是jal、jalr一定要跳,当为bxx类指令时
// The JALR with rs1 == x1 have dependency or xN have dependency //如果解码出来的bxx指令的立即数(带符号)符号位为1,说明是负数,向后跳转
wire dec_jalr_rs1x0 = (dec_jalr_rs1idx == `E203_RFIDX_WIDTH'd0); //bxx指令向后跳转才预测为跳
wire dec_jalr_rs1x1 = (dec_jalr_rs1idx == `E203_RFIDX_WIDTH'd1);
wire dec_jalr_rs1xn = (~dec_jalr_rs1x0) & (~dec_jalr_rs1x1);
// 指令有效,解码出来是jalr指令,jalr指令的rs1操作数是x1寄存器,且oitf非空或jarl的源操作数和目的操作数是同一个寄存器
wire jalr_rs1x1_dep = dec_i_valid & dec_jalr & dec_jalr_rs1x1 & ((~oitf_empty) | (jalr_rs1idx_cam_irrdidx)); //rs1==x1 且具有相关性
wire jalr_rs1xn_dep = dec_i_valid & dec_jalr & dec_jalr_rs1xn & ((~oitf_empty) | (~ir_empty)); //rs1==xN 且具有相关性
//
// 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
wire jalr_rs1xn_dep_ir_clr = (jalr_rs1xn_dep & oitf_empty & (~ir_empty)) & (ir_valid_clr | (~ir_rs1en)); //
wire rs1xn_rdrf_r; // 关于这一块,可以看书上的注释,比较详细;这个1bit表示rs1为xn的时候,当前处于read regfile的状态
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; //如果当前regfile的第一个读端口没有被占用,且当前指令是一个rs1为xn的jalr指令,且当前指令没有相关性,或者IR is underclearing
wire rs1xn_rdrf_ena = rs1xn_rdrf_set | rs1xn_rdrf_clr; // 如果set有效,那么使能也有效,下一拍reg值置1;如果寄存器值本身有效,那寄存器也是处于en的状态
wire rs1xn_rdrf_nxt = rs1xn_rdrf_set | (~rs1xn_rdrf_clr);
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; // 生成用第一个读端口的使能信号,该信号将加载和IR寄存器位于同一级的rs1索引(index)寄存器,从而读取REGFile
assign bpu_wait = jalr_rs1x1_dep | jalr_rs1xn_dep | rs1xn_rdrf_set; //在这三种情况下,bpu需要等待exu执行结束才能去取指令
assign prdt_pc_add_op1 = (dec_bxx | dec_jal) ? pc[`E203_PC_SIZE-1:0] //进行PC计算的操作数1,如果是不需要rs1操作数的跳转指令,那么op1为0
: (dec_jalr & dec_jalr_rs1x0) ? `E203_PC_SIZE'b0 //如果是jalr,jalr的rs1是x0的时候op1为0
: (dec_jalr & dec_jalr_rs1x1) ? rf2bpu_x1[`E203_PC_SIZE-1:0] //如果是jalr,jalr的rs1是x1,那么取regfile直接拉过来的x1的内容
: rf2bpu_rs1[`E203_PC_SIZE-1:0]; //如果是jalr,且jalr的rs1是xn,那么取regfile的读端口出来的值
assign prdt_pc_add_op2 = dec_bjp_imm[`E203_PC_SIZE-1:0]; //进行PC计算所需要的立即数
endmodule