源文件收敛到 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/ 临时目录。
This commit is contained in:
2026-09-13 19:09:36 +08:00
parent fed01f5910
commit cb0cb433ac
51 changed files with 506 additions and 81 deletions
@@ -0,0 +1,309 @@
`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