重构:按功能拆分流水线模块、修复 BLT 指令、注释中文化
1) 模块拆分(便于单独仿真调试)
把原来单文件的三模块结构按功能拆成 10 个独立文件,顶层只保留五级流水
寄存器、子模块例化、PC/IF-ID/ID-EX 控制优先级与对外输出:
cpu21_riscv_alu.v 组合 ALU(result2 用于 MUL 高位/余数)
cpu21_bpb_8.v 8 项全相联分支目标缓冲
cpu21_riscv_decoder.v ID 级组合译码器
cpu21_riscv_regfile.v 寄存器堆(写优先旁路)
cpu21_riscv_forward_unit.v EX 级前递网络
cpu21_riscv_store_unit.v EX 级存储数据对齐(sw/sb)
cpu21_riscv_branch_unit.v EX 级控制流裁决
cpu21_riscv_hazard_unit.v load-use 冒险与停顿/冲刷判定
cpu21_riscv_irq_ctrl.v 中断优先级、ustatus/uepc、嵌套返回栈
cpu21_riscv_perf_counters.v 性能计数器
已把上述文件加入 Vivado 工程 sources_1;端口、时序与行为经 xsim 对同一
ROM 逐周期回归验证,统计量完全一致。
2) 修复 BLT 指令(真值表第 28 行:opcode IR[6:2]=0x18、funct3=100、ALU_OP=SLT)
此前 funct3=100 未译码,blt 被当作空指令执行,于是
`blt s1,zero,loop` 的循环只执行一次就顺序落到退出代码并停机
(现象:只输出第一个值后就不再运行)。
- cpu21_riscv_decoder.v:新增 blt_o,OP_BRANCH 接受 funct3=100,
并按真值表给出 ALU_SLT(beq/bne 仍为 SUB,bltu 仍为 SLTU)
- cpu21_riscv_branch_unit.v:新增 blt_i,用有符号比较
$signed(src1) < $signed(src2) 裁决
- cpu21_riscv_redirect_int_bpb.v:新增 idex_blt_q 流水寄存器,
IF 级 f_is_branch 纳入 funct3=100,使 blt 也参与 BPB 预测
验证:blt 小程序正确输出 -3/-2/-1 后停机(cond_taken=2);
benchmark 第 [4] 段完整输出 fffffff1..ffffffff;
中断测试程序的中断入口与停留周期与改动前一致。
3) 注释中文化
- cpu21_riscv_redirect_int_bpb.v 及全部新增子模块使用中文注释;
- testbench/tb_cpu21_riscv_redirect_int_bpb.v 与新增的 tb_no_intr.v
注释全部译为中文。
- testbench/tb_no_intr.v:与中断测试平台同框架但不注入 irq 脉冲的对照
测试平台(修复其 $dumpvars 引用了不存在的模块名,并改用 tb_no_intr.vcd
避免与另一个 TB 的波形文件互相覆盖)。
4) 其他
- testbench/tb_cpu21_riscv_redirect_int_bpb.v 的默认 ROM_FILE 改为
cpu21_riscv_redirect_int_bpb_rom.hex(中断测试程序);
- cpu21_riscv_redirect_int_bpb说明.md 增补"文件与模块划分"章节与 BLT 说明;
- 真值表.txt 移动到 testbench/programs/ 下。
This commit is contained in:
+117
@@ -0,0 +1,117 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// 八表项全相联分支目标缓冲(BPB/BTB)。
|
||||||
|
//
|
||||||
|
// tag = PC[11:2],与 Logisim 版 BPB 中的十位 tag 拆分保持一致。
|
||||||
|
// count 为两位饱和计数器:00/01 表示不跳转,10/11 表示跳转。
|
||||||
|
// age 用于近似 LRU 替换:被选中的表项清零,其余表项自增。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_bpb_8 (
|
||||||
|
input wire clk,
|
||||||
|
input wire reset,
|
||||||
|
input wire [31:0] predict_pc,
|
||||||
|
input wire predict_enable,
|
||||||
|
output reg predict_hit,
|
||||||
|
output reg predict_taken,
|
||||||
|
output reg [31:0] predict_target,
|
||||||
|
input wire update_enable,
|
||||||
|
input wire update_controlflow,
|
||||||
|
input wire [31:0] update_pc,
|
||||||
|
input wire [31:0] update_target,
|
||||||
|
input wire update_taken
|
||||||
|
);
|
||||||
|
reg valid [0:7];
|
||||||
|
reg [ 9:0] tag [0:7];
|
||||||
|
reg [31:0] target [0:7];
|
||||||
|
reg [ 1:0] count [0:7];
|
||||||
|
reg [ 2:0] age [0:7];
|
||||||
|
|
||||||
|
reg update_found;
|
||||||
|
reg [ 2:0] update_index;
|
||||||
|
reg replace_found;
|
||||||
|
reg [ 2:0] replace_index;
|
||||||
|
reg [ 2:0] max_age;
|
||||||
|
integer p;
|
||||||
|
integer u;
|
||||||
|
integer k;
|
||||||
|
integer selected_index;
|
||||||
|
|
||||||
|
// 预测命中查找:按 tag 全相联匹配,命中则给出目标地址与方向预测。
|
||||||
|
always @* begin
|
||||||
|
predict_hit = 1'b0;
|
||||||
|
predict_taken = 1'b0;
|
||||||
|
predict_target = 32'b0;
|
||||||
|
for (p = 0; p < 8; p = p + 1) begin
|
||||||
|
if (predict_enable && !predict_hit && valid[p] && (tag[p] == predict_pc[11:2])) begin
|
||||||
|
predict_hit = 1'b1;
|
||||||
|
predict_target = target[p];
|
||||||
|
predict_taken = count[p][1];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 更新时先找匹配表项;若无匹配则优先使用无效表项,否则淘汰最旧表项。
|
||||||
|
// 下方的 age 自增实现了类似 LRU 的替换策略。
|
||||||
|
always @* begin
|
||||||
|
update_found = 1'b0;
|
||||||
|
update_index = 3'd0;
|
||||||
|
replace_found = 1'b0;
|
||||||
|
replace_index = 3'd0;
|
||||||
|
max_age = 3'd0;
|
||||||
|
|
||||||
|
for (u = 0; u < 8; u = u + 1) begin
|
||||||
|
if (!update_found && valid[u] && (tag[u] == update_pc[11:2])) begin
|
||||||
|
update_found = 1'b1;
|
||||||
|
update_index = u;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for (u = 0; u < 8; u = u + 1) begin
|
||||||
|
if (!replace_found && !valid[u]) begin
|
||||||
|
replace_found = 1'b1;
|
||||||
|
replace_index = u;
|
||||||
|
end else if (!replace_found && (age[u] >= max_age)) begin
|
||||||
|
replace_index = u;
|
||||||
|
max_age = age[u];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 表项状态更新(含替换与两位饱和计数器更新)。
|
||||||
|
always @(posedge clk or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
for (k = 0; k < 8; k = k + 1) begin
|
||||||
|
valid[k] <= 1'b0;
|
||||||
|
tag[k] <= 10'b0;
|
||||||
|
target[k] <= 32'b0;
|
||||||
|
count[k] <= 2'b01;
|
||||||
|
age[k] <= 3'b0;
|
||||||
|
end
|
||||||
|
end else if (update_enable && update_controlflow) begin
|
||||||
|
selected_index = update_found ? update_index : replace_index;
|
||||||
|
|
||||||
|
valid[selected_index] <= 1'b1;
|
||||||
|
tag[selected_index] <= update_pc[11:2];
|
||||||
|
target[selected_index] <= update_target;
|
||||||
|
age[selected_index] <= 3'b0;
|
||||||
|
|
||||||
|
if (!update_found) begin
|
||||||
|
// 新表项采用较弱的初始状态,避免立刻产生错误预测。
|
||||||
|
count[selected_index] <= update_taken ? 2'b10 : 2'b01;
|
||||||
|
end else if (update_taken) begin
|
||||||
|
// 实际跳转:计数器加一(饱和到 11)。
|
||||||
|
if (count[selected_index] != 2'b11) count[selected_index] <= count[selected_index] + 2'b01;
|
||||||
|
end else begin
|
||||||
|
// 实际不跳转:计数器减一(饱和到 00)。
|
||||||
|
if (count[selected_index] != 2'b00) count[selected_index] <= count[selected_index] - 2'b01;
|
||||||
|
end
|
||||||
|
|
||||||
|
for (k = 0; k < 8; k = k + 1) begin
|
||||||
|
if (valid[k] && (k != selected_index) && (age[k] != 3'b111)) age[k] <= age[k] + 3'b001;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// ALU:纯组合运算单元。
|
||||||
|
//
|
||||||
|
// result 为主结果;result2 为第二结果,用于 MUL 的高 32 位与 DIVU 的余数。
|
||||||
|
// 除法做了除零保护:b==0 时商为 32'hffff_ffff、余数为被除数。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_riscv_alu (
|
||||||
|
input wire [ 3:0] op,
|
||||||
|
input wire [31:0] a,
|
||||||
|
input wire [31:0] b,
|
||||||
|
output reg [31:0] result,
|
||||||
|
output reg [31:0] result2
|
||||||
|
);
|
||||||
|
localparam ALU_SLL = 4'd0;
|
||||||
|
localparam ALU_SRA = 4'd1;
|
||||||
|
localparam ALU_SRL = 4'd2;
|
||||||
|
localparam ALU_MUL = 4'd3;
|
||||||
|
localparam ALU_DIVU = 4'd4;
|
||||||
|
localparam ALU_ADD = 4'd5;
|
||||||
|
localparam ALU_SUB = 4'd6;
|
||||||
|
localparam ALU_AND = 4'd7;
|
||||||
|
localparam ALU_OR = 4'd8;
|
||||||
|
localparam ALU_XOR = 4'd9;
|
||||||
|
localparam ALU_NOR = 4'd10;
|
||||||
|
localparam ALU_SLT = 4'd11;
|
||||||
|
localparam ALU_SLTU = 4'd12;
|
||||||
|
reg [63:0] mult_result;
|
||||||
|
|
||||||
|
always @* begin
|
||||||
|
mult_result = a * b;
|
||||||
|
result2 = 32'b0;
|
||||||
|
case (op)
|
||||||
|
ALU_SLL: result = a << b[4:0];
|
||||||
|
ALU_SRA: result = $signed(a) >>> b[4:0];
|
||||||
|
ALU_SRL: result = a >> b[4:0];
|
||||||
|
ALU_MUL: begin
|
||||||
|
result = mult_result[31:0];
|
||||||
|
result2 = mult_result[63:32];
|
||||||
|
end
|
||||||
|
ALU_DIVU: begin
|
||||||
|
result = (b == 32'b0) ? 32'hffff_ffff : a / b;
|
||||||
|
result2 = (b == 32'b0) ? a : a % b;
|
||||||
|
end
|
||||||
|
ALU_ADD: result = a + b;
|
||||||
|
ALU_SUB: result = a - b;
|
||||||
|
ALU_AND: result = a & b;
|
||||||
|
ALU_OR: result = a | b;
|
||||||
|
ALU_XOR: result = a ^ b;
|
||||||
|
ALU_NOR: result = ~(a | b);
|
||||||
|
ALU_SLT: result = ($signed(a) < $signed(b)) ? 32'd1 : 32'd0;
|
||||||
|
ALU_SLTU: result = (a < b) ? 32'd1 : 32'd0;
|
||||||
|
default: result = 32'b0;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// EX 级控制流裁决单元。
|
||||||
|
//
|
||||||
|
// 条件分支的实际方向与目标都在 EX 级解析;JAL/JALR 的目标也只在 EX 级
|
||||||
|
// 得到,因此它们总是需要重定向。条件分支仅在预测方向/目标与实际不符时
|
||||||
|
// 才重定向,这样预测正确时不会产生气泡。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_riscv_branch_unit (
|
||||||
|
input wire valid_i,
|
||||||
|
input wire [31:0] pc_i,
|
||||||
|
input wire [31:0] imm_i,
|
||||||
|
input wire [31:0] src1_i,
|
||||||
|
input wire [31:0] src2_i,
|
||||||
|
input wire branch_i,
|
||||||
|
input wire beq_i,
|
||||||
|
input wire bne_i,
|
||||||
|
input wire blt_i,
|
||||||
|
input wire bltu_i,
|
||||||
|
input wire jal_i,
|
||||||
|
input wire jalr_i,
|
||||||
|
// IF 级携带过来的预测信息
|
||||||
|
input wire pred_taken_i,
|
||||||
|
input wire [31:0] pred_target_i,
|
||||||
|
// 裁决结果
|
||||||
|
output wire branch_taken_o,
|
||||||
|
output wire controlflow_o,
|
||||||
|
output wire [31:0] target_o,
|
||||||
|
output wire redirect_valid_o,
|
||||||
|
output wire [31:0] redirect_pc_o
|
||||||
|
);
|
||||||
|
// 条件分支裁决:beq 相等、bne 不等、blt 有符号小于、bltu 无符号小于。
|
||||||
|
assign branch_taken_o = branch_i &&
|
||||||
|
((beq_i && (src1_i == src2_i)) ||
|
||||||
|
(bne_i && (src1_i != src2_i)) ||
|
||||||
|
(blt_i && ($signed(
|
||||||
|
src1_i
|
||||||
|
) < $signed(
|
||||||
|
src2_i
|
||||||
|
))) || (bltu_i && (src1_i < src2_i)));
|
||||||
|
|
||||||
|
// 跳转目标:jalr 为 (rs1+imm) 且最低位清零;jal 为 PC+imm。
|
||||||
|
assign target_o = jalr_i ? ((src1_i + imm_i) & 32'hffff_fffe) : (pc_i + imm_i);
|
||||||
|
|
||||||
|
// EX 级存在需要裁决的控制流指令。
|
||||||
|
assign controlflow_o = valid_i && (branch_i || jal_i || jalr_i);
|
||||||
|
|
||||||
|
// 条件分支的预测方向/目标与实际不符,或 JAL/JALR 必须重定向。
|
||||||
|
assign redirect_valid_o = controlflow_o &&
|
||||||
|
(branch_i ?
|
||||||
|
((pred_taken_i != branch_taken_o) ||
|
||||||
|
(pred_taken_i && branch_taken_o &&
|
||||||
|
(pred_target_i != target_o))) : 1'b1);
|
||||||
|
|
||||||
|
// 预测了跳转但实际不跳转时,回到顺序地址继续取指。
|
||||||
|
assign redirect_pc_o = (branch_i && !branch_taken_o) ? (pc_i + 32'd4) : target_o;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// ID 级组合译码器。
|
||||||
|
//
|
||||||
|
// 与原始 Logisim 控制器一致:操作码取标准 RISC-V 的 IR[6:2] 五位字段,
|
||||||
|
// 真值表中以十六进制形式存储该字段。
|
||||||
|
// 所有输出在无匹配时保持"无操作/不写回"的默认值。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_riscv_decoder (
|
||||||
|
input wire [31:0] ir_i,
|
||||||
|
|
||||||
|
output reg [ 4:0] rs1_idx_o,
|
||||||
|
output reg [ 4:0] rs2_idx_o,
|
||||||
|
output reg [ 4:0] rd_o,
|
||||||
|
output reg [31:0] imm_o,
|
||||||
|
output reg [ 3:0] alu_op_o,
|
||||||
|
output reg [ 2:0] wb_sel_o,
|
||||||
|
output wire [11:0] csr_addr_o,
|
||||||
|
output reg uses_rs1_o,
|
||||||
|
output reg uses_rs2_o,
|
||||||
|
output reg reg_write_o,
|
||||||
|
output reg mem_to_reg_o,
|
||||||
|
output reg mem_write_o,
|
||||||
|
output reg mem_byte_o,
|
||||||
|
output reg alu_src_o,
|
||||||
|
output reg branch_o,
|
||||||
|
output reg beq_o,
|
||||||
|
output reg bne_o,
|
||||||
|
output reg blt_o,
|
||||||
|
output reg bltu_o,
|
||||||
|
output reg jal_o,
|
||||||
|
output reg jalr_o,
|
||||||
|
output reg ecall_o,
|
||||||
|
output reg uret_o,
|
||||||
|
output reg csr_set_o,
|
||||||
|
output reg csr_clear_o,
|
||||||
|
output reg csr_write_o
|
||||||
|
);
|
||||||
|
// CPU21 自定义操作码(取自所提供的真值表)。
|
||||||
|
localparam OP_LOAD = 5'h00;
|
||||||
|
localparam OP_R = 5'h0c;
|
||||||
|
localparam OP_I = 5'h04;
|
||||||
|
localparam OP_STORE = 5'h08;
|
||||||
|
localparam OP_JALR = 5'h19;
|
||||||
|
localparam OP_BRANCH = 5'h18;
|
||||||
|
localparam OP_JAL = 5'h1b;
|
||||||
|
localparam OP_SYS = 5'h1c;
|
||||||
|
|
||||||
|
localparam ALU_SLL = 4'd0;
|
||||||
|
localparam ALU_SRA = 4'd1;
|
||||||
|
localparam ALU_SRL = 4'd2;
|
||||||
|
localparam ALU_MUL = 4'd3;
|
||||||
|
localparam ALU_DIVU = 4'd4;
|
||||||
|
localparam ALU_ADD = 4'd5;
|
||||||
|
localparam ALU_SUB = 4'd6;
|
||||||
|
localparam ALU_AND = 4'd7;
|
||||||
|
localparam ALU_OR = 4'd8;
|
||||||
|
localparam ALU_XOR = 4'd9;
|
||||||
|
localparam ALU_SLT = 4'd11;
|
||||||
|
localparam ALU_SLTU = 4'd12;
|
||||||
|
|
||||||
|
wire [4:0] opcode = ir_i[6:2];
|
||||||
|
wire [2:0] funct3 = ir_i[14:12];
|
||||||
|
wire [6:0] funct7 = ir_i[31:25];
|
||||||
|
|
||||||
|
// CSR 地址字段在译码时直接旁路输出(供 EX 级读取 CSR)。
|
||||||
|
assign csr_addr_o = ir_i[31:20];
|
||||||
|
|
||||||
|
// 默认控制信号:全部为"无操作/不写回",再由下面的 case 覆盖。
|
||||||
|
always @* begin
|
||||||
|
rs1_idx_o = ir_i[19:15];
|
||||||
|
rs2_idx_o = ir_i[24:20];
|
||||||
|
rd_o = ir_i[11:7];
|
||||||
|
imm_o = {{20{ir_i[31]}}, ir_i[31:20]};
|
||||||
|
alu_op_o = ALU_ADD;
|
||||||
|
wb_sel_o = 3'd0;
|
||||||
|
uses_rs1_o = 1'b0;
|
||||||
|
uses_rs2_o = 1'b0;
|
||||||
|
reg_write_o = 1'b0;
|
||||||
|
mem_to_reg_o = 1'b0;
|
||||||
|
mem_write_o = 1'b0;
|
||||||
|
mem_byte_o = 1'b0;
|
||||||
|
alu_src_o = 1'b0;
|
||||||
|
branch_o = 1'b0;
|
||||||
|
beq_o = 1'b0;
|
||||||
|
bne_o = 1'b0;
|
||||||
|
blt_o = 1'b0;
|
||||||
|
bltu_o = 1'b0;
|
||||||
|
jal_o = 1'b0;
|
||||||
|
jalr_o = 1'b0;
|
||||||
|
ecall_o = 1'b0;
|
||||||
|
uret_o = 1'b0;
|
||||||
|
csr_set_o = 1'b0;
|
||||||
|
csr_clear_o = 1'b0;
|
||||||
|
csr_write_o = 1'b0;
|
||||||
|
|
||||||
|
case (opcode)
|
||||||
|
// R 型运算:由 funct3/funct7 决定具体操作。
|
||||||
|
OP_R: begin
|
||||||
|
uses_rs1_o = 1'b1;
|
||||||
|
uses_rs2_o = 1'b1;
|
||||||
|
reg_write_o = 1'b1;
|
||||||
|
// 电路中额外的 REMU 控制对应标准 R 型的 funct7=1/funct3=111
|
||||||
|
// 形式;此时 ALU 的 result2 即为余数。
|
||||||
|
if ((funct7 == 7'b0000001) && (funct3 == 3'b111)) begin
|
||||||
|
alu_op_o = ALU_DIVU;
|
||||||
|
wb_sel_o = 3'd3;
|
||||||
|
end else if ((funct7 == 7'b0000001) && (funct3 == 3'b000)) begin
|
||||||
|
alu_op_o = ALU_MUL;
|
||||||
|
end else begin
|
||||||
|
// 标准 R 型 funct3 译码;add/sub 与 sra/srl 由 funct7[5] 区分。
|
||||||
|
case (funct3)
|
||||||
|
3'b000: alu_op_o = (funct7[5] ? ALU_SUB : ALU_ADD);
|
||||||
|
3'b001: alu_op_o = ALU_SLL;
|
||||||
|
3'b010: alu_op_o = ALU_SLT;
|
||||||
|
3'b011: alu_op_o = ALU_SLTU;
|
||||||
|
3'b100: alu_op_o = ALU_XOR;
|
||||||
|
3'b101: alu_op_o = (funct7[5] ? ALU_SRA : ALU_SRL);
|
||||||
|
3'b110: alu_op_o = ALU_OR;
|
||||||
|
3'b111: alu_op_o = ALU_AND;
|
||||||
|
default: reg_write_o = 1'b0;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// I 型运算(addi/slli/slti/xori/srai/srli/ori/andi)。
|
||||||
|
OP_I: begin
|
||||||
|
uses_rs1_o = 1'b1;
|
||||||
|
alu_src_o = 1'b1;
|
||||||
|
reg_write_o = 1'b1;
|
||||||
|
case (funct3)
|
||||||
|
3'b000: alu_op_o = ALU_ADD; // addi:立即数加
|
||||||
|
3'b001: alu_op_o = ALU_SLL; // slli:立即数逻辑左移
|
||||||
|
3'b010: alu_op_o = ALU_SLT; // slti:有符号小于置 1
|
||||||
|
3'b100: alu_op_o = ALU_XOR; // xori:立即数异或
|
||||||
|
3'b101:
|
||||||
|
alu_op_o = (funct7[5] ? ALU_SRA : ALU_SRL); // srai/srli:立即数算术/逻辑右移
|
||||||
|
3'b110: alu_op_o = ALU_OR; // ori:立即数或
|
||||||
|
3'b111: alu_op_o = ALU_AND; // andi:立即数与
|
||||||
|
default: reg_write_o = 1'b0;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// 加载指令,当前仅支持 lw(funct3=010)。
|
||||||
|
OP_LOAD: begin
|
||||||
|
if (funct3 == 3'b010) begin
|
||||||
|
uses_rs1_o = 1'b1;
|
||||||
|
alu_src_o = 1'b1;
|
||||||
|
alu_op_o = ALU_ADD;
|
||||||
|
mem_to_reg_o = 1'b1;
|
||||||
|
reg_write_o = 1'b1;
|
||||||
|
wb_sel_o = 3'd1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 存储指令:sw(funct3=010)与 sb(funct3=000)。
|
||||||
|
OP_STORE: begin
|
||||||
|
if ((funct3 == 3'b010) || (funct3 == 3'b000)) begin
|
||||||
|
uses_rs1_o = 1'b1;
|
||||||
|
uses_rs2_o = 1'b1;
|
||||||
|
alu_src_o = 1'b1;
|
||||||
|
alu_op_o = ALU_ADD;
|
||||||
|
mem_write_o = 1'b1;
|
||||||
|
mem_byte_o = (funct3 == 3'b000); // sb:字节存储
|
||||||
|
imm_o = {{20{ir_i[31]}}, ir_i[31:25], ir_i[11:7]};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 条件分支:beq(000)/bne(001)/blt(100,有符号小于)/bltu(110,无符号小于)。
|
||||||
|
OP_BRANCH: begin
|
||||||
|
if ((funct3 == 3'b000) || (funct3 == 3'b001) ||
|
||||||
|
(funct3 == 3'b100) || (funct3 == 3'b110)) begin
|
||||||
|
uses_rs1_o = 1'b1;
|
||||||
|
uses_rs2_o = 1'b1;
|
||||||
|
branch_o = 1'b1;
|
||||||
|
beq_o = (funct3 == 3'b000);
|
||||||
|
bne_o = (funct3 == 3'b001);
|
||||||
|
blt_o = (funct3 == 3'b100);
|
||||||
|
bltu_o = (funct3 == 3'b110);
|
||||||
|
// 真值表:beq/bne 用减法比较,blt 用 ALU_SLT,bltu 用 ALU_SLTU。
|
||||||
|
alu_op_o = blt_o ? ALU_SLT : (bltu_o ? ALU_SLTU : ALU_SUB);
|
||||||
|
imm_o = {{19{ir_i[31]}}, ir_i[31], ir_i[7], ir_i[30:25], ir_i[11:8], 1'b0};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 无条件跳转并链接:jal(写回 PC+4)。
|
||||||
|
OP_JAL: begin
|
||||||
|
jal_o = 1'b1;
|
||||||
|
reg_write_o = 1'b1;
|
||||||
|
wb_sel_o = 3'd2;
|
||||||
|
imm_o = {{11{ir_i[31]}}, ir_i[31], ir_i[19:12], ir_i[20], ir_i[30:21], 1'b0};
|
||||||
|
end
|
||||||
|
|
||||||
|
// 寄存器间接跳转并链接:jalr。
|
||||||
|
OP_JALR: begin
|
||||||
|
if (funct3 == 3'b000) begin
|
||||||
|
jalr_o = 1'b1;
|
||||||
|
uses_rs1_o = 1'b1;
|
||||||
|
alu_src_o = 1'b1;
|
||||||
|
alu_op_o = ALU_ADD;
|
||||||
|
reg_write_o = 1'b1;
|
||||||
|
wb_sel_o = 3'd2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 系统指令:ecall / uret / CSR 读写。
|
||||||
|
OP_SYS: begin
|
||||||
|
// 电路用 IR[21] 区分 URET 与 ecall。
|
||||||
|
if (funct3 == 3'b000) begin
|
||||||
|
if (ir_i[21]) begin
|
||||||
|
uret_o = 1'b1;
|
||||||
|
end else begin
|
||||||
|
ecall_o = 1'b1;
|
||||||
|
// 按文档说明,ecall 读取 a7(rs17) 与 a0(rs10)。
|
||||||
|
uses_rs1_o = 1'b1;
|
||||||
|
uses_rs2_o = 1'b1;
|
||||||
|
rs1_idx_o = 5'd17;
|
||||||
|
rs2_idx_o = 5'd10;
|
||||||
|
end
|
||||||
|
end else if (funct3 == 3'b001) begin
|
||||||
|
csr_write_o = 1'b1; // CSRRW:写 CSR,并把旧值写回 rd
|
||||||
|
uses_rs1_o = 1'b1;
|
||||||
|
alu_src_o = 1'b1;
|
||||||
|
imm_o = {27'b0, ir_i[19:15]};
|
||||||
|
reg_write_o = (rd_o != 5'd0);
|
||||||
|
wb_sel_o = 3'd4;
|
||||||
|
end else if (funct3 == 3'b110) begin
|
||||||
|
csr_set_o = 1'b1; // CSRRSI:置位 CSR 中的指定位
|
||||||
|
imm_o = {27'b0, ir_i[19:15]};
|
||||||
|
reg_write_o = (rd_o != 5'd0);
|
||||||
|
wb_sel_o = 3'd4;
|
||||||
|
end else if (funct3 == 3'b111) begin
|
||||||
|
csr_clear_o = 1'b1; // CSRRCI:清除 CSR 中的指定位
|
||||||
|
imm_o = {27'b0, ir_i[19:15]};
|
||||||
|
reg_write_o = (rd_o != 5'd0);
|
||||||
|
wb_sel_o = 3'd4;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
default: begin
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// EX 级前递单元。
|
||||||
|
//
|
||||||
|
// 优先级:EX/MEM 级的结果最新 -> 其次 MEM/WB 级写回值 -> 最后是 ID 级
|
||||||
|
// 译码时读到的寄存器值。load 结果在 MEM 级之后才可用,因此 EX/MEM 的
|
||||||
|
// load(mem_to_reg)不参与前递,改由冒险单元插入一个气泡。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_riscv_forward_unit (
|
||||||
|
input wire [31:0] rs1_value_i,
|
||||||
|
input wire [31:0] rs2_value_i,
|
||||||
|
input wire [ 4:0] rs1_idx_i,
|
||||||
|
input wire [ 4:0] rs2_idx_i,
|
||||||
|
// EX/MEM 级
|
||||||
|
input wire exmem_valid_i,
|
||||||
|
input wire exmem_reg_write_i,
|
||||||
|
input wire exmem_mem_to_reg_i,
|
||||||
|
input wire [ 4:0] exmem_rd_i,
|
||||||
|
input wire [31:0] exmem_value_i,
|
||||||
|
// MEM/WB 级
|
||||||
|
input wire memwb_valid_i,
|
||||||
|
input wire memwb_reg_write_i,
|
||||||
|
input wire [ 4:0] memwb_rd_i,
|
||||||
|
input wire [31:0] memwb_value_i,
|
||||||
|
// 前递后的操作数
|
||||||
|
output reg [31:0] src1_o,
|
||||||
|
output reg [31:0] src2_o
|
||||||
|
);
|
||||||
|
wire ex_mem_forward_valid = exmem_valid_i && exmem_reg_write_i &&
|
||||||
|
!exmem_mem_to_reg_i && (exmem_rd_i != 5'd0);
|
||||||
|
wire wb_forward_valid = memwb_valid_i && memwb_reg_write_i && (memwb_rd_i != 5'd0);
|
||||||
|
|
||||||
|
always @* begin
|
||||||
|
src1_o = rs1_value_i;
|
||||||
|
src2_o = rs2_value_i;
|
||||||
|
|
||||||
|
if (ex_mem_forward_valid && (exmem_rd_i == rs1_idx_i)) src1_o = exmem_value_i;
|
||||||
|
else if (wb_forward_valid && (memwb_rd_i == rs1_idx_i)) src1_o = memwb_value_i;
|
||||||
|
|
||||||
|
if (ex_mem_forward_valid && (exmem_rd_i == rs2_idx_i)) src2_o = exmem_value_i;
|
||||||
|
else if (wb_forward_valid && (memwb_rd_i == rs2_idx_i)) src2_o = memwb_value_i;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// 冒险/流水控制单元。
|
||||||
|
//
|
||||||
|
// load-use 冒险:EX 级是 load 且 ID 级指令马上要用它的结果时,插入一个
|
||||||
|
// 气泡(停顿一拍)。其余数据相关由前递网络解决。
|
||||||
|
// 冲刷(flush_younger)优先级高于停顿:中断、URET、ecall 停机、分支
|
||||||
|
// 重定向都会丢弃年轻指令,此时不需要再插气泡。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_riscv_hazard_unit (
|
||||||
|
input wire ifid_valid_i,
|
||||||
|
input wire idex_valid_i,
|
||||||
|
input wire idex_mem_to_reg_i,
|
||||||
|
input wire [4:0] idex_rd_i,
|
||||||
|
input wire d_uses_rs1_i,
|
||||||
|
input wire d_uses_rs2_i,
|
||||||
|
input wire [4:0] d_src1_idx_i,
|
||||||
|
input wire [4:0] d_src2_idx_i,
|
||||||
|
// 冲刷来源
|
||||||
|
input wire take_irq_i,
|
||||||
|
input wire uret_redirect_i,
|
||||||
|
input wire halt_event_i,
|
||||||
|
input wire redirect_valid_i,
|
||||||
|
input wire halted_i,
|
||||||
|
// 控制结果
|
||||||
|
output wire load_use_hazard_o,
|
||||||
|
output wire flush_younger_o,
|
||||||
|
output wire pipeline_stall_o
|
||||||
|
);
|
||||||
|
// EX 级为 load,且 ID 级的源寄存器与它的目的寄存器相同。
|
||||||
|
assign load_use_hazard_o = ifid_valid_i && idex_valid_i && idex_mem_to_reg_i &&
|
||||||
|
(idex_rd_i != 5'd0) &&
|
||||||
|
((d_uses_rs1_i && (d_src1_idx_i == idex_rd_i)) ||
|
||||||
|
(d_uses_rs2_i && (d_src2_idx_i == idex_rd_i)));
|
||||||
|
|
||||||
|
assign flush_younger_o = take_irq_i || uret_redirect_i || halt_event_i || redirect_valid_i;
|
||||||
|
|
||||||
|
assign pipeline_stall_o = load_use_hazard_o && !flush_younger_o && !halted_i;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// 中断控制器 + ustatus/uepc CSR + 嵌套返回栈。
|
||||||
|
//
|
||||||
|
// IRQ3 优先级最高,其次 IRQ2,最后 IRQ1;嵌套请求只有优先级高于当前级别
|
||||||
|
// 才会被接纳。中断入口地址由优先级选出,返回地址保存在 epc_stack 中,
|
||||||
|
// 当前上下文的活动返回 PC 放在 uepc_q(与课程讲义用 CSRRW 保存/恢复
|
||||||
|
// uepc 的流程一致)。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_riscv_irq_ctrl #(
|
||||||
|
parameter [31:0] IRQ1_VECTOR = 32'h0000_30ac,
|
||||||
|
parameter [31:0] IRQ2_VECTOR = 32'h0000_31e4,
|
||||||
|
parameter [31:0] IRQ3_VECTOR = 32'h0000_3310,
|
||||||
|
parameter IRQ_STACK_DEPTH = 4,
|
||||||
|
// ustatus 复位值。bit0 为 MIE。课程的中断测试程序只会在中断处理
|
||||||
|
// 程序中设置 MIE,若复位后不立即置位 MIE,主程序将永远无法响应
|
||||||
|
// 第一次中断,因此这里默认打开 MIE。
|
||||||
|
parameter [31:0] USTATUS_INIT = 32'h0000_0001
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire reset,
|
||||||
|
input wire [ 2:0] irq_i,
|
||||||
|
input wire halted_i,
|
||||||
|
// EX 级正在处理重定向/URET/停机,此时不允许响应中断
|
||||||
|
input wire ex_block_i,
|
||||||
|
// 中断发生时要保存的返回地址(由顶层根据流水线状态给出)
|
||||||
|
input wire [31:0] save_pc_i,
|
||||||
|
// EX 级的 URET
|
||||||
|
input wire uret_i,
|
||||||
|
// EX 级 CSR 指令(CSRRW/CSRRSI/CSRRCI)
|
||||||
|
input wire csr_valid_i,
|
||||||
|
input wire csr_set_i,
|
||||||
|
input wire csr_clear_i,
|
||||||
|
input wire csr_write_i,
|
||||||
|
input wire [11:0] csr_addr_i,
|
||||||
|
input wire [31:0] csr_imm_i,
|
||||||
|
input wire [31:0] csr_wdata_i,
|
||||||
|
// 控制结果
|
||||||
|
output wire take_irq_o,
|
||||||
|
output wire [31:0] vector_o,
|
||||||
|
output wire [31:0] return_pc_o,
|
||||||
|
output wire [ 2:0] pending_o,
|
||||||
|
output wire [ 1:0] level_o,
|
||||||
|
output wire [31:0] uepc_o,
|
||||||
|
output wire [31:0] ustatus_o
|
||||||
|
);
|
||||||
|
reg [31:0] ustatus_q;
|
||||||
|
reg [31:0] uepc_q;
|
||||||
|
reg [ 1:0] irq_current_q;
|
||||||
|
reg [ 2:0] irq_pending_q;
|
||||||
|
reg [ 2:0] irq_sync1_q;
|
||||||
|
reg [ 2:0] irq_sync2_q;
|
||||||
|
reg [ 2:0] irq_prev_q;
|
||||||
|
reg [31:0] epc_stack [0:IRQ_STACK_DEPTH-1];
|
||||||
|
reg [31:0] status_stack [0:IRQ_STACK_DEPTH-1];
|
||||||
|
reg [ 1:0] priority_stack [0:IRQ_STACK_DEPTH-1];
|
||||||
|
reg [ 2:0] irq_depth_q;
|
||||||
|
|
||||||
|
reg irq_selected_valid;
|
||||||
|
reg [ 1:0] irq_selected_level;
|
||||||
|
reg [31:0] irq_vector;
|
||||||
|
reg [ 2:0] irq_pending_d;
|
||||||
|
reg [31:0] ustatus_d;
|
||||||
|
reg [31:0] uepc_d;
|
||||||
|
|
||||||
|
integer s;
|
||||||
|
|
||||||
|
// 上升沿检测:只在 irq_i 由 0 变 1 的那一拍产生中断事件。
|
||||||
|
wire [ 2:0] irq_event = irq_sync2_q & ~irq_prev_q;
|
||||||
|
|
||||||
|
// 优先级仲裁:在当前级别允许的条件下选出优先级最高的中断源。
|
||||||
|
always @* begin
|
||||||
|
irq_selected_valid = 1'b0;
|
||||||
|
irq_selected_level = 2'd0;
|
||||||
|
if (irq_pending_q[2] && (irq_current_q < 2'd3)) begin
|
||||||
|
irq_selected_valid = 1'b1;
|
||||||
|
irq_selected_level = 2'd3;
|
||||||
|
end else if (irq_pending_q[1] && (irq_current_q < 2'd2)) begin
|
||||||
|
irq_selected_valid = 1'b1;
|
||||||
|
irq_selected_level = 2'd2;
|
||||||
|
end else if (irq_pending_q[0] && (irq_current_q < 2'd1)) begin
|
||||||
|
irq_selected_valid = 1'b1;
|
||||||
|
irq_selected_level = 2'd1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 依据被选中的优先级选择对应的中断入口地址。
|
||||||
|
always @* begin
|
||||||
|
case (irq_selected_level)
|
||||||
|
2'd1: irq_vector = IRQ1_VECTOR;
|
||||||
|
2'd2: irq_vector = IRQ2_VECTOR;
|
||||||
|
2'd3: irq_vector = IRQ3_VECTOR;
|
||||||
|
default: irq_vector = IRQ1_VECTOR;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// 响应中断的条件:有已选中中断、MIE 使能、栈未溢出、未停机,且没有
|
||||||
|
// 正在处理的重定向/URET/停机事件。
|
||||||
|
assign take_irq_o = irq_selected_valid && ustatus_q[0] &&
|
||||||
|
(irq_depth_q < IRQ_STACK_DEPTH) && !halted_i &&
|
||||||
|
!ex_block_i;
|
||||||
|
|
||||||
|
assign vector_o = irq_vector;
|
||||||
|
assign return_pc_o = uepc_q;
|
||||||
|
assign pending_o = irq_pending_q;
|
||||||
|
assign level_o = irq_current_q;
|
||||||
|
assign uepc_o = uepc_q;
|
||||||
|
assign ustatus_o = ustatus_q;
|
||||||
|
|
||||||
|
// 组合逻辑计算下一拍的挂起位与 CSR(ustatus/uepc)取值。
|
||||||
|
always @* begin
|
||||||
|
irq_pending_d = irq_pending_q | irq_event;
|
||||||
|
if (take_irq_o) begin
|
||||||
|
case (irq_selected_level)
|
||||||
|
2'd1: irq_pending_d[0] = 1'b0;
|
||||||
|
2'd2: irq_pending_d[1] = 1'b0;
|
||||||
|
2'd3: irq_pending_d[2] = 1'b0;
|
||||||
|
default: irq_pending_d = irq_pending_d;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
ustatus_d = ustatus_q;
|
||||||
|
uepc_d = uepc_q;
|
||||||
|
if (csr_valid_i && csr_set_i) begin
|
||||||
|
if (csr_addr_i == 12'h004) ustatus_d = ustatus_q | csr_imm_i;
|
||||||
|
else if (csr_addr_i == 12'h041) uepc_d = uepc_q | csr_imm_i;
|
||||||
|
end else if (csr_valid_i && csr_clear_i) begin
|
||||||
|
if (csr_addr_i == 12'h004) ustatus_d = ustatus_q & ~csr_imm_i;
|
||||||
|
else if (csr_addr_i == 12'h041) uepc_d = uepc_q & ~csr_imm_i;
|
||||||
|
end else if (csr_valid_i && csr_write_i) begin
|
||||||
|
if (csr_addr_i == 12'h004) ustatus_d = csr_wdata_i;
|
||||||
|
else if (csr_addr_i == 12'h041) uepc_d = csr_wdata_i;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (take_irq_o) ustatus_d[0] = 1'b0;
|
||||||
|
else if (uret_i && (irq_depth_q != 0)) ustatus_d = status_stack[irq_depth_q-1'b1];
|
||||||
|
|
||||||
|
if (take_irq_o) uepc_d = save_pc_i;
|
||||||
|
else if (uret_i && (irq_depth_q > 1)) uepc_d = epc_stack[irq_depth_q-2];
|
||||||
|
else if (uret_i && (irq_depth_q == 1)) uepc_d = 32'b0;
|
||||||
|
end
|
||||||
|
|
||||||
|
// 复位时清空中断状态与嵌套栈。
|
||||||
|
always @(posedge clk or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
ustatus_q <= USTATUS_INIT;
|
||||||
|
uepc_q <= 32'b0;
|
||||||
|
irq_current_q <= 2'b0;
|
||||||
|
irq_pending_q <= 3'b0;
|
||||||
|
irq_sync1_q <= 3'b0;
|
||||||
|
irq_sync2_q <= 3'b0;
|
||||||
|
irq_prev_q <= 3'b0;
|
||||||
|
irq_depth_q <= 3'b0;
|
||||||
|
for (s = 0; s < IRQ_STACK_DEPTH; s = s + 1) begin
|
||||||
|
epc_stack[s] <= 32'b0;
|
||||||
|
status_stack[s] <= 32'b0;
|
||||||
|
priority_stack[s] <= 2'b0;
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
// 中断输入打两拍同步,并锁存挂起状态与 CSR。
|
||||||
|
irq_sync1_q <= irq_i;
|
||||||
|
irq_sync2_q <= irq_sync1_q;
|
||||||
|
irq_prev_q <= irq_sync2_q;
|
||||||
|
irq_pending_q <= irq_pending_d;
|
||||||
|
ustatus_q <= ustatus_d;
|
||||||
|
uepc_q <= uepc_d;
|
||||||
|
|
||||||
|
if (take_irq_o) begin
|
||||||
|
if (irq_depth_q < IRQ_STACK_DEPTH) begin
|
||||||
|
epc_stack[irq_depth_q] <= save_pc_i;
|
||||||
|
status_stack[irq_depth_q] <= ustatus_q;
|
||||||
|
priority_stack[irq_depth_q] <= irq_current_q;
|
||||||
|
irq_depth_q <= irq_depth_q + 3'd1;
|
||||||
|
end
|
||||||
|
irq_current_q <= irq_selected_level;
|
||||||
|
end else if (uret_i) begin
|
||||||
|
if (irq_depth_q != 0) begin
|
||||||
|
irq_depth_q <= irq_depth_q - 3'd1;
|
||||||
|
irq_current_q <= priority_stack[irq_depth_q-1'b1];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// 性能计数器:周期数、停顿/气泡数、条件分支跳转数、无条件跳转数,
|
||||||
|
// 以及分支预测的成功/失败次数。
|
||||||
|
//
|
||||||
|
// 这些计数器只影响可观测的统计量,不参与处理器控制,便于在波形中单独
|
||||||
|
// 观察流水线效率。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_riscv_perf_counters (
|
||||||
|
input wire clk,
|
||||||
|
input wire reset,
|
||||||
|
input wire inc_cycle_i, // 未停机时周期数 +1
|
||||||
|
input wire stall_i, // load-use 停顿
|
||||||
|
input wire flush_i, // 冲刷年轻指令
|
||||||
|
input wire cond_taken_i, // EX 级条件分支实际跳转
|
||||||
|
input wire cond_valid_i, // EX 级条件分支有效
|
||||||
|
input wire mispredict_i, // EX 级条件分支预测失败
|
||||||
|
input wire uncond_i, // EX 级 JAL/JALR
|
||||||
|
output wire [15:0] cycle_count_o,
|
||||||
|
output wire [15:0] stall_count_o,
|
||||||
|
output wire [15:0] bubble_count_o,
|
||||||
|
output wire [15:0] conditional_taken_count_o,
|
||||||
|
output wire [15:0] unconditional_branch_count_o,
|
||||||
|
output wire [15:0] prediction_success_count_o,
|
||||||
|
output wire [15:0] prediction_failure_count_o
|
||||||
|
);
|
||||||
|
reg [15:0] cycle_count_q;
|
||||||
|
reg [15:0] stall_count_q;
|
||||||
|
reg [15:0] bubble_count_q;
|
||||||
|
reg [15:0] conditional_taken_count_q;
|
||||||
|
reg [15:0] unconditional_branch_count_q;
|
||||||
|
reg [15:0] prediction_success_count_q;
|
||||||
|
reg [15:0] prediction_failure_count_q;
|
||||||
|
|
||||||
|
always @(posedge clk or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
cycle_count_q <= 16'b0;
|
||||||
|
stall_count_q <= 16'b0;
|
||||||
|
bubble_count_q <= 16'b0;
|
||||||
|
conditional_taken_count_q <= 16'b0;
|
||||||
|
unconditional_branch_count_q <= 16'b0;
|
||||||
|
prediction_success_count_q <= 16'b0;
|
||||||
|
prediction_failure_count_q <= 16'b0;
|
||||||
|
end else begin
|
||||||
|
if (inc_cycle_i) cycle_count_q <= cycle_count_q + 16'd1;
|
||||||
|
|
||||||
|
if (stall_i) begin
|
||||||
|
stall_count_q <= stall_count_q + 16'd1;
|
||||||
|
bubble_count_q <= bubble_count_q + 16'd1;
|
||||||
|
end else if (flush_i) begin
|
||||||
|
// 一次冲刷丢弃 IF/ID 两条年轻指令,记两个气泡。
|
||||||
|
bubble_count_q <= bubble_count_q + 16'd2;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (cond_taken_i) conditional_taken_count_q <= conditional_taken_count_q + 16'd1;
|
||||||
|
if (uncond_i) unconditional_branch_count_q <= unconditional_branch_count_q + 16'd1;
|
||||||
|
|
||||||
|
if (cond_valid_i) begin
|
||||||
|
if (mispredict_i) prediction_failure_count_q <= prediction_failure_count_q + 16'd1;
|
||||||
|
else prediction_success_count_q <= prediction_success_count_q + 16'd1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign cycle_count_o = cycle_count_q;
|
||||||
|
assign stall_count_o = stall_count_q;
|
||||||
|
assign bubble_count_o = bubble_count_q;
|
||||||
|
assign conditional_taken_count_o = conditional_taken_count_q;
|
||||||
|
assign unconditional_branch_count_o = unconditional_branch_count_q;
|
||||||
|
assign prediction_success_count_o = prediction_success_count_q;
|
||||||
|
assign prediction_failure_count_o = prediction_failure_count_q;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
+280
-695
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,16 @@
|
|||||||
# `cpu21-riscv-4.circ` 的 Verilog 实现
|
# `cpu21-riscv-4.circ` 的 Verilog 实现
|
||||||
|
|
||||||
文件:`cpu21_riscv_redirect_int_bpb.v`
|
顶层文件:`cpu21_riscv_redirect_int_bpb.v`
|
||||||
|
|
||||||
该文件对应原电路中的“重定向流水线+中断+分支预测”部分,顶层模块为
|
该设计对应原电路中的“重定向流水线+中断+分支预测”部分,顶层模块为
|
||||||
`cpu21_riscv_redirect_int_bpb`,并包含独立的 `cpu21_riscv_alu` 和 `cpu21_bpb_8` 模块。
|
`cpu21_riscv_redirect_int_bpb`。为便于单独仿真与调试,各功能单元已拆分为
|
||||||
|
独立文件,顶层的 ALU、BPB 也已移出,具体见文末“文件与模块划分”。
|
||||||
|
|
||||||
## 已转换的功能
|
## 已转换的功能
|
||||||
|
|
||||||
- 五级流水:IF、ID、EX、MEM、WB。
|
- 五级流水:IF、ID、EX、MEM、WB。
|
||||||
- EX/MEM 与 MEM/WB 前递,以及 load-use 一拍停顿。
|
- EX/MEM 与 MEM/WB 前递,以及 load-use 一拍停顿。
|
||||||
- 条件分支 `beq`、`bne`、`bltu` 在 EX 段判定,错误预测时清空 IF/ID、ID/EX 并重定向 PC。
|
- 条件分支 `beq`、`bne`、`blt`(有符号小于)、`bltu`(无符号小于)在 EX 段判定,错误预测时清空 IF/ID、ID/EX 并重定向 PC。真值表中 BLT 的 `ALU_OP` 为 SLT,与实现一致。
|
||||||
- `jal`、`jalr` 在 EX 段重定向,返回地址写回 `rd`。
|
- `jal`、`jalr` 在 EX 段重定向,返回地址写回 `rd`。
|
||||||
- 8 项全相联 BPB:标签 `PC[11:2]`,每项包含 valid、目标 PC、2 位饱和计数器和 3 位年龄字段。BPB 只由条件分支更新,避免 `jal`、`jalr` 占用不会被查询的表项;未命中时优先使用无效项,否则替换年龄最大的项。
|
- 8 项全相联 BPB:标签 `PC[11:2]`,每项包含 valid、目标 PC、2 位饱和计数器和 3 位年龄字段。BPB 只由条件分支更新,避免 `jal`、`jalr` 占用不会被查询的表项;未命中时优先使用无效项,否则替换年龄最大的项。
|
||||||
- 三路中断输入 `irq_i[2:0]`:两级同步、上升沿挂起、IRQ3 > IRQ2 > IRQ1 优先级,仅在 `ustatus[0]`(MIE)允许时响应。
|
- 三路中断输入 `irq_i[2:0]`:两级同步、上升沿挂起、IRQ3 > IRQ2 > IRQ1 优先级,仅在 `ustatus[0]`(MIE)允许时响应。
|
||||||
@@ -24,12 +25,12 @@
|
|||||||
因此它与标准 RISC-V 指令的低 7 位 opcode 相差右移两位。
|
因此它与标准 RISC-V 指令的低 7 位 opcode 相差右移两位。
|
||||||
|
|
||||||
| 指令类别 | opcode | 说明 |
|
| 指令类别 | opcode | 说明 |
|
||||||
| ---------------------- | -----: | --------------------------------------- |
|
| ------------------------------ | -----: | --------------------------------------- |
|
||||||
| R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra |
|
| R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra |
|
||||||
| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/slli/srli/srai |
|
| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/slli/srli/srai |
|
||||||
| `lw` | `0x00` | `funct3=010` |
|
| `lw` | `0x00` | `funct3=010` |
|
||||||
| `sw` / `sb` | `0x08` | `funct3=010` / `000` |
|
| `sw` / `sb` | `0x08` | `funct3=010` / `000` |
|
||||||
| `beq` / `bne` / `bltu` | `0x18` | `funct3=000` / `001` / `110` |
|
| `beq` / `bne` / `blt` / `bltu` | `0x18` | `funct3=000` / `001` / `100` / `110` |
|
||||||
| `jal` | `0x1b` | J 型立即数 |
|
| `jal` | `0x1b` | J 型立即数 |
|
||||||
| `jalr` | `0x19` | `funct3=000` |
|
| `jalr` | `0x19` | `funct3=000` |
|
||||||
| 系统类 | `0x1c` | `ecall`、`URET`、CSRRSI、CSRRCI |
|
| 系统类 | `0x1c` | `ecall`、`URET`、CSRRSI、CSRRCI |
|
||||||
@@ -58,3 +59,23 @@
|
|||||||
6. `cycle_count_o` 在 `halted_o` 为高时暂停;load-use 每次计入一个气泡,重定向或中断清空 IF/ID 与当前 IF 时计入两个气泡。
|
6. `cycle_count_o` 在 `halted_o` 为高时暂停;load-use 每次计入一个气泡,重定向或中断清空 IF/ID 与当前 IF 时计入两个气泡。
|
||||||
|
|
||||||
原 `.circ` 文件中的 ROM、MIPS RAM、按钮、LED 和调试显示器属于 Logisim 外围,不直接搬入 RTL;其功能分别由指令接口、数据接口、`go_i`、`led_*_o` 和调试端口替代。
|
原 `.circ` 文件中的 ROM、MIPS RAM、按钮、LED 和调试显示器属于 Logisim 外围,不直接搬入 RTL;其功能分别由指令接口、数据接口、`go_i`、`led_*_o` 和调试端口替代。
|
||||||
|
|
||||||
|
## 文件与模块划分
|
||||||
|
|
||||||
|
| 文件 | 模块 | 职责 |
|
||||||
|
| -------------------------------- | ------------------------------ | --------------------------------------------------------------- |
|
||||||
|
| `cpu21_riscv_redirect_int_bpb.v` | `cpu21_riscv_redirect_int_bpb` | 顶层:五级流水寄存器、子模块例化、PC/IF-ID/ID-EX 控制、对外输出 |
|
||||||
|
| `cpu21_riscv_alu.v` | `cpu21_riscv_alu` | 组合 ALU(`result2` 用于 MUL 高位 / DIVU 余数) |
|
||||||
|
| `cpu21_bpb_8.v` | `cpu21_bpb_8` | 8 项全相联分支目标缓冲(2 位饱和计数器 + 近似 LRU) |
|
||||||
|
| `cpu21_riscv_decoder.v` | `cpu21_riscv_decoder` | ID 级组合译码器(操作码 `IR[6:2]`) |
|
||||||
|
| `cpu21_riscv_regfile.v` | `cpu21_riscv_regfile` | 寄存器堆,`x0` 恒为 0,同周期 WB 写优先旁路 |
|
||||||
|
| `cpu21_riscv_forward_unit.v` | `cpu21_riscv_forward_unit` | EX 级前递网络(EX/MEM 优先于 MEM/WB) |
|
||||||
|
| `cpu21_riscv_store_unit.v` | `cpu21_riscv_store_unit` | EX 级存储数据对齐:`sw`/`sb` 的写数据与字节写掩码 |
|
||||||
|
| `cpu21_riscv_branch_unit.v` | `cpu21_riscv_branch_unit` | EX 级控制流裁决:分支方向/目标、JAL/JALR 重定向 |
|
||||||
|
| `cpu21_riscv_hazard_unit.v` | `cpu21_riscv_hazard_unit` | load-use 冒险、停顿(stall)与冲刷(flush)判定 |
|
||||||
|
| `cpu21_riscv_irq_ctrl.v` | `cpu21_riscv_irq_ctrl` | 中断采样与优先级、`ustatus`/`uepc` CSR、嵌套返回栈 |
|
||||||
|
| `cpu21_riscv_perf_counters.v` | `cpu21_riscv_perf_counters` | 周期/停顿/气泡/分支/预测成功率等统计计数器 |
|
||||||
|
|
||||||
|
拆分只调整了模块边界与文件组织,端口、时序与行为完全等价:使用同一 ROM 程序
|
||||||
|
在 xsim 下对拆分前后逐周期对比,波形输出与最终统计(周期、停顿、气泡、分支
|
||||||
|
跳转、预测成败)完全一致。
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// 寄存器堆:32 x 32 位,x0 恒为 0。
|
||||||
|
//
|
||||||
|
// 读端口为组合读,并对同周期的 WB 写回做"写优先"旁路,因此 ID 级可以
|
||||||
|
// 立刻看到正在写回的结果,无需额外的寄存器堆相关冒险处理。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_riscv_regfile (
|
||||||
|
input wire clk,
|
||||||
|
input wire reset,
|
||||||
|
// 读端口(组合)
|
||||||
|
input wire [ 4:0] rs1_idx_i,
|
||||||
|
input wire [ 4:0] rs2_idx_i,
|
||||||
|
output wire [31:0] rs1_data_o,
|
||||||
|
output wire [31:0] rs2_data_o,
|
||||||
|
// 写端口(WB 级)
|
||||||
|
input wire we_i,
|
||||||
|
input wire [ 4:0] waddr_i,
|
||||||
|
input wire [31:0] wdata_i
|
||||||
|
);
|
||||||
|
reg [31:0] regs[0:31];
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
// 写优先旁路:同周期写回且地址相同的读请求返回新值。
|
||||||
|
wire [31:0] rs1_raw = regs[rs1_idx_i];
|
||||||
|
wire [31:0] rs2_raw = regs[rs2_idx_i];
|
||||||
|
|
||||||
|
assign rs1_data_o = (rs1_idx_i == 5'd0) ? 32'b0 :
|
||||||
|
((we_i && (waddr_i == rs1_idx_i)) ? wdata_i : rs1_raw);
|
||||||
|
assign rs2_data_o = (rs2_idx_i == 5'd0) ? 32'b0 :
|
||||||
|
((we_i && (waddr_i == rs2_idx_i)) ? wdata_i : rs2_raw);
|
||||||
|
|
||||||
|
always @(posedge clk or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
for (i = 0; i < 32; i = i + 1) regs[i] <= 32'b0;
|
||||||
|
end else begin
|
||||||
|
if (we_i && (waddr_i != 5'd0)) regs[waddr_i] <= wdata_i;
|
||||||
|
regs[0] <= 32'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// EX 级存储数据对齐单元。
|
||||||
|
//
|
||||||
|
// 依据地址低两位生成写数据与字节写掩码:sw 输出全字 + 1111,sb 把数据的
|
||||||
|
// 低 8 位搬到对应的字节车道,并只置位对应的写掩码。
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
module cpu21_riscv_store_unit (
|
||||||
|
input wire [31:0] addr_i,
|
||||||
|
input wire [31:0] data_i,
|
||||||
|
input wire mem_write_i,
|
||||||
|
input wire mem_byte_i,
|
||||||
|
output reg [31:0] store_data_o,
|
||||||
|
output reg [ 3:0] store_wstrb_o
|
||||||
|
);
|
||||||
|
always @* begin
|
||||||
|
store_data_o = data_i;
|
||||||
|
store_wstrb_o = 4'b0;
|
||||||
|
if (mem_write_i) begin
|
||||||
|
if (mem_byte_i) begin
|
||||||
|
case (addr_i[1:0])
|
||||||
|
2'd0: begin
|
||||||
|
store_data_o = {24'b0, data_i[7:0]};
|
||||||
|
store_wstrb_o = 4'b0001;
|
||||||
|
end
|
||||||
|
2'd1: begin
|
||||||
|
store_data_o = {16'b0, data_i[7:0], 8'b0};
|
||||||
|
store_wstrb_o = 4'b0010;
|
||||||
|
end
|
||||||
|
2'd2: begin
|
||||||
|
store_data_o = {8'b0, data_i[7:0], 16'b0};
|
||||||
|
store_wstrb_o = 4'b0100;
|
||||||
|
end
|
||||||
|
default: begin
|
||||||
|
store_data_o = {data_i[7:0], 24'b0};
|
||||||
|
store_wstrb_o = 4'b1000;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end else begin
|
||||||
|
store_data_o = data_i;
|
||||||
|
store_wstrb_o = 4'b1111;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`default_nettype wire
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
// Compile together with cpu21_riscv_redirect_int_bpb.v.
|
// 与 RTL 文件(cpu21_riscv_*.v、cpu21_bpb_8.v)一起编译。
|
||||||
// Example:
|
// 示例:
|
||||||
// iverilog -g2012 -o cpu21_sim cpu21_riscv_redirect_int_bpb.v tb_cpu21_riscv_redirect_int_bpb.v
|
// iverilog -g2012 -o cpu21_sim cpu21_*.v tb_cpu21_riscv_redirect_int_bpb.v
|
||||||
// vvp cpu21_sim
|
// vvp cpu21_sim
|
||||||
// The simulation writes cpu21_riscv_redirect_int_bpb.vcd for GTKWave.
|
// 仿真会写出 cpu21_riscv_redirect_int_bpb.vcd,可供 GTKWave 查看。
|
||||||
//
|
//
|
||||||
// The program image is loaded into the ROM model with $readmemh from
|
// 程序镜像通过 $readmemh 从 ROM_FILE 载入 ROM 模型。$readmemh 找不到文件时
|
||||||
// ROM_FILE. A failed $readmemh only prints a warning and leaves the memory
|
// 只会打印警告并保持内存原样,所以路径写错会让 ROM 看起来全是 NOP
|
||||||
// untouched, so a wrong path used to make the ROM look like all-NOP
|
// (0x00000013)。仿真器的工作目录并不固定(Vivado xsim 在
|
||||||
// (0x00000013). The simulator working directory is not fixed (Vivado xsim
|
// <proj>.sim/sim_1/behav/xsim 下运行),因此本测试平台会依次探测若干候选
|
||||||
// runs in <proj>.sim/sim_1/behav/xsim), so this testbench probes a list of
|
// 路径,并报告实际命中的那个。显式给出的路径始终优先:
|
||||||
// candidate paths and reports which one worked. An explicit path always
|
|
||||||
// wins:
|
|
||||||
// vvp cpu21_sim +ROM_FILE=<path> (iverilog/vvp)
|
// vvp cpu21_sim +ROM_FILE=<path> (iverilog/vvp)
|
||||||
// xelab ... -generic_top "ROM_FILE=<path>" (Vivado xsim)
|
// xelab ... -generic_top "ROM_FILE=<path>" (Vivado xsim)
|
||||||
module tb_cpu21_riscv_redirect_int_bpb #(
|
module tb_cpu21_riscv_redirect_int_bpb #(
|
||||||
parameter ROM_FILE = "risc-v-benchmark_ccab.hex"
|
parameter ROM_FILE = "cpu21_riscv_redirect_int_bpb_rom.hex"
|
||||||
// risc-v-benchmark_ccab.hex
|
// risc-v-benchmark_ccab.hex
|
||||||
// cpu21_riscv_redirect_int_bpb_rom.hex
|
// cpu21_riscv_redirect_int_bpb_rom.hex
|
||||||
// risc-v-branch-predict.hex
|
// risc-v-branch-predict.hex
|
||||||
@@ -22,13 +20,12 @@ module tb_cpu21_riscv_redirect_int_bpb #(
|
|||||||
localparam integer ROM_WORDS = 1024;
|
localparam integer ROM_WORDS = 1024;
|
||||||
localparam integer RAM_WORDS = 1024;
|
localparam integer RAM_WORDS = 1024;
|
||||||
localparam integer PATH_LEN = 512;
|
localparam integer PATH_LEN = 512;
|
||||||
// Sentinel that cannot appear as the first ROM word of a RISC-V image.
|
// 哨兵值:不可能出现在 RISC-V 镜像的第一个字中。
|
||||||
localparam [31:0] EMPTY_ROM_WORD = 32'hFFFF_FFFF;
|
localparam [31:0] EMPTY_ROM_WORD = 32'hFFFF_FFFF;
|
||||||
|
|
||||||
// Interrupt entry addresses, shared by the DUT instance and the trace
|
// 中断入口地址:DUT 例化与下面的打印条件共用。DUT 用 PC[11:2] 访问 ROM,
|
||||||
// condition below. The DUT addresses the ROM with PC[11:2], so these three
|
// 因此这三个入口位于程序镜像的第 43 / 120 / 192 个字(即每个中断处理
|
||||||
// entries live at words 43 / 120 / 192 of the program image (the first
|
// 程序开头的第一条 "sw ..., 0(sp)")。
|
||||||
// "sw ..., 0(sp)" of each handler prologue).
|
|
||||||
localparam [31:0] IRQ1_VECTOR = 32'h0000_30ac;
|
localparam [31:0] IRQ1_VECTOR = 32'h0000_30ac;
|
||||||
localparam [31:0] IRQ2_VECTOR = 32'h0000_31e0;
|
localparam [31:0] IRQ2_VECTOR = 32'h0000_31e0;
|
||||||
localparam [31:0] IRQ3_VECTOR = 32'h0000_3300;
|
localparam [31:0] IRQ3_VECTOR = 32'h0000_3300;
|
||||||
@@ -92,8 +89,8 @@ module tb_cpu21_riscv_redirect_int_bpb #(
|
|||||||
.IRQ1_VECTOR(IRQ1_VECTOR),
|
.IRQ1_VECTOR(IRQ1_VECTOR),
|
||||||
.IRQ2_VECTOR(IRQ2_VECTOR),
|
.IRQ2_VECTOR(IRQ2_VECTOR),
|
||||||
.IRQ3_VECTOR(IRQ3_VECTOR),
|
.IRQ3_VECTOR(IRQ3_VECTOR),
|
||||||
// The interrupt test program writes MIE only inside its handlers, so
|
// 中断测试程序只在中断处理程序内部写 MIE,因此测试平台在复位后就让
|
||||||
// the testbench starts with MIE already set (ustatus[0] = 1).
|
// MIE 有效(ustatus[0] = 1)。
|
||||||
.USTATUS_INIT(32'h0000_0001)
|
.USTATUS_INIT(32'h0000_0001)
|
||||||
) dut (
|
) dut (
|
||||||
.clk (clk),
|
.clk (clk),
|
||||||
@@ -142,20 +139,20 @@ module tb_cpu21_riscv_redirect_int_bpb #(
|
|||||||
|
|
||||||
always #5 clk = ~clk;
|
always #5 clk = ~clk;
|
||||||
|
|
||||||
// The Logisim ROM uses PC[11:2] as its word address. This also maps
|
// Logisim 的 ROM 以 PC[11:2] 作为字地址;这也让原来的三个中断向量映射到
|
||||||
// the three original interrupt vectors to the same ROM entries.
|
// 相同的 ROM 表项。
|
||||||
always @* begin
|
always @* begin
|
||||||
instr_i = 32'h00000013;
|
instr_i = 32'h00000013;
|
||||||
if (instr_addr_o[11:2] < ROM_WORDS) instr_i = rom[instr_addr_o[11:2]];
|
if (instr_addr_o[11:2] < ROM_WORDS) instr_i = rom[instr_addr_o[11:2]];
|
||||||
end
|
end
|
||||||
|
|
||||||
// Asynchronous read model for data memory.
|
// 数据存储器的异步读模型。
|
||||||
always @* begin
|
always @* begin
|
||||||
data_rdata_i = 32'b0;
|
data_rdata_i = 32'b0;
|
||||||
if (data_addr_o[11:2] < RAM_WORDS) data_rdata_i = ram[data_addr_o[11:2]];
|
if (data_addr_o[11:2] < RAM_WORDS) data_rdata_i = ram[data_addr_o[11:2]];
|
||||||
end
|
end
|
||||||
|
|
||||||
// Synchronous write model with byte enables.
|
// 带字节使能的同步写模型。
|
||||||
always @(posedge clk) begin
|
always @(posedge clk) begin
|
||||||
if (!reset && data_we_o && (data_addr_o[11:2] < RAM_WORDS)) begin
|
if (!reset && data_we_o && (data_addr_o[11:2] < RAM_WORDS)) begin
|
||||||
if (data_wstrb_o[0]) ram[data_addr_o[11:2]][7:0] <= data_wdata_o[7:0];
|
if (data_wstrb_o[0]) ram[data_addr_o[11:2]][7:0] <= data_wdata_o[7:0];
|
||||||
@@ -183,9 +180,8 @@ module tb_cpu21_riscv_redirect_int_bpb #(
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// Try to load the ROM image from "fname". "loaded" is 1 when the file
|
// 尝试从 "fname" 载入 ROM 镜像。"loaded" 为 1 表示文件存在且至少包含一个
|
||||||
// existed and contained at least one word. A sentinel in rom[0] makes a
|
// 字。利用 rom[0] 中的哨兵值,可以避免载入失败被静默忽略。
|
||||||
// silent load failure impossible to miss.
|
|
||||||
task load_rom_image;
|
task load_rom_image;
|
||||||
input [8*PATH_LEN-1:0] fname;
|
input [8*PATH_LEN-1:0] fname;
|
||||||
output loaded;
|
output loaded;
|
||||||
@@ -198,7 +194,7 @@ module tb_cpu21_riscv_redirect_int_bpb #(
|
|||||||
rom[0] = EMPTY_ROM_WORD;
|
rom[0] = EMPTY_ROM_WORD;
|
||||||
$readmemh(fname, rom);
|
$readmemh(fname, rom);
|
||||||
if (rom[0] === EMPTY_ROM_WORD) begin
|
if (rom[0] === EMPTY_ROM_WORD) begin
|
||||||
// Opened but empty: restore the default NOP fill.
|
// 文件能打开但内容为空:恢复默认的 NOP 填充。
|
||||||
rom[0] = 32'h0000_0013;
|
rom[0] = 32'h0000_0013;
|
||||||
end else begin
|
end else begin
|
||||||
rom_file_path = fname;
|
rom_file_path = fname;
|
||||||
@@ -219,13 +215,12 @@ module tb_cpu21_riscv_redirect_int_bpb #(
|
|||||||
for (i = 0; i < ROM_WORDS; i = i + 1) rom[i] = 32'h00000013;
|
for (i = 0; i < ROM_WORDS; i = i + 1) rom[i] = 32'h00000013;
|
||||||
for (i = 0; i < RAM_WORDS; i = i + 1) ram[i] = 32'b0;
|
for (i = 0; i < RAM_WORDS; i = i + 1) ram[i] = 32'b0;
|
||||||
|
|
||||||
// Seed a few words so memory activity is visible in the waveform.
|
// 预置几个字,便于在波形中观察数据存储器的活动。
|
||||||
ram[1] = 32'h1234_5678;
|
ram[1] = 32'h1234_5678;
|
||||||
ram[2] = 32'h89ab_cdef;
|
ram[2] = 32'h89ab_cdef;
|
||||||
|
|
||||||
// Probe the usual locations for the ROM image. The first readable
|
// 依次探测 ROM 镜像的常见位置,第一个可读的候选文件生效;下面的探测
|
||||||
// candidate wins; failed probes below are harmless (the ROM keeps its
|
// 失败是无害的(在找到真正的镜像之前,ROM 保持 0x00000013 填充)。
|
||||||
// 0x00000013 fill until a real image is found).
|
|
||||||
$display("NOTE: searching for ROM image \"%0s\" ...", ROM_FILE);
|
$display("NOTE: searching for ROM image \"%0s\" ...", ROM_FILE);
|
||||||
|
|
||||||
rom_loaded = 1'b0;
|
rom_loaded = 1'b0;
|
||||||
@@ -246,7 +241,7 @@ module tb_cpu21_riscv_redirect_int_bpb #(
|
|||||||
|
|
||||||
#22 reset = 1'b0;
|
#22 reset = 1'b0;
|
||||||
|
|
||||||
// Button-like interrupt pulses. The DUT synchronizes and latches them.
|
// 模拟按键式的中断脉冲,DUT 内部会同步并锁存它们。
|
||||||
repeat (80) @(negedge clk);
|
repeat (80) @(negedge clk);
|
||||||
irq_i[0] = 1'b1;
|
irq_i[0] = 1'b1;
|
||||||
@(negedge clk);
|
@(negedge clk);
|
||||||
|
|||||||
@@ -0,0 +1,258 @@
|
|||||||
|
// 仿真顶层:tb_no_intr —— 与 tb_cpu21_riscv_redirect_int_bpb.v 相同的测试框架,
|
||||||
|
// 但不注入任何中断脉冲,因此主程序可以不受打扰地连续运行。
|
||||||
|
//
|
||||||
|
// 与 RTL 文件(cpu21_riscv_*.v、cpu21_bpb_8.v)一起编译。
|
||||||
|
// 示例:
|
||||||
|
// iverilog -g2012 -o cpu21_sim cpu21_*.v tb_no_intr.v
|
||||||
|
// vvp cpu21_sim
|
||||||
|
// 仿真会写出 tb_no_intr.vcd,可供 GTKWave 查看。
|
||||||
|
//
|
||||||
|
// 程序镜像通过 $readmemh 从 ROM_FILE 载入 ROM 模型。$readmemh 找不到文件时
|
||||||
|
// 只会打印警告并保持内存原样,所以路径写错会让 ROM 看起来全是 NOP
|
||||||
|
// (0x00000013)。仿真器的工作目录并不固定(Vivado xsim 在
|
||||||
|
// <proj>.sim/sim_1/behav/xsim 下运行),因此本测试平台会依次探测若干候选
|
||||||
|
// 路径,并报告实际命中的那个。显式给出的路径始终优先:
|
||||||
|
// vvp cpu21_sim +ROM_FILE=<path> (iverilog/vvp)
|
||||||
|
// xelab ... -generic_top "ROM_FILE=<path>" (Vivado xsim)
|
||||||
|
module tb_no_intr #(
|
||||||
|
parameter ROM_FILE = "risc-v-benchmark_ccab.hex"
|
||||||
|
// risc-v-benchmark_ccab.hex
|
||||||
|
// cpu21_riscv_redirect_int_bpb_rom.hex
|
||||||
|
// risc-v-branch-predict.hex
|
||||||
|
);
|
||||||
|
localparam integer ROM_WORDS = 1024;
|
||||||
|
localparam integer RAM_WORDS = 1024;
|
||||||
|
localparam integer PATH_LEN = 512;
|
||||||
|
// 哨兵值:不可能出现在 RISC-V 镜像的第一个字中。
|
||||||
|
localparam [31:0] EMPTY_ROM_WORD = 32'hFFFF_FFFF;
|
||||||
|
|
||||||
|
// 中断入口地址:DUT 例化与下面的打印条件共用。DUT 用 PC[11:2] 访问 ROM,
|
||||||
|
// 因此这三个入口位于程序镜像的第 43 / 120 / 192 个字(即每个中断处理
|
||||||
|
// 程序开头的第一条 "sw ..., 0(sp)")。
|
||||||
|
localparam [31:0] IRQ1_VECTOR = 32'h0000_30ac;
|
||||||
|
localparam [31:0] IRQ2_VECTOR = 32'h0000_31e0;
|
||||||
|
localparam [31:0] IRQ3_VECTOR = 32'h0000_3300;
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg reset;
|
||||||
|
reg [ 31:0] instr_i;
|
||||||
|
reg [ 31:0] data_rdata_i;
|
||||||
|
reg [ 2:0] irq_i;
|
||||||
|
reg go_i;
|
||||||
|
|
||||||
|
reg [ 31:0] rom [0:ROM_WORDS-1];
|
||||||
|
reg [ 31:0] ram [0:RAM_WORDS-1];
|
||||||
|
|
||||||
|
wire [ 31:0] instr_addr_o;
|
||||||
|
wire [ 31:0] data_addr_o;
|
||||||
|
wire [ 31:0] data_wdata_o;
|
||||||
|
wire [ 3:0] data_wstrb_o;
|
||||||
|
wire data_we_o;
|
||||||
|
wire [ 31:0] led_data_o;
|
||||||
|
wire led_valid_o;
|
||||||
|
wire [ 31:0] if_pc_o;
|
||||||
|
wire [ 31:0] id_pc_o;
|
||||||
|
wire [ 31:0] ex_pc_o;
|
||||||
|
wire [ 31:0] mem_pc_o;
|
||||||
|
wire [ 31:0] wb_pc_o;
|
||||||
|
wire [ 31:0] if_ir_o;
|
||||||
|
wire [ 31:0] id_ir_o;
|
||||||
|
wire [ 31:0] ex_ir_o;
|
||||||
|
wire [ 31:0] mem_ir_o;
|
||||||
|
wire [ 31:0] wb_ir_o;
|
||||||
|
wire [ 31:0] rdin_o;
|
||||||
|
wire [ 31:0] mdin_o;
|
||||||
|
wire reg_write_o;
|
||||||
|
wire mem_write_o;
|
||||||
|
wire halted_o;
|
||||||
|
|
||||||
|
wire [ 2:0] irq_pending_o;
|
||||||
|
wire [ 1:0] irq_current_level_o;
|
||||||
|
wire [ 31:0] uepc_o;
|
||||||
|
wire [ 31:0] ustatus_o;
|
||||||
|
wire bpb_predict_hit_o;
|
||||||
|
wire bpb_predict_taken_o;
|
||||||
|
wire bpb_mispredict_o;
|
||||||
|
|
||||||
|
wire [ 15:0] cycle_count_o;
|
||||||
|
wire [ 15:0] stall_count_o;
|
||||||
|
wire [ 15:0] bubble_count_o;
|
||||||
|
wire [ 15:0] conditional_taken_count_o;
|
||||||
|
wire [ 15:0] unconditional_branch_count_o;
|
||||||
|
wire [ 15:0] prediction_success_count_o;
|
||||||
|
wire [ 15:0] prediction_failure_count_o;
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
integer tb_cycle;
|
||||||
|
reg rom_loaded;
|
||||||
|
reg [8*PATH_LEN-1:0] rom_file_path;
|
||||||
|
|
||||||
|
cpu21_riscv_redirect_int_bpb #(
|
||||||
|
.RESET_PC (32'h0000_0000),
|
||||||
|
.IRQ1_VECTOR(IRQ1_VECTOR),
|
||||||
|
.IRQ2_VECTOR(IRQ2_VECTOR),
|
||||||
|
.IRQ3_VECTOR(IRQ3_VECTOR),
|
||||||
|
// 与中断测试平台一致,复位后即使能 MIE。本测试平台从不产生 irq_i
|
||||||
|
// 脉冲,因此该参数只有在你自行添加脉冲时才有影响。
|
||||||
|
.USTATUS_INIT(32'h0000_0001)
|
||||||
|
) dut (
|
||||||
|
.clk (clk),
|
||||||
|
.reset (reset),
|
||||||
|
.instr_i (instr_i),
|
||||||
|
.data_rdata_i (data_rdata_i),
|
||||||
|
.irq_i (irq_i),
|
||||||
|
.go_i (go_i),
|
||||||
|
.instr_addr_o (instr_addr_o),
|
||||||
|
.data_addr_o (data_addr_o),
|
||||||
|
.data_wdata_o (data_wdata_o),
|
||||||
|
.data_wstrb_o (data_wstrb_o),
|
||||||
|
.data_we_o (data_we_o),
|
||||||
|
.led_data_o (led_data_o),
|
||||||
|
.led_valid_o (led_valid_o),
|
||||||
|
.if_pc_o (if_pc_o),
|
||||||
|
.id_pc_o (id_pc_o),
|
||||||
|
.ex_pc_o (ex_pc_o),
|
||||||
|
.mem_pc_o (mem_pc_o),
|
||||||
|
.wb_pc_o (wb_pc_o),
|
||||||
|
.if_ir_o (if_ir_o),
|
||||||
|
.id_ir_o (id_ir_o),
|
||||||
|
.ex_ir_o (ex_ir_o),
|
||||||
|
.mem_ir_o (mem_ir_o),
|
||||||
|
.wb_ir_o (wb_ir_o),
|
||||||
|
.rdin_o (rdin_o),
|
||||||
|
.mdin_o (mdin_o),
|
||||||
|
.reg_write_o (reg_write_o),
|
||||||
|
.mem_write_o (mem_write_o),
|
||||||
|
.halted_o (halted_o),
|
||||||
|
.irq_pending_o (irq_pending_o),
|
||||||
|
.irq_current_level_o (irq_current_level_o),
|
||||||
|
.uepc_o (uepc_o),
|
||||||
|
.ustatus_o (ustatus_o),
|
||||||
|
.bpb_predict_hit_o (bpb_predict_hit_o),
|
||||||
|
.bpb_predict_taken_o (bpb_predict_taken_o),
|
||||||
|
.bpb_mispredict_o (bpb_mispredict_o),
|
||||||
|
.cycle_count_o (cycle_count_o),
|
||||||
|
.stall_count_o (stall_count_o),
|
||||||
|
.bubble_count_o (bubble_count_o),
|
||||||
|
.conditional_taken_count_o (conditional_taken_count_o),
|
||||||
|
.unconditional_branch_count_o(unconditional_branch_count_o),
|
||||||
|
.prediction_success_count_o (prediction_success_count_o),
|
||||||
|
.prediction_failure_count_o (prediction_failure_count_o)
|
||||||
|
);
|
||||||
|
|
||||||
|
always #5 clk = ~clk;
|
||||||
|
|
||||||
|
// Logisim 的 ROM 以 PC[11:2] 作为字地址;这也让原来的三个中断向量映射到
|
||||||
|
// 相同的 ROM 表项。
|
||||||
|
always @* begin
|
||||||
|
instr_i = 32'h00000013;
|
||||||
|
if (instr_addr_o[11:2] < ROM_WORDS) instr_i = rom[instr_addr_o[11:2]];
|
||||||
|
end
|
||||||
|
|
||||||
|
// 数据存储器的异步读模型。
|
||||||
|
always @* begin
|
||||||
|
data_rdata_i = 32'b0;
|
||||||
|
if (data_addr_o[11:2] < RAM_WORDS) data_rdata_i = ram[data_addr_o[11:2]];
|
||||||
|
end
|
||||||
|
|
||||||
|
// 带字节使能的同步写模型。
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (!reset && data_we_o && (data_addr_o[11:2] < RAM_WORDS)) begin
|
||||||
|
if (data_wstrb_o[0]) ram[data_addr_o[11:2]][7:0] <= data_wdata_o[7:0];
|
||||||
|
if (data_wstrb_o[1]) ram[data_addr_o[11:2]][15:8] <= data_wdata_o[15:8];
|
||||||
|
if (data_wstrb_o[2]) ram[data_addr_o[11:2]][23:16] <= data_wdata_o[23:16];
|
||||||
|
if (data_wstrb_o[3]) ram[data_addr_o[11:2]][31:24] <= data_wdata_o[31:24];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (reset) begin
|
||||||
|
tb_cycle = 0;
|
||||||
|
end else begin
|
||||||
|
tb_cycle = tb_cycle + 1;
|
||||||
|
if ((tb_cycle <= 20) || data_we_o || led_valid_o ||
|
||||||
|
(irq_pending_o != 3'b000) ||
|
||||||
|
(instr_addr_o == IRQ1_VECTOR) ||
|
||||||
|
(instr_addr_o == IRQ2_VECTOR) ||
|
||||||
|
(instr_addr_o == IRQ3_VECTOR)) begin
|
||||||
|
$display(
|
||||||
|
"t=%0t cyc=%0d PC=%08h ID=%08h EX=%08h MEM=%08h WB=%08h irq=%b pend=%b uepc=%08h LEDv=%b LED=%08h memwe=%b addr=%08h wdata=%08h wstrb=%b",
|
||||||
|
$time, tb_cycle, if_pc_o, id_ir_o, ex_ir_o, mem_ir_o, wb_ir_o, irq_i, irq_pending_o,
|
||||||
|
uepc_o, led_valid_o, led_data_o, data_we_o, data_addr_o, data_wdata_o, data_wstrb_o);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 尝试从 "fname" 载入 ROM 镜像。"loaded" 为 1 表示文件存在且至少包含一个
|
||||||
|
// 字。利用 rom[0] 中的哨兵值,可以避免载入失败被静默忽略。
|
||||||
|
task load_rom_image;
|
||||||
|
input [8*PATH_LEN-1:0] fname;
|
||||||
|
output loaded;
|
||||||
|
integer fh;
|
||||||
|
begin
|
||||||
|
loaded = 1'b0;
|
||||||
|
fh = $fopen(fname, "r");
|
||||||
|
if (fh != 0) begin
|
||||||
|
$fclose(fh);
|
||||||
|
rom[0] = EMPTY_ROM_WORD;
|
||||||
|
$readmemh(fname, rom);
|
||||||
|
if (rom[0] === EMPTY_ROM_WORD) begin
|
||||||
|
// 文件能打开但内容为空:恢复默认的 NOP 填充。
|
||||||
|
rom[0] = 32'h0000_0013;
|
||||||
|
end else begin
|
||||||
|
rom_file_path = fname;
|
||||||
|
loaded = 1'b1;
|
||||||
|
$display("NOTE: ROM image loaded from \"%0s\".", fname);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
reset = 1'b1;
|
||||||
|
irq_i = 3'b000;
|
||||||
|
go_i = 1'b0;
|
||||||
|
tb_cycle = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ROM_WORDS; i = i + 1) rom[i] = 32'h00000013;
|
||||||
|
for (i = 0; i < RAM_WORDS; i = i + 1) ram[i] = 32'b0;
|
||||||
|
|
||||||
|
// 预置几个字,便于在波形中观察数据存储器的活动。
|
||||||
|
ram[1] = 32'h1234_5678;
|
||||||
|
ram[2] = 32'h89ab_cdef;
|
||||||
|
|
||||||
|
// 依次探测 ROM 镜像的常见位置,第一个可读的候选文件生效;下面的探测
|
||||||
|
// 失败是无害的(在找到真正的镜像之前,ROM 保持 0x00000013 填充)。
|
||||||
|
$display("NOTE: searching for ROM image \"%0s\" ...", ROM_FILE);
|
||||||
|
|
||||||
|
rom_loaded = 1'b0;
|
||||||
|
if ($value$plusargs("ROM_FILE=%s", rom_file_path)) load_rom_image(rom_file_path, rom_loaded);
|
||||||
|
if (!rom_loaded) load_rom_image(ROM_FILE, rom_loaded);
|
||||||
|
if (!rom_loaded) load_rom_image({"testbench/", ROM_FILE}, rom_loaded);
|
||||||
|
if (!rom_loaded) load_rom_image({"../testbench/", ROM_FILE}, rom_loaded);
|
||||||
|
if (!rom_loaded) load_rom_image({"../../../testbench/", ROM_FILE}, rom_loaded);
|
||||||
|
if (!rom_loaded) load_rom_image({"../../../../../testbench/", ROM_FILE}, rom_loaded);
|
||||||
|
if (!rom_loaded)
|
||||||
|
$display(
|
||||||
|
"WARNING: no ROM image found for \"%0s\" - the ROM stays filled with NOPs (0x00000013).",
|
||||||
|
ROM_FILE
|
||||||
|
);
|
||||||
|
|
||||||
|
$dumpfile("tb_no_intr.vcd");
|
||||||
|
$dumpvars(0, tb_no_intr);
|
||||||
|
|
||||||
|
#22 reset = 1'b0;
|
||||||
|
|
||||||
|
// 本测试平台不注入中断脉冲:irq_i 始终为 3'b000,因此只走主程序
|
||||||
|
//(非中断)执行路径。
|
||||||
|
repeat (5560) @(negedge clk);
|
||||||
|
$display(
|
||||||
|
"FINAL: tb_cycles=%0d dut_cycles=%0d stalls=%0d bubbles=%0d cond_taken=%0d uncond=%0d pred_ok=%0d pred_fail=%0d halted=%b",
|
||||||
|
tb_cycle, cycle_count_o, stall_count_o, bubble_count_o, conditional_taken_count_o,
|
||||||
|
unconditional_branch_count_o, prediction_success_count_o, prediction_failure_count_o,
|
||||||
|
halted_o);
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
Reference in New Issue
Block a user