ariane/cva6代码阅读--frontened

ariane代码阅读--frontened

    • instr_scan.sv
    • btb.sv和bht.sv
    • ras.sv

instr_scan.sv

is_rvc信号判断指令是否为riscv的压缩指令
rvc开头的均为对压缩指令的scan
rvi开头的与之相反

rvi_jump_o
判断指令是否为jal类
rvi_jalr_o
判断指令是否为jalr类
rvi_branch_o
判断指令是否为B类
rvi_call_o
若指令为jal或者jalr指令且rd为x1或x5则为真
rvi_return_o
若指令为jalr指令且rs1是x1或x5且rd不是x1则为真

btb.sv和bht.sv

BHT——Branch History Table,顾名思义,这是记录分支历史信息的表格,用于判定一条分支指令是否token。这个表格的索引是指令PC值,一般就用指令的后12位作为BHT表格的索引。在BHT中用1bit位记录分支是否跳转不够准确,一般就用2bit位记录分支是否跳转:例如11和10表示这条分支会跳转;01和00表示分支不会跳转。这个2bit计数器叫做饱和计数器。

BTB——branch target buffer用于记录一条分支指令的跳转地址,BTB一般很小,就32项或者64项。由于这个BTB容量小,并且其用于是记录分支指令的跳转地址,因此,如果这条指令不跳转,即其下一条指令就是PC+4,则不会在BTB中记录的。

基于BTB和BHT的分支预测:
1)在取指阶段利用PC寻址BTB,如果命中,则说明这是一条跳转指令,利用从BTB中获取到的地址去取icache;
2)由于BTB中保存的内容不够多,因此BHT的准确率更高,这个时候索引BHT表格,如果发现BHT也跳转,则说明这条指令预测是跳转的;如果BHT不跳转,则说明不跳转,这个时候就取消BTB中的指令地址,重新PC+4去取icache。

btb信号与变量解释:
INSTR_PER_FETCH:每次最多可以取多少条指令,由于取指宽度为32位,且ariane支持压缩指令(16位),因此最多可以取两条指令
NR_ROWS*INSTR_PER_FETCH就是btb的规模
btb_prediction_o:btb.sv的唯一输出信号,其结构如下(一位有效位和预测目标地址):

typedef struct packed {
        logic                   valid;
        logic [riscv::VLEN-1:0] target_address;
    } btb_prediction_t;

btb用变量index作为匹配指令与预测的目标地址的索引,位数等于log2(NR_ROWS),从指令尾部ROW_ADDR_BITS+OFFSET处开始截取

assign index=vpc_i[PREDICTION_BITS-1:ROW_ADDR_BITS+OFFSET];

如果发生一次不命中,则用信号btb_update_i更新btb,valid位置1,预测的目标地址改为btb_update_i.target_address,该数据结构如下:

typedef struct packed {
        logic                   valid;
        logic [riscv::VLEN-1:0] pc;             // update at PC
        logic [riscv::VLEN-1:0] target_address;
    } btb_update_t;

bht信号与变量解释:
bht_prediction_o是唯一的输出信号,包含一位valid,一位taken。若saturation_counter状态为为10或11时,taken为1,表示分支会跳转。

typedef struct packed {
        logic       valid;
        logic       taken;
    } bht_prediction_t;

bht的索引index同btb。
以下为2bit动态预测器工作原理:

  • 当处于处于00状态时候,预测顺序分支
    预测成功,仍处于00状态
    预测失败,则调整为01状态
  • 当处于01状态时,继续预测顺序分支
    预测成功,则调整为00状态
    预测失败,则调整为10状态
  • 当处于10状态时,预测其他分支
    预测成功,则调整为11状态
    预测失败,则调整为01状态
  • 当处于11状态时,预测其他分支
    预测成功,仍处于11状态
    预测失败,则回退到10状态
    按照以上工作原理,用bht_update_i来更新bht,其结构如下:
typedef struct packed {
        logic                   valid;
        logic [riscv::VLEN-1:0] pc;          // update at PC
        logic                   taken;
    } bht_update_t;

taken表示分支实际上是否跳转。

ras.sv

ras是返回地址栈,用于为jal指令提供返回地址。
data_o是唯一的输出变量,包含有效位valid和返回地址,其结构如下:

typedef struct packed {//连续的地址空间,需要packed
        logic                   valid;
        logic [riscv::VLEN-1:0] ra;
    } ras_t;

输出信号data_o等于stack_q[0],也就是返回地址栈的栈顶。
当输入信号push_i为1时,则将栈顶置为data_i,valid位置1,其余位顺序后移;
当输入信号pop_i为1时,则将栈底置为0,valid位置0,其余位顺序前移;
当同时pop,push时,认为先pop再push,只需要将栈顶置为data_i,valid位置1。

根据以上几个文件的代码,可以看出,aiane一般同时定义两个结构xx_d和xx_q,比如stack_q、stack_d,先用信号更新d,当时钟沿到来时,将d赋值给q。

always_ff @(posedge clk_i or negedge rst_ni) begin
        if (~rst_ni) begin
            stack_q <= '0;
        end else begin
            stack_q <= stack_d;
        end
    end

你可能感兴趣的:(ariane/cva6,cpu,risc-v)