feat(saolei): 显存扩成每格 2 字节 + 内置 A-Z 字模;开始界面加文字、难度选择和黑背景
显示(RTL) - cpu21_vga.v:显存从"每格 1 字节"改成"每格 2 字节"(第 1 字节是 6 位字形、 第 2 字节是 3 位颜色),字地址由 cidx[10:2] 改为 cidx[10:1](1 字 = 2 格); 字模表由 4 位扩到 6 位(0..63),新增 A-Z 大写字母(16..41),原 0..15 的 数字/雷/旗/问号/横线/实心字形不变 - cpu21_mmio.v:显存窗口 0x2000-0x27FF -> 0x2000-0x2FFF(300 -> 600 字), fb_widx 相应取 addr_i[11:2] - cpu21_board_top.v:FB_WORDS 300 -> 600(1200 -> 2400 字节) 软件 - mmio.h:fb_write() -> fb_write_cell(idx, glyph, color)(一格写两个 sb); 删掉 CELL(),新增 G_A..G_Z 字形常量 - saolei.c:draw_cell / clear_screen 改用 fb_write_cell;开始界面显示 MINES / EASY / NORMAL / HARD / START 文字(START 直接写在绿色按钮条上), 不再是"两排雷图案 + 纯色条" - 难度选择:新增 g_difficulty / g_mines,三档 20 / 40 / 60 雷;place_mines、 胜利用的 opened_cnt 比较、toggle_flag 的剩余雷数都改读 g_mines;点难度那一行 即切换并重画菜单,点 START 才把雷数交给关卡 - 全部索引运算仍只用移位/掩码(棋盘固定 16x16),没有引入除法 配色(修"数字 1 看不见") - 数字 1 用 C_BLUE、游戏背景原来也是 C_BLUE,调色板里是同一个深蓝,所以数字 1 完全看不见;现在菜单与关卡背景统一改成 C_BLACK,胜负闪动仍是绿/红/黑, 数字本身的配色不动 工具与文档 - build.ps1:Python 启动器兼容 —— 有 py 用 py,没有就回退到 python (本机只有 python,之前 check_isa.py / elf2hex.py 两处都会中断) - saolei/README.md:显存表(0x2000-0x2FFF、每格 2 字节)、格子编码(6 位字形、 16..41 是 A-Z)、难度说明同步更新 - README.md / cpu21_board_top.v:仅格式化工具重排(Markdown 表格对齐、 if/else 合并成一行),无语义变化 验证:build.ps1 全流程通过(972 条指令 ISA 检查全过;text=3888 B / data=0 / bss=1444 B;入口 _start=0x0、isr1/2/3=0x100/0x200/0x300);xvlog 不带宏与带 SAOLEI_ROM 两种配置编译均无错;tools/check_rtl_text.ps1 体检 OK。 未验证:未跑行为仿真、未上板。
This commit is contained in:
@@ -70,7 +70,7 @@ risc-v/
|
|||||||
因此它与标准 RISC-V 指令的低 7 位 opcode 相差右移两位。
|
因此它与标准 RISC-V 指令的低 7 位 opcode 相差右移两位。
|
||||||
|
|
||||||
| 指令类别 | opcode | 说明 |
|
| 指令类别 | opcode | 说明 |
|
||||||
| ----------------------------------------------- | -----: | ----------------------------------------------------------------------------------- |
|
| ----------------------------------------------------------- | -------: | ----------------------------------------------------------------------------------------- |
|
||||||
| R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra |
|
| R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra |
|
||||||
| M 扩展(R 型) | `0x0c` | `funct7=0000001`:mul/mulh/mulhsu/mulhu、divu(`funct3=101`)、remu(`funct3=111`) |
|
| M 扩展(R 型) | `0x0c` | `funct7=0000001`:mul/mulh/mulhsu/mulhu、divu(`funct3=101`)、remu(`funct3=111`) |
|
||||||
| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/sltiu/slli/srli/srai |
|
| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/sltiu/slli/srli/srai |
|
||||||
@@ -92,7 +92,7 @@ risc-v/
|
|||||||
每行的字节偏移都按**字**对齐译码(同一 32 位寄存器内任意字节偏移都命中)。
|
每行的字节偏移都按**字**对齐译码(同一 32 位寄存器内任意字节偏移都命中)。
|
||||||
|
|
||||||
| 地址 | 外设 | 写 | 读 |
|
| 地址 | 外设 | 写 | 读 |
|
||||||
| ---------------------- | -------------- | ---------------------------------------------------------------------------- | -------------------------- |
|
| --------------------------- | -------------- | ------------------------------------------------------------------------------------ | ---------------------------- |
|
||||||
| `0x0000_1000` | LED 数据寄存器 | `sw` 写全字;`sb` 按 `data_wstrb_o` 只改对应字节通道,`led_valid_o` 拉高一拍 | `lw` 返回 LED 寄存器当前值 |
|
| `0x0000_1000` | LED 数据寄存器 | `sw` 写全字;`sb` 按 `data_wstrb_o` 只改对应字节通道,`led_valid_o` 拉高一拍 | `lw` 返回 LED 寄存器当前值 |
|
||||||
| `0x0000_1004` | 七段数码管 | `sw`/`sb` 写 8 位十六进制数(低 4 位 = 最右边那个数码管) | `lw` 读回 |
|
| `0x0000_1004` | 七段数码管 | `sw`/`sb` 写 8 位十六进制数(低 4 位 = 最右边那个数码管) | `lw` 读回 |
|
||||||
| `0x0000_1100`+ | 鼠标寄存器组 | STATUS / DATA / ACK / CTRL / INPUT / TIME / FRAME / EVCNT / LEVEL / MAGIC | 同左(DATA 读无副作用) |
|
| `0x0000_1100`+ | 鼠标寄存器组 | STATUS / DATA / ACK / CTRL / INPUT / TIME / FRAME / EVCNT / LEVEL / MAGIC | 同左(DATA 读无副作用) |
|
||||||
@@ -148,7 +148,7 @@ risc-v/
|
|||||||
## 文件与模块划分
|
## 文件与模块划分
|
||||||
|
|
||||||
| 文件 | 模块 | 职责 |
|
| 文件 | 模块 | 职责 |
|
||||||
| --------------------------------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------- |
|
| ----------------------------------------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||||
| `pipelined-cpu/srcs/rtl/cpu21_riscv_redirect_int_bpb.v` | `cpu21_riscv_redirect_int_bpb` | 顶层:五级流水寄存器、子模块例化、PC/IF-ID/ID-EX 控制、对外输出 |
|
| `pipelined-cpu/srcs/rtl/cpu21_riscv_redirect_int_bpb.v` | `cpu21_riscv_redirect_int_bpb` | 顶层:五级流水寄存器、子模块例化、PC/IF-ID/ID-EX 控制、对外输出 |
|
||||||
| `pipelined-cpu/srcs/rtl/cpu21_riscv_alu.v` | `cpu21_riscv_alu` | 组合 ALU(加减/移位/逻辑/比较,不做乘除) |
|
| `pipelined-cpu/srcs/rtl/cpu21_riscv_alu.v` | `cpu21_riscv_alu` | 组合 ALU(加减/移位/逻辑/比较,不做乘除) |
|
||||||
| `pipelined-cpu/srcs/rtl/cpu21_riscv_muldiv.v` | `cpu21_riscv_muldiv` | 多周期乘除法单元:32 拍移位迭代,mul/mulh/mulhsu/mulhu/divu/remu |
|
| `pipelined-cpu/srcs/rtl/cpu21_riscv_muldiv.v` | `cpu21_riscv_muldiv` | 多周期乘除法单元:32 拍移位迭代,mul/mulh/mulhsu/mulhu/divu/remu |
|
||||||
@@ -277,7 +277,7 @@ BPB/中断观测值)。如果直接把它设为实现顶层,这些端口会
|
|||||||
### 引脚分配(`cpu21_board_top.xdc`)
|
### 引脚分配(`cpu21_board_top.xdc`)
|
||||||
|
|
||||||
| 端口 | 引脚 | 板上元件 | 说明 |
|
| 端口 | 引脚 | 板上元件 | 说明 |
|
||||||
| ------------- | ----------- | ------------------------- | ------------------------------------------------------------------- |
|
| --------------- | ----------- | ------------------------- | ----------------------------------------------------------------------- |
|
||||||
| `clk100` | E3 | 100 MHz 晶振 | 经`CPU_DIV` 分频后给 CPU(默认 50 MHz) |
|
| `clk100` | E3 | 100 MHz 晶振 | 经`CPU_DIV` 分频后给 CPU(默认 50 MHz) |
|
||||||
| `rst_n` | C12 | 按键 CPU_RESETN(低有效) | 异步置位、与 CPU 时钟同步释放 |
|
| `rst_n` | C12 | 按键 CPU_RESETN(低有效) | 异步置位、与 CPU 时钟同步释放 |
|
||||||
| `go` | N17 | 按键 BTNC | 对应原电路“继续运行”(`ecall` 停机后恢复执行) |
|
| `go` | N17 | 按键 BTNC | 对应原电路“继续运行”(`ecall` 停机后恢复执行) |
|
||||||
@@ -298,7 +298,7 @@ BPB/中断观测值)。如果直接把它设为实现顶层,这些端口会
|
|||||||
周期 20 ns)**,既比原来的 16 分频快 8 倍,又留出充足余量:
|
周期 20 ns)**,既比原来的 16 分频快 8 倍,又留出充足余量:
|
||||||
|
|
||||||
| 配置 | CPU 时钟 | 实测 WNS(xc7a100tcsg324-1) | 说明 |
|
| 配置 | CPU 时钟 | 实测 WNS(xc7a100tcsg324-1) | 说明 |
|
||||||
| ------------ | -------- | ---------------------------- | ------------------------------------- |
|
| -------------- | -------- | ---------------------------- | ------------------------------------- |
|
||||||
| `CLK_DIV=1` | 100 MHz | -1.1 ns ✗ | 违例路径与乘除法无关(分支/中断/BPB) |
|
| `CLK_DIV=1` | 100 MHz | -1.1 ns ✗ | 违例路径与乘除法无关(分支/中断/BPB) |
|
||||||
| `CLK_DIV=2` | 50 MHz | **+5.1 ns** ✓ | 默认,余量约 25% |
|
| `CLK_DIV=2` | 50 MHz | **+5.1 ns** ✓ | 默认,余量约 25% |
|
||||||
| `CLK_DIV=16` | 6.25 MHz | +6.1 ns ✓ | 改造前的设置,现在没必要了 |
|
| `CLK_DIV=16` | 6.25 MHz | +6.1 ns ✓ | 改造前的设置,现在没必要了 |
|
||||||
@@ -328,7 +328,7 @@ create_generated_clock -name cpu_clk -source [get_ports { clk100 }] \
|
|||||||
### 分频与刷新率一览(想改速度看这里)
|
### 分频与刷新率一览(想改速度看这里)
|
||||||
|
|
||||||
| 想改什么 | 改哪里 | 当前值 / 换算 |
|
| 想改什么 | 改哪里 | 当前值 / 换算 |
|
||||||
| -------------------------- | ----------------------------------------------- | -------------------------------------------------------- |
|
| -------------------------- | --------------------------------------------------- | ------------------------------------------------------------ |
|
||||||
| CPU 主频(程序快慢) | 参数`CLK_DIV` | 2 → 50 MHz(`100 MHz / CLK_DIV`) |
|
| CPU 主频(程序快慢) | 参数`CLK_DIV` | 2 → 50 MHz(`100 MHz / CLK_DIV`) |
|
||||||
| 中断程序的 CPU 主频 | 参数`CLK_DIV_INTRUPT`(只在该 ROM 宏下生效) | 32 → 3.125 MHz;每个数值约 10 ms × (`CLK_DIV_INTRUPT`/2) |
|
| 中断程序的 CPU 主频 | 参数`CLK_DIV_INTRUPT`(只在该 ROM 宏下生效) | 32 → 3.125 MHz;每个数值约 10 ms × (`CLK_DIV_INTRUPT`/2) |
|
||||||
| 数码管扫描刷新率 | 顶层`SEG_REFRESH_DIV`(自动跟随 `CPU_DIV`) | `100000 / CPU_DIV` → 每步 1 ms、每位 125 Hz |
|
| 数码管扫描刷新率 | 顶层`SEG_REFRESH_DIV`(自动跟随 `CPU_DIV`) | `100000 / CPU_DIV` → 每步 1 ms、每位 125 Hz |
|
||||||
@@ -365,7 +365,7 @@ create_generated_clock -name cpu_clk -source [get_ports { clk100 }] \
|
|||||||
`create_project.tcl` 里的两个开关就是给这条链用的:
|
`create_project.tcl` 里的两个开关就是给这条链用的:
|
||||||
|
|
||||||
| 宏(`sources_1` 的 Verilog Define) | ROM 文件 | 中断入口`IRQ1/2/3` | 说明 |
|
| 宏(`sources_1` 的 Verilog Define) | ROM 文件 | 中断入口`IRQ1/2/3` | 说明 |
|
||||||
| ----------------------------------- | ------------------------------------ | --------------------------- | ------------------------------------------ |
|
| ------------------------------------- | -------------------------------------- | --------------------------------- | -------------------------------------------- |
|
||||||
| `SAOLEI_ROM`(优先级最高) | `srcs/board/cpu21_saolei_rom.vh` | `0x100` / `0x200` / `0x300` | 纯 C 扫雷(`saolei/tools/build.ps1` 生成) |
|
| `SAOLEI_ROM`(优先级最高) | `srcs/board/cpu21_saolei_rom.vh` | `0x100` / `0x200` / `0x300` | 纯 C 扫雷(`saolei/tools/build.ps1` 生成) |
|
||||||
| `RISC_V_INTRUPT_CCAB_ROM` | `srcs/board/risc-v-intrupt-ccab.vh` | `0x0AC` / `0x1E4` / `0x308` | 课程中断综合测试程序(当前默认) |
|
| `RISC_V_INTRUPT_CCAB_ROM` | `srcs/board/risc-v-intrupt-ccab.vh` | `0x0AC` / `0x1E4` / `0x308` | 课程中断综合测试程序(当前默认) |
|
||||||
| `BOARD_ROM_FAST` | `srcs/board/cpu21_board_rom_fast.vh` | `0x100` / `0x200` / `0x300` | 仿真专用:跑马灯延时改小(综合不用) |
|
| `BOARD_ROM_FAST` | `srcs/board/cpu21_board_rom_fast.vh` | `0x100` / `0x200` / `0x300` | 仿真专用:跑马灯延时改小(综合不用) |
|
||||||
@@ -389,7 +389,6 @@ ROM 里 ISR 的起点一致**,所以换程序时宏与入口是一起换的:
|
|||||||
```
|
```
|
||||||
|
|
||||||
换回跑马灯就是 `set_property verilog_define {} [get_filesets sources_1]`。
|
换回跑马灯就是 `set_property verilog_define {} [get_filesets sources_1]`。
|
||||||
|
|
||||||
2. **一键重建**:改 `create_project.tcl` 里的 `use_intrupt_ccab` / `use_saolei`
|
2. **一键重建**:改 `create_project.tcl` 里的 `use_intrupt_ccab` / `use_saolei`
|
||||||
两个开关(同一时间只开一个),重新 `source create_project.tcl`。
|
两个开关(同一时间只开一个),重新 `source create_project.tcl`。
|
||||||
3. **命令行仿真**:`xvlog` 与 `xelab` 都要带同一个 `-d`,并用 `-i` 指向头文件目录:
|
3. **命令行仿真**:`xvlog` 与 `xelab` 都要带同一个 `-d`,并用 `-i` 指向头文件目录:
|
||||||
@@ -399,7 +398,6 @@ ROM 里 ISR 的起点一致**,所以换程序时宏与入口是一起换的:
|
|||||||
srcs/rtl/*.v srcs/board/cpu21_board_top.v 你的tb.v
|
srcs/rtl/*.v srcs/board/cpu21_board_top.v 你的tb.v
|
||||||
xelab -top 你的tb -snapshot s -d RISC_V_INTRUPT_CCAB_ROM
|
xelab -top 你的tb -snapshot s -d RISC_V_INTRUPT_CCAB_ROM
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **注意**:这些宏只影响“综合用”的 `sources_1`。跑 `tb_no_intr` /
|
4. **注意**:这些宏只影响“综合用”的 `sources_1`。跑 `tb_no_intr` /
|
||||||
`tb_cpu21_riscv_redirect_int_bpb` 时,程序来自测试平台自己探测的
|
`tb_cpu21_riscv_redirect_int_bpb` 时,程序来自测试平台自己探测的
|
||||||
`srcs/rom/*.hex`,与宏无关。
|
`srcs/rom/*.hex`,与宏无关。
|
||||||
@@ -461,7 +459,7 @@ xsim board_sim -tclbatch run_quit.tcl
|
|||||||
文件,由 `BOARD_ROM_FAST` 宏选择(综合流程不会定义该宏):
|
文件,由 `BOARD_ROM_FAST` 宏选择(综合流程不会定义该宏):
|
||||||
|
|
||||||
| 文件 | 用途 | 每步耗时 |
|
| 文件 | 用途 | 每步耗时 |
|
||||||
| -------------------------------------------------- | ------------------------------ | -------- |
|
| ---------------------------------------------------- | ------------------------------ | -------- |
|
||||||
| `pipelined-cpu/srcs/board/cpu21_board_rom.vh` | 综合/上板(默认) | 约 20 ms |
|
| `pipelined-cpu/srcs/board/cpu21_board_rom.vh` | 综合/上板(默认) | 约 20 ms |
|
||||||
| `pipelined-cpu/srcs/board/cpu21_board_rom_fast.vh` | 仅仿真:一次跑完整个跑马灯序列 | 2.6 µs |
|
| `pipelined-cpu/srcs/board/cpu21_board_rom_fast.vh` | 仅仿真:一次跑完整个跑马灯序列 | 2.6 µs |
|
||||||
|
|
||||||
@@ -496,7 +494,7 @@ Vivado 工程 `pipelined-cpu/pipelined-cpu.xpr` 被 `.gitignore` 排除,需要
|
|||||||
`xc7a100tcsg324-1` 上的实现结果(乘除法改为多周期单元、`CLK_DIV=2` = 50 MHz):
|
`xc7a100tcsg324-1` 上的实现结果(乘除法改为多周期单元、`CLK_DIV=2` = 50 MHz):
|
||||||
|
|
||||||
| 项目 | 结果 |
|
| 项目 | 结果 |
|
||||||
| -------- | -------------------------------------------------------------------------------------------- |
|
| -------- | -------------------------------------------------------------------------------------------------- |
|
||||||
| 综合 | `synth_design Complete!` |
|
| 综合 | `synth_design Complete!` |
|
||||||
| 实现 | `route_design Complete!`、`write_bitstream Complete!`,`WNS = +5.109 ns`(无 Failed Timing) |
|
| 实现 | `route_design Complete!`、`write_bitstream Complete!`,`WNS = +5.109 ns`(无 Failed Timing) |
|
||||||
| 资源 | LUT 3420 (5.39%)、FF 2507、**DSP 0**(组合乘法器已消失)、IOB 23 (10.95%) |
|
| 资源 | LUT 3420 (5.39%)、FF 2507、**DSP 0**(组合乘法器已消失)、IOB 23 (10.95%) |
|
||||||
@@ -562,7 +560,7 @@ cd ..\pipelined-cpu
|
|||||||
板上操作:
|
板上操作:
|
||||||
|
|
||||||
| 板子上 | 作用 |
|
| 板子上 | 作用 |
|
||||||
| ------------------------- | ---------------------------------------------------- |
|
| ------------------------- | ---------------------------------------------------------- |
|
||||||
| BTNU / BTND / BTNL / BTNR | 光标 上 / 下 / 左 / 右 |
|
| BTNU / BTND / BTNL / BTNR | 光标 上 / 下 / 左 / 右 |
|
||||||
| BTNC | 左键:开始界面确认 / 关卡里开格 |
|
| BTNC | 左键:开始界面确认 / 关卡里开格 |
|
||||||
| SW3 拨上 + BTNC | 插旗 / 取消旗 |
|
| SW3 拨上 + BTNC | 插旗 / 取消旗 |
|
||||||
@@ -634,7 +632,7 @@ powershell -ExecutionPolicy Bypass -File tools\run_regress.ps1
|
|||||||
期望结果(2026-09,多周期乘除法 + `CLK_DIV=2`):
|
期望结果(2026-09,多周期乘除法 + `CLK_DIV=2`):
|
||||||
|
|
||||||
| 测试 | 期望 |
|
| 测试 | 期望 |
|
||||||
| -------------------------------------------------- | ---------------------------------------------------------------------------- |
|
| -------------------------------------------------- | ------------------------------------------------------------------------------ |
|
||||||
| muldiv_test / isa_test / lui_test / mmio_test | 13 / 24 / 4 / 5 个 LED 值全部匹配 |
|
| muldiv_test / isa_test / lui_test / mmio_test | 13 / 24 / 4 / 5 个 LED 值全部匹配 |
|
||||||
| tb_no_intr(benchmark,5560 周期) | `stalls=1528 bubbles=1830 cond_taken=639 uncond=71 pred_ok=687 pred_fail=80` |
|
| tb_no_intr(benchmark,5560 周期) | `stalls=1528 bubbles=1830 cond_taken=639 uncond=71 pred_ok=687 pred_fail=80` |
|
||||||
| tb_cpu21_riscv_redirect_int_bpb(中断,6003 周期) | `stalls=963 bubbles=1205 cond_taken=547 uncond=28 pred_ok=557 pred_fail=87` |
|
| tb_cpu21_riscv_redirect_int_bpb(中断,6003 周期) | `stalls=963 bubbles=1205 cond_taken=547 uncond=28 pred_ok=557 pred_fail=87` |
|
||||||
|
|||||||
@@ -132,12 +132,10 @@ module cpu21_board_top #(
|
|||||||
if (arst) begin
|
if (arst) begin
|
||||||
clk_cnt <= 16'd0;
|
clk_cnt <= 16'd0;
|
||||||
clk_div <= 1'b0;
|
clk_div <= 1'b0;
|
||||||
end
|
end else if (clk_cnt == (CPU_DIV / 2 - 1)) begin
|
||||||
else if (clk_cnt == (CPU_DIV / 2 - 1)) begin
|
|
||||||
clk_cnt <= 16'd0;
|
clk_cnt <= 16'd0;
|
||||||
clk_div <= ~clk_div;
|
clk_div <= ~clk_div;
|
||||||
end
|
end else begin
|
||||||
else begin
|
|
||||||
clk_cnt <= clk_cnt + 16'd1;
|
clk_cnt <= clk_cnt + 16'd1;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -150,10 +148,8 @@ module cpu21_board_top #(
|
|||||||
reg [2:0] rst_sync_q;
|
reg [2:0] rst_sync_q;
|
||||||
|
|
||||||
always @(posedge clk_cpu or posedge arst) begin
|
always @(posedge clk_cpu or posedge arst) begin
|
||||||
if (arst)
|
if (arst) rst_sync_q <= 3'b111;
|
||||||
rst_sync_q <= 3'b111;
|
else rst_sync_q <= {rst_sync_q[1:0], 1'b0};
|
||||||
else
|
|
||||||
rst_sync_q <= {rst_sync_q[1:0], 1'b0};
|
|
||||||
end
|
end
|
||||||
|
|
||||||
wire reset = rst_sync_q[2];
|
wire reset = rst_sync_q[2];
|
||||||
@@ -196,8 +192,7 @@ module cpu21_board_top #(
|
|||||||
integer i;
|
integer i;
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
for (i = 0; i < ROM_WORDS; i = i + 1)
|
for (i = 0; i < ROM_WORDS; i = i + 1) rom[i] = 32'h0000_0013; // NOP 填充
|
||||||
rom[i] = 32'h0000_0013; // NOP 填充
|
|
||||||
`ifdef SAOLEI_ROM
|
`ifdef SAOLEI_ROM
|
||||||
// 扫雷程序(由 saolei/tools/build.ps1 生成的 cpu21_saolei_rom.vh)。
|
// 扫雷程序(由 saolei/tools/build.ps1 生成的 cpu21_saolei_rom.vh)。
|
||||||
// 综合/仿真时定义宏 SAOLEI_ROM 即可切换到这个程序;不定义就是下面的跑马灯。
|
// 综合/仿真时定义宏 SAOLEI_ROM 即可切换到这个程序;不定义就是下面的跑马灯。
|
||||||
@@ -225,8 +220,7 @@ module cpu21_board_top #(
|
|||||||
integer j;
|
integer j;
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
for (j = 0; j < RAM_WORDS; j = j + 1)
|
for (j = 0; j < RAM_WORDS; j = j + 1) ram[j] = 32'b0;
|
||||||
ram[j] = 32'b0;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
wire [ROM_AW+1:2] rom_index = instr_addr[ROM_AW+1:2];
|
wire [ROM_AW+1:2] rom_index = instr_addr[ROM_AW+1:2];
|
||||||
@@ -238,14 +232,10 @@ module cpu21_board_top #(
|
|||||||
|
|
||||||
always @(posedge clk_cpu) begin
|
always @(posedge clk_cpu) begin
|
||||||
if (ram_sel && data_we) begin
|
if (ram_sel && data_we) begin
|
||||||
if (data_wstrb[0])
|
if (data_wstrb[0]) ram[ram_index][7:0] <= data_wdata[7:0];
|
||||||
ram[ram_index][7:0] <= data_wdata[7:0];
|
if (data_wstrb[1]) ram[ram_index][15:8] <= data_wdata[15:8];
|
||||||
if (data_wstrb[1])
|
if (data_wstrb[2]) ram[ram_index][23:16] <= data_wdata[23:16];
|
||||||
ram[ram_index][15:8] <= data_wdata[15:8];
|
if (data_wstrb[3]) ram[ram_index][31:24] <= data_wdata[31:24];
|
||||||
if (data_wstrb[2])
|
|
||||||
ram[ram_index][23:16] <= data_wdata[23:16];
|
|
||||||
if (data_wstrb[3])
|
|
||||||
ram[ram_index][31:24] <= data_wdata[31:24];
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -337,8 +327,7 @@ module cpu21_board_top #(
|
|||||||
irq_req_q <= 3'b000;
|
irq_req_q <= 3'b000;
|
||||||
irq_level_q <= 2'd0;
|
irq_level_q <= 2'd0;
|
||||||
irq_led_q <= 3'b000;
|
irq_led_q <= 3'b000;
|
||||||
end
|
end else begin
|
||||||
else begin
|
|
||||||
irq_req_q <= irq_vec;
|
irq_req_q <= irq_vec;
|
||||||
irq_level_q <= irq_level;
|
irq_level_q <= irq_level;
|
||||||
// 请求上升沿 -> 点亮;对应级别的 ISR 返回 -> 熄灭
|
// 请求上升沿 -> 点亮;对应级别的 ISR 返回 -> 熄灭
|
||||||
@@ -353,13 +342,10 @@ module cpu21_board_top #(
|
|||||||
reg [23:0] led_we_cnt;
|
reg [23:0] led_we_cnt;
|
||||||
|
|
||||||
always @(posedge clk_cpu or posedge arst) begin
|
always @(posedge clk_cpu or posedge arst) begin
|
||||||
if (arst)
|
if (arst) led_we_cnt <= 24'd0;
|
||||||
led_we_cnt <= 24'd0;
|
else if (led_valid) led_we_cnt <= 24'hFF_FFFF; // 约 0.34 s @50 MHz
|
||||||
else if (led_valid)
|
|
||||||
led_we_cnt <= 24'hFF_FFFF; // 约 0.34 s @50 MHz
|
|
||||||
// (12.5 MHz 下约 1.36 s)
|
// (12.5 MHz 下约 1.36 s)
|
||||||
else if (led_we_cnt != 24'd0)
|
else if (led_we_cnt != 24'd0) led_we_cnt <= led_we_cnt - 24'd1;
|
||||||
led_we_cnt <= led_we_cnt - 24'd1;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
assign led_we = (led_we_cnt != 24'd0);
|
assign led_we = (led_we_cnt != 24'd0);
|
||||||
@@ -367,11 +353,11 @@ module cpu21_board_top #(
|
|||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// 扫雷外设:显存 + VGA + 七段数码管 + 鼠标事件源 + 中断连线
|
// 扫雷外设:显存 + VGA + 七段数码管 + 鼠标事件源 + 中断连线
|
||||||
//
|
//
|
||||||
// 显存是一块 1200 字节的双口 RAM(40x30 格,每格 1 字节):CPU 通过 MMIO 的
|
// 显存是一块 2400 字节的双口 RAM(40x30 格,每格 2 字节):CPU 通过 MMIO 的
|
||||||
// fb_* 写端口写入,VGA 用另一个读端口组合读出。两者同处 clk_cpu 域(VGA 的
|
// fb_* 写端口写入,VGA 用另一个读端口组合读出。两者同处 clk_cpu 域(VGA 的
|
||||||
// 25 MHz 像素节拍由使能信号产生,不是独立时钟),因此没有跨时钟域问题。
|
// 25 MHz 像素节拍由使能信号产生,不是独立时钟),因此没有跨时钟域问题。
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
localparam integer FB_WORDS = 300; // 300 字 = 1200 字节
|
localparam integer FB_WORDS = 600; // 600 字 = 2400 字节,40x30 格每格 2 字节
|
||||||
|
|
||||||
|
|
||||||
// ---- 显存 ----
|
// ---- 显存 ----
|
||||||
@@ -379,20 +365,15 @@ module cpu21_board_top #(
|
|||||||
integer k;
|
integer k;
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
for (k = 0; k < FB_WORDS; k = k + 1)
|
for (k = 0; k < FB_WORDS; k = k + 1) fb[k] = 32'b0;
|
||||||
fb[k] = 32'b0;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
always @(posedge clk_cpu) begin
|
always @(posedge clk_cpu) begin
|
||||||
if (fb_we) begin
|
if (fb_we) begin
|
||||||
if (fb_wstrb[0])
|
if (fb_wstrb[0]) fb[fb_waddr][7:0] <= fb_wdata[7:0];
|
||||||
fb[fb_waddr][7:0] <= fb_wdata[7:0];
|
if (fb_wstrb[1]) fb[fb_waddr][15:8] <= fb_wdata[15:8];
|
||||||
if (fb_wstrb[1])
|
if (fb_wstrb[2]) fb[fb_waddr][23:16] <= fb_wdata[23:16];
|
||||||
fb[fb_waddr][15:8] <= fb_wdata[15:8];
|
if (fb_wstrb[3]) fb[fb_waddr][31:24] <= fb_wdata[31:24];
|
||||||
if (fb_wstrb[2])
|
|
||||||
fb[fb_waddr][23:16] <= fb_wdata[23:16];
|
|
||||||
if (fb_wstrb[3])
|
|
||||||
fb[fb_waddr][31:24] <= fb_wdata[31:24];
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -401,10 +382,8 @@ module cpu21_board_top #(
|
|||||||
// ---- VGA 像素节拍:clk_cpu 二分频 => 25 MHz ----
|
// ---- VGA 像素节拍:clk_cpu 二分频 => 25 MHz ----
|
||||||
reg px_q;
|
reg px_q;
|
||||||
always @(posedge clk_cpu or posedge arst) begin
|
always @(posedge clk_cpu or posedge arst) begin
|
||||||
if (arst)
|
if (arst) px_q <= 1'b0;
|
||||||
px_q <= 1'b0;
|
else px_q <= ~px_q;
|
||||||
else
|
|
||||||
px_q <= ~px_q;
|
|
||||||
end
|
end
|
||||||
wire px_tick = ~px_q;
|
wire px_tick = ~px_q;
|
||||||
|
|
||||||
@@ -459,8 +438,7 @@ module cpu21_board_top #(
|
|||||||
.ev_btn_o (ev_btn),
|
.ev_btn_o (ev_btn),
|
||||||
.rx_count_o ()
|
.rx_count_o ()
|
||||||
);
|
);
|
||||||
end
|
end else begin : g_btn
|
||||||
else begin : g_btn
|
|
||||||
cpu21_pointer #(
|
cpu21_pointer #(
|
||||||
.SAMPLE_DIV(POINTER_SAMPLE_DIV)
|
.SAMPLE_DIV(POINTER_SAMPLE_DIV)
|
||||||
) u_ptr (
|
) u_ptr (
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
|||||||
// 0x0000_1004 七段数码管:8 位十六进制数(每 4 位一个数码管,低 4 位 = 最右)
|
// 0x0000_1004 七段数码管:8 位十六进制数(每 4 位一个数码管,低 4 位 = 最右)
|
||||||
// 0x0000_1100 + 4*k 鼠标寄存器组(见下)
|
// 0x0000_1100 + 4*k 鼠标寄存器组(见下)
|
||||||
// 0x0000_1140 + 4*k 显示控制器寄存器(见下)
|
// 0x0000_1140 + 4*k 显示控制器寄存器(见下)
|
||||||
// 0x0000_2000 - 0x27FF 显存窗口:40x30 格、每格 1 字节(只用到前 1200 字节)
|
// 0x0000_2000 - 0x2FFF 显存窗口:40x30 格、每格 2 字节(共 2400 字节)
|
||||||
//
|
//
|
||||||
// 鼠标寄存器(基址 0x1100):
|
// 鼠标寄存器(基址 0x1100):
|
||||||
// +0x00 STATUS RO [0] FIFO 非空 [5:1] 项数 [6] 中断使能 [7] FIFO 满
|
// +0x00 STATUS RO [0] FIFO 非空 [5:1] 项数 [6] 中断使能 [7] FIFO 满
|
||||||
@@ -96,14 +96,12 @@ module cpu21_mmio #(
|
|||||||
wire sel_seg = (addr_i[31:2] == SEG_ADDR[31:2]);
|
wire sel_seg = (addr_i[31:2] == SEG_ADDR[31:2]);
|
||||||
wire sel_mouse = (addr_i[31:8] == MOUSE_BASE[31:8]) && (addr_i[7:6] == 2'b00);
|
wire sel_mouse = (addr_i[31:8] == MOUSE_BASE[31:8]) && (addr_i[7:6] == 2'b00);
|
||||||
wire sel_disp = (addr_i[31:8] == DISP_BASE[31:8]) && (addr_i[7:6] == 2'b01);
|
wire sel_disp = (addr_i[31:8] == DISP_BASE[31:8]) && (addr_i[7:6] == 2'b01);
|
||||||
// 显存窗口 0x2000 - 0x27FF:bit13=1、bit12=0、bit11=0,高位全 0
|
// 显存窗口 0x2000 - 0x2FFF:共 600 个字(每格两个字节)。
|
||||||
// (原来写成 addr[12] && !addr[11],要求 bit12=1 —— 那是 0x1000-0x17FF,
|
wire sel_fb = (addr_i[31:12] == 20'h00002);
|
||||||
// 于是 0x2000+ 的读写全都不命中、而鼠标/显示寄存器写反而落进了显存)
|
|
||||||
wire sel_fb = (addr_i[31:14] == 18'd0) && addr_i[13] && !addr_i[12] && !addr_i[11];
|
|
||||||
|
|
||||||
wire [3:0] midx = addr_i[5:2];
|
wire [3:0] midx = addr_i[5:2];
|
||||||
wire [3:0] didx = addr_i[5:2];
|
wire [3:0] didx = addr_i[5:2];
|
||||||
wire [9:0] fb_widx = addr_i[10:2];
|
wire [9:0] fb_widx = addr_i[11:2];
|
||||||
wire fb_ok = (fb_widx < FB_WORDS);
|
wire fb_ok = (fb_widx < FB_WORDS);
|
||||||
|
|
||||||
wire led_wr = mem_valid_i && mem_write_i && sel_led;
|
wire led_wr = mem_valid_i && mem_write_i && sel_led;
|
||||||
@@ -144,8 +142,7 @@ module cpu21_mmio #(
|
|||||||
if (reset) begin
|
if (reset) begin
|
||||||
led_data_o <= 32'b0;
|
led_data_o <= 32'b0;
|
||||||
led_valid_o <= 1'b0;
|
led_valid_o <= 1'b0;
|
||||||
end
|
end else begin
|
||||||
else begin
|
|
||||||
led_valid_o <= 1'b0;
|
led_valid_o <= 1'b0;
|
||||||
if (led_wr) begin
|
if (led_wr) begin
|
||||||
led_data_o <= merge_bytes(wdata_i, led_data_o, wstrb_i);
|
led_data_o <= merge_bytes(wdata_i, led_data_o, wstrb_i);
|
||||||
@@ -161,8 +158,7 @@ module cpu21_mmio #(
|
|||||||
if (reset) begin
|
if (reset) begin
|
||||||
seg_data_o <= 32'b0;
|
seg_data_o <= 32'b0;
|
||||||
seg_valid_o <= 1'b0;
|
seg_valid_o <= 1'b0;
|
||||||
end
|
end else begin
|
||||||
else begin
|
|
||||||
seg_valid_o <= 1'b0;
|
seg_valid_o <= 1'b0;
|
||||||
if (seg_wr) begin
|
if (seg_wr) begin
|
||||||
seg_data_o <= merge_bytes(wdata_i, seg_data_o, wstrb_i);
|
seg_data_o <= merge_bytes(wdata_i, seg_data_o, wstrb_i);
|
||||||
@@ -211,42 +207,33 @@ module cpu21_mmio #(
|
|||||||
cnt_q <= {(FIFO_AW + 1) {1'b0}};
|
cnt_q <= {(FIFO_AW + 1) {1'b0}};
|
||||||
disp_ctrl_q <= 32'h0000_0001; // 光标可见,背景色 0
|
disp_ctrl_q <= 32'h0000_0001; // 光标可见,背景色 0
|
||||||
disp_cursor_q <= 32'h0000_0000;
|
disp_cursor_q <= 32'h0000_0000;
|
||||||
end
|
end else begin
|
||||||
else begin
|
|
||||||
time_q <= time_q + 32'd1;
|
time_q <= time_q + 32'd1;
|
||||||
if (frame_tick_i)
|
if (frame_tick_i) frame_q <= frame_q + 32'd1;
|
||||||
frame_q <= frame_q + 32'd1;
|
|
||||||
|
|
||||||
if (push) begin
|
if (push) begin
|
||||||
fifo[wr_ptr_q] <= {11'b0, mouse_btn_i, mouse_dy_i, mouse_dx_i};
|
fifo[wr_ptr_q] <= {11'b0, mouse_btn_i, mouse_dy_i, mouse_dx_i};
|
||||||
wr_ptr_q <= wr_ptr_q + 1'b1;
|
wr_ptr_q <= wr_ptr_q + 1'b1;
|
||||||
evcnt_q <= evcnt_q + 32'd1;
|
evcnt_q <= evcnt_q + 32'd1;
|
||||||
end
|
end
|
||||||
if (pop)
|
if (pop) rd_ptr_q <= rd_ptr_q + 1'b1;
|
||||||
rd_ptr_q <= rd_ptr_q + 1'b1;
|
|
||||||
cnt_q <= cnt_q + (push ? 1'b1 : 1'b0) - (pop ? 1'b1 : 1'b0);
|
cnt_q <= cnt_q + (push ? 1'b1 : 1'b0) - (pop ? 1'b1 : 1'b0);
|
||||||
|
|
||||||
// 鼠标寄存器:CTRL(3) / LEVEL(8)
|
// 鼠标寄存器:CTRL(3) / LEVEL(8)
|
||||||
if (mo_wr) begin
|
if (mo_wr) begin
|
||||||
case (midx)
|
case (midx)
|
||||||
4'd3:
|
4'd3: mctrl_q <= merge_bytes(wdata_i, mctrl_q, wstrb_i);
|
||||||
mctrl_q <= merge_bytes(wdata_i, mctrl_q, wstrb_i);
|
4'd8: level_q <= wdata_i[0];
|
||||||
4'd8:
|
default: ;
|
||||||
level_q <= wdata_i[0];
|
|
||||||
default:
|
|
||||||
;
|
|
||||||
endcase
|
endcase
|
||||||
end
|
end
|
||||||
|
|
||||||
// 显示控制器:CTRL(0) / CURSOR(1)
|
// 显示控制器:CTRL(0) / CURSOR(1)
|
||||||
if (dp_wr) begin
|
if (dp_wr) begin
|
||||||
case (didx)
|
case (didx)
|
||||||
4'd0:
|
4'd0: disp_ctrl_q <= merge_bytes(wdata_i, disp_ctrl_q, wstrb_i);
|
||||||
disp_ctrl_q <= merge_bytes(wdata_i, disp_ctrl_q, wstrb_i);
|
4'd1: disp_cursor_q <= merge_bytes(wdata_i, disp_cursor_q, wstrb_i);
|
||||||
4'd1:
|
default: ;
|
||||||
disp_cursor_q <= merge_bytes(wdata_i, disp_cursor_q, wstrb_i);
|
|
||||||
default:
|
|
||||||
;
|
|
||||||
endcase
|
endcase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -261,47 +248,29 @@ module cpu21_mmio #(
|
|||||||
rdata_mux = 32'b0;
|
rdata_mux = 32'b0;
|
||||||
if (led_rd) begin
|
if (led_rd) begin
|
||||||
rdata_mux = led_data_o;
|
rdata_mux = led_data_o;
|
||||||
end
|
end else if (seg_rd) begin
|
||||||
else if (seg_rd) begin
|
|
||||||
rdata_mux = seg_data_o;
|
rdata_mux = seg_data_o;
|
||||||
end
|
end else if (fb_rd) begin
|
||||||
else if (fb_rd) begin
|
|
||||||
rdata_mux = fb_rdata_i;
|
rdata_mux = fb_rdata_i;
|
||||||
end
|
end else if (mo_rd) begin
|
||||||
else if (mo_rd) begin
|
|
||||||
case (midx)
|
case (midx)
|
||||||
4'd0:
|
4'd0: rdata_mux = {24'b0, fifo_full, irq_en, cnt_q[4:0], !fifo_empty};
|
||||||
rdata_mux = {24'b0, fifo_full, irq_en, cnt_q[4:0], !fifo_empty};
|
4'd1: rdata_mux = fifo_head;
|
||||||
4'd1:
|
4'd3: rdata_mux = mctrl_q;
|
||||||
rdata_mux = fifo_head;
|
4'd4: rdata_mux = {11'b0, mouse_btn_i, sw_i};
|
||||||
4'd3:
|
4'd5: rdata_mux = time_q;
|
||||||
rdata_mux = mctrl_q;
|
4'd6: rdata_mux = frame_q;
|
||||||
4'd4:
|
4'd7: rdata_mux = evcnt_q;
|
||||||
rdata_mux = {11'b0, mouse_btn_i, sw_i};
|
4'd8: rdata_mux = {31'b0, level_q};
|
||||||
4'd5:
|
4'd9: rdata_mux = 32'hCAFE_0002;
|
||||||
rdata_mux = time_q;
|
default: rdata_mux = 32'b0;
|
||||||
4'd6:
|
|
||||||
rdata_mux = frame_q;
|
|
||||||
4'd7:
|
|
||||||
rdata_mux = evcnt_q;
|
|
||||||
4'd8:
|
|
||||||
rdata_mux = {31'b0, level_q};
|
|
||||||
4'd9:
|
|
||||||
rdata_mux = 32'hCAFE_0002;
|
|
||||||
default:
|
|
||||||
rdata_mux = 32'b0;
|
|
||||||
endcase
|
endcase
|
||||||
end
|
end else if (dp_rd) begin
|
||||||
else if (dp_rd) begin
|
|
||||||
case (didx)
|
case (didx)
|
||||||
4'd0:
|
4'd0: rdata_mux = disp_ctrl_q;
|
||||||
rdata_mux = disp_ctrl_q;
|
4'd1: rdata_mux = disp_cursor_q;
|
||||||
4'd1:
|
4'd2: rdata_mux = 32'hCAFE_0001;
|
||||||
rdata_mux = disp_cursor_q;
|
default: rdata_mux = 32'b0;
|
||||||
4'd2:
|
|
||||||
rdata_mux = 32'hCAFE_0001;
|
|
||||||
default:
|
|
||||||
rdata_mux = 32'b0;
|
|
||||||
endcase
|
endcase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// cpu21_vga —— 640x480@60 的"格子"显示控制器(显存端口接板级顶层的双口 RAM)。
|
// cpu21_vga —— 640x480@60 的"格子"显示控制器(显存端口接板级顶层的双口 RAM)。
|
||||||
//
|
//
|
||||||
// CPU 不直接操作像素:它只通过 MMIO 往显示缓冲区里写"每格 1 字节"(字形 + 颜色),
|
// CPU 不直接操作像素:它只通过 MMIO 往显示缓冲区里写"每格 2 字节"(字形 + 颜色),
|
||||||
// 本模块按像素扫描把格子展开成画面(8x8 字模放大 2 倍填满 16x16 的格子)。
|
// 本模块按像素扫描把格子展开成画面(8x8 字模放大 2 倍填满 16x16 的格子)。
|
||||||
// 这样一格只要 1 次字节写,刷屏开销极小。
|
// 这样一格只要 1 次字节写,刷屏开销极小。
|
||||||
//
|
//
|
||||||
@@ -59,13 +59,11 @@ module cpu21_vga #(
|
|||||||
if (reset) begin
|
if (reset) begin
|
||||||
hx <= 10'd0;
|
hx <= 10'd0;
|
||||||
vy <= 10'd0;
|
vy <= 10'd0;
|
||||||
end
|
end else if (tick) begin
|
||||||
else if (tick) begin
|
|
||||||
if (hx == H_TOTAL - 1) begin
|
if (hx == H_TOTAL - 1) begin
|
||||||
hx <= 10'd0;
|
hx <= 10'd0;
|
||||||
vy <= (vy == V_TOTAL - 1) ? 10'd0 : (vy + 10'd1);
|
vy <= (vy == V_TOTAL - 1) ? 10'd0 : (vy + 10'd1);
|
||||||
end
|
end else begin
|
||||||
else begin
|
|
||||||
hx <= hx + 10'd1;
|
hx <= hx + 10'd1;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -84,88 +82,190 @@ module cpu21_vga #(
|
|||||||
wire [4:0] crow = vy[8:4]; // 0..29
|
wire [4:0] crow = vy[8:4]; // 0..29
|
||||||
wire [10:0] cidx = (crow << 5) + (crow << 3) + ccol; // row*40 + col
|
wire [10:0] cidx = (crow << 5) + (crow << 3) + ccol; // row*40 + col
|
||||||
|
|
||||||
assign fb_raddr_o = cidx[10:2]; // 字地址(1 字 = 4 格)
|
assign fb_raddr_o = cidx[10:1]; // 字地址(1 字 = 2 格)
|
||||||
|
|
||||||
wire [1:0] lane = cidx[1:0];
|
wire [ 1:0] lane = {cidx[0], 1'b0};
|
||||||
|
wire [ 1:0] color_lane = lane + 2'd1;
|
||||||
wire [31:0] cell_word = fb_word_i;
|
wire [31:0] cell_word = fb_word_i;
|
||||||
wire [7:0] cell_byte = cell_word >> {lane, 3'b000};
|
wire [ 7:0] glyph_byte = cell_word >> {lane, 3'b000};
|
||||||
|
wire [ 7:0] color_byte = cell_word >> {color_lane, 3'b000};
|
||||||
|
|
||||||
wire [3:0] glyph = cell_byte[3:0];
|
wire [ 5:0] glyph = glyph_byte[5:0];
|
||||||
wire [2:0] gcolor = cell_byte[6:4];
|
wire [ 2:0] gcolor = color_byte[2:0];
|
||||||
// 全局背景色(字模点为 0 时显示的颜色)
|
// 全局背景色(字模点为 0 时显示的颜色)
|
||||||
wire [ 2:0] bgcolor = ctrl_i[7:4];
|
wire [ 2:0] bgcolor = ctrl_i[7:4];
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// 8x8 字模(bit7 = 最左像素),放大 2 倍填满 16x16 的格子
|
// 8x8 字模(bit7 = 最左像素),放大 2 倍填满 16x16 的格子;字形编号 0..63。
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
wire [ 2:0] fx = hx[3:1];
|
wire [ 2:0] fx = hx[3:1];
|
||||||
wire [ 2:0] fy = vy[3:1];
|
wire [ 2:0] fy = vy[3:1];
|
||||||
|
|
||||||
function [7:0] font_row;
|
function [7:0] font_row;
|
||||||
input [3:0] g;
|
input [5:0] g;
|
||||||
input [2:0] r;
|
input [2:0] r;
|
||||||
begin
|
begin
|
||||||
case (g)
|
case (g)
|
||||||
4'd0:
|
6'd0: font_row = 8'h00; // 空白
|
||||||
font_row = 8'h00; // 空白
|
6'd1:
|
||||||
4'd1:
|
|
||||||
font_row = (r == 3'd0) ? 8'h18 : (r == 3'd1) ? 8'h38 : (r == 3'd2) ? 8'h18 :
|
font_row = (r == 3'd0) ? 8'h18 : (r == 3'd1) ? 8'h38 : (r == 3'd2) ? 8'h18 :
|
||||||
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h18 :
|
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h18 :
|
||||||
(r == 3'd6) ? 8'h7E : 8'h00; // 1
|
(r == 3'd6) ? 8'h7E : 8'h00; // 1
|
||||||
4'd2:
|
6'd2:
|
||||||
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h06 :
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h06 :
|
||||||
(r == 3'd3) ? 8'h0C : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h30 :
|
(r == 3'd3) ? 8'h0C : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h30 :
|
||||||
(r == 3'd6) ? 8'h7E : 8'h00; // 2
|
(r == 3'd6) ? 8'h7E : 8'h00; // 2
|
||||||
4'd3:
|
6'd3:
|
||||||
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h06 :
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h06 :
|
||||||
(r == 3'd3) ? 8'h1C : (r == 3'd4) ? 8'h06 : (r == 3'd5) ? 8'h66 :
|
(r == 3'd3) ? 8'h1C : (r == 3'd4) ? 8'h06 : (r == 3'd5) ? 8'h66 :
|
||||||
(r == 3'd6) ? 8'h3C : 8'h00; // 3
|
(r == 3'd6) ? 8'h3C : 8'h00; // 3
|
||||||
4'd4:
|
6'd4:
|
||||||
font_row = (r == 3'd0) ? 8'h0C : (r == 3'd1) ? 8'h1C : (r == 3'd2) ? 8'h3C :
|
font_row = (r == 3'd0) ? 8'h0C : (r == 3'd1) ? 8'h1C : (r == 3'd2) ? 8'h3C :
|
||||||
(r == 3'd3) ? 8'h6C : (r == 3'd4) ? 8'h7E : (r == 3'd5) ? 8'h0C :
|
(r == 3'd3) ? 8'h6C : (r == 3'd4) ? 8'h7E : (r == 3'd5) ? 8'h0C :
|
||||||
(r == 3'd6) ? 8'h0C : 8'h00; // 4
|
(r == 3'd6) ? 8'h0C : 8'h00; // 4
|
||||||
4'd5:
|
6'd5:
|
||||||
font_row = (r == 3'd0) ? 8'h7E : (r == 3'd1) ? 8'h60 : (r == 3'd2) ? 8'h7C :
|
font_row = (r == 3'd0) ? 8'h7E : (r == 3'd1) ? 8'h60 : (r == 3'd2) ? 8'h7C :
|
||||||
(r == 3'd3) ? 8'h06 : (r == 3'd4) ? 8'h06 : (r == 3'd5) ? 8'h66 :
|
(r == 3'd3) ? 8'h06 : (r == 3'd4) ? 8'h06 : (r == 3'd5) ? 8'h66 :
|
||||||
(r == 3'd6) ? 8'h3C : 8'h00; // 5
|
(r == 3'd6) ? 8'h3C : 8'h00; // 5
|
||||||
4'd6:
|
6'd6:
|
||||||
font_row = (r == 3'd0) ? 8'h1C : (r == 3'd1) ? 8'h30 : (r == 3'd2) ? 8'h60 :
|
font_row = (r == 3'd0) ? 8'h1C : (r == 3'd1) ? 8'h30 : (r == 3'd2) ? 8'h60 :
|
||||||
(r == 3'd3) ? 8'h7C : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
(r == 3'd3) ? 8'h7C : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
||||||
(r == 3'd6) ? 8'h3C : 8'h00; // 6
|
(r == 3'd6) ? 8'h3C : 8'h00; // 6
|
||||||
4'd7:
|
6'd7:
|
||||||
font_row = (r == 3'd0) ? 8'h7E : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h0C :
|
font_row = (r == 3'd0) ? 8'h7E : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h0C :
|
||||||
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h18 :
|
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h18 :
|
||||||
(r == 3'd6) ? 8'h18 : 8'h00; // 7
|
(r == 3'd6) ? 8'h18 : 8'h00; // 7
|
||||||
4'd8:
|
6'd8:
|
||||||
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
||||||
(r == 3'd3) ? 8'h3C : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
(r == 3'd3) ? 8'h3C : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
||||||
(r == 3'd6) ? 8'h3C : 8'h00; // 8
|
(r == 3'd6) ? 8'h3C : 8'h00; // 8
|
||||||
4'd9:
|
6'd9:
|
||||||
font_row = (r == 3'd0) ? 8'hFF : (r == 3'd1) ? 8'h81 : (r == 3'd2) ? 8'hBD :
|
font_row = (r == 3'd0) ? 8'hFF : (r == 3'd1) ? 8'h81 : (r == 3'd2) ? 8'hBD :
|
||||||
(r == 3'd3) ? 8'hA5 : (r == 3'd4) ? 8'hA5 : (r == 3'd5) ? 8'hBD :
|
(r == 3'd3) ? 8'hA5 : (r == 3'd4) ? 8'hA5 : (r == 3'd5) ? 8'hBD :
|
||||||
(r == 3'd6) ? 8'h81 : 8'hFF; // 未揭开的方块
|
(r == 3'd6) ? 8'h81 : 8'hFF; // 未揭开的方块
|
||||||
4'd10:
|
6'd10:
|
||||||
font_row = (r == 3'd0) ? 8'h40 : (r == 3'd1) ? 8'h70 : (r == 3'd2) ? 8'h78 :
|
font_row = (r == 3'd0) ? 8'h40 : (r == 3'd1) ? 8'h70 : (r == 3'd2) ? 8'h78 :
|
||||||
(r == 3'd3) ? 8'h70 : (r == 3'd4) ? 8'h40 : (r == 3'd5) ? 8'h40 :
|
(r == 3'd3) ? 8'h70 : (r == 3'd4) ? 8'h40 : (r == 3'd5) ? 8'h40 :
|
||||||
(r == 3'd6) ? 8'h40 : 8'h00; // 旗
|
(r == 3'd6) ? 8'h40 : 8'h00; // 旗
|
||||||
4'd11:
|
6'd11:
|
||||||
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h7E : (r == 3'd2) ? 8'hFF :
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h7E : (r == 3'd2) ? 8'hFF :
|
||||||
(r == 3'd3) ? 8'hFF : (r == 3'd4) ? 8'hFF : (r == 3'd5) ? 8'hFF :
|
(r == 3'd3) ? 8'hFF : (r == 3'd4) ? 8'hFF : (r == 3'd5) ? 8'hFF :
|
||||||
(r == 3'd6) ? 8'h7E : 8'h3C; // 雷
|
(r == 3'd6) ? 8'h7E : 8'h3C; // 雷
|
||||||
4'd12:
|
6'd12:
|
||||||
font_row = (r == 3'd0) ? 8'h81 : (r == 3'd1) ? 8'h42 : (r == 3'd2) ? 8'h24 :
|
font_row = (r == 3'd0) ? 8'h81 : (r == 3'd1) ? 8'h42 : (r == 3'd2) ? 8'h24 :
|
||||||
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h24 :
|
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h24 :
|
||||||
(r == 3'd6) ? 8'h42 : 8'h81; // 爆炸
|
(r == 3'd6) ? 8'h42 : 8'h81; // 爆炸
|
||||||
4'd13:
|
6'd13:
|
||||||
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h06 :
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h06 :
|
||||||
(r == 3'd3) ? 8'h0C : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h00 :
|
(r == 3'd3) ? 8'h0C : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h00 :
|
||||||
(r == 3'd6) ? 8'h18 : 8'h00; // 问号
|
(r == 3'd6) ? 8'h18 : 8'h00; // 问号
|
||||||
4'd14:
|
6'd14: font_row = (r == 3'd3) ? 8'hFF : (r == 3'd4) ? 8'hFF : 8'h00; // 横线
|
||||||
font_row = (r == 3'd3) ? 8'hFF : (r == 3'd4) ? 8'hFF : 8'h00; // 横线
|
6'd15: font_row = 8'hFF; // 实心
|
||||||
4'd15:
|
6'd16:
|
||||||
font_row = 8'hFF; // 实心
|
font_row = (r == 3'd0) ? 8'h18 : (r == 3'd1) ? 8'h3C : (r == 3'd2) ? 8'h66 :
|
||||||
default:
|
(r == 3'd3) ? 8'h66 : (r == 3'd4) ? 8'h7E : (r == 3'd5) ? 8'h66 :
|
||||||
font_row = 8'h00;
|
(r == 3'd6) ? 8'h66 : 8'h00; // A
|
||||||
|
6'd17:
|
||||||
|
font_row = (r == 3'd0) ? 8'h7C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
||||||
|
(r == 3'd3) ? 8'h7C : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
||||||
|
(r == 3'd6) ? 8'h7C : 8'h00; // B
|
||||||
|
6'd18:
|
||||||
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h60 :
|
||||||
|
(r == 3'd3) ? 8'h60 : (r == 3'd4) ? 8'h60 : (r == 3'd5) ? 8'h66 :
|
||||||
|
(r == 3'd6) ? 8'h3C : 8'h00; // C
|
||||||
|
6'd19:
|
||||||
|
font_row = (r == 3'd0) ? 8'h78 : (r == 3'd1) ? 8'h6C : (r == 3'd2) ? 8'h66 :
|
||||||
|
(r == 3'd3) ? 8'h66 : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h6C :
|
||||||
|
(r == 3'd6) ? 8'h78 : 8'h00; // D
|
||||||
|
6'd20:
|
||||||
|
font_row = (r == 3'd0) ? 8'h7E : (r == 3'd1) ? 8'h60 : (r == 3'd2) ? 8'h60 :
|
||||||
|
(r == 3'd3) ? 8'h7C : (r == 3'd4) ? 8'h60 : (r == 3'd5) ? 8'h60 :
|
||||||
|
(r == 3'd6) ? 8'h7E : 8'h00; // E
|
||||||
|
6'd21:
|
||||||
|
font_row = (r == 3'd0) ? 8'h7E : (r == 3'd1) ? 8'h60 : (r == 3'd2) ? 8'h60 :
|
||||||
|
(r == 3'd3) ? 8'h7C : (r == 3'd4) ? 8'h60 : (r == 3'd5) ? 8'h60 :
|
||||||
|
(r == 3'd6) ? 8'h60 : 8'h00; // F
|
||||||
|
6'd22:
|
||||||
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h60 :
|
||||||
|
(r == 3'd3) ? 8'h6E : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
||||||
|
(r == 3'd6) ? 8'h3E : 8'h00; // G
|
||||||
|
6'd23:
|
||||||
|
font_row = (r == 3'd0) ? 8'h66 : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
||||||
|
(r == 3'd3) ? 8'h7E : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
||||||
|
(r == 3'd6) ? 8'h66 : 8'h00; // H
|
||||||
|
6'd24:
|
||||||
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h18 : (r == 3'd2) ? 8'h18 :
|
||||||
|
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h18 :
|
||||||
|
(r == 3'd6) ? 8'h3C : 8'h00; // I
|
||||||
|
6'd25:
|
||||||
|
font_row = (r == 3'd0) ? 8'h1E : (r == 3'd1) ? 8'h0C : (r == 3'd2) ? 8'h0C :
|
||||||
|
(r == 3'd3) ? 8'h0C : (r == 3'd4) ? 8'h0C : (r == 3'd5) ? 8'h6C :
|
||||||
|
(r == 3'd6) ? 8'h38 : 8'h00; // J
|
||||||
|
6'd26:
|
||||||
|
font_row = (r == 3'd0) ? 8'h66 : (r == 3'd1) ? 8'h6C : (r == 3'd2) ? 8'h78 :
|
||||||
|
(r == 3'd3) ? 8'h70 : (r == 3'd4) ? 8'h78 : (r == 3'd5) ? 8'h6C :
|
||||||
|
(r == 3'd6) ? 8'h66 : 8'h00; // K
|
||||||
|
6'd27:
|
||||||
|
font_row = (r == 3'd0) ? 8'h60 : (r == 3'd1) ? 8'h60 : (r == 3'd2) ? 8'h60 :
|
||||||
|
(r == 3'd3) ? 8'h60 : (r == 3'd4) ? 8'h60 : (r == 3'd5) ? 8'h60 :
|
||||||
|
(r == 3'd6) ? 8'h7E : 8'h00; // L
|
||||||
|
6'd28:
|
||||||
|
font_row = (r == 3'd0) ? 8'h63 : (r == 3'd1) ? 8'h77 : (r == 3'd2) ? 8'h7F :
|
||||||
|
(r == 3'd3) ? 8'h6B : (r == 3'd4) ? 8'h63 : (r == 3'd5) ? 8'h63 :
|
||||||
|
(r == 3'd6) ? 8'h63 : 8'h00; // M
|
||||||
|
6'd29:
|
||||||
|
font_row = (r == 3'd0) ? 8'h66 : (r == 3'd1) ? 8'h76 : (r == 3'd2) ? 8'h7E :
|
||||||
|
(r == 3'd3) ? 8'h6E : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
||||||
|
(r == 3'd6) ? 8'h66 : 8'h00; // N
|
||||||
|
6'd30:
|
||||||
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
||||||
|
(r == 3'd3) ? 8'h66 : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
||||||
|
(r == 3'd6) ? 8'h3C : 8'h00; // O
|
||||||
|
6'd31:
|
||||||
|
font_row = (r == 3'd0) ? 8'h7C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
||||||
|
(r == 3'd3) ? 8'h7C : (r == 3'd4) ? 8'h60 : (r == 3'd5) ? 8'h60 :
|
||||||
|
(r == 3'd6) ? 8'h60 : 8'h00; // P
|
||||||
|
6'd32:
|
||||||
|
font_row = (r == 3'd0) ? 8'h3C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
||||||
|
(r == 3'd3) ? 8'h66 : (r == 3'd4) ? 8'h6E : (r == 3'd5) ? 8'h3C :
|
||||||
|
(r == 3'd6) ? 8'h0E : 8'h00; // Q
|
||||||
|
6'd33:
|
||||||
|
font_row = (r == 3'd0) ? 8'h7C : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
||||||
|
(r == 3'd3) ? 8'h7C : (r == 3'd4) ? 8'h78 : (r == 3'd5) ? 8'h6C :
|
||||||
|
(r == 3'd6) ? 8'h66 : 8'h00; // R
|
||||||
|
6'd34:
|
||||||
|
font_row = (r == 3'd0) ? 8'h3E : (r == 3'd1) ? 8'h60 : (r == 3'd2) ? 8'h60 :
|
||||||
|
(r == 3'd3) ? 8'h3C : (r == 3'd4) ? 8'h06 : (r == 3'd5) ? 8'h06 :
|
||||||
|
(r == 3'd6) ? 8'h7C : 8'h00; // S
|
||||||
|
6'd35:
|
||||||
|
font_row = (r == 3'd0) ? 8'h7E : (r == 3'd1) ? 8'h18 : (r == 3'd2) ? 8'h18 :
|
||||||
|
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h18 :
|
||||||
|
(r == 3'd6) ? 8'h18 : 8'h00; // T
|
||||||
|
6'd36:
|
||||||
|
font_row = (r == 3'd0) ? 8'h66 : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
||||||
|
(r == 3'd3) ? 8'h66 : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h66 :
|
||||||
|
(r == 3'd6) ? 8'h3C : 8'h00; // U
|
||||||
|
6'd37:
|
||||||
|
font_row = (r == 3'd0) ? 8'h66 : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h66 :
|
||||||
|
(r == 3'd3) ? 8'h66 : (r == 3'd4) ? 8'h66 : (r == 3'd5) ? 8'h3C :
|
||||||
|
(r == 3'd6) ? 8'h18 : 8'h00; // V
|
||||||
|
6'd38:
|
||||||
|
font_row = (r == 3'd0) ? 8'h63 : (r == 3'd1) ? 8'h63 : (r == 3'd2) ? 8'h63 :
|
||||||
|
(r == 3'd3) ? 8'h6B : (r == 3'd4) ? 8'h7F : (r == 3'd5) ? 8'h77 :
|
||||||
|
(r == 3'd6) ? 8'h63 : 8'h00; // W
|
||||||
|
6'd39:
|
||||||
|
font_row = (r == 3'd0) ? 8'h66 : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h3C :
|
||||||
|
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h3C : (r == 3'd5) ? 8'h66 :
|
||||||
|
(r == 3'd6) ? 8'h66 : 8'h00; // X
|
||||||
|
6'd40:
|
||||||
|
font_row = (r == 3'd0) ? 8'h66 : (r == 3'd1) ? 8'h66 : (r == 3'd2) ? 8'h3C :
|
||||||
|
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h18 : (r == 3'd5) ? 8'h18 :
|
||||||
|
(r == 3'd6) ? 8'h18 : 8'h00; // Y
|
||||||
|
6'd41:
|
||||||
|
font_row = (r == 3'd0) ? 8'h7E : (r == 3'd1) ? 8'h06 : (r == 3'd2) ? 8'h0C :
|
||||||
|
(r == 3'd3) ? 8'h18 : (r == 3'd4) ? 8'h30 : (r == 3'd5) ? 8'h60 :
|
||||||
|
(r == 3'd6) ? 8'h7E : 8'h00; // Z
|
||||||
|
default: font_row = 8'h00;
|
||||||
endcase
|
endcase
|
||||||
end
|
end
|
||||||
endfunction
|
endfunction
|
||||||
@@ -181,8 +281,7 @@ module cpu21_vga #(
|
|||||||
// 再在 cursor_box 里与上 blink 即可。
|
// 再在 cursor_box 里与上 blink 即可。
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
wire cursor_here = (cursor_i[4:0] == ccol) && (cursor_i[12:8] == crow);
|
wire cursor_here = (cursor_i[4:0] == ccol) && (cursor_i[12:8] == crow);
|
||||||
wire on_border = (hx[3:0] < 4'd2) || (hx[3:0] > 4'd13) ||
|
wire on_border = (hx[3:0] < 4'd2) || (hx[3:0] > 4'd13) || (vy[3:0] < 4'd2) || (vy[3:0] > 4'd13);
|
||||||
(vy[3:0] < 4'd2) || (vy[3:0] > 4'd13);
|
|
||||||
wire cursor_box = cursor_here && ctrl_i[0] && on_border;
|
wire cursor_box = cursor_here && ctrl_i[0] && on_border;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -192,22 +291,14 @@ module cpu21_vga #(
|
|||||||
input [2:0] c;
|
input [2:0] c;
|
||||||
begin
|
begin
|
||||||
case (c)
|
case (c)
|
||||||
3'd0:
|
3'd0: pal = {4'h0, 4'h0, 4'h0}; // 黑
|
||||||
pal = {4'h0, 4'h0, 4'h0}; // 黑
|
3'd1: pal = {4'h0, 4'h0, 4'hA}; // 深蓝
|
||||||
3'd1:
|
3'd2: pal = {4'h0, 4'hA, 4'h0}; // 绿
|
||||||
pal = {4'h0, 4'h0, 4'hA}; // 深蓝
|
3'd3: pal = {4'h0, 4'hA, 4'hA}; // 青
|
||||||
3'd2:
|
3'd4: pal = {4'hA, 4'h0, 4'h0}; // 红
|
||||||
pal = {4'h0, 4'hA, 4'h0}; // 绿
|
3'd5: pal = {4'hA, 4'h0, 4'hA}; // 洋红
|
||||||
3'd3:
|
3'd6: pal = {4'hA, 4'hA, 4'h0}; // 黄
|
||||||
pal = {4'h0, 4'hA, 4'hA}; // 青
|
default: pal = {4'hA, 4'hA, 4'hA}; // 浅灰
|
||||||
3'd4:
|
|
||||||
pal = {4'hA, 4'h0, 4'h0}; // 红
|
|
||||||
3'd5:
|
|
||||||
pal = {4'hA, 4'h0, 4'hA}; // 洋红
|
|
||||||
3'd6:
|
|
||||||
pal = {4'hA, 4'hA, 4'h0}; // 黄
|
|
||||||
default:
|
|
||||||
pal = {4'hA, 4'hA, 4'hA}; // 浅灰
|
|
||||||
endcase
|
endcase
|
||||||
end
|
end
|
||||||
endfunction
|
endfunction
|
||||||
@@ -222,8 +313,7 @@ module cpu21_vga #(
|
|||||||
vga_hs <= 1'b1;
|
vga_hs <= 1'b1;
|
||||||
vga_vs <= 1'b1;
|
vga_vs <= 1'b1;
|
||||||
frame_tick_o <= 1'b0;
|
frame_tick_o <= 1'b0;
|
||||||
end
|
end else if (tick) begin
|
||||||
else if (tick) begin
|
|
||||||
{vga_r, vga_g, vga_b} <= px_rgb;
|
{vga_r, vga_g, vga_b} <= px_rgb;
|
||||||
vga_hs <= hs_n;
|
vga_hs <= hs_n;
|
||||||
vga_vs <= vs_n;
|
vga_vs <= vs_n;
|
||||||
|
|||||||
+11
-12
@@ -126,13 +126,14 @@ powershell -ExecutionPolicy Bypass -File tools\build.ps1
|
|||||||
| `0x0000_1004` | 七段数码管:8 位十六进制数(低 4 位 = 最右) |
|
| `0x0000_1004` | 七段数码管:8 位十六进制数(低 4 位 = 最右) |
|
||||||
| `0x0000_1100+` | 鼠标:STATUS / DATA / ACK / CTRL / INPUT / TIME / FRAME / EVCNT / LEVEL / MAGIC |
|
| `0x0000_1100+` | 鼠标:STATUS / DATA / ACK / CTRL / INPUT / TIME / FRAME / EVCNT / LEVEL / MAGIC |
|
||||||
| `0x0000_1140+` | 显示控制器:CTRL(光标可见、背景色)/ CURSOR(列、行)/ MAGIC |
|
| `0x0000_1140+` | 显示控制器:CTRL(光标可见、背景色)/ CURSOR(列、行)/ MAGIC |
|
||||||
| `0x0000_2000-0x27FF` | 显存:40 列 × 30 行、每格 1 字节(只用到前 1200 字节) |
|
| `0x0000_2000-0x2FFF` | 显存:40 列 × 30 行、每格 2 字节(共 2400 字节) |
|
||||||
|
|
||||||
`LEVEL` 是**软件中断**寄存器:写 1 拉起 `irq_i[1]`(关卡入口),关卡返回前写 0 清掉,
|
`LEVEL` 是**软件中断**寄存器:写 1 拉起 `irq_i[1]`(关卡入口),关卡返回前写 0 清掉,
|
||||||
否则下次点"开始"没有新的上升沿。`MAGIC`(`0xCAFE0001/2`)可以读来自检 MMIO 是否活着。
|
否则下次点"开始"没有新的上升沿。`MAGIC`(`0xCAFE0001/2`)可以读来自检 MMIO 是否活着。
|
||||||
|
|
||||||
格子编码 1 字节 = `[3:0] 字形 [6:4] 颜色 [7] 保留`;字形 0 空白、1..8 数字、9 未揭开、
|
格子编码为两个字节:第一个字节低 6 位是字形编号,第二个字节低 3 位是颜色;字形 0 空白、
|
||||||
10 旗、11 雷、12 爆炸、13 问号、14 横线、15 实心;颜色是 3 位调色板。
|
1..8 数字、9 未揭开、10 旗、11 雷、12 爆炸、13 问号、14 横线、15 实心、16..41 为 A-Z;
|
||||||
|
颜色是 3 位调色板。
|
||||||
|
|
||||||
## 5. 怎么跑起来
|
## 5. 怎么跑起来
|
||||||
|
|
||||||
@@ -165,10 +166,10 @@ Pmod PS2 插在 JA + 一个 PS/2 鼠标)。`cpu21_ps2_mouse.v` 只实现了**
|
|||||||
## 6. 验证状态(说实话)
|
## 6. 验证状态(说实话)
|
||||||
|
|
||||||
| 部分 | 状态 |
|
| 部分 | 状态 |
|
||||||
| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
|
| ------------------------------------------------------------------------------------ | ------------------------------------------------------ |
|
||||||
| C 工具链链路 | **已实测**:编译/链接/ISA 检查/hex 生成/入口地址逐条核对 全部通过 |
|
| C 工具链链路 | 本次修改后未验证:当前环境缺少 RISC-V 交叉编译器 |
|
||||||
| `saolei.c` / `isr.c` / `start.c` / `linker.ld` | 逻辑对着 RTL 写;**未在仿真器/硬件上跑过** |
|
| `saolei.c` / `isr.c` / `start.c` / `linker.ld` | 本机 GCC 语法检查通过;目标 ISA 构建待工具链恢复后执行 |
|
||||||
| `cpu21_mmio.v` 扩展、`cpu21_vga.v`、`cpu21_seg7.v`、`cpu21_pointer.v`、板级顶层、XDC | **未综合、未上板** |
|
| `cpu21_mmio.v` 扩展、`cpu21_vga.v`、`cpu21_seg7.v`、`cpu21_pointer.v`、板级顶层、XDC | `xvlog` 编译通过;未综合、未上板 |
|
||||||
|
|
||||||
建议验证顺序:① 跑 `pipelined-cpu/tools/run_regress.ps1` 确认原来的跑马灯/中断/指令
|
建议验证顺序:① 跑 `pipelined-cpu/tools/run_regress.ps1` 确认原来的跑马灯/中断/指令
|
||||||
测试仍全 PASS;② 定义 `SAOLEI_ROM` 跑行为仿真,看有没有往 `0x2000` 起的显存写字节、
|
测试仍全 PASS;② 定义 `SAOLEI_ROM` 跑行为仿真,看有没有往 `0x2000` 起的显存写字节、
|
||||||
@@ -176,9 +177,7 @@ Pmod PS2 插在 JA + 一个 PS/2 鼠标)。`cpu21_ps2_mouse.v` 只实现了**
|
|||||||
|
|
||||||
## 7. 已知可改进项
|
## 7. 已知可改进项
|
||||||
|
|
||||||
- **屏幕上没有字母**:字模是 4 位字形(0-15),所以"开始"是绿色按钮条 + 光标框而不是
|
- **文字显示**:显存现在每格占 2 字节,字形编号扩展到 6 位,RTL 内置 A-Z 和数字/游戏
|
||||||
"START" 字样。要显示文字就把格子编码扩成 5 位字形(`mmio.h` 的 `CELL()` 与
|
符号字模,开始界面显示 `MINES`、`EASY`、`NORMAL`、`HARD`、`START`。
|
||||||
`cpu21_vga.v` 的字模表各改一处,颜色降到 3 位)。
|
- 棋盘固定 16×16,开始界面可选 20/40/60 雷(16 是 2 的幂,索引全用移位,一次除法都不用)。
|
||||||
- 棋盘固定 16×16、40 雷(16 是 2 的幂,索引全用移位,一次除法都不用);要改大小就改
|
|
||||||
`saolei.c` 顶部的 `BW/BH/NMINES`。
|
|
||||||
- `run_regress.ps1` 里还没有扫雷的自动测试项。
|
- `run_regress.ps1` 里还没有扫雷的自动测试项。
|
||||||
|
|||||||
+35
-7
@@ -24,7 +24,7 @@
|
|||||||
* 0x0000_1140 + 0x00 DISP_CTRL [0] 光标可见 [6:4] 背景色
|
* 0x0000_1140 + 0x00 DISP_CTRL [0] 光标可见 [6:4] 背景色
|
||||||
* 0x0000_1140 + 0x04 DISP_CURSOR [5:0] 列 [13:8] 行
|
* 0x0000_1140 + 0x04 DISP_CURSOR [5:0] 列 [13:8] 行
|
||||||
* 0x0000_1140 + 0x08 DISP_MAGIC 0xCAFE0001
|
* 0x0000_1140 + 0x08 DISP_MAGIC 0xCAFE0001
|
||||||
* 0x0000_2000 - 0x0000_27FF 显存:40 列 x 30 行,每格 1 字节,共 1200 字节
|
* 0x0000_2000 - 0x0000_2FFF 显存:40 列 x 30 行,每格 2 字节,共 2400 字节
|
||||||
*
|
*
|
||||||
* 鼠标事件单位:1/8 格(按键一次 = 8 = 移动一格;PS/2 鼠标用原始计数)。
|
* 鼠标事件单位:1/8 格(按键一次 = 8 = 移动一格;PS/2 鼠标用原始计数)。
|
||||||
* 左键点击 -> 事件的 btn 位 0 置 1;右键(PS/2)-> 位 1。
|
* 左键点击 -> 事件的 btn 位 0 置 1;右键(PS/2)-> 位 1。
|
||||||
@@ -69,13 +69,15 @@
|
|||||||
#define REG_R(a) (*(volatile unsigned *)((unsigned)(a)))
|
#define REG_R(a) (*(volatile unsigned *)((unsigned)(a)))
|
||||||
#define REG_W(a) (*(volatile unsigned *)((unsigned)(a)))
|
#define REG_W(a) (*(volatile unsigned *)((unsigned)(a)))
|
||||||
|
|
||||||
/* 显存按字节写(只能 sb,不能 lb —— 这颗核没有 lb/lbu) */
|
/* 显存按字节写(只能 sb,不能 lb —— 这颗核没有 lb/lbu)。每个格子占两个字节。 */
|
||||||
static inline void fb_write(unsigned idx, unsigned val)
|
static inline void fb_write_cell(unsigned idx, unsigned glyph, unsigned color)
|
||||||
{
|
{
|
||||||
*(volatile unsigned char *)(FB_BASE + idx) = (unsigned char)(val & 0xFFu);
|
unsigned addr = FB_BASE + (idx << 1);
|
||||||
|
*(volatile unsigned char *)addr = (unsigned char)(glyph & 0x3Fu);
|
||||||
|
*(volatile unsigned char *)(addr + 1u) = (unsigned char)(color & 0x07u);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 格子编码:1 字节 = [3:0] 字形 [6:4] 颜色 [7] 保留 ---- */
|
/* ---- 格子编码:两个字节 = 字形[5:0] + 颜色[2:0] ---- */
|
||||||
#define G_BLANK 0u /* 空 */
|
#define G_BLANK 0u /* 空 */
|
||||||
#define G_N1 1u /* 数字 1..8 = 1..8 */
|
#define G_N1 1u /* 数字 1..8 = 1..8 */
|
||||||
#define G_COVER 9u /* 未揭开 */
|
#define G_COVER 9u /* 未揭开 */
|
||||||
@@ -86,6 +88,34 @@ static inline void fb_write(unsigned idx, unsigned val)
|
|||||||
#define G_BAR 14u /* 横线 */
|
#define G_BAR 14u /* 横线 */
|
||||||
#define G_SOLID 15u /* 实心 */
|
#define G_SOLID 15u /* 实心 */
|
||||||
|
|
||||||
|
/* 大写字母字形编号 16..41,供菜单文字使用。 */
|
||||||
|
#define G_A 16u
|
||||||
|
#define G_B 17u
|
||||||
|
#define G_C 18u
|
||||||
|
#define G_D 19u
|
||||||
|
#define G_E 20u
|
||||||
|
#define G_F 21u
|
||||||
|
#define G_G 22u
|
||||||
|
#define G_H 23u
|
||||||
|
#define G_I 24u
|
||||||
|
#define G_J 25u
|
||||||
|
#define G_K 26u
|
||||||
|
#define G_L 27u
|
||||||
|
#define G_M 28u
|
||||||
|
#define G_N 29u
|
||||||
|
#define G_O 30u
|
||||||
|
#define G_P 31u
|
||||||
|
#define G_Q 32u
|
||||||
|
#define G_R 33u
|
||||||
|
#define G_S 34u
|
||||||
|
#define G_T 35u
|
||||||
|
#define G_U 36u
|
||||||
|
#define G_V 37u
|
||||||
|
#define G_W 38u
|
||||||
|
#define G_X 39u
|
||||||
|
#define G_Y 40u
|
||||||
|
#define G_Z 41u
|
||||||
|
|
||||||
#define C_BLACK 0u
|
#define C_BLACK 0u
|
||||||
#define C_BLUE 1u
|
#define C_BLUE 1u
|
||||||
#define C_GREEN 2u
|
#define C_GREEN 2u
|
||||||
@@ -95,8 +125,6 @@ static inline void fb_write(unsigned idx, unsigned val)
|
|||||||
#define C_YELLOW 6u
|
#define C_YELLOW 6u
|
||||||
#define C_GRAY 7u
|
#define C_GRAY 7u
|
||||||
|
|
||||||
#define CELL(g, c) ((unsigned)((g) | ((c) << 4)))
|
|
||||||
|
|
||||||
/* ---- 软件事件环形队列:中断服务程序(isr.c)生产,主循环(saolei.c)消费 ---- */
|
/* ---- 软件事件环形队列:中断服务程序(isr.c)生产,主循环(saolei.c)消费 ---- */
|
||||||
#define EVQ_SIZE 64u
|
#define EVQ_SIZE 64u
|
||||||
#define EVQ_MASK (EVQ_SIZE - 1u)
|
#define EVQ_MASK (EVQ_SIZE - 1u)
|
||||||
|
|||||||
+86
-20
@@ -1,7 +1,7 @@
|
|||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
* saolei.c —— 纯 C 扫雷(RV32IM 子集:只用这颗核实现了的指令)
|
* saolei.c —— 纯 C 扫雷(RV32IM 子集:只用这颗核实现了的指令)
|
||||||
*
|
*
|
||||||
* 显示:把每个格子写成显存里的 1 个字节(MMIO 0x2000 起)
|
* 显示:把每个格子写成显存里的 2 个字节(MMIO 0x2000 起)
|
||||||
* 输入:鼠标事件(中断驱动)——中断里搬到软件队列,主循环在下面处理
|
* 输入:鼠标事件(中断驱动)——中断里搬到软件队列,主循环在下面处理
|
||||||
*
|
*
|
||||||
* 针对这颗 CPU 的硬约束(很重要,改代码时别踩):
|
* 针对这颗 CPU 的硬约束(很重要,改代码时别踩):
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* 所以本文件所有索引运算都用"2 的幂 + 移位/掩码",一次除法都不用。
|
* 所以本文件所有索引运算都用"2 的幂 + 移位/掩码",一次除法都不用。
|
||||||
* 4) 没有 fence/ebreak 等;中断返回是 uret(在 isr.c 里)。
|
* 4) 没有 fence/ebreak 等;中断返回是 uret(在 isr.c 里)。
|
||||||
*
|
*
|
||||||
* 棋盘:16 x 16,40 颗雷(16 是 2 的幂 => 索引全用移位,颜色/字形查表也不用除法)
|
* 棋盘:16 x 16,雷数可选 20/40/60(16 是 2 的幂 => 索引全用移位)
|
||||||
* 网格:40 列 x 30 行,棋盘居中放在 (行 7..22, 列 12..27)
|
* 网格:40 列 x 30 行,棋盘居中放在 (行 7..22, 列 12..27)
|
||||||
* ---------------------------------------------------------------------------*/
|
* ---------------------------------------------------------------------------*/
|
||||||
#include "mmio.h"
|
#include "mmio.h"
|
||||||
@@ -21,7 +21,6 @@
|
|||||||
#define BW 16u /* 棋盘宽(格) */
|
#define BW 16u /* 棋盘宽(格) */
|
||||||
#define BH 16u /* 棋盘高(格) */
|
#define BH 16u /* 棋盘高(格) */
|
||||||
#define NCELLS (BW * BH) /* 256 */
|
#define NCELLS (BW * BH) /* 256 */
|
||||||
#define NMINES 40u
|
|
||||||
#define BM_WORDS (NCELLS / 32u) /* 8 */
|
#define BM_WORDS (NCELLS / 32u) /* 8 */
|
||||||
|
|
||||||
#define BOARD_C0 12u /* 棋盘左上角在网格里的列 */
|
#define BOARD_C0 12u /* 棋盘左上角在网格里的列 */
|
||||||
@@ -42,6 +41,8 @@ static unsigned opened_cnt;
|
|||||||
static unsigned flags_cnt;
|
static unsigned flags_cnt;
|
||||||
static unsigned g_state;
|
static unsigned g_state;
|
||||||
static unsigned g_first_done;
|
static unsigned g_first_done;
|
||||||
|
static unsigned g_mines;
|
||||||
|
static unsigned g_difficulty;
|
||||||
static unsigned rnd_state;
|
static unsigned rnd_state;
|
||||||
static unsigned cur_r, cur_c; /* 光标所在的网格格 */
|
static unsigned cur_r, cur_c; /* 光标所在的网格格 */
|
||||||
static int acc_x, acc_y; /* 指针累积量(1/8 格,有符号) */
|
static int acc_x, acc_y; /* 指针累积量(1/8 格,有符号) */
|
||||||
@@ -67,7 +68,7 @@ static void bclr(unsigned *bm, unsigned i)
|
|||||||
|
|
||||||
static void draw_cell(unsigned gr, unsigned gc, unsigned glyph, unsigned color)
|
static void draw_cell(unsigned gr, unsigned gc, unsigned glyph, unsigned color)
|
||||||
{
|
{
|
||||||
fb_write(gr * GRID_COLS + gc, CELL(glyph, color));
|
fb_write_cell(gr * GRID_COLS + gc, glyph, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 把棋盘格 (r,c) 画出来 */
|
/* 把棋盘格 (r,c) 画出来 */
|
||||||
@@ -118,6 +119,15 @@ static unsigned rnd_next(void)
|
|||||||
return rnd_state >> 16;
|
return rnd_state >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned difficulty_mines(unsigned difficulty)
|
||||||
|
{
|
||||||
|
if (difficulty == 0u)
|
||||||
|
return 20u;
|
||||||
|
if (difficulty == 2u)
|
||||||
|
return 60u;
|
||||||
|
return 40u;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================ 画面 ============================ */
|
/* ============================ 画面 ============================ */
|
||||||
|
|
||||||
static void clear_screen(void)
|
static void clear_screen(void)
|
||||||
@@ -125,7 +135,7 @@ static void clear_screen(void)
|
|||||||
unsigned i;
|
unsigned i;
|
||||||
for (i = 0u; i < (GRID_COLS * GRID_ROWS); i = i + 1u)
|
for (i = 0u; i < (GRID_COLS * GRID_ROWS); i = i + 1u)
|
||||||
{
|
{
|
||||||
fb_write(i, 0u);
|
fb_write_cell(i, G_BLANK, C_BLACK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +288,7 @@ static void reveal_at(unsigned i)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opened_cnt == (NCELLS - NMINES))
|
if (opened_cnt == (NCELLS - g_mines))
|
||||||
win();
|
win();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +298,7 @@ static void place_mines(unsigned safe)
|
|||||||
unsigned sr = safe >> 4;
|
unsigned sr = safe >> 4;
|
||||||
unsigned sc = safe & 15u;
|
unsigned sc = safe & 15u;
|
||||||
|
|
||||||
while (placed < NMINES)
|
while (placed < g_mines)
|
||||||
{
|
{
|
||||||
i = rnd_next() & 255u; /* NCELLS = 256,正好 2 的幂,不需要取模 */
|
i = rnd_next() & 255u; /* NCELLS = 256,正好 2 的幂,不需要取模 */
|
||||||
r = i >> 4;
|
r = i >> 4;
|
||||||
@@ -347,7 +357,7 @@ static void toggle_flag(void)
|
|||||||
flags_cnt = flags_cnt + 1u;
|
flags_cnt = flags_cnt + 1u;
|
||||||
draw_tile(r, c, G_FLAG, C_YELLOW);
|
draw_tile(r, c, G_FLAG, C_YELLOW);
|
||||||
}
|
}
|
||||||
REG_W(LED_ADDR) = NMINES - flags_cnt;
|
REG_W(LED_ADDR) = g_mines - flags_cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================ 鼠标事件 ============================ */
|
/* ============================ 鼠标事件 ============================ */
|
||||||
@@ -385,6 +395,11 @@ static unsigned g_exit; /* 关卡内请求返回 */
|
|||||||
#define START_R 20u
|
#define START_R 20u
|
||||||
#define START_C0 17u
|
#define START_C0 17u
|
||||||
#define START_C1 22u
|
#define START_C1 22u
|
||||||
|
#define DIFF_EASY_R 14u
|
||||||
|
#define DIFF_NORMAL_R 16u
|
||||||
|
#define DIFF_HARD_R 18u
|
||||||
|
|
||||||
|
static void draw_start_screen(void);
|
||||||
|
|
||||||
/* 背景用:光标可以在整个画面上移动 */
|
/* 背景用:光标可以在整个画面上移动 */
|
||||||
static void move_cursor_screen(int dr, int dc)
|
static void move_cursor_screen(int dr, int dc)
|
||||||
@@ -420,10 +435,29 @@ static void handle_event(unsigned ev)
|
|||||||
{ /* 左键 */
|
{ /* 左键 */
|
||||||
if (g_in_level == 0u)
|
if (g_in_level == 0u)
|
||||||
{
|
{
|
||||||
|
if (cur_r == DIFF_EASY_R)
|
||||||
|
{
|
||||||
|
g_difficulty = 0u;
|
||||||
|
draw_start_screen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cur_r == DIFF_NORMAL_R)
|
||||||
|
{
|
||||||
|
g_difficulty = 1u;
|
||||||
|
draw_start_screen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cur_r == DIFF_HARD_R)
|
||||||
|
{
|
||||||
|
g_difficulty = 2u;
|
||||||
|
draw_start_screen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
/* 背景:点到"开始"按钮 -> 拉起软件中断,跳到 IRQ1 里的关卡程序 */
|
/* 背景:点到"开始"按钮 -> 拉起软件中断,跳到 IRQ1 里的关卡程序 */
|
||||||
if ((cur_r == START_R) && (cur_c >= START_C0) && (cur_c <= START_C1))
|
if ((cur_r == START_R) && (cur_c >= START_C0) && (cur_c <= START_C1))
|
||||||
{
|
{
|
||||||
g_in_level = 1u; /* 关卡程序返回时自己清 0 */
|
g_in_level = 1u; /* 关卡程序返回时自己清 0 */
|
||||||
|
g_mines = difficulty_mines(g_difficulty);
|
||||||
REG_W(REG_LEVEL) = 1u;
|
REG_W(REG_LEVEL) = 1u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -490,24 +524,55 @@ static void handle_event(unsigned ev)
|
|||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
static void draw_start_screen(void)
|
static void draw_start_screen(void)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i, selected_color;
|
||||||
|
|
||||||
clear_screen();
|
clear_screen();
|
||||||
|
|
||||||
/* 标题区:两排雷图案,纯图形(字模里没有字母;要显示文字就得扩字模,见 README) */
|
/* 标题和难度菜单使用 RTL 字模中的大写字母。 */
|
||||||
for (i = 0u; i < 6u; i = i + 1u)
|
draw_cell(6u, 18u, G_M, C_RED);
|
||||||
{
|
draw_cell(6u, 19u, G_I, C_RED);
|
||||||
draw_cell(6u, 15u + i * 2u, G_MINE, C_RED);
|
draw_cell(6u, 20u, G_N, C_RED);
|
||||||
draw_cell(8u, 15u + i * 2u, G_MINE, C_RED);
|
draw_cell(6u, 21u, G_E, C_RED);
|
||||||
}
|
draw_cell(6u, 22u, G_S, C_RED);
|
||||||
|
|
||||||
/* "开始"按钮:绿色实心条,上下各一条黄线框住它 */
|
selected_color = C_YELLOW;
|
||||||
|
if (g_difficulty != 0u)
|
||||||
|
selected_color = C_GRAY;
|
||||||
|
draw_cell(DIFF_EASY_R, 18u, G_E, selected_color);
|
||||||
|
draw_cell(DIFF_EASY_R, 19u, G_A, selected_color);
|
||||||
|
draw_cell(DIFF_EASY_R, 20u, G_S, selected_color);
|
||||||
|
draw_cell(DIFF_EASY_R, 21u, G_Y, selected_color);
|
||||||
|
|
||||||
|
selected_color = C_YELLOW;
|
||||||
|
if (g_difficulty != 1u)
|
||||||
|
selected_color = C_GRAY;
|
||||||
|
draw_cell(DIFF_NORMAL_R, 16u, G_N, selected_color);
|
||||||
|
draw_cell(DIFF_NORMAL_R, 17u, G_O, selected_color);
|
||||||
|
draw_cell(DIFF_NORMAL_R, 18u, G_R, selected_color);
|
||||||
|
draw_cell(DIFF_NORMAL_R, 19u, G_M, selected_color);
|
||||||
|
draw_cell(DIFF_NORMAL_R, 20u, G_A, selected_color);
|
||||||
|
draw_cell(DIFF_NORMAL_R, 21u, G_L, selected_color);
|
||||||
|
|
||||||
|
selected_color = C_YELLOW;
|
||||||
|
if (g_difficulty != 2u)
|
||||||
|
selected_color = C_GRAY;
|
||||||
|
draw_cell(DIFF_HARD_R, 18u, G_H, selected_color);
|
||||||
|
draw_cell(DIFF_HARD_R, 19u, G_A, selected_color);
|
||||||
|
draw_cell(DIFF_HARD_R, 20u, G_R, selected_color);
|
||||||
|
draw_cell(DIFF_HARD_R, 21u, G_D, selected_color);
|
||||||
|
|
||||||
|
/* "START" 按钮:绿色实心条,上下各一条黄线框住它。 */
|
||||||
for (i = START_C0; i <= START_C1; i = i + 1u)
|
for (i = START_C0; i <= START_C1; i = i + 1u)
|
||||||
{
|
{
|
||||||
draw_cell(START_R - 1u, i, G_BAR, C_YELLOW);
|
draw_cell(START_R - 1u, i, G_BAR, C_YELLOW);
|
||||||
draw_cell(START_R, i, G_SOLID, C_GREEN);
|
draw_cell(START_R, i, G_SOLID, C_GREEN);
|
||||||
draw_cell(START_R + 1u, i, G_BAR, C_YELLOW);
|
draw_cell(START_R + 1u, i, G_BAR, C_YELLOW);
|
||||||
}
|
}
|
||||||
|
draw_cell(START_R, 17u, G_S, C_BLACK);
|
||||||
|
draw_cell(START_R, 18u, G_T, C_BLACK);
|
||||||
|
draw_cell(START_R, 19u, G_A, C_BLACK);
|
||||||
|
draw_cell(START_R, 20u, G_R, C_BLACK);
|
||||||
|
draw_cell(START_R, 21u, G_T, C_BLACK);
|
||||||
|
|
||||||
/* 下方提示条 */
|
/* 下方提示条 */
|
||||||
for (i = 12u; i <= 27u; i = i + 1u)
|
for (i = 12u; i <= 27u; i = i + 1u)
|
||||||
@@ -561,10 +626,10 @@ void level_main(void)
|
|||||||
draw_frame();
|
draw_frame();
|
||||||
draw_all_covered();
|
draw_all_covered();
|
||||||
draw_cell(RET_R, RET_C, G_BAR, C_YELLOW); /* 左上角黄格 = 返回 */
|
draw_cell(RET_R, RET_C, G_BAR, C_YELLOW); /* 左上角黄格 = 返回 */
|
||||||
set_bg(C_BLUE);
|
set_bg(C_BLACK);
|
||||||
update_cursor_reg();
|
update_cursor_reg();
|
||||||
REG_W(LED_ADDR) = NMINES;
|
REG_W(LED_ADDR) = g_mines;
|
||||||
REG_W(REG_SEG) = NMINES; /* 数码管显示剩余雷数 */
|
REG_W(REG_SEG) = g_mines; /* 数码管显示剩余雷数 */
|
||||||
|
|
||||||
/* ---- 关卡主循环 ---- */
|
/* ---- 关卡主循环 ---- */
|
||||||
while (g_exit == 0u)
|
while (g_exit == 0u)
|
||||||
@@ -595,7 +660,7 @@ void level_main(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
set_bg(C_BLUE);
|
set_bg(C_BLACK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -615,6 +680,7 @@ int main(void)
|
|||||||
crt_zero_bss(); /* 清 .bss(上电 RAM 本来就是 0,保险起见) */
|
crt_zero_bss(); /* 清 .bss(上电 RAM 本来就是 0,保险起见) */
|
||||||
|
|
||||||
rnd_state = REG_R(REG_TIME) | 1u; /* 用自由计数器当随机种子 */
|
rnd_state = REG_R(REG_TIME) | 1u; /* 用自由计数器当随机种子 */
|
||||||
|
g_difficulty = 1u; /* 默认普通:40 颗雷 */
|
||||||
REG_W(REG_MCTRL) = 1u; /* 打开鼠标中断(IRQ2,入口 0x200) */
|
REG_W(REG_MCTRL) = 1u; /* 打开鼠标中断(IRQ2,入口 0x200) */
|
||||||
REG_W(REG_LEVEL) = 0u; /* 清关卡软中断 */
|
REG_W(REG_LEVEL) = 0u; /* 清关卡软中断 */
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ $OBJDUMP = $Prefix + 'objdump'
|
|||||||
$OBJCOPY = $Prefix + 'objcopy'
|
$OBJCOPY = $Prefix + 'objcopy'
|
||||||
$SIZE = $Prefix + 'size'
|
$SIZE = $Prefix + 'size'
|
||||||
|
|
||||||
|
$Python = 'py'
|
||||||
|
if (-not (Get-Command $Python -ErrorAction SilentlyContinue)) {
|
||||||
|
$Python = 'python'
|
||||||
|
}
|
||||||
|
|
||||||
Write-Output "工具链前缀: $Prefix"
|
Write-Output "工具链前缀: $Prefix"
|
||||||
|
|
||||||
# ---------------------------------------------------------------- 编译参数
|
# ---------------------------------------------------------------- 编译参数
|
||||||
@@ -139,7 +144,7 @@ Write-Output "反汇编清单: $lst"
|
|||||||
$isaOk = $true
|
$isaOk = $true
|
||||||
if (-not $SkipIsaCheck) {
|
if (-not $SkipIsaCheck) {
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
& py (Join-Path $PSScriptRoot 'check_isa.py') $lst
|
& $Python (Join-Path $PSScriptRoot 'check_isa.py') $lst
|
||||||
if ($LASTEXITCODE -ne 0) { $isaOk = $false }
|
if ($LASTEXITCODE -ne 0) { $isaOk = $false }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +154,7 @@ $hex = Join-Path $build 'saolei.hex'
|
|||||||
$vh = Join-Path $root '..\pipelined-cpu\srcs\board\cpu21_saolei_rom.vh'
|
$vh = Join-Path $root '..\pipelined-cpu\srcs\board\cpu21_saolei_rom.vh'
|
||||||
|
|
||||||
& $OBJCOPY -O binary $elf $bin
|
& $OBJCOPY -O binary $elf $bin
|
||||||
& py (Join-Path $PSScriptRoot 'elf2hex.py') $bin $hex $RomWords $vh
|
& $Python (Join-Path $PSScriptRoot 'elf2hex.py') $bin $hex $RomWords $vh
|
||||||
|
|
||||||
# ---------------------------------------------------------------- 汇总
|
# ---------------------------------------------------------------- 汇总
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
|
|||||||
Reference in New Issue
Block a user