乘除法改为多周期时序单元,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:
2026-09-13 18:45:17 +08:00
parent 556fbe9530
commit 4a81685066
17 changed files with 621 additions and 154 deletions
+14 -20
View File
@@ -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