From ccd1b917d05bff66cb81a0a2a0011056c101a6ac Mon Sep 17 00:00:00 2001 From: ANCCEND <3064429095@qq.com> Date: Sat, 12 Sep 2026 19:10:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20ISA=20=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E8=B5=84=E4=BA=A7=EF=BC=9A=E8=BF=B7=E4=BD=A0=E6=B1=87=E7=BC=96?= =?UTF-8?q?=E5=99=A8=20+=20=E6=8C=87=E4=BB=A4=E6=B5=8B=E8=AF=95=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tools/rv_asm.py - 极简 RISC-V 汇编器(Python 3,无第三方依赖),支持 add/sub/and/or/xor/sll/srl/sra/slt/sltu、mul/mulh/mulhsu/mulhu、 addi/slti/sltiu/xori/ori/andi、slli/srli/srai、lw/sw、 beq/bne/blt/bge/bltu/bgeu、lui、jal/jalr、ecall、标签与 # 注释; - 提供 print / halt 两条双字伪指令(分别等价于 addi a7,zero,34 + ecall、 addi a7,zero,10 + ecall); - I 型与访存立即数做 12 位有符号范围检查,越界直接报错,避免静默截断; - 除 hex 外同时输出 .lst 编码清单,便于与 .asm 逐行核对。 testbench/programs/isa_test.asm + isa_test.expected - 指令扩展验证程序:sltiu(两个方向)、xor/sll/srl/sra、mul/mulh/mulhu/mulhsu、 bltu/bge/bgeu 的有符号与无符号区分,以及 bge/bgeu/bltu 三个循环, 共 24 项 LED 输出,期望值逐行保存在 isa_test.expected。 testbench/programs/lui_test.asm - LUI 验证程序:0x12345 / 0xfffff / 0x80000 与 lui+addi 组合,共 4 项输出。 另外包含编辑器/格式化工具对 cpu21_riscv_alu.v、cpu21_riscv_branch_unit.v、 cpu21_riscv_decoder.v 的重排(纯格式,语义等价)。 验证(xsim + testbench/tb_no_intr.v,按提交后的正式路径生成 hex) - isa_test:24 项 LED 输出与 isa_test.expected 逐项一致,143 周期后 halt 停机; - lui_test:12345000 / fffff000 / 80000000 / 12345678,21 周期后停机; - 重新编译无 error / warning。 --- cpu21_riscv_alu.v | 10 +- cpu21_riscv_branch_unit.v | 6 +- cpu21_riscv_decoder.v | 11 +- cpu21_riscv_redirect_int_bpb说明.md | 24 +-- testbench/programs/isa_test.asm | 109 +++++++++++++ testbench/programs/isa_test.expected | 24 +++ testbench/programs/lui_test.asm | 26 ++++ tools/rv_asm.py | 220 +++++++++++++++++++++++++++ 8 files changed, 405 insertions(+), 25 deletions(-) create mode 100644 testbench/programs/isa_test.asm create mode 100644 testbench/programs/isa_test.expected create mode 100644 testbench/programs/lui_test.asm create mode 100644 tools/rv_asm.py diff --git a/cpu21_riscv_alu.v b/cpu21_riscv_alu.v index e0c4e95..57eec36 100644 --- a/cpu21_riscv_alu.v +++ b/cpu21_riscv_alu.v @@ -32,8 +32,8 @@ module cpu21_riscv_alu ( 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 无符号 + reg signed [63:0] mult_ss; // 有符号 x 有符号 + reg signed [63:0] mult_su; // 有符号 x 无符号 always @* begin mult_result = a * b; @@ -57,8 +57,10 @@ module cpu21_riscv_alu ( 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_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_PASSB: result = b; default: result = 32'b0; diff --git a/cpu21_riscv_branch_unit.v b/cpu21_riscv_branch_unit.v index 57496d0..6369bf5 100644 --- a/cpu21_riscv_branch_unit.v +++ b/cpu21_riscv_branch_unit.v @@ -34,9 +34,9 @@ module cpu21_riscv_branch_unit ( output wire [31:0] redirect_pc_o ); // 比较结果预先算好,避免表达式过长被自动换行拆散。 - wire lt_signed = $signed(src1_i) < $signed(src2_i); - wire ge_signed = $signed(src1_i) >= $signed(src2_i); - wire lt_unsigned = src1_i < src2_i; + wire lt_signed = $signed(src1_i) < $signed(src2_i); + wire ge_signed = $signed(src1_i) >= $signed(src2_i); + wire lt_unsigned = src1_i < src2_i; wire ge_unsigned = src1_i >= src2_i; // 条件分支裁决:beq 相等、bne 不等、blt/bge 有符号比较、bltu/bgeu 无符号比较。 diff --git a/cpu21_riscv_decoder.v b/cpu21_riscv_decoder.v index 37af867..2be54ea 100644 --- a/cpu21_riscv_decoder.v +++ b/cpu21_riscv_decoder.v @@ -115,14 +115,14 @@ module cpu21_riscv_decoder ( // (funct3=111 形式)。mulhu 与 remu 都取 ALU 的 result2(wb_sel=3)。 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'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'b011: begin // mulhu:无符号乘积高 32 位 alu_op_o = ALU_MUL; wb_sel_o = 3'd3; end - 3'b111: begin // remu(课程约定):取余数 + 3'b111: begin // remu(课程约定):取余数 alu_op_o = ALU_DIVU; wb_sel_o = 3'd3; end @@ -213,8 +213,7 @@ module cpu21_riscv_decoder ( 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); + 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 diff --git a/cpu21_riscv_redirect_int_bpb说明.md b/cpu21_riscv_redirect_int_bpb说明.md index 8bb3bff..2c35c59 100644 --- a/cpu21_riscv_redirect_int_bpb说明.md +++ b/cpu21_riscv_redirect_int_bpb说明.md @@ -27,18 +27,18 @@ 表中的 `opcode` 是控制器使用的五位字段 `IR[6:2]`,不是包含最低两位的 7 位原始 opcode。 因此它与标准 RISC-V 指令的低 7 位 opcode 相差右移两位。 -| 指令类别 | opcode | 说明 | -| ------------------------------ | -----: | --------------------------------------- | -| R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra | -| M 扩展(R 型) | `0x0c` | `funct7=0000001`:mul/mulh/mulhsu/mulhu(另有课程约定的 remu,`funct3=111`) | -| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/sltiu/slli/srli/srai | -| `lui`(扩展) | `0x0d` | 加载高位立即数,`rd = imm[31:12] << 12` | -| `lw` | `0x00` | `funct3=010` | -| `sw` / `sb` | `0x08` | `funct3=010` / `000` | -| `beq` / `bne` / `blt` / `bge` / `bltu` / `bgeu` | `0x18` | `funct3=000` / `001` / `100` / `101` / `110` / `111` | -| `jal` | `0x1b` | J 型立即数 | -| `jalr` | `0x19` | `funct3=000` | -| 系统类 | `0x1c` | `ecall`、`URET`、CSRRSI、CSRRCI | +| 指令类别 | opcode | 说明 | +| ----------------------------------------------- | -----: | ---------------------------------------------------------------------------- | +| R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra | +| M 扩展(R 型) | `0x0c` | `funct7=0000001`:mul/mulh/mulhsu/mulhu(另有课程约定的 remu,`funct3=111`) | +| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/sltiu/slli/srli/srai | +| `lui`(扩展) | `0x0d` | 加载高位立即数,`rd = imm[31:12] << 12` | +| `lw` | `0x00` | `funct3=010` | +| `sw` / `sb` | `0x08` | `funct3=010` / `000` | +| `beq` / `bne` / `blt` / `bge` / `bltu` / `bgeu` | `0x18` | `funct3=000` / `001` / `100` / `101` / `110` / `111` | +| `jal` | `0x1b` | J 型立即数 | +| `jalr` | `0x19` | `funct3=000` | +| 系统类 | `0x1c` | `ecall`、`URET`、CSRRSI、CSRRCI | `ecall` 和 `URET` 都是 `opcode=0x1c、funct3=0`,由 `IR[21]` 区分: `IR[21]=0` 是 `ecall`,`IR[21]=1` 是 `URET`。这一点来自原控制器中的 `IR21` 分支逻辑。 diff --git a/testbench/programs/isa_test.asm b/testbench/programs/isa_test.asm new file mode 100644 index 0000000..87947cc --- /dev/null +++ b/testbench/programs/isa_test.asm @@ -0,0 +1,109 @@ +# 指令扩展验证程序:每条被测指令的结果都通过 ecall(a7=34) 输出,最后用 halt 停机。 +# 期望输出顺序见每行注释(共 24 次输出),同时保存在同目录 isa_test.expected(每行一项 LED 值)。 +# +# 用法(在仓库根目录执行): +# python tools/rv_asm.py testbench/programs/isa_test.asm <仿真工作目录>/risc-v-benchmark_ccab.hex +# 再用 testbench/tb_no_intr.v 仿真;hex 命名为 risc-v-benchmark_ccab.hex 时会被测试 +# 平台优先从仿真工作目录载入(参见 tb_no_intr.v 的 ROM 探测顺序)。 +# print / halt 是 tools/rv_asm.py 提供的双字伪指令: +# print = addi a7,zero,34 + ecall;halt = addi a7,zero,10 + ecall。 + +# ---- sltiu:立即数符号扩展后做无符号比较 ---- + addi t0, zero, 1 + addi t1, zero, -1 # t1 = 0xffffffff + sltiu a0, t0, -1 # 1 00000001 + print + sltiu a0, t1, 1 # 0xffffffff 00000000 + print + +# ---- xor / sll / srl / sra(原有指令回归)---- + addi t0, zero, 0xff + addi t1, zero, -1 + xor a0, t0, t1 # 0xff ^ 0xffffffff -> ffffff00 + print + + addi t0, zero, 1 + addi t1, zero, 31 + sll a0, t0, t1 # 1 << 31 -> 80000000 + print + + addi t0, zero, -1 + addi t1, zero, 28 + srl a0, t0, t1 # 逻辑右移 -> 0000000f + print + sra a0, t0, t1 # 算术右移 -> ffffffff + print + +# ---- mul:无符号乘积低 32 位 ---- + addi t0, zero, 1234 + addi t1, zero, 567 + mul a0, t0, t1 # 1234*567 = 0x000aad1e + print + +# ---- mulh / mulhu / mulhsu:64 位乘积的高 32 位 ---- + addi t0, zero, -1 # 有符号 -1 / 无符号 0xffffffff + addi t1, zero, -1 + mulh a0, t0, t1 # (-1)*(-1)=1 -> 00000000 + print + mulhu a0, t0, t1 # 0xffffffff^2 -> fffffffe + print + addi t0, zero, -1 + addi t1, zero, 2 + mulhsu a0, t0, t1 # (-1)*2=-2 -> ffffffff + print + +# ---- bltu:0 = ,(-1) >= 0 不成立,应走 fall-through ---- + addi t0, zero, -1 + bge t0, zero, bge_bad + addi a0, zero, 0x602 # 00000602 + print + jal zero, bge_done +bge_bad: + addi a0, zero, 0x102 # 不应执行 + print +bge_done: + +# ---- bgeu:无符号 >= ,0xffffffff >=u 0 成立 ---- + addi t0, zero, -1 + bgeu t0, zero, bgeu_ok + addi a0, zero, 0x103 # 不应执行 + print +bgeu_ok: + addi a0, zero, 0x603 # 00000603 + print + +# ---- bge 循环:从 -3 递增到 0(共 4 次输出)---- + addi s1, zero, -3 +bge_loop: + add a0, zero, s1 + print # fffffffd / fffffffe / ffffffff / 00000000 + addi s1, s1, 1 + bge zero, s1, bge_loop + +# ---- bgeu 循环:3 -> 1(共 3 次输出)---- + addi t0, zero, 3 +bgeu_loop: + add a0, zero, t0 + print # 00000003 / 00000002 / 00000001 + addi t0, t0, -1 + addi t2, zero, 1 + bgeu t0, t2, bgeu_loop + +# ---- bltu 循环:4 -> 1(共 4 次输出)---- + addi s1, zero, 4 +bltu_loop: + add a0, zero, s1 + print # 00000004 / 00000003 / 00000002 / 00000001 + addi s1, s1, -1 + bltu zero, s1, bltu_loop + + halt diff --git a/testbench/programs/isa_test.expected b/testbench/programs/isa_test.expected new file mode 100644 index 0000000..15caef8 --- /dev/null +++ b/testbench/programs/isa_test.expected @@ -0,0 +1,24 @@ +00000001 +00000000 +ffffff00 +80000000 +0000000f +ffffffff +000aad1e +00000000 +fffffffe +ffffffff +00000601 +00000602 +00000603 +fffffffd +fffffffe +ffffffff +00000000 +00000003 +00000002 +00000001 +00000004 +00000003 +00000002 +00000001 diff --git a/testbench/programs/lui_test.asm b/testbench/programs/lui_test.asm new file mode 100644 index 0000000..c6bcd67 --- /dev/null +++ b/testbench/programs/lui_test.asm @@ -0,0 +1,26 @@ +# LUI 指令测试:依次打印 lui 的结果,最后停机。 +# 期望输出:12345000 / fffff000 / 80000000 / 12345678,随后停机(halted=1)。 +# 用法:python tools/rv_asm.py testbench/programs/lui_test.asm <仿真工作目录>/risc-v-benchmark_ccab.hex + lui t0, 0x12345 # t0 = 0x12345000 + add a0, zero, t0 + addi a7, zero, 34 + ecall + + lui t0, 0xfffff # t0 = 0xfffff000 + add a0, zero, t0 + addi a7, zero, 34 + ecall + + lui t0, 0x80000 # t0 = 0x80000000 + add a0, zero, t0 + addi a7, zero, 34 + ecall + + lui t0, 0x12345 # 常见用法 lui+addi:0x12345678 + addi t0, t0, 0x678 + add a0, zero, t0 + addi a7, zero, 34 + ecall + + addi a7, zero, 10 # a7=10 -> 停机 + ecall diff --git a/tools/rv_asm.py b/tools/rv_asm.py new file mode 100644 index 0000000..23d34e1 --- /dev/null +++ b/tools/rv_asm.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +"""极简 RISC-V 汇编器:仅支持本项目测试所需的指令子集。 + +用法: python tools/rv_asm.py +同时生成 .lst 反汇编/编码清单,便于核对。 +支持:add/sub/and/or/xor/sll/srl/sra/slt/sltu、 + mul/mulh/mulhsu/mulhu、 + addi/slti/sltiu/xori/ori/andi、slli/srli/srai、 + lw/sw、beq/bne/blt/bge/bltu/bgeu、lui、jal/jalr、ecall、标签、# 注释 +伪指令:print(= addi a7,zero,34 + ecall)、halt(= addi a7,zero,10 + ecall),各占两个字。 +示例:python tools/rv_asm.py testbench/programs/isa_test.asm out.hex +""" + +import re +import sys + +REGS = { + "zero": 0, + "ra": 1, + "sp": 2, + "gp": 3, + "tp": 4, + "t0": 5, + "t1": 6, + "t2": 7, + "s0": 8, + "s1": 9, + "a0": 10, + "a1": 11, + "a2": 12, + "a3": 13, + "a4": 14, + "a5": 15, + "a6": 16, + "a7": 17, + "s2": 18, + "s3": 19, + "s4": 20, + "s5": 21, + "s6": 22, + "s7": 23, + "s8": 24, + "s9": 25, + "s10": 26, + "s11": 27, + "t3": 28, + "t4": 29, + "t5": 30, + "t6": 31, +} + + +def reg(t): + t = t.strip() + if t in REGS: + return REGS[t] + m = re.fullmatch(r"x(\d+)", t) + if m: + return int(m.group(1)) + raise SystemExit("未知寄存器: " + t) + + +def num(t): + return int(t.strip(), 0) + + +def enc_r(f7, rs2, rs1, f3, rd): + return (f7 << 25) | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 0x33 + + +def enc_i(imm, rs1, f3, rd, op=0x13): + return ((imm & 0xFFF) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | op + + +def enc_s(imm, rs2, rs1, f3): + imm &= 0xFFF + return ( + ((imm >> 5) << 25) + | (rs2 << 20) + | (rs1 << 15) + | (f3 << 12) + | ((imm & 0x1F) << 7) + | 0x23 + ) + + +def enc_b(imm, rs2, rs1, f3): + imm &= 0x1FFF + return ( + (((imm >> 12) & 1) << 31) + | (((imm >> 5) & 0x3F) << 25) + | (rs2 << 20) + | (rs1 << 15) + | (f3 << 12) + | (((imm >> 1) & 0xF) << 8) + | (((imm >> 11) & 1) << 7) + | 0x63 + ) + + +def enc_u(imm, rd): + return ((imm & 0xFFFFF) << 12) | (rd << 7) | 0x37 + + +def enc_j(imm, rd): + imm &= 0x1FFFFF + return ( + (((imm >> 20) & 1) << 31) + | (((imm >> 1) & 0x3FF) << 21) + | (((imm >> 11) & 1) << 20) + | (((imm >> 12) & 0xFF) << 12) + | (rd << 7) + | 0x6F + ) + + +RTYPE = { + "add": (0x00, 0), + "sub": (0x20, 0), + "sll": (0x00, 1), + "slt": (0x00, 2), + "sltu": (0x00, 3), + "xor": (0x00, 4), + "srl": (0x00, 5), + "sra": (0x20, 5), + "or": (0x00, 6), + "and": (0x00, 7), + "mul": (0x01, 0), + "mulh": (0x01, 1), + "mulhsu": (0x01, 2), + "mulhu": (0x01, 3), +} +ITYPE = {"addi": 0, "slti": 2, "sltiu": 3, "xori": 4, "ori": 6, "andi": 7} +SHIFT = {"slli": 1, "srli": 5, "srai": 5} +BTYPE = {"beq": 0, "bne": 1, "blt": 4, "bge": 5, "bltu": 6, "bgeu": 7} +# 双字伪指令:print = addi a7,zero,34 + ecall;halt = addi a7,zero,10 + ecall +PSEUDO_SIZE = {"print": 2, "halt": 2} + + +def assemble(lines): + labels, items, addr = {}, [], 0 + for raw in lines: + line = raw.split("#")[0].strip() + if not line: + continue + while True: + m = re.match(r"^([A-Za-z_.$][\w.$]*)\s*:\s*(.*)$", line) + if not m: + break + labels[m.group(1)] = addr + line = m.group(2).strip() + if not line: + break + if not line: + continue + items.append((addr, line)) + addr += 4 * PSEUDO_SIZE.get(line.split()[0].lower(), 1) + + out = [] + for addr, line in items: + parts = re.split(r"[\s,]+", line) + mn, a = parts[0].lower(), parts[1:] + if mn in RTYPE: + f7, f3 = RTYPE[mn] + w = enc_r(f7, reg(a[2]), reg(a[1]), f3, reg(a[0])) + elif mn in ITYPE: + im = num(a[2]) + if not (-2048 <= im <= 2047): + raise SystemExit("I 型立即数超出 12 位有符号范围: " + line) + w = enc_i(im, reg(a[1]), ITYPE[mn], reg(a[0])) + elif mn in SHIFT: + w = enc_i(num(a[2]) & 0x1F, reg(a[1]), SHIFT[mn], reg(a[0])) + elif mn == "lui": + w = enc_u(num(a[1]), reg(a[0])) + elif mn in ("lw", "sw"): + m = re.fullmatch(r"(-?\w+)\((\w+)\)", a[1]) + if not m: + raise SystemExit("内存操作数格式应为 imm(rs1): " + line) + im = num(m.group(1)) + if not (-2048 <= im <= 2047): + raise SystemExit("访存偏移超出 12 位有符号范围: " + line) + if mn == "lw": + w = enc_i(im, reg(m.group(2)), 2, reg(a[0]), 0x03) + else: + w = enc_s(im, reg(a[0]), reg(m.group(2)), 2) + elif mn in BTYPE: + tgt = labels[a[2]] if a[2] in labels else num(a[2]) + w = enc_b(tgt - addr, reg(a[1]), reg(a[0]), BTYPE[mn]) + elif mn == "jal": + tgt = labels[a[1]] if a[1] in labels else num(a[1]) + w = enc_j(tgt - addr, reg(a[0])) + elif mn == "jalr": + w = enc_i(num(a[2]), reg(a[1]), 0, reg(a[0]), 0x67) + elif mn == "ecall": + w = 0x00000073 + elif mn == "print": + w = [enc_i(34, 0, 0, 17), 0x00000073] + elif mn == "halt": + w = [enc_i(10, 0, 0, 17), 0x00000073] + else: + raise SystemExit("未知助记符: " + mn) + out.append((addr, line, w if isinstance(w, list) else [w])) + return out + + +def main(): + src, dst = sys.argv[1], sys.argv[2] + with open(src, encoding="utf-8") as f: + prog = assemble(f.readlines()) + with open(dst, "w", encoding="ascii") as f: + for _, _, ws in prog: + for w in ws: + f.write("%08X\n" % w) + with open(dst + ".lst", "w", encoding="utf-8") as f: + for addr, line, ws in prog: + f.write("%04X: %s %s\n" % (addr, " ".join("%08X" % w for w in ws), line)) + print("已生成 %s(%d 个字)" % (dst, sum(len(ws) for _, _, ws in prog))) + + +main()