哈工大计算机体系结构课程实验——分支预测 实验分享

这个实验让人有些出乎意料,不是因为太难,而是因为太简单了,代码量很少,构思也很简单,看看课本对应内容就可以上手了,实现方法有很多,我使用的是最简单的BPB+BTB,仅供参考。


总体构思

实验要求

实验要求放在这里,说的很清晰,需要我们实现的只有一个模块branch_predictor,需要根据它给出的接口实现分支预测功能,其他的不需要管。

实验报告提出了几种实现方法,最简单也是最容易实现的还是BTB+BPB,课本对相关技术介绍的很明白了。

BPB

总体来说,就是定义一个BTB表,里面存放着一些指令信息,比如转移指令的PC值、转移指令目的PC值等等,根据实验报告,可以将其设置为PC[7:2]位寻址,也就是类似直接映射模式,每条指令只能存储在特定的位置,在后续查询比对的时候也只需要和这一个位置的数据进行对比,非常方便。

另外BPB可以看看上面的图,跳转与否根据BPB的值。

实现方法

我并没有区分BTB与BPB两个寄存器数组,而是只定义了一个寄存器数组,与一条指令相关的所有信息全部存储在里面。

初始时,整个BTB表是空的,我们的分支预测此时也不可以工作,所以我们利用模块给出的upd返回接口对我们的BTB表进行填充,当我们返回来的指令是跳转指令的时候,我们就把指令PC以及目的PC放进BTB表。

后续就可以根据BTB表的内容进行预测了。

比较简单,代码我也做了注释,很容易看懂。

实现代码

`timescale 1ns / 1ps



module branch_predictor(
    input           clk,        //时钟信号,必须与CPU保持一致
    input           resetn,     //低有效复位信号,必须与CPU保持一致

    //供CPU第一级流水段使用的接口:
    input[31:0]     old_PC,     //上一个指令地址

    input           predict_en,     //这周期是否需要更新PC(进行分支预测)

    output[31:0]    new_PC,     //预测出的下一个指令地址

    output          predict_jump,       //是否被预测为执行转移的转移指令

    //分支预测器更新接口:
    //更新使能
    input           upd_en,
    //转移指令地址
    input[31:0]     upd_addr,
    //是否为转移指令
    input           upd_jumpinst,
    //若为转移指令,则是否转移
    input           upd_jump,
    //是否预测失败
    input           upd_predfail,
    //转移指令本身的目标地址(无论是否转移)
    input[31:0]     upd_target
);

    reg [65:0]BTB[63:0];    //创建一个根据PC[7:2]寻址的BTB表项,相当于直接映射后续寻找只需要对比一个项就可以
    integer i;
    
    initial begin
    for (i = 0; i < 64; i = i + 1) begin
      BTB[i] = 66'h0;            //必须全部初始化,不能存在XXXXX,否则最后assign赋值会赋值失败
    end
    for(i=0;i<64;i=i+1) begin
      BTB[i][1:0]=2'b11;        //将BPB两位初始化为11
    end
    end
    /*为BTB赋值,BTB初始时没有任何信息,之后随着upd反馈来添加转移指令以及转移目标*/
    always @(posedge clk) begin
    if(upd_en==1'b1&&upd_jumpinst==1'b1&&BTB[upd_addr[7:2]][33:2]==32'h0) begin  //只有在指定位置全是0才能进行赋值,不是0代表已经存在指令
    BTB[upd_addr[7:2]][33:2]=upd_addr;    //存储转移PC
    BTB[upd_addr[7:2]][65:34]=upd_target;  //存储目的PC
    end
    end
    
    always @(upd_en) begin          //根据转移结果调整两位BPB
    if(upd_jumpinst==1'b1&&upd_en==1'b1&&upd_addr==BTB[upd_addr[7:2]]) begin  //更新条件
    
    if(upd_predfail==1'b1) begin   //预测失败而更新
    
    if(BTB[upd_addr[7:2]][1:0]==2'b00) begin
    BTB[upd_addr[7:2]][1:0]<=2'b00;
    end
    else if(BTB[upd_addr[7:2]][1:0]==2'b01) begin
    BTB[upd_addr[7:2]][1:0]<=2'b00;
    end
    else if(BTB[upd_addr[7:2]][1:0]==2'b10) begin
    BTB[upd_addr[7:2]][1:0]<=2'b00;
    end
    else if(BTB[upd_addr[7:2]][1:0]==2'b11) begin
    BTB[upd_addr[7:2]][1:0]<=2'b10;
    end
    
    end
    
    else if(upd_predfail==1'b0) begin   //预测成功而更新
    
    if(BTB[upd_addr[7:2]][1:0]==2'b00) begin
    BTB[upd_addr[7:2]][1:0]<=2'b01;
    end
    else if(BTB[upd_addr[7:2]][1:0]==2'b01) begin
    BTB[upd_addr[7:2]][1:0]<=2'b11;
    end
    else if(BTB[upd_addr[7:2]][1:0]==2'b10) begin
    BTB[upd_addr[7:2]][1:0]<=2'b11;
    end
    else if(BTB[upd_addr[7:2]][1:0]==2'b11) begin
    BTB[upd_addr[7:2]][1:0]<=2'b11;
    end
    
    end
    
    end
    end
    //预测开始
    assign new_PC=(old_PC!=32'b0&&old_PC==BTB[old_PC[7:2]][33:2]&&predict_en==1'b1&&(BTB[old_PC[7:2]][1:0]==2'b10||BTB[old_PC[7:2]][1:0]==2'b11))? BTB[old_PC[7:2]][65:34]:old_PC+4;
    assign predict_jump=(old_PC!=32'b0&&old_PC==BTB[old_PC[7:2]][33:2]&&predict_en==1'b1&&(BTB[old_PC[7:2]][1:0]==2'b10||BTB[old_PC[7:2]][1:0]==2'b11))? 1'b1:1'b0;
    

endmodule

结尾

这次实验没什么挑战性,下周估计更新lab3cache

理性借鉴,欢迎交流!

最后修改:2023 年 10 月 20 日
如果觉得我的文章对你有用,请随意赞赏