- 新增 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 文件已更新。
310 lines
11 KiB
Verilog
310 lines
11 KiB
Verilog
`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 reg [ 2:0] md_op_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 bge_o,
|
||
output reg bltu_o,
|
||
output reg bgeu_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;
|
||
// LUI 为扩展指令(标准 RISC-V opcode 0x37 -> IR[6:2]=0x0d)。
|
||
localparam OP_LUI = 5'h0d;
|
||
|
||
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;
|
||
localparam ALU_MULH = 4'd13;
|
||
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];
|
||
|
||
// 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;
|
||
md_op_o = MD_NONE;
|
||
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;
|
||
bge_o = 1'b0;
|
||
bltu_o = 1'b0;
|
||
bgeu_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;
|
||
// 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: 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'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;
|
||
endcase
|
||
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/sltiu/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'b011: alu_op_o = ALU_SLTU; // sltiu:无符号小于置 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
|
||
|
||
// 加载高位立即数 lui:rd = imm[31:12] << 12。
|
||
// 不读寄存器;用 ALU_SRC 选立即数,ALU 直通 B 端输出。
|
||
OP_LUI: begin
|
||
reg_write_o = 1'b1;
|
||
alu_src_o = 1'b1;
|
||
alu_op_o = ALU_PASSB;
|
||
imm_o = {ir_i[31:12], 12'b0};
|
||
wb_sel_o = 3'd0;
|
||
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)/bge(101)/bltu(110)/bgeu(111)。
|
||
OP_BRANCH: begin
|
||
if ((funct3 == 3'b000) || (funct3 == 3'b001) || (funct3 == 3'b100) ||
|
||
(funct3 == 3'b101) || (funct3 == 3'b110) || (funct3 == 3'b111)) 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);
|
||
bge_o = (funct3 == 3'b101);
|
||
bltu_o = (funct3 == 3'b110);
|
||
bgeu_o = (funct3 == 3'b111);
|
||
// 有符号比较用 ALU_SLT、无符号比较用 ALU_SLTU,相等/不等用减法;
|
||
// 分支是否成立由 EX 级的分支裁决单元决定,这里只与真值表保持一致。
|
||
alu_op_o = (blt_o || bge_o) ? ALU_SLT : ((bltu_o || bgeu_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
|