diff --git a/Makefile b/Makefile index bb1e965..2ee819a 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ BOOTLOAD_MONITOR_ARG ?= --monitor FIRMWARE_PROFILE ?= baremetal FIRMWARE_RUNTIME ?= FIRMWARE_DEBUG ?= none +FIRMWARE_ACCEL ?= none FIRMWARE_MAIN ?= $(REPO_ROOT)/firmware/apps/baremetal/soc_selftest.c GDB_STUB_MAIN ?= $(REPO_ROOT)/firmware/apps/baremetal/gdb_stub_smoke.c APP ?= @@ -92,7 +93,7 @@ SELECTED_FIRMWARE_PROFILE := $(if $(strip $(APP)),,$(FIRMWARE_PROFILE)) SELECTED_FIRMWARE_RUNTIME := $(if $(strip $(APP)),$(APP_RUNTIME),$(FIRMWARE_RUNTIME)) SELECTED_FIRMWARE_MAIN := $(if $(strip $(APP)),$(APP_SOURCE),$(FIRMWARE_MAIN)) -.PHONY: help check-env firmware firmware-load firmware-debug timer-irq-smoke timer-irq-load freertos-smoke freertos-queue freertos-acceptance freertos-load freertos-acceptance-load bootload gdb-stub-load gdb-stub-debug boot-image-test-build boot-image-test-load bad-apple-build bad-apple-load bad-apple-full-build bad-apple-full-preview bad-apple-source-audio-preview bad-apple-compact-midi-preview bad-apple-full-load bad-apple-window-build bad-apple-window-preview bad-apple-window-load rtl-syntax sim test-probe test-platform test-soc test-smoke test-freertos test-dual-core test-all ci ci-full perf benchmark board-benchmark ise-export +.PHONY: help check-env firmware firmware-load firmware-debug dot4-bench dot4-load timer-irq-smoke timer-irq-load freertos-smoke freertos-queue freertos-acceptance freertos-load freertos-acceptance-load bootload gdb-stub-load gdb-stub-debug boot-image-test-build boot-image-test-load bad-apple-build bad-apple-load bad-apple-full-build bad-apple-full-preview bad-apple-source-audio-preview bad-apple-compact-midi-preview bad-apple-full-load bad-apple-window-build bad-apple-window-preview bad-apple-window-load rtl-syntax sim test-probe test-platform test-soc test-smoke test-freertos test-dual-core test-all ci ci-full perf benchmark board-benchmark ise-export help: @echo "常用目标:" @@ -101,6 +102,8 @@ help: @echo " make firmware APP=baremetal/hello.c 按 apps 目录构建用户程序" @echo " make firmware-load APP=... PORT=COM8 构建、上传并监视用户程序" @echo " make firmware-debug APP=baremetal/... PORT=COM8 启动 Windows GDB" + @echo " make dot4-bench 仿真比较 RV32I scalar 与 custom-0 DOT4" + @echo " make dot4-load PORT=COM8 构建并上传 DarkRISCV DOT4 benchmark" @echo " make firmware FIRMWARE_OUT=... 构建到指定输出前缀" @echo " make timer-irq-smoke 构建 DarkRISCV timer IRQ 专用镜像" @echo " make timer-irq-load PORT=COM8 构建、上传并监视 timer IRQ 验收镜像" @@ -137,6 +140,7 @@ help: @echo " make perf / make benchmark 跑完整双核 benchmark,并生成日志、表格和环境快照" @echo " make board-benchmark PORT=COM9 BOOTLOAD_BAUD=115200 上板运行性能测试" @echo " make ise-export ISE_TARGET=minisoc 导出 ISE 工程所需文件到新目录" + @echo " make ise-export ISE_TARGET=minisoc_dot4_dark 导出 DOT4 ISE/上板包" check-env: "$(CHECK_ENV)" @@ -144,7 +148,8 @@ check-env: firmware: FIRMWARE_PROFILE="$(SELECTED_FIRMWARE_PROFILE)" \ FIRMWARE_RUNTIME="$(SELECTED_FIRMWARE_RUNTIME)" \ - FIRMWARE_DEBUG="$(FIRMWARE_DEBUG)" \ + FIRMWARE_DEBUG="$(FIRMWARE_DEBUG)" \ + FIRMWARE_ACCEL="$(FIRMWARE_ACCEL)" \ FIRMWARE_MAIN="$(SELECTED_FIRMWARE_MAIN)" \ "$(BUILD_FIRMWARE)" @@ -162,6 +167,16 @@ firmware-debug: $(if $(filter baremetal,$(APP_RUNTIME)),,$(error 当前 GDB 调试尚不支持 $(APP_RUNTIME) 应用)) @$(MAKE) gdb-stub-debug PORT="$(PORT)" GDB_STUB_MAIN="$(APP_SOURCE)" +dot4-bench: + "$(REPO_ROOT)/scripts/test_dot4_benchmark.sh" + +dot4-load: + $(if $(PORT),,$(error dot4-load 需要 PORT,例如 PORT=COM8)) + @$(MAKE) bootload PORT="$(PORT)" \ + FIRMWARE_PROFILE=baremetal \ + FIRMWARE_ACCEL=dot4 \ + FIRMWARE_MAIN="$(REPO_ROOT)/firmware/apps/baremetal/benchmarks/dot4_bench.c" + timer-irq-smoke: FIRMWARE_PROFILE=dark_irq \ FIRMWARE_MAIN="$(REPO_ROOT)/firmware/apps/irq/timer_irq_smoke.c" \ @@ -196,6 +211,7 @@ bootload: @FIRMWARE_PROFILE="$(FIRMWARE_PROFILE)" \ FIRMWARE_RUNTIME="$(FIRMWARE_RUNTIME)" \ FIRMWARE_DEBUG="$(FIRMWARE_DEBUG)" \ + FIRMWARE_ACCEL="$(FIRMWARE_ACCEL)" \ FIRMWARE_MAIN="$(FIRMWARE_MAIN)" \ FIRMWARE_OUT="$(BOOTLOAD_FIRMWARE_OUT)" \ "$(BUILD_FIRMWARE)" diff --git a/README.md b/README.md index de476fe..fff7ac3 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,19 @@ TecPlusRV 是一个面向 TEC-PLUS Spartan-6 XC6SLX9-2FTG256 平台的 RISC-V So - 从 SDRAM 取指或启动的执行路径 - ISE 工程文件或 bitstream +## DarkRISCV custom-0 DOT4 + +DarkRISCV profile 实现了一条非标准 `dot4.s8 rd, rs1, rs2` 指令:两个源寄存器 +各打包四个 signed INT8,结果为四组乘积之和。它不是完整 SIMD/Vector 扩展, +PicoRV32 也不会执行这条指令。详细编码、验证边界和 ISE/上板步骤见 +[`docs/DOT4_CUSTOM_ISA.md`](docs/DOT4_CUSTOM_ISA.md)。 + +```bash +make dot4-bench +make ise-export ISE_TARGET=minisoc_dot4_dark +make dot4-load PORT=COM8 +``` + ## 仓库结构 - `rtl/probe/`:早期上板探针顶层 diff --git a/docs/BENCHMARKS.md b/docs/BENCHMARKS.md index 15adfb0..8387d5e 100644 --- a/docs/BENCHMARKS.md +++ b/docs/BENCHMARKS.md @@ -47,3 +47,21 @@ BENCHMARK_RUN_ID=after-change make perf ## 已归档的 #19 基线 2026-07-10 的完整仿真样本已固定在 [`results/issue19-2026-07-10/`](../results/issue19-2026-07-10/),课程化的结论见 [`reports/issue19-performance-summary.md`](../reports/issue19-performance-summary.md)。该样本是 Icarus 仿真基线,不是板级 benchmark;后续优化和上板 PPA 的记录方式见 [`docs/FUTURE_PERFORMANCE_PLAN.md`](FUTURE_PERFORMANCE_PLAN.md)。 + +## DarkRISCV custom-0 DOT4 microbenchmark + +```bash +make dot4-bench +``` + +该命令只运行 DarkRISCV,比较相同 packed INT8 输入下的纯 RV32I scalar kernel 与 +`dot4.s8`。脚本会自动检查 checksum 一致,并要求 custom 的 `cycles` 和 `instret` +均低于 scalar。2026-07-13 当前 Icarus 样本为: + +| 模式 | cycles | instret | checksum | +| --- | ---: | ---: | --- | +| RV32I scalar | 694296 | 242718 | `0x00000170` | +| custom-0 DOT4 | 25752 | 6718 | `0x00000170` | + +在该固定 workload 下 cycle speedup 为 `26.96x`。这是 RTL 仿真相对结果;实际吞吐、 +DSP/LUT 代价与 Fmax 必须用 ISE Map/PAR/Timing Report 和真实上板日志重新确认。 diff --git a/docs/DOT4_CUSTOM_ISA.md b/docs/DOT4_CUSTOM_ISA.md new file mode 100644 index 0000000..0e22707 --- /dev/null +++ b/docs/DOT4_CUSTOM_ISA.md @@ -0,0 +1,101 @@ +# DarkRISCV custom-0 DOT4 扩展 + +## 指令语义 + +`dot4.s8 rd, rs1, rs2` 使用 RISC-V `custom-0` opcode,只在 TecPlusRV 的 +DarkRISCV profile 中实现: + +```text +opcode = 0001011 +funct3 = 000 +funct7 = 0000000 + +rd = signed(rs1[7:0]) * signed(rs2[7:0]) + + signed(rs1[15:8]) * signed(rs2[15:8]) + + signed(rs1[23:16]) * signed(rs2[23:16]) + + signed(rs1[31:24]) * signed(rs2[31:24]) +``` + +结果写回 signed 32-bit。它是一条 packed INT8、SIMD-style dot-product 指令, +不是完整 RISC-V Vector/SIMD 扩展,也不是带旧 `rd` 输入的 MAC。其他 +`custom-0` 编码必须触发 illegal-instruction trap。 + +## 硬件路径 + +```text +DarkRISCV decode/execute + -> CPR_REQ + CPR_PC + rs1/rs2 + -> rtl/accel/dot4_int8.v + -> CPR_ACK + result + -> register file rd +``` + +`CPR_PC` 是 transaction tag。协处理器将注册结果与发起指令 PC 绑定:流水线因 +`CPR_ACK` 继续后,如果下一条仍是 DOT4,PC 改变会立即撤销旧 ACK,从而避免同一条 +重复执行或相邻指令错误复用旧结果。等待期间 interrupt 延后到完整指令边界。 + +软件 ABI 位于 `firmware/accel/dot4.S`: + +```c +int dot4_s8(unsigned int packed_a, unsigned int packed_b); +``` + +默认 firmware 不包含该非标准指令;只有显式设置 `FIRMWARE_ACCEL=dot4` 才链接包装函数。 + +## 自动验证 + +```bash +./sim/run_sim.sh dot4_int8 +./sim/run_sim.sh darkriscv_dot4 +make dot4-bench +python3 scripts/test_runner.py run-suite rv32i_safe --keep-going +python3 scripts/test_runner.py run-suite rv32mi_dark --keep-going +``` + +覆盖范围包括 signed lane 边界、相邻请求、reset、结果写回、`rd=x0`、illegal +custom-0、协处理器 stall 中的 timer IRQ,以及 scalar/custom checksum 和性能方向。 + +## ISE 与上板人工 Gate + +```bash +make ise-export ISE_TARGET=minisoc_dot4_dark +make dot4-load PORT=COM8 +``` + +阶段性人工记录见 [`reports/dot4-board-validation.md`](../reports/dot4-board-validation.md)。 +该报告已经记录 Map 资源、真实开发板 UART benchmark、50 MHz timing 和 +`OVERMAPPED` 搜索结果;XST report 已出现 `Unit `,`g_darkriscv` +层级名未直接检索到,当前以 `CPU_IMPL=1`、DSP48A1 使用量与 UART PASS +作为间接证据。 + +本分支的面积基线是共同祖先 `35968e6` 上的 `minisoc_dark`,不是当前分支的 +`minisoc_dark`:当前分支已经全局接入 DOT4,拿它作对照会把加速器也综合进去。本地已 +准备两份脱离源码树仍可独立 elaboration 的导出包: + +```text +build/ise-export/minisoc_dark_baseline-35968e6 +build/ise-export/minisoc_dot4_dark-final +``` + +两份工程都设置相同的 `CPU_IMPL=1`、`BOOTLOADER_ENABLE=1`、 +`VGA_TEXT_ENABLE=0`,使用同一器件、ISE 设置和 50 MHz 约束。报告至少并排记录: + +| 配置 | Slice | LUT | FF | RAMB16 | DSP48A1 | 50 MHz slack | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| DarkRISCV baseline (`35968e6`) | 待填写 | 待填写 | 待填写 | 待填写 | 待填写 | 待填写 | +| DarkRISCV + DOT4(报告中记录实际 feature HEAD) | 待填写 | 待填写 | 待填写 | 待填写 | 待填写 | 待填写 | +| 增量 | 待计算 | 待计算 | 待计算 | 待计算 | 待计算 | — | + +DSP48A1 数量用于解释实现方式,不作为功能正确性的硬性条件:XST 可能把四个 signed +8x8 乘法映射到 DSP,也可能使用 LUT。无论采用哪种映射,都必须确认 `u_dot4` 未被 +trim、Map 未 overmap 且 post-route slack 为正。 + +ISE 中设置 `CPU_IMPL=1`、`BOOTLOADER_ENABLE=1`、`VGA_TEXT_ENABLE=0`,并保存: + +1. XST/Map 报告:无 `OVERMAPPED`,记录 Slice/LUT/FF 与 DSP48A1 数量; +2. PAR/Timing Report:50 MHz post-route slack 必须为正; +3. hierarchy:`g_darkriscv` 和 `u_dot4` 未被 trim; +4. UART 原始日志:scalar/custom checksum 相同,custom cycles/instret 更低。 + +本地 Icarus 不能证明 DSP 推断、板级 Fmax 或真实上板行为,因此在收到这些报告前, +文档只能声明“仿真通过”,不能声明硬件验收完成。 diff --git a/docs/PROJECT_SPEC.md b/docs/PROJECT_SPEC.md index 299bfca..e134729 100644 --- a/docs/PROJECT_SPEC.md +++ b/docs/PROJECT_SPEC.md @@ -26,7 +26,7 @@ TecPlusRV 对应北京邮电大学《项目式课程阶段 2》题目 B:基于 ## 拓展目标 -- 增加轻量级加速器,例如 popcount、Hamming distance、DOT4 INT8 +- 已实现 DarkRISCV custom-0 `DOT4 INT8` 指令;后续可增加 MMIO frontend,比较接口开销 - 增加更丰富的调试/可观测机制 - 尝试 Flash / EEPROM 相关启动或配置功能 - 如果时间允许,再增加显示类可观测外设 diff --git a/docs/superpowers/plans/2026-07-13-darkriscv-custom-dot4.md b/docs/superpowers/plans/2026-07-13-darkriscv-custom-dot4.md new file mode 100644 index 0000000..eb3a982 --- /dev/null +++ b/docs/superpowers/plans/2026-07-13-darkriscv-custom-dot4.md @@ -0,0 +1,85 @@ +# DarkRISCV custom-0 DOT4 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 为 DarkRISCV 增加 signed INT8 `dot4.s8` custom-0 指令,并以 RTL、core、MiniSoC benchmark、官方 ISA 回归和 ISE/上板 Gate 验证。 + +**Architecture:** 独立 DOT4 模块通过 DarkRISCV `CPR_*` 接口接入,`CPR_PC` 绑定 transaction;软件用独立汇编 ABI 调用,默认 RV32I firmware 不包含自定义指令。 + +**Tech Stack:** Verilog-2001、Icarus Verilog、RV32I assembly、riscv64-unknown-elf GCC/binutils、TecPlusRV test runner、ISE 14.7。 + +## Global Constraints + +- 所有开发者文本使用中文,保留常用英文术语。 +- 第一版仅支持 DarkRISCV,MMIO、PicoRV32 PCPI、完整 SIMD/Vector 和浮点不在范围内。 +- 指令为 signed INT8 纯点积,lane 0=`[7:0]`。 +- 每项行为先观察失败测试,再写最小实现。 +- ISE/PAR 和上板属于人工 Gate,不能用 Icarus 替代。 + +--- + +### Task 1: DOT4 计算与 transaction 握手 + +**Files:** +- Create: `rtl/accel/dot4_int8.v` +- Create: `sim/tb_dot4_int8.v` +- Modify: `sim/run_sim.sh` +- Modify: `scripts/rtl_syntax_case.sh` +- Modify: `scripts/test_catalog.json` +- Modify: `rtl/files.f` + +**Interfaces:** `req/tag/rs1/rs2 -> ack/result`。 + +- [x] 写缺少 module 时失败的 testbench。 +- [x] 实现 signed 四路乘法、18-bit 求和与 32-bit 符号扩展。 +- [x] 用 PC tag 覆盖同一请求保持和相邻请求连续为高。 +- [x] 运行 `./sim/run_sim.sh dot4_int8` 与 syntax case。 + +### Task 2: DarkRISCV custom-0 接入 + +**Files:** +- Create: `firmware/tests/darkriscv_dot4.S` +- Create: `sim/tb_darkriscv_dot4.v` +- Modify: `rtl/core/darkriscv.v` +- Modify: `rtl/core/darkriscv_config.vh` +- Modify: `rtl/soc/darkriscv_adapter.v` + +**Interfaces:** 仅 `custom-0/funct3=0/funct7=0` 合法。 + +- [x] 先观察协处理器端口缺失的失败测试。 +- [x] 接入 CPR request/result/ack 与 `CPR_PC`。 +- [x] 覆盖结果、相邻 DOT4、x0、非法编码和 stall 中 timer IRQ。 +- [x] 运行 machine trap、`rv32i_safe` 和 `rv32mi_dark`。 + +### Task 3: firmware ABI 与性能对照 + +**Files:** +- Create: `firmware/accel/dot4.h` +- Create: `firmware/accel/dot4.S` +- Create: `firmware/apps/baremetal/benchmarks/dot4_bench.c` +- Create: `scripts/test_dot4_benchmark.sh` +- Modify: `scripts/build_firmware.sh` +- Modify: `Makefile` + +**Interfaces:** `int dot4_s8(unsigned int a, unsigned int b)`;显式 +`FIRMWARE_ACCEL=dot4`。 + +- [x] 先观察 `dot4_s8` undefined reference。 +- [x] 增加 `.insn r CUSTOM_0, 0, 0, a0, a0, a1` ABI。 +- [x] 比较相同输入的 checksum、cycles 和 instret。 +- [x] 自动要求 custom cycles/instret 均低于 scalar。 + +### Task 4: 文档、回归与人工 Gate + +**Files:** +- Create: `docs/DOT4_CUSTOM_ISA.md` +- Modify: `README.md` +- Modify: `docs/PROJECT_SPEC.md` +- Modify: `docs/BENCHMARKS.md` +- Modify: `scripts/export_ise_project.sh` + +- [x] 增加 `dot4` suite 和 `make dot4-bench`。 +- [x] 增加 `minisoc_dot4_dark` ISE 导出目标。 +- [x] 从共同基线 `35968e6` 生成无 DOT4 的 `minisoc_dark` PPA 对照包,并验证两份导出包可脱离源码树独立 elaboration。 +- [x] fresh 运行 `dot4`、smoke、RV32I、RV32MI completion gate。 +- [ ] 用户执行 ISE Map/PAR/Timing 与真实上板验证。 diff --git a/docs/superpowers/specs/2026-07-13-darkriscv-custom-dot4-design.md b/docs/superpowers/specs/2026-07-13-darkriscv-custom-dot4-design.md new file mode 100644 index 0000000..692c73f --- /dev/null +++ b/docs/superpowers/specs/2026-07-13-darkriscv-custom-dot4-design.md @@ -0,0 +1,46 @@ +# DarkRISCV custom-0 DOT4 设计 + +## 目标与边界 + +TecPlusRV 在 DarkRISCV profile 中实现一条非标准 `dot4.s8 rd, rs1, rs2`:两个 +32-bit 源寄存器各打包四个 signed INT8,硬件完成四组乘法并求和,将 signed +32-bit 结果写回 `rd`。 + +第一版不修改 PicoRV32,不实现完整 SIMD/Vector ISA,不使用旧 `rd` 作为累加输入。 +MMIO frontend 作为后续接口开销实验,不进入本轮实现。 + +## 编码与异常 + +```text +opcode = 0001011 (custom-0) +funct3 = 000 +funct7 = 0000000 +``` + +lane 0 固定为 `[7:0]`,lane 3 固定为 `[31:24]`。除上述编码外的 custom-0 +必须触发 illegal-instruction trap;`rd=x0` 不得改变 x0。 + +## 硬件结构 + +独立 `rtl/accel/dot4_int8.v` 负责 signed 乘加。DarkRISCV 通过现有 `CPR_*` +协处理器接口连接,新增 `CPR_PC` 作为 transaction tag。结果寄存后只对相同 PC +拉高 ACK;相邻 DOT4 的 PC 改变会立即撤销旧 ACK,避免重复接收或复用旧结果。 + +协处理器等待沿用 core 的 `HLT`。interrupt 在 DOT4 完成后的完整指令边界进入, +性能计数器对每条合法 DOT4 只计一次提交。 + +## 软件与验证 + +`firmware/accel/dot4.S` 提供 `dot4_s8(a, b)` ABI,只有显式 +`FIRMWARE_ACCEL=dot4` 才链接;其余 firmware 保持 RV32I。 + +验证分为: + +1. signed lane、边界值、reset 和相邻 transaction 的模块级 testbench; +2. 写回、x0、非法编码、stall/retire 和 timer IRQ 的 core-level 汇编测试; +3. scalar/custom checksum、cycles、instret 的 MiniSoC benchmark; +4. 官方 RV32I/RV32MI 与现有 smoke 回归; +5. ISE Map/PAR/Timing 和真实上板人工 Gate。 + +Icarus 只证明 RTL 功能和相对周期结果。DSP48A1 推断、资源增量、50 MHz slack 与 +板级 UART 行为必须以 ISE 和真实硬件证据为准。 diff --git a/firmware/accel/dot4.S b/firmware/accel/dot4.S new file mode 100644 index 0000000..2564a8b --- /dev/null +++ b/firmware/accel/dot4.S @@ -0,0 +1,10 @@ + # int dot4_s8(unsigned int packed_a, unsigned int packed_b) + # a0/a1 直接作为 rs1/rs2,结果按 ABI 返回到 a0。 + .section .text + .align 2 + .globl dot4_s8 + .type dot4_s8, @function +dot4_s8: + .insn r CUSTOM_0, 0, 0, a0, a0, a1 + ret + .size dot4_s8, .-dot4_s8 diff --git a/firmware/accel/dot4.h b/firmware/accel/dot4.h new file mode 100644 index 0000000..919d35e --- /dev/null +++ b/firmware/accel/dot4.h @@ -0,0 +1,7 @@ +// DarkRISCV custom-0 DOT4 软件接口。 +#ifndef DOT4_H +#define DOT4_H + +int dot4_s8(unsigned int packed_a, unsigned int packed_b); + +#endif diff --git a/firmware/apps/baremetal/benchmarks/dot4_bench.c b/firmware/apps/baremetal/benchmarks/dot4_bench.c new file mode 100644 index 0000000..e9d2801 --- /dev/null +++ b/firmware/apps/baremetal/benchmarks/dot4_bench.c @@ -0,0 +1,123 @@ +// 纯 RV32I 与 DarkRISCV custom-0 DOT4 的正确性/性能对照。 +#include "tests/testlib.h" +#include "drivers/perf.h" +#include "accel/dot4.h" + +#define VECTOR_COUNT 32u +#define ROUNDS 16u + +static volatile unsigned int input_a[VECTOR_COUNT]; +static volatile unsigned int input_b[VECTOR_COUNT]; +static volatile int scalar_result[VECTOR_COUNT]; +static volatile int custom_result[VECTOR_COUNT]; + +static int __attribute__((noinline)) dot4_scalar_s8( + unsigned int packed_a, + unsigned int packed_b +) +{ + signed char a0 = (signed char)(packed_a & 0xffu); + signed char a1 = (signed char)((packed_a >> 8) & 0xffu); + signed char a2 = (signed char)((packed_a >> 16) & 0xffu); + signed char a3 = (signed char)((packed_a >> 24) & 0xffu); + signed char b0 = (signed char)(packed_b & 0xffu); + signed char b1 = (signed char)((packed_b >> 8) & 0xffu); + signed char b2 = (signed char)((packed_b >> 16) & 0xffu); + signed char b3 = (signed char)((packed_b >> 24) & 0xffu); + + return a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3; +} + +static void init_vectors(void) +{ + unsigned int i; + + for (i = 0u; i < VECTOR_COUNT; i++) { + input_a[i] = 0x80ff017fu ^ (i * 0x01030507u); + input_b[i] = 0x7f02ff80u + (i * 0x07050301u); + } +} + +static unsigned int run_scalar(void) +{ + unsigned int round; + unsigned int i; + unsigned int checksum = 0u; + + for (round = 0u; round < ROUNDS; round++) { + for (i = 0u; i < VECTOR_COUNT; i++) { + int value = dot4_scalar_s8(input_a[i], input_b[i]); + scalar_result[i] = value; + checksum ^= (unsigned int)value + round + i; + } + } + return checksum; +} + +static unsigned int run_custom(void) +{ + unsigned int round; + unsigned int i; + unsigned int checksum = 0u; + + for (round = 0u; round < ROUNDS; round++) { + for (i = 0u; i < VECTOR_COUNT; i++) { + int value = dot4_s8(input_a[i], input_b[i]); + custom_result[i] = value; + checksum ^= (unsigned int)value + round + i; + } + } + return checksum; +} + +static void report_result( + const char *mode, + unsigned int checksum, + const perf_snapshot_t *snap +) +{ + uart_puts("RESULT: benchmark=dot4 mode="); + uart_puts(mode); + uart_puts(" vectors=32 rounds=16 checksum="); + uart_put_hex(checksum); + uart_puts(" cycles="); + uart_put_dec(snap->cycle); + uart_puts(" instret="); + uart_put_dec(snap->instret); + uart_puts(" mem_wait="); + uart_put_dec(snap->mem_wait); + uart_puts("\n"); +} + +int main(void) +{ + perf_snapshot_t scalar_perf; + perf_snapshot_t custom_perf; + unsigned int scalar_checksum; + unsigned int custom_checksum; + unsigned int i; + + test_banner("dot4_bench"); + init_vectors(); + + perf_begin(&scalar_perf); + scalar_checksum = run_scalar(); + perf_end(&scalar_perf); + + perf_begin(&custom_perf); + custom_checksum = run_custom(); + perf_end(&custom_perf); + + for (i = 0u; i < VECTOR_COUNT; i++) { + test_expect(custom_result[i] == scalar_result[i], 0xdead8000u + i); + } + test_expect(custom_checksum == scalar_checksum, 0xdead8100u); + test_expect(custom_perf.cycle < scalar_perf.cycle, 0xdead8101u); + test_expect(custom_perf.instret < scalar_perf.instret, 0xdead8102u); + + report_result("scalar", scalar_checksum, &scalar_perf); + report_result("custom", custom_checksum, &custom_perf); + uart_flush(); + test_pass(); + return 0; +} diff --git a/firmware/tests/darkriscv_dot4.S b/firmware/tests/darkriscv_dot4.S new file mode 100644 index 0000000..ce9cb08 --- /dev/null +++ b/firmware/tests/darkriscv_dot4.S @@ -0,0 +1,62 @@ + # DarkRISCV custom-0 DOT4 core-level 定向测试。 + .section .text.init + .globl _start + +_start: + la t0, trap_entry_dot4 + csrw mtvec, t0 + li t0, (1 << 7) + csrw mie, t0 + csrsi mstatus, 8 + + li a0, 0x04030201 + li a1, 0x08070605 + .insn r CUSTOM_0, 0, 0, s0, a0, a1 +after_irq_dot4: + addi s1, s1, 1 + + li a0, 0x80ff017f + li a1, 0x7f02ff80 + .insn r CUSTOM_0, 0, 0, s2, a0, a1 + + # rd=x0 仍应完成 transaction,但不能改变 x0。 + .insn r CUSTOM_0, 0, 0, zero, a0, a1 + + li a0, 0x01010101 + li a1, 0x01010101 + .insn r CUSTOM_0, 0, 0, s3, a0, a1 + li a0, 0x02020202 + li a1, 0x03030303 + .insn r CUSTOM_0, 0, 0, s4, a0, a1 + + # funct7=1 未定义,必须进入 illegal-instruction trap。 + .insn r CUSTOM_0, 0, 1, s5, a0, a1 +after_illegal: + sw s0, 0x100(zero) + sw s1, 0x104(zero) + sw s2, 0x108(zero) + sw s3, 0x10c(zero) + sw s4, 0x110(zero) + sw zero, 0x114(zero) + li t0, 1 + sw t0, 0x13c(zero) + +done: + j done + + .org 0x200 + .align 2 +trap_entry_dot4: + csrr t0, mcause + bltz t0, interrupt_trap + + sw t0, 0x120(zero) + csrr t1, mepc + addi t1, t1, 4 + csrw mepc, t1 + mret + +interrupt_trap: + sw t0, 0x124(zero) + csrw mie, zero + mret diff --git a/reports/dot4-board-validation.md b/reports/dot4-board-validation.md new file mode 100644 index 0000000..a1ea5e3 --- /dev/null +++ b/reports/dot4-board-validation.md @@ -0,0 +1,203 @@ +# DOT4 上板人工验证记录 + +## 结论 + +本报告记录 issue #29 中 DarkRISCV custom-0 `dot4.s8` 指令的阶段性人工验证结果。当前已经确认: + +- `dot4_bench` 在真实开发板 UART 输出 `PASS`; +- scalar 与 custom DOT4 的 checksum 一致; +- custom DOT4 的 `cycles` 与 `instret` 明显低于 scalar 版本; +- ISE Map 阶段资源可放下,错误数为 0; +- `DSP48A1 = 4`,符合四路 signed INT8 乘法被综合为硬件乘法资源的预期。 +- ISE timing 对 20 ns / 50 MHz 约束的 SETUP 与 HOLD 检查均通过。 +- Map Report 中未检索到 `OVERMAPPED`。 + +当前尚未确认: + +- `g_darkriscv` 在 hierarchy 或 report 中的直接层级名证据。 + +因此本轮可以写作“DOT4 功能上板通过,Map 资源阶段通过,50 MHz timing 通过”。XST report 已出现 `Unit `,说明 DOT4 单元没有被整体 trim;`g_darkriscv` 文本检索未命中时,不能把它当作 CPU 路径错误的证据,ISE 可能在综合、Map 或导出流程中 flatten / rename 层级。当前用 `CPU_IMPL=1`、UART `PASS`、checksum 一致、custom cycles 明显降低以及 `DSP48A1=4` 作为 DarkRISCV DOT4 路径工作的证据。 + +需要注意:issue #29 中提到的“资源增量”尚未完整闭环。当前已经有 DOT4 版本的绝对资源使用量,但还缺共同基线 `35968e6` 上 `minisoc_dark` 的同口径 Map/PAR/Timing 数据,因此暂时不能计算严格的 baseline -> DOT4 增量。 + +## 验证环境 + +| 项目 | 内容 | +| --- | --- | +| 分支 | `feat/darkriscv-dot4` | +| 记录 commit | `6b980ac` | +| ISE target | `minisoc_dot4_dark` | +| top module | `tecplus_minisoc_top` | +| CPU 参数 | `CPU_IMPL=1` | +| bootloader 参数 | `BOOTLOADER_ENABLE=1` | +| VGA text 参数 | `VGA_TEXT_ENABLE=0` | +| UART benchmark | `make dot4-load PORT=COM9 BOOTLOAD_BAUD=115200` | + +数据来源为本轮人工操作中从 ISE report 与 UART monitor 手动摘录的文本。当前仓库尚未归档完整 `.mrp`、`.par`、`.twr` 或串口原始日志文件。 + +## Issue #29 Gate 对照 + +| Gate | 当前状态 | 依据 | 备注 | +| --- | --- | --- | --- | +| ISE Map | 已有结果 | Map errors=0,资源表已记录,未检索到 `OVERMAPPED` | 当前为 DOT4 版本绝对资源 | +| ISE PAR | 已有流程证据 | 已生成 post-route timing,并且 bitstream 可上板运行 UART benchmark | 尚未单独归档 PAR report 原文 | +| ISE Timing | 通过 | 20 ns 约束下 SETUP slack=0.650 ns,HOLD slack=0.343 ns,timing errors=0 | 50 MHz 余量较小,后续改动需重跑 | +| 资源增量 | 未完整 | DOT4 版本资源已记录 | 缺 baseline `35968e6` 的同口径资源与 timing | +| 真实开发板 UART benchmark | 通过 | `dot4_bench` 输出 scalar/custom 两组 `RESULT` 并最终 `PASS` | checksum 一致,custom cycles/instret 更低 | + +## 本地 preflight + +上板前已运行: + +```bash +make dot4-bench +``` + +本地仿真结果: + +| mode | checksum | cycles | instret | mem_wait | +| --- | ---: | ---: | ---: | ---: | +| scalar | `0x00000170` | 694304 | 242721 | 22548 | +| custom | `0x00000170` | 25760 | 6721 | 6164 | + +仿真中 custom DOT4 相对 scalar 的 cycle speedup 约为 `26.95x`。 + +## ISE Map 资源记录 + +用户从 ISE Map Report 摘录: + +| 资源 | 使用量 | 总量 | 利用率 | +| --- | ---: | ---: | ---: | +| Slice | 1319 | 1430 | 92% | +| Slice Registers / FF | 1607 | 11440 | 14% | +| Slice LUTs | 4200 | 5720 | 73% | +| RAMB16BWER | 32 | 32 | 100% | +| RAMB8BWER | 0 | 64 | 0% | +| DSP48A1 | 4 | 16 | 25% | + +同时记录: + +```text +Number of errors: 0 +Number of warnings: 42 +``` + +判断: + +- Slice 利用率已经达到 92%,后续继续增加逻辑会很紧张; +- RAMB16BWER 已经 100% 用满,后续不能再轻易增加片上 BRAM; +- DSP48A1 使用 4 个,符合 DOT4 四路乘法的实现预期; +- Map 错误数为 0,是资源阶段可继续推进的正向证据; +- Map Report 中未检索到 `OVERMAPPED`,当前记录为无 overmap 证据。 + +层级名与 trim 检查: + +- XST report 中出现 `Unit `,这是 DOT4 单元存在的直接证据; +- XST 同时报告 `u_dot4` 内部部分 `result_*` FF/Latch 等价并被移除,例如 `result_17` 等价于若干高位 result 寄存器; +- 这类优化不是 `u_dot4` 被整体 trim,而是 XST 对等价寄存器位做合并/删除;DOT4 signed 结果存在符号扩展和等价高位时,出现这类优化是可以接受的; +- 用户在 report / hierarchy 文本中尚未检索到 `g_darkriscv`;这不等价于 CPU 路径错误,因为 ISE 可能 flatten / rename 层级; +- 如需更强证据,可在 ISE 中开启 `Keep Hierarchy = Yes` 后重新综合,或保存 Technology Schematic / FPGA Editor 中对应 DOT4 乘法路径的截图。 + +## 资源增量状态 + +当前只有 DOT4 版本的绝对资源数据: + +| 配置 | Slice | LUT | FF | RAMB16BWER | DSP48A1 | 50 MHz setup slack | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| DarkRISCV + DOT4 (`6b980ac`) | 1319 | 4200 | 1607 | 32 | 4 | 0.650 ns | +| DarkRISCV baseline (`35968e6`) | 待补充 | 待补充 | 待补充 | 待补充 | 待补充 | 待补充 | +| 增量 | 待计算 | 待计算 | 待计算 | 待计算 | 待计算 | 待计算 | + +因此现在可以报告“DOT4 版本资源可放下”,但不能报告“DOT4 相比 baseline 的资源增量”。若要完成资源增量,需要对 `build/ise-export/minisoc_dark_baseline-35968e6` 运行同样 ISE 设置,并记录同口径 Map/PAR/Timing。 + +## 上板 UART benchmark + +用户从串口 monitor 摘录: + +```text +RESULT: benchmark=dot4 mode=scalar vectors=32 rounds=16 checksum=0x00000170 cycles=694304 instret=242721 mem_wait=22548 +RESULT: benchmark=dot4 mode=custom vectors=32 rounds=16 checksum=0x00000170 cycles=25760 instret=6721 mem_wait=6164 +PASS +``` + +结构化结果: + +| mode | checksum | cycles | instret | mem_wait | +| --- | ---: | ---: | ---: | ---: | +| scalar | `0x00000170` | 694304 | 242721 | 22548 | +| custom | `0x00000170` | 25760 | 6721 | 6164 | + +计算得到: + +| 指标 | 数值 | +| --- | ---: | +| cycle speedup | 26.95x | +| instret reduction | 36.11x | +| mem_wait reduction | 3.66x | + +判断: + +- checksum 一致,说明 custom DOT4 与 scalar 参考实现计算结果一致; +- `PASS` 出现,说明 firmware 内部 correctness 与 performance expectation 均通过; +- custom 的 cycle / instret 显著下降,符合 issue #29 的性能预期。 + +## Timing 状态 + +用户从 ISE timing summary 摘录: + +```text +Constraint: TS_clk_grp = PERIOD TIMEGRP "clk_grp" 20 ns HIGH 50% +SETUP Worst Case Slack: 0.650 ns +SETUP Best Case Achievable: 19.350 ns +SETUP Timing Errors: 0 +SETUP Timing Score: 0 +HOLD Worst Case Slack: 0.343 ns +HOLD Timing Errors: 0 +HOLD Timing Score: 0 +``` + +判断: + +- 目标时钟为 50 MHz,对应周期为 20 ns; +- SETUP slack 为 `0.650 ns`,为正; +- HOLD slack 为 `0.343 ns`,为正; +- timing errors 为 0,timing score 为 0; +- `Best Case Achievable = 19.350 ns`,折合约 `51.68 MHz`,说明对 50 MHz 约束有约 `0.650 ns` 余量。 + +因此当前记录为: + +```text +Target period: 20 ns +50 MHz setup slack: 0.650 ns +50 MHz hold slack: 0.343 ns +Best case achievable period: 19.350 ns +Best case achievable frequency: 51.68 MHz +Timing conclusion: PASS +``` + +注意:这组 timing 余量不大,后续再增加逻辑、打开 VGA text 或修改约束后必须重新跑 Post-Place & Route timing。 + +## PAR 状态 + +当前没有单独摘录 PAR report 的 `Place & Route completed successfully` 原文。不过本轮已经拿到 post-place & route timing summary,并且生成的 bitstream 已经完成真实开发板 UART benchmark,因此可以作为 PAR 流程已跑通的证据。 + +为了报告更严谨,后续可以补充 PAR report 中的 summary 原文,例如: + +```text +Place & Route completed successfully +Number of errors: 0 +``` + +## 当前收口状态 + +| Gate | 状态 | 说明 | +| --- | --- | --- | +| DOT4 RTL / MiniSoC 仿真 | 通过 | `make dot4-bench` 通过,cycle speedup 约 26.95x | +| ISE Map 资源 | 阶段通过 | Map errors=0,资源可放下;Slice/RAMB 使用率较高 | +| 资源增量 | 待补充 | 已记录 DOT4 版本绝对资源;缺 baseline `35968e6` 同口径数据 | +| DSP 推断 | 有正向证据 | DSP48A1=4 | +| 真实开发板 UART benchmark | 通过 | checksum 一致,输出 `PASS` | +| OVERMAPPED 显式检查 | 通过 | Map Report 中未检索到 `OVERMAPPED` | +| `u_dot4` 层级名检查 | 通过 | XST report 出现 `Unit `;仅内部等价 `result_*` FF 被优化 | +| `g_darkriscv` 层级名检查 | 未取得直接证据 | 文本检索未命中;以 `CPU_IMPL=1`、DSP48A1=4 与 UART PASS 作为间接证据 | +| 50 MHz post-route timing | 通过 | SETUP slack=0.650 ns,HOLD slack=0.343 ns,timing errors=0 | diff --git a/rtl/accel/dot4_int8.v b/rtl/accel/dot4_int8.v new file mode 100644 index 0000000..429123c --- /dev/null +++ b/rtl/accel/dot4_int8.v @@ -0,0 +1,56 @@ +// 四路 signed INT8 点积协处理器。 +// req 有效时锁存结果;tag 绑定当前指令,防止流水线停顿时重复接收同一请求。 +module dot4_int8 ( + input clk, + input reset, + input req, + input [31:0] tag, + input [31:0] rs1, + input [31:0] rs2, + output ack, + output reg [31:0] result +); + +reg result_valid; +reg [31:0] result_tag; + +wire signed [7:0] a0 = rs1[7:0]; +wire signed [7:0] a1 = rs1[15:8]; +wire signed [7:0] a2 = rs1[23:16]; +wire signed [7:0] a3 = rs1[31:24]; +wire signed [7:0] b0 = rs2[7:0]; +wire signed [7:0] b1 = rs2[15:8]; +wire signed [7:0] b2 = rs2[23:16]; +wire signed [7:0] b3 = rs2[31:24]; + +wire signed [15:0] product0 = a0 * b0; +wire signed [15:0] product1 = a1 * b1; +wire signed [15:0] product2 = a2 * b2; +wire signed [15:0] product3 = a3 * b3; +wire signed [17:0] sum = + {{2{product0[15]}}, product0} + + {{2{product1[15]}}, product1} + + {{2{product2[15]}}, product2} + + {{2{product3[15]}}, product3}; + +// 结果只对产生它的 instruction PC 有效。相邻 custom 指令即使 req 连续为高, +// tag 改变也会立即撤销旧 ack,让 CPU 为新结果停顿。 +assign ack = result_valid && req && (tag == result_tag); + +always @(posedge clk) begin + if (reset) begin + result_valid <= 1'b0; + result_tag <= 32'h0000_0000; + result <= 32'h0000_0000; + end else begin + if (!req) begin + result_valid <= 1'b0; + end else if (!result_valid || tag != result_tag) begin + result <= {{14{sum[17]}}, sum}; + result_tag <= tag; + result_valid <= 1'b1; + end + end +end + +endmodule diff --git a/rtl/core/darkriscv.v b/rtl/core/darkriscv.v index 5f6bf77..82159bd 100644 --- a/rtl/core/darkriscv.v +++ b/rtl/core/darkriscv.v @@ -98,6 +98,7 @@ module darkriscv output CPR_REQ, output [ 2:0] CPR_FCT3, output [ 6:0] CPR_FCT7, + output [31:0] CPR_PC, output [31:0] CPR_RS1, output [31:0] CPR_RS2, output [31:0] CPR_RDR, @@ -358,6 +359,14 @@ module darkriscv //wire FCC = FLUSH ? 0 : XFCC; // OPCODE==7'b0001111; //FCT3 wire SYS = FLUSH ? 0 : XSYS; // OPCODE==7'b1110011; //FCT3 +`ifdef __COPROCESSOR__ + // TecPlusRV 第一版只占用 custom-0 中的一个精确编码。 + // 其余 custom-0 必须继续走 illegal-instruction trap。 + wire DOT4 = CUS && FCT3==3'b000 && FCT7==7'b0000000; +`else + wire DOT4 = 1'b0; +`endif + reg [31:0] REGS [0:`RLEN-1]; // synthesis attribute ram_style of REGS is "distributed"; @@ -447,7 +456,7 @@ module darkriscv wire JTARGET_MISALIGNED; // 当前单 hart、无 cache,标准 FENCE 作为合法 NOP;FENCE.I 仍留给后续实现。 wire FENCE = !FLUSH && XIDATA[6:0]==7'b0001111 && XIDATA[14:12]==3'b000; - wire IERR = FLUSH ? 0 : !(XLUI||XAUIPC||XJAL||XJALR||XBCC||XLCC||XSCC||XMCC||XRCC||XCUS||XSYS||FENCE); + wire IERR = FLUSH ? 0 : !(XLUI||XAUIPC||XJAL||XJALR||XBCC||XLCC||XSCC||XMCC||XRCC||DOT4||XSYS||FENCE); wire DBER = DBERR; wire IBER = IBERR; @@ -614,9 +623,10 @@ module darkriscv `endif `ifdef __COPROCESSOR__ - assign CPR_REQ = CUS; + assign CPR_REQ = DOT4; assign CPR_FCT3 = FCT3; assign CPR_FCT7 = FCT7; + assign CPR_PC = PC; assign CPR_RS1 = U1REG; assign CPR_RS2 = U2REG; assign CPR_RDR = DREG; @@ -800,7 +810,7 @@ module darkriscv MCC||RCC ? RMDATA: `ifdef __COPROCESSOR__ - CUS ? CPR_RDW : + DOT4 ? CPR_RDW : `endif `ifdef __CSR__ CSRX ? CRDATA : diff --git a/rtl/core/darkriscv_config.vh b/rtl/core/darkriscv_config.vh index 9421644..103c0d5 100644 --- a/rtl/core/darkriscv_config.vh +++ b/rtl/core/darkriscv_config.vh @@ -6,6 +6,8 @@ `define __3STAGE__ `define __CSR__ `define __CSR_ESSENTIAL__ +// 启用 TecPlusRV custom-0 DOT4 协处理器握手。 +`define __COPROCESSOR__ // TecPlusRV 只实现 M-mode-only 的 machine trap 子集。 `define __INTERRUPT__ `define __RESETPC__ 32'd0 diff --git a/rtl/files.f b/rtl/files.f index 840be20..453483a 100644 --- a/rtl/files.f +++ b/rtl/files.f @@ -1,5 +1,6 @@ # rtl/files.f rtl/core/picorv32.v +rtl/accel/dot4_int8.v rtl/periph/uart_tx.v rtl/periph/uart_rx.v rtl/periph/traffic_light_gpio.v diff --git a/rtl/soc/darkriscv_adapter.v b/rtl/soc/darkriscv_adapter.v index b4fd61f..7dd2cc2 100644 --- a/rtl/soc/darkriscv_adapter.v +++ b/rtl/soc/darkriscv_adapter.v @@ -35,6 +35,15 @@ wire dbus_wr; wire [31:0] dbus_wdata; wire [3:0] debug; +wire cpr_req; +wire [2:0] cpr_fct3; +wire [6:0] cpr_fct7; +wire [31:0] cpr_pc; +wire [31:0] cpr_rs1; +wire [31:0] cpr_rs2; +wire [31:0] cpr_rdr; +wire [31:0] cpr_rdw; +wire cpr_ack; assign ifetch_valid = ibus_req; assign ifetch_addr = ibus_addr; @@ -44,6 +53,17 @@ assign mem_addr = dbus_addr; assign mem_wdata = dbus_wdata; assign mem_wstrb = dbus_wr ? dbus_be : 4'b0000; +dot4_int8 u_dot4 ( + .clk(clk), + .reset(!resetn), + .req(cpr_req), + .tag(cpr_pc), + .rs1(cpr_rs1), + .rs2(cpr_rs2), + .ack(cpr_ack), + .result(cpr_rdw) +); + darkriscv u_cpu ( .CLK(clk), .RES(!resetn), @@ -65,6 +85,15 @@ darkriscv u_cpu ( .DATAI(mem_rdata), .DDACK(mem_ready), .DBERR(1'b0), + .CPR_REQ(cpr_req), + .CPR_FCT3(cpr_fct3), + .CPR_FCT7(cpr_fct7), + .CPR_PC(cpr_pc), + .CPR_RS1(cpr_rs1), + .CPR_RS2(cpr_rs2), + .CPR_RDR(cpr_rdr), + .CPR_RDW(cpr_rdw), + .CPR_ACK(cpr_ack), .PERF_CYCLE(counter_cycle), .PERF_INSTRET(counter_instret), .DEBUG(debug) diff --git a/scripts/build_firmware.sh b/scripts/build_firmware.sh index 0405702..8bf3e17 100755 --- a/scripts/build_firmware.sh +++ b/scripts/build_firmware.sh @@ -17,6 +17,7 @@ FIRMWARE_OUT=${FIRMWARE_OUT:-$REPO_ROOT/firmware/build/firmware} FIRMWARE_PROFILE=${FIRMWARE_PROFILE:-} FIRMWARE_RUNTIME=${FIRMWARE_RUNTIME:-} FIRMWARE_DEBUG=${FIRMWARE_DEBUG:-none} +FIRMWARE_ACCEL=${FIRMWARE_ACCEL:-none} case "$FIRMWARE_OUT" in */) echo "FIRMWARE_OUT 必须是文件前缀,不能以 / 结尾:$FIRMWARE_OUT" >&2; exit 1 ;; @@ -64,6 +65,19 @@ DEBUG_SOURCES="" EXTRA_CFLAGS="" EXTRA_INCLUDES="" EXTRA_LDFLAGS="" +ACCEL_SOURCES="" + +case "$FIRMWARE_ACCEL" in + none) + ;; + dot4) + ACCEL_SOURCES="$REPO_ROOT/firmware/accel/dot4.S" + ;; + *) + echo "未知 FIRMWARE_ACCEL:$FIRMWARE_ACCEL" >&2 + exit 1 + ;; +esac select_zicsr_march() { # 新工具链要求显式声明 Zicsr;旧 GCC 10 只接受 rv32i, @@ -187,6 +201,7 @@ $REPO_ROOT/firmware/runtime/rt_print.c $REPO_ROOT/firmware/runtime/rt_alloc.c $RUNTIME_SOURCES $DEBUG_SOURCES +$ACCEL_SOURCES $FIRMWARE_MAIN $REPO_ROOT/firmware/drivers/perf.c $REPO_ROOT/firmware/tests/selftest.c @@ -234,6 +249,7 @@ echo "firmware 构建完成:" echo " entry: $FIRMWARE_MAIN" echo " runtime: $FIRMWARE_RUNTIME" echo " debug: $FIRMWARE_DEBUG" +echo " accel: $FIRMWARE_ACCEL" echo " output: $FIRMWARE_OUT" echo " $FIRMWARE_OUT.elf" echo " $FIRMWARE_OUT.bin" diff --git a/scripts/export_ise_project.sh b/scripts/export_ise_project.sh index 236c276..57f8f0c 100755 --- a/scripts/export_ise_project.sh +++ b/scripts/export_ise_project.sh @@ -269,14 +269,17 @@ package_minisoc() { local firmware_main="${1:-}" local target_note="${2:-}" local firmware_profile="${3:-baremetal}" + local firmware_accel="${4:-none}" local cpu_note="如果当前工程支持双核 wrapper,可在 ISE 的 Generics, Parameters 中覆写 CPU_IMPL:0 表示 PicoRV32,1 表示 DarkRISCV。" if [ -n "$firmware_main" ]; then FIRMWARE_PROFILE="$firmware_profile" \ + FIRMWARE_ACCEL="$firmware_accel" \ FIRMWARE_MAIN="$firmware_main" FIRMWARE_OUT="$ISE_FIRMWARE_OUT" \ "$BUILD_FIRMWARE_SCRIPT" else FIRMWARE_PROFILE="$firmware_profile" \ + FIRMWARE_ACCEL="$firmware_accel" \ FIRMWARE_OUT="$ISE_FIRMWARE_OUT" "$BUILD_FIRMWARE_SCRIPT" fi @@ -294,6 +297,7 @@ package_minisoc() { need_file "rtl/soc/mmio_test_exit.v" need_file "rtl/soc/sdram_data_ctrl.v" need_file "rtl/soc/tecplus_minisoc_top.v" + need_file "rtl/accel/dot4_int8.v" need_file "$ucf" copy_flat "rtl/periph/uart_tx.v" @@ -317,6 +321,7 @@ package_minisoc() { copy_flat_if_exists "rtl/soc/tecplus_cpu_wrapper.v" copy_flat_if_exists "rtl/soc/picorv32_adapter.v" copy_flat_if_exists "rtl/soc/darkriscv_adapter.v" + copy_flat "rtl/accel/dot4_int8.v" if [ -f "$REPO_ROOT/rtl/soc/bram_dualport.v" ]; then copy_flat "rtl/soc/bram_dualport.v" @@ -371,6 +376,9 @@ case "$ISE_TARGET" in minisoc_dark) package_minisoc "$REPO_ROOT/firmware/apps/irq/timer_irq_smoke.c" "这是 DarkRISCV machine timer IRQ 验收目标。请在 ISE 的 Generics, Parameters 中设置 CPU_IMPL=1、BOOTLOADER_ENABLE=1、VGA_TEXT_ENABLE=0。导出包同时包含 firmware/build/firmware.bin,可通过共用 bootloader 装载;仓库中也可运行 make timer-irq-load PORT=COM8。UART 输出 timer irq pass: ticks=<次数> loops=<前台循环次数> 表示 firmware 验收通过。2026-07-11 当前 revision 已完成 Map/PAR 与真实上板:50 MHz post-route timing slack 为 0.462 ns,上板输出 ticks=3 loops=46;后续 RTL 或约束变化必须重新验收。" "dark_irq" ;; + minisoc_dot4_dark|dot4_dark) + package_minisoc "$REPO_ROOT/firmware/apps/baremetal/benchmarks/dot4_bench.c" "这是 DarkRISCV custom-0 DOT4 验收目标。请在 ISE 的 Generics, Parameters 中设置 CPU_IMPL=1、BOOTLOADER_ENABLE=1、VGA_TEXT_ENABLE=0。必须重新检查 Map/PAR:无 OVERMAPPED、记录 DSP48A1/LUT/FF 增量、50 MHz post-route slack 为正。上板后运行 make dot4-load PORT=COM8;UART 中 scalar/custom checksum 应一致,且 custom cycles/instret 应更低。当前只有 Icarus 功能和性能对照证据,ISE 与真实上板仍是人工 Gate。" "baremetal" "dot4" + ;; minisoc_freertos_dark|freertos_dark) FREERTOS_CPU_CLOCK_HZ=50000000 \ package_minisoc "$REPO_ROOT/firmware/apps/freertos/freertos_smoke.c" "这是 DarkRISCV FreeRTOS timer/preemption/delay/critical smoke 上板目标。请在 ISE 的 Generics, Parameters 中设置 CPU_IMPL=1、BOOTLOADER_ENABLE=1、VGA_TEXT_ENABLE=0。导出包的 firmware/build/firmware.bin 可通过共用 bootloader 装载;仓库中也可运行 make freertos-load PORT=COM8。LED=5 且 test_exit=1 表示 smoke 完成。导出包内的 third_party/ 与 firmware/freertos/ 是 payload 可复现源码,不要作为 RTL source 加入 ISE。真实 Map/PAR/timing 与上板 UART 仍属于人工 Gate。" "freertos" @@ -400,7 +408,7 @@ case "$ISE_TARGET" in ;; *) echo "未知 ISE 导出目标:$ISE_TARGET" >&2 - echo "支持:probe_led_key, probe_uart, probe_sdram_smoke, probe_minisoc_sdram, probe_bigboard_tl, probe_buzzer_uart, probe_vga, probe_vga_text, minisoc, minisoc_pico, minisoc_dark, minisoc_freertos_dark, minisoc_freertos_acceptance_dark, minisoc_vga_bitmap_dark, bad_apple_minimal, bad_apple_full_dark" >&2 + echo "支持:probe_led_key, probe_uart, probe_sdram_smoke, probe_minisoc_sdram, probe_bigboard_tl, probe_buzzer_uart, probe_vga, probe_vga_text, minisoc, minisoc_pico, minisoc_dark, minisoc_dot4_dark, minisoc_freertos_dark, minisoc_freertos_acceptance_dark, minisoc_vga_bitmap_dark, bad_apple_minimal, bad_apple_full_dark" >&2 exit 1 ;; esac diff --git a/scripts/rtl_syntax_case.sh b/scripts/rtl_syntax_case.sh index a74a254..365d068 100644 --- a/scripts/rtl_syntax_case.sh +++ b/scripts/rtl_syntax_case.sh @@ -59,6 +59,10 @@ need_tool iverilog mkdir -p "$BUILD_DIR" case "$CASE_ID" in + dot4_int8) + run_iverilog dot4_int8 \ + "$REPO_ROOT/rtl/accel/dot4_int8.v" + ;; probe_led_key_top) run_iverilog probe_led_key_top \ "$REPO_ROOT/rtl/probe/probe_led_key_top.v" @@ -219,7 +223,8 @@ case "$CASE_ID" in iverilog -g2001 -I "$REPO_ROOT/rtl/core" -s darkriscv_adapter \ -o "$BUILD_DIR/darkriscv_adapter.syntax.out" \ "$REPO_ROOT/rtl/soc/darkriscv_adapter.v" \ - "$REPO_ROOT/rtl/core/darkriscv.v" + "$REPO_ROOT/rtl/core/darkriscv.v" \ + "$REPO_ROOT/rtl/accel/dot4_int8.v" ;; tecplus_cpu_wrapper) require_files tecplus_cpu_wrapper \ @@ -232,7 +237,8 @@ case "$CASE_ID" in "$REPO_ROOT/rtl/soc/picorv32_adapter.v" \ "$REPO_ROOT/rtl/soc/darkriscv_adapter.v" \ "$REPO_ROOT/rtl/core/picorv32.v" \ - "$REPO_ROOT/rtl/core/darkriscv.v" + "$REPO_ROOT/rtl/core/darkriscv.v" \ + "$REPO_ROOT/rtl/accel/dot4_int8.v" ;; tecplus_minisoc_top) require_files tecplus_minisoc_top \ @@ -249,6 +255,7 @@ case "$CASE_ID" in "$REPO_ROOT/rtl/soc/darkriscv_adapter.v" \ "$REPO_ROOT/rtl/core/picorv32.v" \ "$REPO_ROOT/rtl/core/darkriscv.v" \ + "$REPO_ROOT/rtl/accel/dot4_int8.v" \ "$REPO_ROOT/rtl/soc/bram_dualport.v" \ "$REPO_ROOT/rtl/soc/tinybus_decode.v" \ "$REPO_ROOT/rtl/soc/mmio_test_exit.v" \ diff --git a/scripts/test_catalog.json b/scripts/test_catalog.json index 6ba8317..8b438ce 100644 --- a/scripts/test_catalog.json +++ b/scripts/test_catalog.json @@ -4,6 +4,17 @@ "rtl_syntax": ["python3", "scripts/test_runner.py", "run-suite", "rtl_syntax_internal"] }, "cases": [ + { + "id": "rtl_syntax__dot4_int8", + "kind": "script", + "script": "scripts/rtl_syntax_case.sh", + "args": ["dot4_int8"] + }, + { + "id": "dot4_int8", + "kind": "sim", + "sim_target": "dot4_int8" + }, { "id": "rtl_syntax__probe_led_key_top", "kind": "script", @@ -666,6 +677,17 @@ "kind": "sim", "sim_target": "darkriscv_machine_trap" }, + { + "id": "darkriscv_dot4", + "kind": "sim", + "sim_target": "darkriscv_dot4" + }, + { + "id": "dot4_benchmark", + "kind": "script", + "script": "scripts/test_dot4_benchmark.sh", + "timeout_sec": 120 + }, { "id": "machine_timer", "kind": "sim", @@ -692,7 +714,14 @@ } ], "suites": { + "dot4": [ + "rtl_syntax__dot4_int8", + "dot4_int8", + "darkriscv_dot4", + "dot4_benchmark" + ], "rtl_syntax_internal": [ + "rtl_syntax__dot4_int8", "rtl_syntax__probe_led_key_top", "rtl_syntax__probe_uart_top", "rtl_syntax__uart_rx", @@ -750,6 +779,8 @@ "bram_dualport", "tinybus_decode", "mmio_test_exit", + "dot4_int8", + "darkriscv_dot4", "darkriscv_machine_trap", "machine_timer", "minisoc_timer_irq_dark", diff --git a/scripts/test_dot4_benchmark.sh b/scripts/test_dot4_benchmark.sh new file mode 100755 index 0000000..8d9b06b --- /dev/null +++ b/scripts/test_dot4_benchmark.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# 运行 DarkRISCV DOT4 benchmark,并自动验证正确性与性能方向。 +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd) +REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd) +LOG_FILE="$REPO_ROOT/sim/build/tb_dot4_bench_dark.log" + +"$REPO_ROOT/sim/run_sim.sh" dot4_bench_dark + +scalar_line=$(sed -n '/RESULT: benchmark=dot4 mode=scalar /p' "$LOG_FILE") +custom_line=$(sed -n '/RESULT: benchmark=dot4 mode=custom /p' "$LOG_FILE") + +if [ -z "$scalar_line" ] || [ -z "$custom_line" ]; then + echo "FAIL: DOT4 benchmark 缺少 scalar/custom 结果行" >&2 + exit 1 +fi + +field_value() { + line=$1 + field=$2 + echo "$line" | sed -n "s/.* $field=\\([^ ]*\\).*/\\1/p" | tr -d '\r' +} + +scalar_checksum=$(field_value "$scalar_line" checksum) +custom_checksum=$(field_value "$custom_line" checksum) +scalar_cycles=$(field_value "$scalar_line" cycles) +custom_cycles=$(field_value "$custom_line" cycles) +scalar_instret=$(field_value "$scalar_line" instret) +custom_instret=$(field_value "$custom_line" instret) + +if [ "$scalar_checksum" != "$custom_checksum" ]; then + echo "FAIL: DOT4 checksum 不一致 scalar=$scalar_checksum custom=$custom_checksum" >&2 + exit 1 +fi +if [ "$custom_cycles" -ge "$scalar_cycles" ]; then + echo "FAIL: DOT4 custom cycles 未降低 scalar=$scalar_cycles custom=$custom_cycles" >&2 + exit 1 +fi +if [ "$custom_instret" -ge "$scalar_instret" ]; then + echo "FAIL: DOT4 custom instret 未降低 scalar=$scalar_instret custom=$custom_instret" >&2 + exit 1 +fi + +speedup_x100=$((scalar_cycles * 100 / custom_cycles)) +speedup_whole=$((speedup_x100 / 100)) +speedup_frac=$((speedup_x100 % 100)) +speedup=$(printf '%d.%02d' "$speedup_whole" "$speedup_frac") + +echo "PASS: DOT4 benchmark checksum=$custom_checksum cycles=$scalar_cycles->$custom_cycles instret=$scalar_instret->$custom_instret speedup=${speedup}x" diff --git a/scripts/test_firmware_app_workflow.sh b/scripts/test_firmware_app_workflow.sh index 36b8c5f..2b74887 100755 --- a/scripts/test_firmware_app_workflow.sh +++ b/scripts/test_firmware_app_workflow.sh @@ -98,6 +98,7 @@ for app in \ baremetal/benchmarks/memset_bench.c \ baremetal/benchmarks/stride_bench.c \ baremetal/benchmarks/crc32_bench.c \ + baremetal/benchmarks/dot4_bench.c \ baremetal/benchmarks/system_bench.c \ irq/timer_irq_smoke.c \ freertos/bad_apple_full.c; do @@ -107,7 +108,7 @@ for app in \ fi done -legacy_board_sources='firmware/tests/(board_demo|boot_payload|boot_image_verify|buzzer_tone|gdb_stub_smoke|sdram_memtest|traffic_light_mmio|vga_bitmap_smoke|vga_bitmap_animation|timer_irq_smoke|freertos_smoke|freertos_acceptance|bad_apple_minimal|bad_apple_full|perf_mix|system_bench|sdram_sum_bench|riscv_bench_median|riscv_bench_memcpy|memset_bench|stride_bench|crc32_bench)\.c' +legacy_board_sources='firmware/tests/(board_demo|boot_payload|boot_image_verify|buzzer_tone|gdb_stub_smoke|sdram_memtest|traffic_light_mmio|vga_bitmap_smoke|vga_bitmap_animation|timer_irq_smoke|freertos_smoke|freertos_acceptance|bad_apple_minimal|bad_apple_full|perf_mix|system_bench|sdram_sum_bench|riscv_bench_median|riscv_bench_memcpy|memset_bench|stride_bench|crc32_bench|dot4_bench)\.c' if grep -E "$legacy_board_sources" \ "$repo_root/Makefile" \ "$repo_root/scripts/export_ise_project.sh" \ diff --git a/sim/run_sim.sh b/sim/run_sim.sh index 0d340eb..ab66311 100755 --- a/sim/run_sim.sh +++ b/sim/run_sim.sh @@ -202,6 +202,7 @@ compile_minisoc_tb() { "$REPO_ROOT/rtl/soc/tecplus_cpu_wrapper.v" \ "$REPO_ROOT/rtl/soc/picorv32_adapter.v" \ "$REPO_ROOT/rtl/soc/darkriscv_adapter.v" \ + "$REPO_ROOT/rtl/accel/dot4_int8.v" \ "$REPO_ROOT/rtl/soc/bram_dualport.v" \ "$REPO_ROOT/rtl/soc/tinybus_decode.v" \ "$REPO_ROOT/rtl/soc/mmio_test_exit.v" \ @@ -260,6 +261,7 @@ compile_minisoc_perf_tb() { "$REPO_ROOT/rtl/soc/tecplus_cpu_wrapper.v" \ "$REPO_ROOT/rtl/soc/picorv32_adapter.v" \ "$REPO_ROOT/rtl/soc/darkriscv_adapter.v" \ + "$REPO_ROOT/rtl/accel/dot4_int8.v" \ "$REPO_ROOT/rtl/soc/bram_dualport.v" \ "$REPO_ROOT/rtl/soc/tinybus_decode.v" \ "$REPO_ROOT/rtl/soc/mmio_test_exit.v" \ @@ -276,6 +278,13 @@ compile_minisoc_perf_tb() { } case "$SIM_KIND" in + dot4_int8) + iverilog -g2001 -s tb_dot4_int8 \ + -o "$BUILD_DIR/tb_dot4_int8.out" \ + "$REPO_ROOT/sim/tb_dot4_int8.v" \ + "$REPO_ROOT/rtl/accel/dot4_int8.v" + run_and_check "$BUILD_DIR/tb_dot4_int8.log" vvp "$BUILD_DIR/tb_dot4_int8.out" + ;; uart_tx) iverilog -g2001 -o "$BUILD_DIR/tb_uart_tx.out" \ "$REPO_ROOT/sim/tb_uart_tx.v" \ @@ -788,6 +797,43 @@ case "$SIM_KIND" in "$REPO_ROOT/rtl/core/darkriscv.v" run_and_check "$BUILD_DIR/tb_darkriscv_machine_trap.log" vvp "$BUILD_DIR/tb_darkriscv_machine_trap.out" ;; + darkriscv_dot4) + need_tool riscv64-unknown-elf-gcc + need_tool riscv64-unknown-elf-objcopy + dot4_march=rv32i + if riscv64-unknown-elf-gcc -march=rv32i_zicsr -mabi=ilp32 \ + -E -x c /dev/null -o /dev/null >/dev/null 2>&1; then + dot4_march=rv32i_zicsr + fi + riscv64-unknown-elf-gcc -march="$dot4_march" -mabi=ilp32 \ + -nostdlib -nostartfiles \ + -T "$REPO_ROOT/tests/riscv_tests/tecplus_p/link.ld" \ + -o "$BUILD_DIR/darkriscv_dot4.elf" \ + "$REPO_ROOT/firmware/tests/darkriscv_dot4.S" + riscv64-unknown-elf-objcopy -O binary \ + "$BUILD_DIR/darkriscv_dot4.elf" \ + "$BUILD_DIR/darkriscv_dot4.bin" + python3 "$REPO_ROOT/scripts/bin2mem.py" \ + "$BUILD_DIR/darkriscv_dot4.bin" \ + "$BUILD_DIR/darkriscv_dot4.mem" 16384 + iverilog -g2001 -DSIMULATION -I "$REPO_ROOT/rtl/core" \ + -s tb_darkriscv_dot4 \ + -P "tb_darkriscv_dot4.FIRMWARE_MEM_FILE=\"$BUILD_DIR/darkriscv_dot4.mem\"" \ + -o "$BUILD_DIR/tb_darkriscv_dot4.out" \ + "$REPO_ROOT/sim/tb_darkriscv_dot4.v" \ + "$REPO_ROOT/rtl/soc/darkriscv_adapter.v" \ + "$REPO_ROOT/rtl/core/darkriscv.v" \ + "$REPO_ROOT/rtl/accel/dot4_int8.v" + run_and_check "$BUILD_DIR/tb_darkriscv_dot4.log" vvp "$BUILD_DIR/tb_darkriscv_dot4.out" + ;; + dot4_bench_dark) + FIRMWARE_MAIN="$REPO_ROOT/firmware/apps/baremetal/benchmarks/dot4_bench.c" \ + FIRMWARE_ACCEL=dot4 \ + compile_minisoc_tb "$BUILD_DIR/tb_dot4_bench_dark.out" 1 \ + tb_minisoc "$REPO_ROOT/sim/tb_minisoc.v" + run_and_check "$BUILD_DIR/tb_dot4_bench_dark.log" \ + vvp "$BUILD_DIR/tb_dot4_bench_dark.out" + ;; machine_timer) iverilog -g2001 -s tb_machine_timer \ -o "$BUILD_DIR/tb_machine_timer.out" \ @@ -797,7 +843,7 @@ case "$SIM_KIND" in ;; *) echo "未知仿真目标:$SIM_KIND" >&2 - echo "支持的目标:uart_tx、uart_rx、bootloader_ctrl、bootloader_pico、bootloader_dark、bad_apple_minimal_pico、bad_apple_full、boot_image_verify_pico、boot_image_verify_dark、traffic_light_gpio、buzzer_pwm、probe_led_key、probe_uart_top、bram、bram_dualport、tinybus_decode、mmio_test_exit、minisoc、minisoc_pico、minisoc_dark、minisoc_smoke_pico、minisoc_smoke_dark、freertos_frame_contract、freertos_first_task、freertos_yield_smoke、freertos_smoke、freertos_queue、freertos_acceptance、minisoc_timer_irq_dark、minisoc_vga_bitmap_dark、minisoc_sdram_pico、minisoc_sdram_dark、minisoc_sdram_subword_pico、minisoc_sdram_subword_dark、minisoc_uart_once_pico、minisoc_uart_once_dark、minisoc_uart_echo_pico、minisoc_uart_echo_dark、minisoc_traffic_pico、minisoc_traffic_dark、minisoc_buzzer_pico、minisoc_buzzer_dark、minisoc_perf_pico、minisoc_perf_dark、minisoc_counter_source_pico、minisoc_counter_source_dark、minisoc_counter_reset_pico、minisoc_counter_reset_dark、board_demo_pico、board_demo_dark、sdram_smoke、sdram_data_ctrl、sdram_tester、sdram_tester_fail、sdram_tester_reset、sdram_tester_uart_reporter、sdram_data_ctrl_probe_reporter、probe_sdram_data_ctrl、bigboard_tl、probe_buzzer_uart、probe_vga、font_rom_8x8、vga_text_mode、vga_bitmap_1bpp、darkriscv_machine_trap、machine_timer" >&2 + echo "支持的目标:uart_tx、uart_rx、bootloader_ctrl、bootloader_pico、bootloader_dark、bad_apple_minimal_pico、bad_apple_full、boot_image_verify_pico、boot_image_verify_dark、traffic_light_gpio、buzzer_pwm、probe_led_key、probe_uart_top、bram、bram_dualport、tinybus_decode、mmio_test_exit、minisoc、minisoc_pico、minisoc_dark、minisoc_smoke_pico、minisoc_smoke_dark、freertos_frame_contract、freertos_first_task、freertos_yield_smoke、freertos_smoke、freertos_queue、freertos_acceptance、minisoc_timer_irq_dark、minisoc_vga_bitmap_dark、minisoc_sdram_pico、minisoc_sdram_dark、minisoc_sdram_subword_pico、minisoc_sdram_subword_dark、minisoc_uart_once_pico、minisoc_uart_once_dark、minisoc_uart_echo_pico、minisoc_uart_echo_dark、minisoc_traffic_pico、minisoc_traffic_dark、minisoc_buzzer_pico、minisoc_buzzer_dark、minisoc_perf_pico、minisoc_perf_dark、minisoc_counter_source_pico、minisoc_counter_source_dark、minisoc_counter_reset_pico、minisoc_counter_reset_dark、board_demo_pico、board_demo_dark、sdram_smoke、sdram_data_ctrl、sdram_tester、sdram_tester_fail、sdram_tester_reset、sdram_tester_uart_reporter、sdram_data_ctrl_probe_reporter、probe_sdram_data_ctrl、bigboard_tl、probe_buzzer_uart、probe_vga、font_rom_8x8、vga_text_mode、vga_bitmap_1bpp、dot4_int8、darkriscv_dot4、dot4_bench_dark、darkriscv_machine_trap、machine_timer" >&2 exit 1 ;; esac diff --git a/sim/tb_darkriscv_dot4.v b/sim/tb_darkriscv_dot4.v new file mode 100644 index 0000000..702da00 --- /dev/null +++ b/sim/tb_darkriscv_dot4.v @@ -0,0 +1,151 @@ +// DarkRISCV adapter + custom-0 DOT4 端到端定向测试。 +`timescale 1ns/1ps + +module tb_darkriscv_dot4 #( + parameter FIRMWARE_MEM_FILE = "sim/build/darkriscv_dot4.mem" +); + +reg clk; +reg resetn; +reg irq_external; +reg irq_timer; +reg ifetch_ready; +reg [31:0] ifetch_rdata; +reg [31:0] imem [0:16383]; +reg [31:0] observed [0:127]; +reg timer_injected; +integer ack_count; +integer wait_cycles; +integer commit_count; +integer i; + +wire ifetch_valid; +wire [31:0] ifetch_addr; +wire mem_valid; +wire mem_instr; +wire [31:0] mem_addr; +wire [31:0] mem_wdata; +wire [3:0] mem_wstrb; +wire [31:0] counter_cycle; +wire [31:0] counter_instret; + +darkriscv_adapter dut ( + .clk(clk), + .resetn(resetn), + .irq_external(irq_external), + .irq_timer(irq_timer), + .ifetch_valid(ifetch_valid), + .ifetch_addr(ifetch_addr), + .ifetch_ready(ifetch_ready), + .ifetch_rdata(ifetch_rdata), + .mem_valid(mem_valid), + .mem_instr(mem_instr), + .mem_ready(1'b1), + .mem_addr(mem_addr), + .mem_wdata(mem_wdata), + .mem_wstrb(mem_wstrb), + .mem_rdata(32'h0000_0000), + .counter_cycle(counter_cycle), + .counter_instret(counter_instret) +); + +always #5 clk = ~clk; + +// 与 MiniSoC BRAM 取指口一致:请求后一拍返回数据和 ready。 +always @(posedge clk) begin + if (!resetn) begin + ifetch_ready <= 1'b0; + ifetch_rdata <= 32'h0000_0000; + end else if (ifetch_ready) begin + ifetch_ready <= 1'b0; + end else if (ifetch_valid) begin + ifetch_rdata <= imem[ifetch_addr[15:2]]; + ifetch_ready <= 1'b1; + end +end + +always @(posedge clk) begin + if (!resetn) begin + irq_timer <= 1'b0; + timer_injected <= 1'b0; + ack_count <= 0; + wait_cycles <= 0; + commit_count <= 0; + end else begin + if (dut.u_cpu.CPR_REQ && !dut.u_cpu.CPR_ACK) begin + wait_cycles <= wait_cycles + 1; + if (!timer_injected) begin + irq_timer <= 1'b1; + timer_injected <= 1'b1; + end + end + if (dut.u_cpu.CPR_ACK) + ack_count <= ack_count + 1; + if (dut.u_cpu.CPR_REQ && !dut.u_cpu.HLT && !(|dut.u_cpu.FLUSH)) + commit_count <= commit_count + 1; + if (mem_valid && |mem_wstrb) begin + observed[mem_addr[8:2]] <= mem_wdata; + if (mem_addr == 32'h0000_0124) + irq_timer <= 1'b0; + end + end +end + +initial begin + clk = 1'b0; + resetn = 1'b0; + irq_external = 1'b0; + irq_timer = 1'b0; + ifetch_ready = 1'b0; + ifetch_rdata = 32'h0000_0000; + timer_injected = 1'b0; + ack_count = 0; + wait_cycles = 0; + commit_count = 0; + for (i = 0; i < 128; i = i + 1) + observed[i] = 32'h0000_0000; + $readmemh(FIRMWARE_MEM_FILE, imem); + + repeat (8) @(posedge clk); + resetn = 1'b1; + + wait (observed[32'h13c >> 2] == 32'd1); + @(posedge clk); + if (observed[32'h100 >> 2] !== 32'd70 || + observed[32'h104 >> 2] !== 32'd1 || + observed[32'h108 >> 2] !== -32'sd32515 || + observed[32'h10c >> 2] !== 32'd4 || + observed[32'h110 >> 2] !== 32'd24 || + observed[32'h114 >> 2] !== 32'd0) begin + $display("FAIL: DOT4 写回/lane/相邻指令错误 s0=%08x s1=%08x s2=%08x s3=%08x s4=%08x x0=%08x", + observed[32'h100 >> 2], observed[32'h104 >> 2], + observed[32'h108 >> 2], observed[32'h10c >> 2], + observed[32'h110 >> 2], observed[32'h114 >> 2]); + $finish; + end + if (observed[32'h120 >> 2] !== 32'd2 || + observed[32'h124 >> 2] !== 32'h8000_0007) begin + $display("FAIL: custom-0 illegal/timer trap 错误 illegal=%08x irq=%08x", + observed[32'h120 >> 2], observed[32'h124 >> 2]); + $finish; + end + if (ack_count != 5 || commit_count != 5 || wait_cycles < 5 || + dut.u_cpu.REGS[0] !== 32'd0) begin + $display("FAIL: DOT4 transaction/retire/stall/x0 错误 ack=%0d commit=%0d wait=%0d x0=%08x", + ack_count, commit_count, wait_cycles, dut.u_cpu.REGS[0]); + $finish; + end + + $display("PASS: DarkRISCV custom-0 DOT4 写回、停顿与 trap 语义正确"); + $finish; +end + +initial begin + repeat (2000) @(posedge clk); + $display("TIMEOUT: DarkRISCV DOT4 测试未完成 pc=%08x xidata=%08x ack=%0d wait=%0d illegal=%08x irq=%08x", + dut.u_cpu.PC, dut.u_cpu.XIDATA, ack_count, wait_cycles, + observed[32'h120 >> 2], observed[32'h124 >> 2]); + $finish; +end + +endmodule diff --git a/sim/tb_dot4_int8.v b/sim/tb_dot4_int8.v new file mode 100644 index 0000000..c61a1bc --- /dev/null +++ b/sim/tb_dot4_int8.v @@ -0,0 +1,147 @@ +// signed INT8 DOT4 协处理器单元测试。 +// 检查 lane 顺序、有符号边界、固定响应节奏和请求去重。 +`timescale 1ns/1ps + +module tb_dot4_int8; + +reg clk; +reg reset; +reg req; +reg [31:0] tag; +reg [31:0] rs1; +reg [31:0] rs2; +wire ack; +wire [31:0] result; + +dot4_int8 dut ( + .clk(clk), + .reset(reset), + .req(req), + .tag(tag), + .rs1(rs1), + .rs2(rs2), + .ack(ack), + .result(result) +); + +always #5 clk = ~clk; + +task run_case; + input [31:0] case_rs1; + input [31:0] case_rs2; + input [31:0] expected; + begin + @(negedge clk); + rs1 = case_rs1; + rs2 = case_rs2; + tag = tag + 32'd4; + req = 1'b1; + + @(posedge clk); + #1; + if (ack !== 1'b1 || result !== expected) begin + $display("FAIL: DOT4 结果错误 rs1=%08x rs2=%08x got=%08x expected=%08x ack=%b", + case_rs1, case_rs2, result, expected, ack); + $finish; + end + + // CPU 在看到 ack 后推进到下一条指令,普通指令会在下一个采样沿前撤销 req。 + @(negedge clk); + req = 1'b0; + @(posedge clk); + #1; + if (ack !== 1'b0) begin + $display("FAIL: 同一 req 被重复 ack"); + $finish; + end + end +endtask + +task run_back_to_back; + begin + @(negedge clk); + rs1 = 32'h0101_0101; + rs2 = 32'h0101_0101; + tag = 32'h0000_0100; + req = 1'b1; + @(posedge clk); + #1; + if (ack !== 1'b1 || result !== 32'd4) begin + $display("FAIL: 背靠背测试的第一条 DOT4 未响应"); + $finish; + end + + // ACK 代表上一 transaction 完成;下一条 DOT4 可以让 req 连续为高。 + @(negedge clk); + rs1 = 32'h0202_0202; + rs2 = 32'h0303_0303; + tag = 32'h0000_0104; + #1; + if (ack !== 1'b0) begin + $display("FAIL: transaction tag 改变后旧 ack 未立即失效"); + $finish; + end + @(posedge clk); + #1; + if (ack !== 1'b1 || result !== 32'd24) begin + $display("FAIL: 连续为高的 req 未被识别为下一条 DOT4"); + $finish; + end + + @(negedge clk); + req = 1'b0; + @(posedge clk); + #1; + end +endtask + +initial begin + clk = 1'b0; + reset = 1'b1; + req = 1'b0; + tag = 32'h0000_0000; + rs1 = 32'h0000_0000; + rs2 = 32'h0000_0000; + + repeat (2) @(posedge clk); + reset = 1'b0; + + run_case(32'h0000_0000, 32'h0000_0000, 32'd0); + run_case(32'h0403_0201, 32'h0807_0605, 32'd70); + run_case(32'h80ff_017f, 32'h7f02_ff80, -32'sd32515); + run_case(32'h8080_8080, 32'h8080_8080, 32'd65536); + run_back_to_back(); + + // pending 请求被 reset 时必须取消,不能在 reset 后冒出旧响应。 + @(negedge clk); + rs1 = 32'h0101_0101; + rs2 = 32'h0101_0101; + req = 1'b1; + @(posedge clk); + #1; + reset = 1'b1; + @(posedge clk); + #1; + if (ack !== 1'b0) begin + $display("FAIL: reset 未清除 pending 响应"); + $finish; + end + req = 1'b0; + reset = 1'b0; + repeat (2) @(posedge clk); + if (ack !== 1'b0) begin + $display("FAIL: reset 后出现伪 ack"); + $finish; + end + + $display("PASS: signed INT8 DOT4 计算与握手语义正确"); + $finish; +end + +initial begin + repeat (100) @(posedge clk); + $display("TIMEOUT: DOT4 单元测试未完成"); + $finish; +end + +endmodule