diff --git a/cpu21_mmio.v b/cpu21_mmio.v new file mode 100644 index 0000000..5ea65c8 --- /dev/null +++ b/cpu21_mmio.v @@ -0,0 +1,62 @@ +`timescale 1ns / 1ps +`default_nettype none + +// ----------------------------------------------------------------------------- +// MMIO:把外设寄存器映射到数据存储器地址空间。 +// +// 目前实现 LED 数据寄存器(LED_ADDR 默认 0x0000_1000,位于 4 KB 数据 RAM 之外): +// * 写:sw / sb 命中该地址(按字对齐,即同一 32 位寄存器内的任意字节偏移 +// 都算命中)时更新 LED 寄存器,led_valid_o 拉高一拍;sb 依据 wstrb_i 只 +// 修改对应的字节通道(与 RAM 的字节写语义一致)。 +// * 读:lw 命中该地址时 rdata_hit_o 拉高,顶层据此选择本模块的 rdata_o +// 而不是外部 data_rdata_i,从而支持把 LED 值读回。 +// +// 以后要增加外设(拨码开关、UART 等)时只需在这里扩展地址译码, +// 处理器核心与流水线时序不需要改动。 +// ----------------------------------------------------------------------------- +module cpu21_mmio #( + parameter [31:0] LED_ADDR = 32'h0000_1000 +) ( + input wire clk, + input wire reset, + // MEM 级的访存请求(来自 EX/MEM 流水寄存器) + input wire mem_valid_i, + input wire mem_write_i, + input wire mem_read_i, + input wire [31:0] addr_i, + input wire [31:0] wdata_i, + input wire [ 3:0] wstrb_i, + // MMIO 读回 + output wire [31:0] rdata_o, + output wire rdata_hit_o, + // LED 外设 + output reg [31:0] led_data_o, + output reg led_valid_o +); + // 地址译码按“字”对齐:同一 32 位寄存器内的任意字节偏移都算命中, + // 具体修改哪几个字节由 wstrb_i 决定(sw 全字、sb 单字节)。 + wire led_wr = mem_valid_i && mem_write_i && (addr_i[31:2] == LED_ADDR[31:2]); + wire led_rd = mem_valid_i && mem_read_i && (addr_i[31:2] == LED_ADDR[31:2]); + + assign rdata_hit_o = led_rd; + assign rdata_o = led_data_o; + + always @(posedge clk or posedge reset) begin + if (reset) begin + led_data_o <= 32'b0; + led_valid_o <= 1'b0; + end else begin + // led_valid_o 只拉高一拍,作为“本周期有 LED 写入”的指示。 + led_valid_o <= 1'b0; + if (led_wr) begin + if (wstrb_i[0]) led_data_o[7:0] <= wdata_i[7:0]; + if (wstrb_i[1]) led_data_o[15:8] <= wdata_i[15:8]; + if (wstrb_i[2]) led_data_o[23:16] <= wdata_i[23:16]; + if (wstrb_i[3]) led_data_o[31:24] <= wdata_i[31:24]; + led_valid_o <= 1'b1; + end + end + end +endmodule + +`default_nettype wire diff --git a/cpu21_riscv_redirect_int_bpb.v b/cpu21_riscv_redirect_int_bpb.v index d8971c4..8723cad 100644 --- a/cpu21_riscv_redirect_int_bpb.v +++ b/cpu21_riscv_redirect_int_bpb.v @@ -22,13 +22,17 @@ // cpu21_riscv_hazard_unit.v load-use 冒险与冲刷/停顿判定 // cpu21_riscv_irq_ctrl.v 中断控制、ustatus/uepc 与嵌套返回栈 // cpu21_riscv_perf_counters.v 性能计数器 +// cpu21_mmio.v MMIO:LED 数据寄存器(可写、可读回) // // 主要特性: // * EX 级前递(forwarding)与 load-use 冒险互锁; // * EX 级控制流裁决与流水线重定向; // * 八表项全相联分支目标缓冲(BPB); // * 三路采样的中断源,带优先级与可嵌套的 URET 返回; -// * 沿用原 ecall 约定:a7==34 时写出 LED 值,否则流水线等待 go_i。 +// * LED 显示通过 MMIO 完成:向 LED_ADDR(默认 0x0000_1000)存储即写 LED, +// 也可以从该地址读回 LED 当前值; +// * ecall 仍用于停机:a7==34 的历史“显示”调用保留为无操作,其余(如 a7=10) +// 令流水线停机并等待 go_i。 // // 存储器接口约定: // instr_addr_o 与 data_addr_o 均为字节地址。data_wstrb_o 是字节写掩码, @@ -41,6 +45,8 @@ module cpu21_riscv_redirect_int_bpb #( parameter IRQ2_VECTOR = 32'h0000_31e4, parameter IRQ3_VECTOR = 32'h0000_3310, parameter IRQ_STACK_DEPTH = 4, + // MMIO:LED 数据寄存器地址(默认 0x0000_1000,位于 4 KB 数据 RAM 之外)。 + parameter [31:0] LED_ADDR = 32'h0000_1000, // ustatus 复位值。详见 cpu21_riscv_irq_ctrl 中的说明。 parameter [31:0] USTATUS_INIT = 32'h0000_0001 ) ( @@ -171,9 +177,7 @@ module cpu21_riscv_redirect_int_bpb #( reg memwb_mem_to_reg_q; reg [2:0] memwb_wb_sel_q; - // LED 输出与停机标志。 - reg [31:0] led_data_q; - reg led_valid_q; + // 停机标志(LED 寄存器已移入 cpu21_mmio.v)。 reg halted_q; // ------------------------------------------------------------------------- @@ -411,8 +415,35 @@ module cpu21_riscv_redirect_int_bpb #( .store_wstrb_o(ex_store_wstrb) ); - // ecall 约定:a7==34 时输出 LED;否则令处理器停机,等待 go_i 重新启动。 - wire ex_led_event = idex_valid_q && idex_ecall_q && (ex_src1 == 32'd34); + // ------------------------------------------------------------------------- + // MMIO:LED 等外设映射到数据地址空间,在 MEM 级做地址译码。 + // ------------------------------------------------------------------------- + wire [31:0] mmio_rdata; + wire mmio_rdata_hit; + + cpu21_mmio #( + .LED_ADDR(LED_ADDR) + ) u_mmio ( + .clk (clk), + .reset (reset), + .mem_valid_i(exmem_valid_q), + .mem_write_i(exmem_mem_write_q), + .mem_read_i (exmem_mem_to_reg_q), + .addr_i (exmem_alu_result_q), + .wdata_i (exmem_write_data_q), + .wstrb_i (exmem_wstrb_q), + .rdata_o (mmio_rdata), + .rdata_hit_o(mmio_rdata_hit), + .led_data_o (led_data_o), + .led_valid_o(led_valid_o) + ); + + // 访存读数据:命中 MMIO 时用外设数据,否则用外部存储器数据。 + wire [31:0] mem_rdata = mmio_rdata_hit ? mmio_rdata : data_rdata_i; + + // ecall 约定:LED 显示已改由 MMIO 存储完成(见上方的 cpu21_mmio 例化)。 + // a7==34 的历史“显示”调用保留为无操作;其余(例如 a7=10)令处理器停机, + // 等待 go_i 重新启动。 wire ex_halt_event = idex_valid_q && idex_ecall_q && (ex_src1 != 32'd34); // ------------------------------------------------------------------------- @@ -495,9 +526,7 @@ module cpu21_riscv_redirect_int_bpb #( assign data_wstrb_o = (exmem_valid_q && exmem_mem_write_q) ? exmem_wstrb_q : 4'b0; assign data_we_o = |data_wstrb_o; - // LED 输出与流水线各级 PC/指令回显(用于调试与波形观察)。 - assign led_data_o = led_data_q; - assign led_valid_o = led_valid_q; + // LED 输出由 cpu21_mmio 驱动,这里只保留流水线各级 PC/指令回显。 assign if_pc_o = pc_q; assign id_pc_o = ifid_valid_q ? ifid_pc_q : 32'b0; assign ex_pc_o = idex_valid_q ? idex_pc_q : 32'b0; @@ -596,16 +625,8 @@ module cpu21_riscv_redirect_int_bpb #( memwb_mem_to_reg_q <= 1'b0; memwb_wb_sel_q <= 3'b0; - led_data_q <= 32'b0; - led_valid_q <= 1'b0; halted_q <= 1'b0; end else begin - led_valid_q <= 1'b0; - - if (ex_led_event) begin - led_data_q <= ex_src2; - led_valid_q <= 1'b1; - end if (go_i) halted_q <= 1'b0; if (ex_halt_event) halted_q <= 1'b1; @@ -616,7 +637,7 @@ module cpu21_riscv_redirect_int_bpb #( memwb_alu_result_q <= exmem_alu_result_q; memwb_alu_result2_q <= exmem_alu_result2_q; memwb_csr_old_q <= exmem_csr_old_q; - memwb_mem_data_q <= data_rdata_i; + memwb_mem_data_q <= mem_rdata; memwb_rd_q <= exmem_rd_q; memwb_reg_write_q <= exmem_reg_write_q; memwb_mem_to_reg_q <= exmem_mem_to_reg_q; diff --git a/cpu21_riscv_redirect_int_bpb说明.md b/cpu21_riscv_redirect_int_bpb说明.md index 2c35c59..ea40c25 100644 --- a/cpu21_riscv_redirect_int_bpb说明.md +++ b/cpu21_riscv_redirect_int_bpb说明.md @@ -16,7 +16,7 @@ - 三路中断输入 `irq_i[2:0]`:两级同步、上升沿挂起、IRQ3 > IRQ2 > IRQ1 优先级,仅在 `ustatus[0]`(MIE)允许时响应。 - 支持嵌套中断:保存 EPC、`ustatus` 和当前优先级;`URET` 使用当前 `uepc` 返回,并从硬件栈恢复外层上下文。三个入口按照原电路的 `INTpc` 选择:IRQ1=`0x30ac`、IRQ2=`0x31e4`、IRQ3=`0x3310`。`0x22` 是 `ecall` 的服务号 34,不是中断向量。 - CSRRSI、CSRRCI,以及 CSRRW:支持 CSR 地址 `0x004`(`ustatus`)和 `0x041`(`uepc`)。立即数为 `IR[19:15]` 的零扩展值,并按 RISC-V 规则把旧 CSR 值写回 `rd`;这覆盖任务书中的 `csrrsi s6,0x41,0` 和 `csrrw zero,0x41,s6` 用法。 -- `ecall`:`a7 == 34` 时把 `a0` 锁存到 `led_data_o`;否则置 `halted_o`,直到 `go_i` 为高。 +- `ecall`:停机和调试用。`a7 == 34` 是历史遗留的“显示”调用,现已保留为无操作;其余取值(例如程序结尾惯用的 `a7 = 10`)置 `halted_o`,直到 `go_i` 为高。LED 显示改由 MMIO 存储完成(见下文“MMIO 与外设”)。 - `sw` 和 `sb`:输出 `data_wstrb_o`,支持按字节写入。 - `lui`:加载高位立即数,`rd = imm[31:12] << 12`(按需求补充的扩展指令,标准 opcode `0x37`)。 - `sltiu`:I 型 `funct3=011`,立即数符号扩展后按无符号与 rs1 比较。 @@ -43,6 +43,34 @@ `ecall` 和 `URET` 都是 `opcode=0x1c、funct3=0`,由 `IR[21]` 区分: `IR[21]=0` 是 `ecall`,`IR[21]=1` 是 `URET`。这一点来自原控制器中的 `IR21` 分支逻辑。 +## MMIO 与外设 + +外设寄存器映射在数据存储器地址空间,由 `cpu21_mmio.v` 在 MEM 级译码, +顶层参数 `LED_ADDR`(默认 `0x0000_1000`)指定 LED 数据寄存器的地址。 +该地址位于 4 KB 数据 RAM(`0x0000`–`0x0FFC`)之外,因此不会与 RAM 冲突。 + +| 地址 | 外设 | 写 | 读 | +| ------------ | -------------- | ------------------------------------------------------------------ | ------------------------ | +| `0x0000_1000` | LED 数据寄存器 | `sw` 写全字;`sb` 按 `data_wstrb_o` 只改对应字节通道,`led_valid_o` 拉高一拍 | `lw` 返回 LED 寄存器当前值 | + +地址译码按**字**对齐:同一 32 位寄存器内的任意字节偏移(如 `0x1001`)都算命中, +具体修改哪几个字节由 `data_wstrb_o` 决定。`data_addr_o` 在命中 MMIO 时不会 +访问外部 RAM;读数据由 `cpu21_mmio` 的输出在顶层与 `data_rdata_i` 做选择。 + +典型用法(LED 写 `0x12345678`,可用 `tools/rv_asm.py` 汇编): + +```asm + lui t0, 0x1 # t0 = LED_ADDR = 0x00001000 + lui t1, 0x12345 + addi t1, t1, 0x678 # t1 = 0x12345678 + sw t1, 0(t0) # MMIO 写 LED -> LED 输出 12345678 + lw a0, 0(t0) # MMIO 读回 +``` + +`testbench/programs/mmio_test.asm`(期望值见 `mmio_test.expected`)覆盖了 +全字写、读回、字节写三种情况。加入新外设(拨码开关、UART 等)时只需在 +`cpu21_mmio.v` 里扩展地址译码,处理器流水线无需改动。 + ## 顶层端口约定 - `instr_i`:当前 `instr_addr_o` 对应的 32 位指令。原 Logisim 中由 ROM 提供。 @@ -63,7 +91,7 @@ 5. `bpb_predict_hit_o`、`bpb_predict_taken_o`、`bpb_mispredict_o` 和 `irq_*_o` 是观测端口,不参与外部控制,可以直接接逻辑分析仪或测试平台。 6. `cycle_count_o` 在 `halted_o` 为高时暂停;load-use 每次计入一个气泡,重定向或中断清空 IF/ID 与当前 IF 时计入两个气泡。 -原 `.circ` 文件中的 ROM、MIPS RAM、按钮、LED 和调试显示器属于 Logisim 外围,不直接搬入 RTL;其功能分别由指令接口、数据接口、`go_i`、`led_*_o` 和调试端口替代。 +原 `.circ` 文件中的 ROM、MIPS RAM、按钮、LED 和调试显示器属于 Logisim 外围,不直接搬入 RTL;其功能分别由指令接口、数据接口、`go_i`、MMIO(`led_data_o`/`led_valid_o`)和调试端口替代。 ## 文件与模块划分 @@ -80,6 +108,7 @@ | `cpu21_riscv_hazard_unit.v` | `cpu21_riscv_hazard_unit` | load-use 冒险、停顿(stall)与冲刷(flush)判定 | | `cpu21_riscv_irq_ctrl.v` | `cpu21_riscv_irq_ctrl` | 中断采样与优先级、`ustatus`/`uepc` CSR、嵌套返回栈 | | `cpu21_riscv_perf_counters.v` | `cpu21_riscv_perf_counters` | 周期/停顿/气泡/分支/预测成功率等统计计数器 | +| `cpu21_mmio.v` | `cpu21_mmio` | MMIO 外设:LED 数据寄存器(可写、可读回,支持字节写) | 拆分只调整了模块边界与文件组织,端口、时序与行为完全等价:使用同一 ROM 程序 在 xsim 下对拆分前后逐周期对比,波形输出与最终统计(周期、停顿、气泡、分支 diff --git a/testbench/programs/isa_test.asm b/testbench/programs/isa_test.asm index 87947cc..e3a62e5 100644 --- a/testbench/programs/isa_test.asm +++ b/testbench/programs/isa_test.asm @@ -1,12 +1,13 @@ -# 指令扩展验证程序:每条被测指令的结果都通过 ecall(a7=34) 输出,最后用 halt 停机。 +# 指令扩展验证程序:每条被测指令的结果都通过 MMIO 写 LED 输出,最后用 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。 +# print / halt 是 tools/rv_asm.py 提供的双字伪指令(print 会占用 t6): +# print = lui t6,0x1000>>12 + sw a0,0(t6)(写 MMIO 的 LED 寄存器); +# halt = addi a7,zero,10 + ecall(ecall 停机)。 # ---- sltiu:立即数符号扩展后做无符号比较 ---- addi t0, zero, 1 diff --git a/testbench/programs/lui_test.asm b/testbench/programs/lui_test.asm index c6bcd67..cf5342a 100644 --- a/testbench/programs/lui_test.asm +++ b/testbench/programs/lui_test.asm @@ -1,26 +1,21 @@ -# LUI 指令测试:依次打印 lui 的结果,最后停机。 +# LUI 指令测试:依次把 lui 的结果写到 MMIO 的 LED 寄存器,最后停机。 # 期望输出: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 + print # MMIO 写 LED -> 12345000 lui t0, 0xfffff # t0 = 0xfffff000 add a0, zero, t0 - addi a7, zero, 34 - ecall + print # -> fffff000 lui t0, 0x80000 # t0 = 0x80000000 add a0, zero, t0 - addi a7, zero, 34 - ecall + print # -> 80000000 lui t0, 0x12345 # 常见用法 lui+addi:0x12345678 addi t0, t0, 0x678 add a0, zero, t0 - addi a7, zero, 34 - ecall + print # -> 12345678 - addi a7, zero, 10 # a7=10 -> 停机 - ecall + halt # 停机 diff --git a/testbench/programs/mmio_test.asm b/testbench/programs/mmio_test.asm new file mode 100644 index 0000000..fb82fa4 --- /dev/null +++ b/testbench/programs/mmio_test.asm @@ -0,0 +1,24 @@ +# MMIO 验证程序:写 LED 寄存器(0x0000_1000)-> 读回 -> 字节写 -> 再读回。 +# 期望 LED 输出顺序(共 4 次,见同目录 mmio_test.expected): +# ① 12345678 sw 0x12345678 +# ② 12345678 lw 读回后再 print +# ③ ffffffff sw 0xffffffff +# ④ ffff7fff sb 0x7f 覆盖 led[15:8] 后读回 +# 用法:python tools/rv_asm.py testbench/programs/mmio_test.asm <仿真工作目录>/risc-v-benchmark_ccab.hex + lui t0, 0x1 # t0 = LED_ADDR = 0x00001000 + + lui t1, 0x12345 + addi t1, t1, 0x678 # t1 = 0x12345678 + sw t1, 0(t0) # ① MMIO 写 LED + lw a0, 0(t0) # MMIO 读回 + print # ② 应为 12345678 + + addi t2, zero, -1 # t2 = 0xffffffff + sw t2, 0(t0) # ③ 应为 ffffffff + + addi t3, zero, 0x7f + sb t3, 1(t0) # 字节写:led[15:8] = 0x7f + lw a0, 0(t0) # 读回 + print # ④ 应为 ffff7fff + + halt diff --git a/testbench/programs/mmio_test.expected b/testbench/programs/mmio_test.expected new file mode 100644 index 0000000..348c7cb --- /dev/null +++ b/testbench/programs/mmio_test.expected @@ -0,0 +1,4 @@ +12345678 +12345678 +ffffffff +ffff7fff diff --git a/testbench/tb_cpu21_riscv_redirect_int_bpb.v b/testbench/tb_cpu21_riscv_redirect_int_bpb.v index fcba92c..978ab4b 100644 --- a/testbench/tb_cpu21_riscv_redirect_int_bpb.v +++ b/testbench/tb_cpu21_riscv_redirect_int_bpb.v @@ -152,7 +152,8 @@ module tb_cpu21_riscv_redirect_int_bpb #( if (data_addr_o[11:2] < RAM_WORDS) data_rdata_i = ram[data_addr_o[11:2]]; end - // 带字节使能的同步写模型。 + // 带字节使能的同步写模型。MMIO 区域(LED_ADDR=0x1000 起)不在本模型的 + // 译码范围(data_addr_o[11:2] < 1024)内,因此对 LED 的存储不会写进 RAM。 always @(posedge clk) begin if (!reset && data_we_o && (data_addr_o[11:2] < RAM_WORDS)) begin if (data_wstrb_o[0]) ram[data_addr_o[11:2]][7:0] <= data_wdata_o[7:0]; diff --git a/testbench/tb_no_intr.v b/testbench/tb_no_intr.v index 380a0fa..ed92a84 100644 --- a/testbench/tb_no_intr.v +++ b/testbench/tb_no_intr.v @@ -155,7 +155,8 @@ module tb_no_intr #( if (data_addr_o[11:2] < RAM_WORDS) data_rdata_i = ram[data_addr_o[11:2]]; end - // 带字节使能的同步写模型。 + // 带字节使能的同步写模型。MMIO 区域(LED_ADDR=0x1000 起)不在本模型的 + // 译码范围(data_addr_o[11:2] < 1024)内,因此对 LED 的存储不会写进 RAM。 always @(posedge clk) begin if (!reset && data_we_o && (data_addr_o[11:2] < RAM_WORDS)) begin if (data_wstrb_o[0]) ram[data_addr_o[11:2]][7:0] <= data_wdata_o[7:0]; diff --git a/tools/rv_asm.py b/tools/rv_asm.py index 23d34e1..b3c8f11 100644 --- a/tools/rv_asm.py +++ b/tools/rv_asm.py @@ -6,8 +6,9 @@ 支持: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),各占两个字。 + lw/sw/sb、beq/bne/blt/bge/bltu/bgeu、lui、jal/jalr、ecall、标签、# 注释 +伪指令:print(= lui t6,LED_ADDR>>12 + sw a0,0(t6),向 MMIO 的 LED 寄存器写入 a0)、 +halt(= addi a7,zero,10 + ecall),各占两个字。 示例:python tools/rv_asm.py testbench/programs/isa_test.asm out.hex """ @@ -133,7 +134,10 @@ RTYPE = { 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 +# MMIO:LED 数据寄存器地址,需与 RTL 顶层 parameter LED_ADDR 保持一致 +LED_ADDR = 0x1000 + +# 双字伪指令:print 向 MMIO 的 LED 写寄存器写入 a0;halt 停机 PSEUDO_SIZE = {"print": 2, "halt": 2} @@ -172,7 +176,7 @@ def assemble(lines): 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"): + elif mn in ("lw", "sw", "sb"): m = re.fullmatch(r"(-?\w+)\((\w+)\)", a[1]) if not m: raise SystemExit("内存操作数格式应为 imm(rs1): " + line) @@ -181,8 +185,10 @@ def assemble(lines): raise SystemExit("访存偏移超出 12 位有符号范围: " + line) if mn == "lw": w = enc_i(im, reg(m.group(2)), 2, reg(a[0]), 0x03) - else: + elif mn == "sw": w = enc_s(im, reg(a[0]), reg(m.group(2)), 2) + else: # sb + w = enc_s(im, reg(a[0]), reg(m.group(2)), 0) 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]) @@ -194,7 +200,11 @@ def assemble(lines): elif mn == "ecall": w = 0x00000073 elif mn == "print": - w = [enc_i(34, 0, 0, 17), 0x00000073] + # 写 MMIO 的 LED 寄存器:lui t6, LED_ADDR>>12 ; sw a0, 0(t6) + w = [ + enc_u(LED_ADDR >> 12, reg("t6")), + enc_s(0, reg("a0"), reg("t6"), 2), + ] elif mn == "halt": w = [enc_i(10, 0, 0, 17), 0x00000073] else: