一、这是一份BTB模块代码
`timescale 1ns / 1ps
`include "defines.v"
module btb #(
parameter ENTRY_NUM =128,
parameter INDEX_W =7
)(
input wire clk ,
input wire rst ,
//from pc_reg
input wire [`AW-1:0] lookup_pc_i ,
output wire hit_o ,
output wire [`AW-1:0] target_o ,
output wire is_uncond_o , // jal jalr unconditional jump
output wire is_return_o ,
input wire update_valid_i ,
input wire [`AW-1:0] update_pc_i ,
input wire [`AW-1:0] update_target_i ,
input wire update_is_uncond_i,
input wire update_is_return_i
);
localparam TAG_W = `AW-INDEX_W - 2;
reg valid_r [0:ENTRY_NUM-1] ;
reg [TAG_W-1:0] tag_r [0:ENTRY_NUM-1] ;
reg [`AW-1:0] target_r [0:ENTRY_NUM-1] ;
reg is_uncond_r [0:ENTRY_NUM-1] ;
reg is_return_r [0:ENTRY_NUM-1] ;
wire [INDEX_W-1:0] lookup_index = lookup_pc_i[INDEX_W+1:2];
wire [TAG_W-1:0] lookup_tag = lookup_pc_i[`AW-1:INDEX_W+2];
wire [INDEX_W-1:0] update_index = update_pc_i[INDEX_W+1:2];
wire [TAG_W-1:0] update_tag = update_pc_i[`AW-1:INDEX_W+2];
wire hit = valid_r[lookup_index]&& lookup_tag == tag_r[lookup_index] ;
assign hit_o = hit;
assign is_uncond_o = is_uncond_r[lookup_index];
assign target_o = target_r[lookup_index];
assign is_return_o = is_return_r[lookup_index];
integer i;
always @(posedge clk) begin
if (rst == `RstEnable) begin
for (i = 0; i < ENTRY_NUM; i = i + 1) begin
valid_r[i] <= 1'b0;
tag_r[i] <= {TAG_W{1'b0}};
target_r[i] <= `ZeroWord;
is_uncond_r[i] <= 1'b0;
is_return_r[i] <= 1'b0;
end
end else if (update_valid_i) begin
valid_r[update_index] <= 1'b1;
tag_r[update_index] <= update_tag;
target_r[update_index] <= update_target_i;
is_uncond_r[update_index] <= update_is_uncond_i;
is_return_r[update_index] <= update_is_return_i;
end
end
endmodule
二、BTB结构
| Index | valid | tag[22:0] | target[31:0] | is_uncond | is_return |
|---|---|---|---|---|---|
| 0 | 1 | 0x000001 | 0x0000_0800 | 0 | 0 |
| 1 | 1 | 0x000002 | 0x0000_1200 | 1 | 0 |
| 2 | 0 | 0x000000 | 0x0000_0000 | 0 | 0 |
| 3 | 1 | 0x000005 | 0x0000_1A00 | 0 | 1 |
| 4 | 0 | 0x000000 | 0x0000_0000 | 0 | 0 |
| 5 | 1 | 0x00000A | 0x0000_2C00 | 1 | 0 |
| 6 | 0 | 0x000000 | 0x0000_0000 | 0 | 0 |
| 7 | 0 | 0x000000 | 0x0000_0000 | 0 | 0 |
| … | … | … | … | … | … |
| 123 | 0 | 0x000000 | 0x0000_0000 | 0 | 0 |
| 124 | 1 | 0x001F4A | 0x0004_D200 | 0 | 1 |
| 125 | 0 | 0x000000 | 0x0000_0000 | 0 | 0 |
| 126 | 0 | 0x000000 | 0x0000_0000 | 0 | 0 |
| 127 | 1 | 0x002B1C | 0x0008_1000 | 1 | 0 |
对应的代码:
reg valid_r [0:ENTRY_NUM-1] ;
reg [TAG_W-1:0] tag_r [0:ENTRY_NUM-1] ;
reg [`AW-1:0] target_r [0:ENTRY_NUM-1] ;
reg is_uncond_r [0:ENTRY_NUM-1] ;
reg is_return_r [0:ENTRY_NUM-1] ;
要注意:表格中的index是逻辑抽象出来的,并没有物理结构,只是为了展示BTB有多少项
三、问题是valid除了在rst置为0之外没有别的把他置为0,那能不能去掉呢?节省资源
四、结论是不能。虽然在运行起来之后valid就一直是1,但是不能去掉。因为在刚开始的时候要用到。
五、去掉 valid 会发生什么
复位后数组的初始状态是:valid=0, tag=0, target=0x0
如果没有 valid 参与判定,命中条件就只剩:
hit = (lookup_tag == tag_r[lookup_index]); // 未分配的项 tag = 0
于是所有 tag 为 0 的 PC 都会假命中。
tag = pc[31:9](TAG_W = 32 - 7 - 2 = 23),也就是说 PC < 0x200 的任何分支,查到未初始化的项都会"命中",然后拿到 target = 0x0——取指直接跳回地址 0。而复位向量就在低地址,等于启动阶段必然踩雷。
六、具体例子说明
假设条件
| 参数 | 值 |
|---|---|
| 复位向量地址 | 0x0000_0000 |
| BTB 复位后状态 | 所有 entry: tag=0, target=0x0000_0000 |
| 分支指令地址 | PC = 0x0000_0100(在 .text 段早期,tag 计算后为 0) |
执行流程
| 时钟周期 | 事件 | 结果 |
|---|---|---|
| T0 | 取指 PC = 0x100,查 BTB | lookup_index = 0x100[8:2] = 0x20<br>lookup_tag = 0x100[31:9] = 0 |
| T1 | 比较:lookup_tag(0) == tag_r[0x20](0) | 假命中!(没有 valid 过滤) |
| T2 | target_o = 0x0000_0000 | 下一条取指地址变成复位向量 |
| T3 | CPU 跳回地址 0 | 无限循环在复位向量附近,程序跑飞 |
> ⚠️ 这就是"假命中"的灾难:一个从未被训练过的分支,因为 tag 恰好为 0,被当成已训练的分支,跳转到一个完全错误的目标地址。

4352

被折叠的 条评论
为什么被折叠?



