Files
risc-v-cpu/pipelined-cpu/srcs/rtl/cpu21_riscv_decoder.v
T
Anccend cb0cb433ac 源文件收敛到 pipelined-cpu/ 一份:删除根目录 rtl/board/testbench/tools,补充回归脚本
- 现在只有一份源文件,就在 pipelined-cpu/ 下:
    srcs/rtl(13 个处理器 RTL)、srcs/board、srcs/constrs、srcs/tb、srcs/rom、
    tools/(rv_asm.py、hex2vh.py、run_regress.ps1)、programs/(汇编源码与期望值)。
  仓库根目录的 rtl/、board/、testbench/、tools/ 已删除(删除前逐个文件比对过 MD5,
  与工程目录里的副本完全一致),说明文档仍放在最外层
  cpu21_riscv_redirect_int_bpb说明.md。
- .gitignore 改为:忽略 pipelined-cpu/ 下的工程生成物(*.xpr、*.runs、*.sim、*.cache…),
  只跟踪 srcs/、tools/、programs/、create_project.tcl;_tmp/、_vivado/ 等本地临时目录
  继续忽略。
- 新增 tools/run_regress.ps1:一键回归脚本。编译 srcs/rtl/ → 跑 muldiv/isa/lui/mmio
  四个指令测试并与 programs/*.expected 逐值比对 → 跑两个流水基线 → 跑板级顶层
  (-d BOARD_ROM_FAST)检查跑马灯,最后汇总 PASS/FAIL 并返回失败数作为退出码。
  编译与仿真的中间文件写在 %TEMP%\cpu21_regress,不污染工程目录。实测全部通过。
- 新增 programs/board_demo_fast.asm:快速版演示程序(延时初值 64,约 2.6 µs/步),
  对应 srcs/board/cpu21_board_rom_fast.vh;仿真时用 -d BOARD_ROM_FAST 才会选到它。
- 两个测试平台增加 srcs/rom 探测路径,工程目录内直接 Run Simulation 即可加载镜像。
- 文档:目录结构一节按新布局重写;新增“回归测试”一节(脚本用法 + 期望基线数字);
  上板章节的生成/仿真命令改为相对工程目录(python tools/rv_asm.py programs/…、
  xvlog -d BOARD_ROM_FAST srcs/rtl/*.v …);tools 里的用法示例同步更新;
  “工程分享”一节去掉已不存在的副本同步说明。
- 清理:删除本地 _tmp/(约 57 MB)、_vivado/、.Xil/ 临时目录。
2026-09-13 19:09:36 +08:00

310 lines
11 KiB
Verilog
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
`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