乘除法改为多周期时序单元,CPU 时钟从 6.25 MHz 提到 50 MHz
- 新增 rtl/cpu21_riscv_muldiv.v:移位迭代的多周期乘除法单元。
* 乘法:32 拍移位加法得到 64 位乘积;有符号变体先取操作数绝对值,迭代结束后
按符号对 64 位乘积取负。
* 除法:32 拍恢复余数法得到商与余数;除零按 RISC-V 约定(商全 1、余数为被除数)。
* 一次乘/除独占 EX 共 34 拍(1 拍装载 + 32 拍迭代 + 1 拍定型):期间 ID/EX 保持、
EX/MEM 插气泡、取指/译码冻结,并屏蔽中断(避免结果未定型就写回 rd)。
- rtl/cpu21_riscv_alu.v:删除组合的 32x32 乘法与除法/取余(原来关键路径约 94 ns、
325 级逻辑、286 个 CARRY4,是 100 MHz 无法收敛的根因)。ALU_MUL/ALU_DIVU 现在
只作为“这是乘除法指令”的标识,结果由顶层从 muldiv 选取(wb_sel=3 取 result2 的
约定保持不变,译码器与写回选择都不用改)。
- rtl/cpu21_riscv_decoder.v:新增 md_op_o(乘除法操作类型),并补上课程真值表里的
divu(funct3=101)——之前这一项译码缺失,会导致 divu 不写 rd。
- rtl/cpu21_riscv_redirect_int_bpb.v:ID/EX 增加 md_op;EX 级停留、结果选择与中断
屏蔽。另外为时序做了三处行为等价的重构(100 MHz 下 WNS 从 -3.54 ns 改善到
-1.10 ns):
* JAL/JALR 的链接地址 PC+4 改在 EX 级预先算好并随流水寄存器传递,去掉
“写回值/前递 → 停机判定 → 流水控制”路径上的两个 32 位加法器;
* ID/EX 的 payload 时钟使能不再依赖冲刷(只有 valid 位受冲刷控制),把
“EX 级组合判决 → 流水寄存器时钟使能”这条最长路径缩到只剩 1 位;
* ex_block_irq 不再包含 ex_redirect_valid / ex_halt_event(它们本身就会让
flush_younger 有效,中断优先时返回地址由 irq_save_pc 正确给出),切断
“分支判决 → 中断控制器 → flush”的绕行路径。
- rtl/cpu21_riscv_hazard_unit.v:停顿条件去掉 flush_younger(冲刷由 valid 位与
PC/IF-ID 的冲刷分支处理,同时发生停顿只会多保持一拍 payload),避免把长路径
引到 payload 的时钟使能上。
- board/cpu21_board_top.v / .xdc:乘除法已不在关键路径上,分频比从 16 降到 2
(50 MHz,周期 20 ns)。实测(xc7a100tcsg324-1):CLK_DIV=1(100 MHz)下
WNS=-1.1 ns,剩余违例路径是 EX 级“分支判决/中断保存 → BPB 更新”,与乘除法无关
(逻辑只占约 3 ns,其余 ~8 ns 是布线);CLK_DIV=2 时 WNS=+5.109 ns,余量约 25%。
- 上板 ROM 按 50 MHz 重新生成(延时计数 0x7A000 ≈ 20 ms/步,快速版 64 次循环),
CPU 时钟比改造前快 8 倍。
- tools/rv_asm.py:支持 divu / remu 助记符。新增 testbench/programs/muldiv_test.asm
与 muldiv_test.expected(13 个用例:mul/mulh/mulhsu/mulhu/divu/remu、负数、
0xffffffff、除零、0x12345678 大数)。
- 验证:muldiv_test 13/13、isa_test 24/24、lui_test 4/4、mmio_test 5/5 全部 PASS;
两个基线(5560 / 6003 周期)与改造前的流水统计一致(差异只来自多周期乘除法带来的
停顿/气泡计数);中断测试仍能正常进入 0x30ac / 0x31e0 / 0x3300 三层嵌套处理程序;
板级仿真:默认 ROM 首次写入 LED=0001、快速 ROM 跑马灯 0001..8000 循环正常。
- 实现结果:synth_design / route_design / write_bitstream 全部完成,
LUT 5400 → 3420(5.39%)、DSP 12 → 0、IOB 23,时钟 6.25 MHz → 50 MHz,
bit 文件已更新。
This commit is contained in:
+14
-20
@@ -2,11 +2,14 @@
|
||||
`default_nettype none
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ALU:纯组合运算单元。
|
||||
// ALU:组合运算单元(只做加减/移位/逻辑/比较等单拍运算)。
|
||||
//
|
||||
// result 为主结果;result2 为第二结果(乘法的无符号高 32 位 / DIVU 的余数),
|
||||
// 由译码器用 wb_sel=3 选取,用于无符号高位乘法 mulhu 与课程约定的 remu。
|
||||
// 除法做了除零保护:b==0 时商为 32'hffff_ffff、余数为被除数。
|
||||
// 乘法与除法已移到 cpu21_riscv_muldiv.v(时序多周期单元),因为组合的 a*b / a%b
|
||||
// 关键路径过长(综合后约 94 ns,100 MHz 下无法收敛)。因此本模块不再实现
|
||||
// ALU_MUL / ALU_DIVU:这两个操作码只作为“这是乘除法指令”的标识,对应结果由
|
||||
// 顶层从 muldiv 的输出选取(result2 的 wb_sel=3 约定保持不变)。
|
||||
//
|
||||
// result 为主结果;result2 为第二结果,由译码器用 wb_sel=3 选取。
|
||||
// -----------------------------------------------------------------------------
|
||||
module cpu21_riscv_alu (
|
||||
input wire [ 3:0] op,
|
||||
@@ -31,37 +34,28 @@ module cpu21_riscv_alu (
|
||||
localparam ALU_MULH = 4'd13;
|
||||
localparam ALU_MULHSU = 4'd14;
|
||||
localparam ALU_PASSB = 4'd15; // 直接输出 B 端(供 LUI 使用)
|
||||
reg [63:0] mult_result; // 无符号 32x32 乘积
|
||||
reg signed [63:0] mult_ss; // 有符号 x 有符号
|
||||
reg signed [63:0] mult_su; // 有符号 x 无符号
|
||||
|
||||
always @* begin
|
||||
mult_result = a * b;
|
||||
mult_ss = $signed(a) * $signed(b);
|
||||
mult_su = $signed(a) * $signed({32'b0, b});
|
||||
result = 32'b0;
|
||||
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_MUL)与除法(ALU_DIVU)在本模块内不产生结果:
|
||||
// 它们由 cpu21_riscv_muldiv 多周期单元完成,顶层用 ex_muldiv 选择其输出。
|
||||
ALU_MUL: ;
|
||||
ALU_DIVU: ;
|
||||
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_MULH: result = mult_ss[63:32];
|
||||
ALU_MULHSU: result = mult_su[63:32];
|
||||
ALU_SLT: result = ($signed(a) < $signed(b)) ? 32'd1 : 32'd0;
|
||||
ALU_SLTU: result = (a < b) ? 32'd1 : 32'd0;
|
||||
ALU_MULH: ;
|
||||
ALU_MULHSU: ;
|
||||
ALU_PASSB: result = b;
|
||||
default: result = 32'b0;
|
||||
endcase
|
||||
|
||||
@@ -17,6 +17,7 @@ module cpu21_riscv_decoder (
|
||||
output reg [31:0] imm_o,
|
||||
output reg [ 3:0] alu_op_o,
|
||||
output reg [ 2:0] wb_sel_o,
|
||||
output reg [ 2:0] md_op_o,
|
||||
output wire [11:0] csr_addr_o,
|
||||
output reg uses_rs1_o,
|
||||
output reg uses_rs2_o,
|
||||
@@ -68,6 +69,15 @@ module cpu21_riscv_decoder (
|
||||
localparam ALU_MULHSU = 4'd14;
|
||||
localparam ALU_PASSB = 4'd15; // LUI:直接输出 B 端(立即数)
|
||||
|
||||
// 乘除法单元(cpu21_riscv_muldiv)的操作编码,MD_NONE 表示非乘除法指令。
|
||||
localparam [2:0] MD_NONE = 3'd0;
|
||||
localparam [2:0] MD_MUL = 3'd1;
|
||||
localparam [2:0] MD_MULH = 3'd2;
|
||||
localparam [2:0] MD_MULHSU = 3'd3;
|
||||
localparam [2:0] MD_MULHU = 3'd4;
|
||||
localparam [2:0] MD_DIVU = 3'd5;
|
||||
localparam [2:0] MD_REMU = 3'd6;
|
||||
|
||||
wire [4:0] opcode = ir_i[6:2];
|
||||
wire [2:0] funct3 = ir_i[14:12];
|
||||
wire [6:0] funct7 = ir_i[31:25];
|
||||
@@ -83,6 +93,7 @@ module cpu21_riscv_decoder (
|
||||
imm_o = {{20{ir_i[31]}}, ir_i[31:20]};
|
||||
alu_op_o = ALU_ADD;
|
||||
wb_sel_o = 3'd0;
|
||||
md_op_o = MD_NONE;
|
||||
uses_rs1_o = 1'b0;
|
||||
uses_rs2_o = 1'b0;
|
||||
reg_write_o = 1'b0;
|
||||
@@ -111,19 +122,36 @@ module cpu21_riscv_decoder (
|
||||
uses_rs1_o = 1'b1;
|
||||
uses_rs2_o = 1'b1;
|
||||
reg_write_o = 1'b1;
|
||||
// M 扩展(funct7=0000001):mul/mulh/mulhsu/mulhu,外加课程约定的 remu
|
||||
// (funct3=111 形式)。mulhu 与 remu 都取 ALU 的 result2(wb_sel=3)。
|
||||
// M 扩展(funct7=0000001):mul/mulh/mulhsu/mulhu,以及课程真值表里的
|
||||
// divu(funct3=101,商)与 remu(funct3=111,余数)。它们都由多周期
|
||||
// 乘除法单元完成,这里只给出操作类型 md_op_o;mulhu 与 remu 依旧用
|
||||
// wb_sel=3 取 result2(高位乘积 / 余数)。
|
||||
if (funct7 == 7'b0000001) begin
|
||||
case (funct3)
|
||||
3'b000: alu_op_o = ALU_MUL; // mul:无符号乘积低 32 位
|
||||
3'b001: alu_op_o = ALU_MULH; // mulh:有符号乘积高 32 位
|
||||
3'b010: alu_op_o = ALU_MULHSU; // mulhsu:有符号 x 无符号高 32 位
|
||||
3'b011: begin // mulhu:无符号乘积高 32 位
|
||||
3'b000: begin // mul:低 32 位
|
||||
alu_op_o = ALU_MUL;
|
||||
md_op_o = MD_MUL;
|
||||
end
|
||||
3'b001: begin // mulh:有符号高 32 位
|
||||
alu_op_o = ALU_MULH;
|
||||
md_op_o = MD_MULH;
|
||||
end
|
||||
3'b010: begin // mulhsu:有符号 x 无符号高 32 位
|
||||
alu_op_o = ALU_MULHSU;
|
||||
md_op_o = MD_MULHSU;
|
||||
end
|
||||
3'b011: begin // mulhu:无符号高 32 位
|
||||
alu_op_o = ALU_MUL;
|
||||
md_op_o = MD_MULHU;
|
||||
wb_sel_o = 3'd3;
|
||||
end
|
||||
3'b111: begin // remu(课程约定):取余数
|
||||
3'b101: begin // divu:无符号商
|
||||
alu_op_o = ALU_DIVU;
|
||||
md_op_o = MD_DIVU;
|
||||
end
|
||||
3'b111: begin // remu:无符号余数
|
||||
alu_op_o = ALU_DIVU;
|
||||
md_op_o = MD_REMU;
|
||||
wb_sel_o = 3'd3;
|
||||
end
|
||||
default: reg_write_o = 1'b0;
|
||||
|
||||
@@ -37,7 +37,10 @@ module cpu21_riscv_hazard_unit (
|
||||
|
||||
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;
|
||||
// 停顿条件里不再包含 flush_younger:冲刷时 valid 位会被清 0、PC/IF-ID 也由
|
||||
// 冲刷分支(优先级高于停顿)处理,同时发生停顿只会多保持一拍 payload,
|
||||
// 不影响正确性。这样可以避免把“冲刷”这条长路径引到 payload 的时钟使能上。
|
||||
assign pipeline_stall_o = load_use_hazard_o && !halted_i;
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 多周期乘除法单元(时序逻辑,32 拍迭代)
|
||||
//
|
||||
// 原来乘除法直接写在 ALU 里(组合的 a*b / a%b):综合后关键路径约 94 ns
|
||||
// (325 级逻辑、286 个 CARRY4),100 MHz 下无法收敛,只能把板上 100 MHz 分频
|
||||
// 到 6.25 MHz 再跑。这里改成移位迭代:
|
||||
// * 乘法:32 拍移位加法得到 64 位乘积;有符号变体先取操作数绝对值,
|
||||
// 迭代结束后按符号对 64 位乘积取负。
|
||||
// * 除法:32 拍恢复余数法得到商与余数;除零按 RISC-V 约定处理
|
||||
// (商为全 1、余数为被除数)。DIVU/REMU 都是无符号运算,不需要符号修正。
|
||||
//
|
||||
// 顶层的 EX 级会停留若干拍(往 EX/MEM 插气泡)等待 done_o 有效,然后照常写回。
|
||||
// 迭代期间顶层会屏蔽中断,避免在结果未定型时提前推进流水线。
|
||||
//
|
||||
// op_i 编码(由 cpu21_riscv_decoder 给出,MD_NONE 表示不是乘除法指令):
|
||||
// MD_MUL mul :无符号乘积低 32 位(有符号乘法的低 32 位与之相同)
|
||||
// MD_MULH mulh :有符号 x 有符号,取高 32 位
|
||||
// MD_MULHSU mulhsu :有符号 x 无符号,取高 32 位
|
||||
// MD_MULHU mulhu :无符号 x 无符号,取高 32 位
|
||||
// MD_DIVU divu :无符号商
|
||||
// MD_REMU remu :无符号余数
|
||||
//
|
||||
// result_o / result2_o 的含义与原来的 ALU 保持一致:乘法时 result_o 为低 32 位、
|
||||
// result2_o 为高 32 位;除法时 result_o 为商、result2_o 为余数。译码器用 wb_sel
|
||||
// 决定写回哪一个(mulhu、remu 用 wb_sel=3 取 result2),因此顶层只需把这一对
|
||||
// 结果接到原来的 EX 结果总线上,其余流水逻辑不用改。
|
||||
// -----------------------------------------------------------------------------
|
||||
module cpu21_riscv_muldiv (
|
||||
input wire clk,
|
||||
input wire reset, // 异步复位,高有效(与顶层一致)
|
||||
input wire start_i, // 指令在 EX 级且是乘除法(电平,模块内部只在空闲时响应)
|
||||
input wire [2:0] op_i, // 乘除法操作类型
|
||||
input wire [31:0] a_i, // EX 级 src1
|
||||
input wire [31:0] b_i, // EX 级 src2(ALU 的 B 端)
|
||||
output wire busy_o, // 迭代中(不含结果定型那一拍)
|
||||
output wire done_o, // 结果有效(单拍,此时顶层推进流水线)
|
||||
output wire [31:0] result_o, // 主结果
|
||||
output wire [31:0] result2_o // 第二结果(乘法高 32 位 / 除法余数)
|
||||
);
|
||||
localparam [2:0] MD_NONE = 3'd0;
|
||||
localparam [2:0] MD_MUL = 3'd1;
|
||||
localparam [2:0] MD_MULH = 3'd2;
|
||||
localparam [2:0] MD_MULHSU = 3'd3;
|
||||
localparam [2:0] MD_MULHU = 3'd4;
|
||||
localparam [2:0] MD_DIVU = 3'd5;
|
||||
localparam [2:0] MD_REMU = 3'd6;
|
||||
|
||||
localparam [1:0] S_IDLE = 2'd0;
|
||||
localparam [1:0] S_RUN = 2'd1;
|
||||
localparam [1:0] S_DONE = 2'd2;
|
||||
|
||||
reg [ 1:0] state_q;
|
||||
reg [ 5:0] cnt_q; // 0..32:0..31 为迭代,32 为结果定型
|
||||
reg [ 2:0] op_q;
|
||||
reg is_div_q;
|
||||
|
||||
// 乘法数据通路:64 位被乘数(每拍左移)、32 位乘数(每拍右移)、64 位累加器。
|
||||
reg [63:0] mcand_q;
|
||||
reg [31:0] mplier_q;
|
||||
reg [63:0] prod_q;
|
||||
reg sign_q;
|
||||
|
||||
// 除法数据通路:32 位除数、32 位商/被除数移位寄存器、33 位余数。
|
||||
reg [31:0] dvsr_q;
|
||||
reg [31:0] quo_q;
|
||||
reg [32:0] rem_q;
|
||||
reg [31:0] dvd_q; // 原始被除数(除零时原样返回)
|
||||
reg div_zero_q;
|
||||
|
||||
// 结果寄存器。
|
||||
reg [31:0] res_q;
|
||||
reg [31:0] res2_q;
|
||||
|
||||
assign busy_o = (state_q == S_RUN);
|
||||
assign done_o = (state_q == S_DONE);
|
||||
assign result_o = res_q;
|
||||
assign result2_o = res2_q;
|
||||
|
||||
// 乘法操作数准备:按操作类型决定是否取绝对值,并给出结果符号。
|
||||
wire a_signed = (op_i == MD_MULH) || (op_i == MD_MULHSU);
|
||||
wire b_signed = (op_i == MD_MULH);
|
||||
wire [31:0] a_mag = (a_signed && a_i[31]) ? (~a_i + 32'd1) : a_i;
|
||||
wire [31:0] b_mag = (b_signed && b_i[31]) ? (~b_i + 32'd1) : b_i;
|
||||
wire sign_in = ((op_i == MD_MULH) && (a_i[31] ^ b_i[31])) || ((op_i == MD_MULHSU) && a_i[31]);
|
||||
|
||||
// 除法的一步(恢复余数法):把被除数最高位移进余数,够减则减并把商位置 1。
|
||||
wire [32:0] rem_shift = {rem_q[31:0], quo_q[31]};
|
||||
wire rem_ge = (rem_shift >= {1'b0, dvsr_q});
|
||||
wire [32:0] rem_next = rem_ge ? (rem_shift - {1'b0, dvsr_q}) : rem_shift;
|
||||
wire [31:0] quo_next = {quo_q[30:0], rem_ge};
|
||||
|
||||
// 迭代结束时的符号修正:prod_mag 为无符号乘积,sign_q 为 1 时取负。
|
||||
wire [63:0] prod_fix = sign_q ? (~prod_q + 64'd1) : prod_q;
|
||||
|
||||
always @(posedge clk or posedge reset) begin
|
||||
if (reset) begin
|
||||
state_q <= S_IDLE;
|
||||
cnt_q <= 6'd0;
|
||||
op_q <= MD_NONE;
|
||||
is_div_q <= 1'b0;
|
||||
mcand_q <= 64'd0;
|
||||
mplier_q <= 32'd0;
|
||||
prod_q <= 64'd0;
|
||||
sign_q <= 1'b0;
|
||||
dvsr_q <= 32'd0;
|
||||
quo_q <= 32'd0;
|
||||
rem_q <= 33'd0;
|
||||
dvd_q <= 32'd0;
|
||||
div_zero_q <= 1'b0;
|
||||
res_q <= 32'd0;
|
||||
res2_q <= 32'd0;
|
||||
end else begin
|
||||
case (state_q)
|
||||
// 空闲:指令进入 EX 且是乘除法时装载操作数,下一拍开始迭代。
|
||||
S_IDLE: begin
|
||||
if (start_i) begin
|
||||
state_q <= S_RUN;
|
||||
cnt_q <= 6'd0;
|
||||
op_q <= op_i;
|
||||
is_div_q <= (op_i == MD_DIVU) || (op_i == MD_REMU);
|
||||
// 乘法初始化(先取绝对值,符号留到最后修正)。
|
||||
mcand_q <= {32'b0, a_mag};
|
||||
mplier_q <= b_mag;
|
||||
prod_q <= 64'd0;
|
||||
sign_q <= sign_in;
|
||||
// 除法初始化(无符号)。
|
||||
dvsr_q <= b_i;
|
||||
quo_q <= a_i;
|
||||
rem_q <= 33'd0;
|
||||
dvd_q <= a_i;
|
||||
div_zero_q <= (b_i == 32'd0);
|
||||
end
|
||||
end
|
||||
|
||||
// 迭代:cnt 0..31 各走一步,32 拍后把结果定型。
|
||||
S_RUN: begin
|
||||
if (cnt_q == 6'd32) begin
|
||||
state_q <= S_DONE;
|
||||
if (is_div_q) begin
|
||||
res_q <= div_zero_q ? 32'hffff_ffff : quo_q;
|
||||
res2_q <= div_zero_q ? dvd_q : rem_q[31:0];
|
||||
end else begin
|
||||
res_q <= (op_q == MD_MULHU || op_q == MD_MUL) ? prod_fix[31:0] : prod_fix[63:32];
|
||||
res2_q <= prod_fix[63:32];
|
||||
end
|
||||
end else begin
|
||||
cnt_q <= cnt_q + 6'd1;
|
||||
if (is_div_q) begin
|
||||
rem_q <= rem_next;
|
||||
quo_q <= quo_next;
|
||||
end else begin
|
||||
prod_q <= mplier_q[0] ? (prod_q + mcand_q) : prod_q;
|
||||
mcand_q <= {mcand_q[62:0], 1'b0};
|
||||
mplier_q <= {1'b0, mplier_q[31:1]};
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// 结果有效一拍,顶层据此推进流水线;随后回到空闲。
|
||||
default: state_q <= S_IDLE;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
@@ -12,7 +12,7 @@ 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 stall_i, // load-use 停顿或多周期乘除法停留
|
||||
input wire flush_i, // 冲刷年轻指令
|
||||
input wire cond_taken_i, // EX 级条件分支实际跳转
|
||||
input wire cond_valid_i, // EX 级条件分支有效
|
||||
|
||||
@@ -131,6 +131,7 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
reg idex_mem_write_q;
|
||||
reg idex_mem_byte_q;
|
||||
reg [2:0] idex_wb_sel_q;
|
||||
reg [2:0] idex_md_op_q; // 乘除法操作类型(0=非乘除法)
|
||||
reg [11:0] idex_csr_addr_q;
|
||||
reg idex_branch_q;
|
||||
reg idex_beq_q;
|
||||
@@ -234,6 +235,7 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
wire [31:0] d_imm;
|
||||
wire [3:0] d_aluop;
|
||||
wire [2:0] d_wb_sel;
|
||||
wire [2:0] d_md_op;
|
||||
wire [11:0] d_csr_addr;
|
||||
wire d_uses_rs1;
|
||||
wire d_uses_rs2;
|
||||
@@ -265,6 +267,7 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
.imm_o (d_imm),
|
||||
.alu_op_o (d_aluop),
|
||||
.wb_sel_o (d_wb_sel),
|
||||
.md_op_o (d_md_op),
|
||||
.csr_addr_o (d_csr_addr),
|
||||
.uses_rs1_o (d_uses_rs1),
|
||||
.uses_rs2_o (d_uses_rs2),
|
||||
@@ -289,11 +292,10 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
.csr_write_o (d_csr_write)
|
||||
);
|
||||
|
||||
// 写回值选择:wb_sel 0=ALU 结果,1=访存数据,2=PC+4,3=MUL 高位/余数,
|
||||
// 4=CSR 旧值。
|
||||
// 写回值选择:wb_sel 0=ALU 结果,1=访存数据,2=PC+4(已存在 alu_result 里),
|
||||
// 3=MUL 高位/余数,4=CSR 旧值。
|
||||
wire [31:0] wb_value;
|
||||
assign wb_value = (memwb_wb_sel_q == 3'd1) ? memwb_mem_data_q :
|
||||
(memwb_wb_sel_q == 3'd2) ? (memwb_pc_q + 32'd4) :
|
||||
(memwb_wb_sel_q == 3'd3) ? memwb_alu_result2_q :
|
||||
(memwb_wb_sel_q == 3'd4) ? memwb_csr_old_q :
|
||||
memwb_alu_result_q;
|
||||
@@ -329,9 +331,10 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
// -------------------------------------------------------------------------
|
||||
// EX 级:前递网络、ALU 运算、存储对齐与控制流裁决
|
||||
// -------------------------------------------------------------------------
|
||||
// EX/MEM 级要前递的值(按 wb_sel 选取)。
|
||||
// EX/MEM 级要前递的值(按 wb_sel 选取)。JAL/JALR 的链接地址(PC+4)已在
|
||||
// EX 级预先算好并存入 exmem_alu_result_q,所以这里 wb_sel=2 直接取该寄存器,
|
||||
// 前递路径上不再有 PC+4 加法器(100 MHz 下这是最长路径之一)。
|
||||
wire [31:0] exmem_forward_value =
|
||||
(exmem_wb_sel_q == 3'd2) ? (exmem_pc_q + 32'd4) :
|
||||
(exmem_wb_sel_q == 3'd3) ? exmem_alu_result2_q :
|
||||
(exmem_wb_sel_q == 3'd4) ? exmem_csr_old_q :
|
||||
exmem_alu_result_q;
|
||||
@@ -368,7 +371,7 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
(idex_csr_addr_q == 12'h004) ? ustatus_value :
|
||||
(idex_csr_addr_q == 12'h041) ? uepc_value : 32'b0;
|
||||
|
||||
// ALU 实例(纯组合逻辑)。
|
||||
// ALU 实例(只做单拍运算;乘除法见下面的多周期单元)。
|
||||
cpu21_riscv_alu u_ex_alu (
|
||||
.op (idex_aluop_q),
|
||||
.a (ex_src1),
|
||||
@@ -377,6 +380,42 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
.result2(ex_alu_result2)
|
||||
);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 多周期乘除法单元:EX 级停留若干拍等待结果,不再把 32x32 乘法/除法
|
||||
// 放在组合路径上(原来这部分的组合延时约 94 ns,100 MHz 下无法收敛)。
|
||||
// 指令在 EX 级期间 ex_md_stall 有效:ID/EX 保持不变、EX/MEM 插气泡、
|
||||
// 取指/译码冻结;迭代结束(md_done)后照常推进。
|
||||
// -------------------------------------------------------------------------
|
||||
wire md_busy;
|
||||
wire md_done;
|
||||
wire [31:0] md_result;
|
||||
wire [31:0] md_result2;
|
||||
|
||||
wire ex_is_muldiv = idex_valid_q && (idex_md_op_q != 3'd0);
|
||||
wire ex_md_stall = ex_is_muldiv && !md_done;
|
||||
|
||||
cpu21_riscv_muldiv u_muldiv (
|
||||
.clk (clk),
|
||||
.reset (reset),
|
||||
.start_i (ex_is_muldiv),
|
||||
.op_i (idex_md_op_q),
|
||||
.a_i (ex_src1),
|
||||
.b_i (ex_alu_b),
|
||||
.busy_o (md_busy),
|
||||
.done_o (md_done),
|
||||
.result_o (md_result),
|
||||
.result2_o(md_result2)
|
||||
);
|
||||
|
||||
// EX 级最终结果:乘除法取多周期单元的输出;JAL/JALR(wb_sel=2)的链接地址
|
||||
// PC+4 在这里算好随流水寄存器传下去,避免“写回值 → 前递 → 停机判定 →
|
||||
// 流水寄存器时钟使能”这条组合路径上出现 32 位加法器。
|
||||
wire [31:0] ex_result =
|
||||
ex_is_muldiv ? md_result :
|
||||
(idex_wb_sel_q == 3'd2) ? (idex_pc_q + 32'd4) :
|
||||
ex_alu_result;
|
||||
wire [31:0] ex_result2 = ex_is_muldiv ? md_result2 : ex_alu_result2;
|
||||
|
||||
// 控制流裁决:条件分支方向/目标、JAL/JALR 目标、是否需要重定向。
|
||||
cpu21_riscv_branch_unit u_branch_unit (
|
||||
.valid_i (idex_valid_q),
|
||||
@@ -450,8 +489,14 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
// 中断 / CSR / 嵌套返回栈
|
||||
// -------------------------------------------------------------------------
|
||||
wire ex_uret_redirect = idex_valid_q && idex_uret_q;
|
||||
// EX 级正在重定向/URET/停机时不允许响应中断。
|
||||
wire ex_block_irq = ex_redirect_valid || ex_uret_redirect || ex_halt_event;
|
||||
// EX 级正在 URET,或乘除法还没算完时,不响应中断:前者与中断同时发生会丢掉
|
||||
// URET 的返回地址,后者会在结果未定型时推进流水线,向 rd 写回错误值。
|
||||
// 这里不再包含 ex_redirect_valid / ex_halt_event:它们本身就会让
|
||||
// flush_younger 有效(流水线照常被冲刷),而中断优先于重定向时返回地址由
|
||||
// irq_save_pc 给出(已正确跳转的分支取目标地址)。这样就把
|
||||
// “分支判决 → 中断控制器 → flush → 流水寄存器控制”这条 100 MHz 下的
|
||||
// 关键路径切断了(原来要绕经中断控制器再回到 flush)。
|
||||
wire ex_block_irq = ex_uret_redirect || ex_md_stall;
|
||||
// 中断发生时要保存的返回地址取"下一个架构 PC":若 EX 是一条预测正确
|
||||
// 且已跳转的分支,则该 PC 就是它的目标地址;若 EX 为空,则 IF/ID 中
|
||||
// 保存的就是尚未执行的最老指令。
|
||||
@@ -581,6 +626,7 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
idex_mem_write_q <= 1'b0;
|
||||
idex_mem_byte_q <= 1'b0;
|
||||
idex_wb_sel_q <= 3'b0;
|
||||
idex_md_op_q <= 3'b0;
|
||||
idex_csr_addr_q <= 12'b0;
|
||||
idex_branch_q <= 1'b0;
|
||||
idex_beq_q <= 1'b0;
|
||||
@@ -643,29 +689,48 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
memwb_mem_to_reg_q <= exmem_mem_to_reg_q;
|
||||
memwb_wb_sel_q <= exmem_wb_sel_q;
|
||||
|
||||
// EX/MEM 流水寄存器捕获。
|
||||
exmem_valid_q <= idex_valid_q;
|
||||
exmem_pc_q <= idex_pc_q;
|
||||
exmem_ir_q <= idex_ir_q;
|
||||
exmem_alu_result_q <= ex_alu_result;
|
||||
exmem_alu_result2_q <= ex_alu_result2;
|
||||
exmem_csr_old_q <= ex_csr_old_value;
|
||||
exmem_write_data_q <= ex_store_data;
|
||||
exmem_rd_q <= idex_rd_q;
|
||||
exmem_reg_write_q <= idex_reg_write_q;
|
||||
exmem_mem_to_reg_q <= idex_mem_to_reg_q;
|
||||
exmem_mem_write_q <= idex_mem_write_q;
|
||||
exmem_wstrb_q <= ex_store_wstrb;
|
||||
exmem_wb_sel_q <= idex_wb_sel_q;
|
||||
|
||||
// 控制优先级:中断 > URET > ecall 停机 > 分支重定向 >
|
||||
// load-use 停顿 > 正常流水推进。
|
||||
if (flush_younger || pipeline_stall || halted_q) begin
|
||||
idex_valid_q <= 1'b0;
|
||||
idex_pred_taken_q <= 1'b0;
|
||||
idex_pred_target_q <= 32'b0;
|
||||
// EX/MEM 流水寄存器捕获。乘除法迭代期间(ex_md_stall)往 EX/MEM 插气泡,
|
||||
// 让乘除法指令继续占用 EX,结果在 md_done 那一拍才写入 EX/MEM。
|
||||
if (ex_md_stall) begin
|
||||
exmem_valid_q <= 1'b0;
|
||||
exmem_pc_q <= 32'b0;
|
||||
exmem_ir_q <= 32'b0;
|
||||
exmem_alu_result_q <= 32'b0;
|
||||
exmem_alu_result2_q <= 32'b0;
|
||||
exmem_csr_old_q <= 32'b0;
|
||||
exmem_write_data_q <= 32'b0;
|
||||
exmem_rd_q <= 5'b0;
|
||||
exmem_reg_write_q <= 1'b0;
|
||||
exmem_mem_to_reg_q <= 1'b0;
|
||||
exmem_mem_write_q <= 1'b0;
|
||||
exmem_wstrb_q <= 4'b0;
|
||||
exmem_wb_sel_q <= 3'b0;
|
||||
end else begin
|
||||
idex_valid_q <= ifid_valid_q;
|
||||
exmem_valid_q <= idex_valid_q;
|
||||
exmem_pc_q <= idex_pc_q;
|
||||
exmem_ir_q <= idex_ir_q;
|
||||
exmem_alu_result_q <= ex_result;
|
||||
exmem_alu_result2_q <= ex_result2;
|
||||
exmem_csr_old_q <= ex_csr_old_value;
|
||||
exmem_write_data_q <= ex_store_data;
|
||||
exmem_rd_q <= idex_rd_q;
|
||||
exmem_reg_write_q <= idex_reg_write_q;
|
||||
exmem_mem_to_reg_q <= idex_mem_to_reg_q;
|
||||
exmem_mem_write_q <= idex_mem_write_q;
|
||||
exmem_wstrb_q <= ex_store_wstrb;
|
||||
exmem_wb_sel_q <= idex_wb_sel_q;
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// ID/EX 装载。
|
||||
//
|
||||
// payload(PC/IR/控制位/预测位)只在 load-use 停顿、乘除法迭代、停机时
|
||||
// 保持(时钟使能与“冲刷”无关),其余情况一律装载;只有 valid 位受冲刷
|
||||
// 控制,冲刷时清 0。这样“EX 级组合判决(分支/中断)→ 流水寄存器时钟
|
||||
// 使能”这条 100 MHz 下最长的路径就只剩下一个触发器(valid),payload
|
||||
// 即使装进错误路径的数据也无害——所有使用者都以 idex_valid_q 为前提。
|
||||
// ---------------------------------------------------------------------
|
||||
if (!(pipeline_stall || halted_q || ex_md_stall)) begin
|
||||
idex_pc_q <= ifid_pc_q;
|
||||
idex_ir_q <= ifid_ir_q;
|
||||
idex_rs1_value_q <= d_rs1_value;
|
||||
@@ -681,6 +746,7 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
idex_mem_write_q <= d_mem_write;
|
||||
idex_mem_byte_q <= d_mem_byte;
|
||||
idex_wb_sel_q <= d_wb_sel;
|
||||
idex_md_op_q <= d_md_op;
|
||||
idex_csr_addr_q <= d_csr_addr;
|
||||
idex_branch_q <= d_branch;
|
||||
idex_beq_q <= d_beq;
|
||||
@@ -700,6 +766,16 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
idex_pred_target_q <= ifid_pred_target_q;
|
||||
end
|
||||
|
||||
if (halted_q || ex_md_stall) begin
|
||||
// 停机 / 乘除法迭代:valid 保持不变(指令继续占用 EX)。
|
||||
end else if (pipeline_stall || flush_younger) begin
|
||||
// load-use 停顿:load 已经进 EX/MEM,这里给 EX 装一个气泡(payload 保持
|
||||
// 原值,valid=0 时无害);冲刷同理。
|
||||
idex_valid_q <= 1'b0;
|
||||
end else begin
|
||||
idex_valid_q <= ifid_valid_q;
|
||||
end
|
||||
|
||||
if (take_irq) begin
|
||||
pc_q <= irq_vector;
|
||||
ifid_valid_q <= 1'b0;
|
||||
@@ -720,7 +796,7 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
ifid_valid_q <= 1'b0;
|
||||
ifid_pred_taken_q <= 1'b0;
|
||||
ifid_pred_target_q <= 32'b0;
|
||||
end else if (pipeline_stall) begin
|
||||
end else if (pipeline_stall || ex_md_stall) begin
|
||||
pc_q <= pc_q;
|
||||
ifid_valid_q <= ifid_valid_q;
|
||||
ifid_pc_q <= ifid_pc_q;
|
||||
@@ -748,7 +824,7 @@ module cpu21_riscv_redirect_int_bpb #(
|
||||
.clk (clk),
|
||||
.reset (reset),
|
||||
.inc_cycle_i (!halted_q),
|
||||
.stall_i (pipeline_stall),
|
||||
.stall_i (pipeline_stall || ex_md_stall),
|
||||
.flush_i (flush_younger),
|
||||
.cond_taken_i (idex_valid_q && idex_branch_q && ex_branch_taken),
|
||||
.cond_valid_i (idex_valid_q && idex_branch_q),
|
||||
|
||||
Reference in New Issue
Block a user