Init Push
This commit is contained in:
259
README.md
Normal file
259
README.md
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
# SLWChipVerify
|
||||||
|
|
||||||
|
SLWChipVerify 是一个轻量级芯片验证 EDA 工具(MVP),支持 Verilog 组合逻辑和时序逻辑验证。
|
||||||
|
|
||||||
|
## 功能
|
||||||
|
|
||||||
|
- 读取 JSON 验证规格
|
||||||
|
- 自动生成 testbench
|
||||||
|
- `examples/simple_cpu_seq_spec.json`: 时序逻辑(simple_cpu)示例
|
||||||
|
- `examples/simple_cpu_eventually_spec.json`: 时序窗口断言(eventually)示例
|
||||||
|
- `examples/simple_cpu_window_modes_spec.json`: 时序窗口断言(always/never/until)示例
|
||||||
|
- `examples/seq_auto_demo.v`: 自动时序激励演示 DUT
|
||||||
|
- `examples/seq_auto_demo_spec.json`: 自动时序激励演示规格
|
||||||
|
- `examples/seq_random_demo_spec.json`: 随机激励+固定种子示例
|
||||||
|
- `examples/github_cpu_validation/projects.json`: GitHub 热门 CPU 项目验证清单
|
||||||
|
- `examples/github_cpu_validation/run_github_cpu_validation.py`: GitHub 热门 CPU 自动验证脚本
|
||||||
|
- `examples/github_cpu_validation/README.md`: GitHub 热门 CPU 示例说明与打包方式
|
||||||
|
|
||||||
|
## 环境要求
|
||||||
|
|
||||||
|
- `vvp`
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./slwchipverify/one_click_verify.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
脚本会交互询问:
|
||||||
|
|
||||||
|
- Verilog 目录
|
||||||
|
- 输出目录
|
||||||
|
- 仿真周期、时钟周期、复位周期
|
||||||
|
|
||||||
|
随后自动完成:
|
||||||
|
|
||||||
|
- 递归扫描 `.v` 文件并解析模块结构
|
||||||
|
- 自动识别或选择顶层模块
|
||||||
|
- 自动生成 smoke testbench
|
||||||
|
- 自动调用 `iverilog + vvp` 仿真
|
||||||
|
- 自动生成 VCD 波形文件
|
||||||
|
|
||||||
|
也可以用非交互模式:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/one_click_verify.py \
|
||||||
|
--dir simple_cpu \
|
||||||
|
--top simple_cpu \
|
||||||
|
--out slwchipverify_auto \
|
||||||
|
--cycles 40 \
|
||||||
|
--period 10 \
|
||||||
|
--reset-cycles 2
|
||||||
|
```
|
||||||
|
|
||||||
|
批量模式(自动跑所有候选顶层模块):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/one_click_verify.py \
|
||||||
|
--cycles 40 \
|
||||||
|
--period 10 \
|
||||||
|
--reset-cycles 2 \
|
||||||
|
--batch-report slwchipverify/auto_batch/batch_summary.json
|
||||||
|
```
|
||||||
|
|
||||||
|
也可以直接通过启动脚本透传参数:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./slwchipverify/one_click_verify.sh --dir simple_cpu --batch --out slwchipverify/auto_batch
|
||||||
|
```
|
||||||
|
|
||||||
|
批量模式输出说明:
|
||||||
|
|
||||||
|
- 每个顶层模块会在输出目录下生成独立子目录(包含该模块 TB、仿真可执行文件、日志、VCD)
|
||||||
|
- 根输出目录下生成 `batch_summary.json`(或 `--batch-report` 指定路径)
|
||||||
|
- 返回码:全部通过返回 `0`,若任一模块失败返回 `1`
|
||||||
|
|
||||||
|
### B) 规格驱动流程(高级)
|
||||||
|
|
||||||
|
在 `Verilog-Learn` 目录下执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/slwchipverify.py run \
|
||||||
|
-d and-gate/top.v \
|
||||||
|
--top top \
|
||||||
|
--spec slwchipverify/examples/and_gate_spec.json \
|
||||||
|
--report slwchipverify/and_gate_report.json \
|
||||||
|
--keep \
|
||||||
|
--workdir slwchipverify/build \
|
||||||
|
--dump-vcd and_gate_verify.vcd
|
||||||
|
```
|
||||||
|
|
||||||
|
成功时输出示例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[SLWChipVerify] PASS: 4/4 cases
|
||||||
|
[SLWChipVerify] Report written: .../slwchipverify/and_gate_report.json
|
||||||
|
```
|
||||||
|
|
||||||
|
时序模式示例(使用 `simple_cpu/simple_cpu.v`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/slwchipverify.py run \
|
||||||
|
-d simple_cpu/simple_cpu.v \
|
||||||
|
--top simple_cpu \
|
||||||
|
--spec slwchipverify/examples/simple_cpu_seq_spec.json \
|
||||||
|
--report slwchipverify/simple_cpu_seq_report.json
|
||||||
|
```
|
||||||
|
|
||||||
|
自动时序激励示例(`binary_count`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/slwchipverify.py run \
|
||||||
|
-d slwchipverify/examples/seq_auto_demo.v \
|
||||||
|
--top seq_auto_demo \
|
||||||
|
--spec slwchipverify/examples/seq_auto_demo_spec.json
|
||||||
|
```
|
||||||
|
|
||||||
|
窗口断言示例(N 周期内 eventually):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/slwchipverify.py run \
|
||||||
|
-d simple_cpu/simple_cpu.v \
|
||||||
|
--top simple_cpu \
|
||||||
|
--spec slwchipverify/examples/simple_cpu_eventually_spec.json
|
||||||
|
```
|
||||||
|
|
||||||
|
窗口断言全集示例(always/never/until):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/slwchipverify.py run \
|
||||||
|
-d simple_cpu/simple_cpu.v \
|
||||||
|
--top simple_cpu \
|
||||||
|
--spec slwchipverify/examples/simple_cpu_window_modes_spec.json
|
||||||
|
```
|
||||||
|
|
||||||
|
随机激励 + 可复现种子 + CI 报告导出:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/slwchipverify.py run \
|
||||||
|
-d slwchipverify/examples/seq_auto_demo.v \
|
||||||
|
--top seq_auto_demo \
|
||||||
|
--spec slwchipverify/examples/seq_random_demo_spec.json \
|
||||||
|
--report slwchipverify/random_report.json \
|
||||||
|
--junit slwchipverify/random_junit.xml \
|
||||||
|
--csv slwchipverify/random_cases.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
GitHub 热门 CPU 仓库可用性验证示例:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/examples/github_cpu_validation/run_github_cpu_validation.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON 规格格式
|
||||||
|
|
||||||
|
### 1) 组合逻辑(truth_table)
|
||||||
|
|
||||||
|
{
|
||||||
|
"inputs": ["a", "b"],
|
||||||
|
"outputs": ["f"],
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"name": "00",
|
||||||
|
"in": {"a": 0, "b": 0},
|
||||||
|
"out": {"f": 0}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- `inputs` / `outputs` 可以是字符串数组,也可以使用对象指定位宽:
|
||||||
|
- `{"name": "data", "width": 8}`
|
||||||
|
- 值支持:
|
||||||
|
- 整数:`15`
|
||||||
|
- 常见前缀:`0b1010` / `0x1f` / `0o17`
|
||||||
|
- Verilog 字面量:`8'h1f` / `4'b1010`
|
||||||
|
|
||||||
|
### 2) 时序逻辑(sequential)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kind": "sequential",
|
||||||
|
"clock": {"name": "clk", "period_ns": 10, "initial": 0},
|
||||||
|
"reset": {"name": "rst", "active": 1, "cycles": 2},
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [],
|
||||||
|
"observes": [
|
||||||
|
{"name": "cnt", "expr": "dut.cnt", "width": 4}
|
||||||
|
],
|
||||||
|
"max_cycles": 6,
|
||||||
|
"auto_stimulus": {
|
||||||
|
"enabled": true,
|
||||||
|
"start_cycle": 0,
|
||||||
|
"mode": "random",
|
||||||
|
"seed": 20260419
|
||||||
|
},
|
||||||
|
"assertions": [
|
||||||
|
{"name": "reset0", "cycle": 0, "expect": {"cnt": 0}},
|
||||||
|
{"name": "inc3", "cycle": 3, "expect": {"cnt": 2}},
|
||||||
|
{
|
||||||
|
"name": "eventually_hit_3",
|
||||||
|
"cycle": 2,
|
||||||
|
"mode": "eventually",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
- `clock`: 时钟定义(名称、周期、初值)
|
||||||
|
- `reset`: 复位定义(名称、有效电平、保持周期)
|
||||||
|
- `assertions.mode`: `cycle`(默认)/ `eventually` / `always` / `never` / `until`
|
||||||
|
- `assertions.within`: 窗口模式(eventually/always/never/until)使用,表示窗口宽度(从 `cycle` 起算 N 周期内)
|
||||||
|
- `assertions.until_expect`: `until` 模式必填,表示终止条件;在终止条件满足前,`expect` 必须持续成立
|
||||||
|
- `auto_stimulus`: 自动时序激励配置,支持 `binary_count` 和 `random`
|
||||||
|
- `auto_stimulus.seed`: 随机激励种子,保证可复现
|
||||||
|
|
||||||
|
覆盖率统计:
|
||||||
|
|
||||||
|
CI 集成报告:
|
||||||
|
|
||||||
|
- `--junit`: 导出 JUnit XML
|
||||||
|
- `--csv`: 导出逐用例 CSV
|
||||||
|
|
||||||
|
## GitHub Actions 示例
|
||||||
|
|
||||||
|
仓库已提供示例工作流:
|
||||||
|
|
||||||
|
- `.github/workflows/slwchipverify-ci.yml`
|
||||||
|
|
||||||
|
- 自动运行组合与时序验证
|
||||||
|
- 直接消费 `--junit` 产物并生成 GitHub Checks 测试结果
|
||||||
|
- 读取 `--csv` 并写入 Job Summary
|
||||||
|
- 上传 JSON/JUnit/CSV 报告为构建产物
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./package_slwchipverify_source.sh
|
||||||
|
脚本会自动生成 ZIP 源码包(默认在 `dist/` 目录),包含:
|
||||||
|
|
||||||
|
- `slwchipverify/` 全部源码与示例
|
||||||
|
- `.github/workflows/slwchipverify-ci.yml`(如果存在)
|
||||||
|
|
||||||
|
可直接将 ZIP 解压后上传到 GitHub 仓库,或在本地解压后 `git init` / `git add` / `git commit` 推送。
|
||||||
|
|
||||||
|
## 返回码
|
||||||
|
|
||||||
|
- `0`: 全部用例通过
|
||||||
|
- `1`: 有用例失败
|
||||||
|
- `2`: 工具错误(规格错误、编译失败、仿真错误、超时等)
|
||||||
|
|
||||||
|
## 当前限制
|
||||||
|
|
||||||
|
- 时序模式当前不包含 UVM、约束随机激励、功能覆盖组等高级能力
|
||||||
|
- 随机激励当前基于仿真器 `$random(seed)`,用于轻量回归与冒烟验证
|
||||||
|
|
||||||
|
后续可扩展到事务级激励、功能覆盖点与协议检查器。
|
||||||
|
```
|
||||||
21
auto_batch/batch_summary.json
Normal file
21
auto_batch/batch_summary.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"timestamp": "2026-04-19T14:24:11",
|
||||||
|
"project_dir": "/Users/ningyedong/Coding/Verilog-Learn/simple_cpu",
|
||||||
|
"output_dir": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_batch",
|
||||||
|
"total_candidates": 1,
|
||||||
|
"passed": 1,
|
||||||
|
"failed": 0,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"module": "simple_cpu",
|
||||||
|
"module_file": "/Users/ningyedong/Coding/Verilog-Learn/simple_cpu/simple_cpu.v",
|
||||||
|
"output_dir": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_batch/simple_cpu",
|
||||||
|
"status": "pass",
|
||||||
|
"tb_path": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_batch/simple_cpu/tb_simple_cpu_auto.v",
|
||||||
|
"sim_binary": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_batch/simple_cpu/auto_sim.out",
|
||||||
|
"sim_log": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_batch/simple_cpu/sim_output.log",
|
||||||
|
"vcd_path": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_batch/simple_cpu/simple_cpu_auto.vcd",
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
100
auto_batch/simple_cpu/auto_sim.out
Normal file
100
auto_batch/simple_cpu/auto_sim.out
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#! /opt/homebrew/Cellar/icarus-verilog/13.0/bin/vvp
|
||||||
|
:ivl_version "13.0 (stable)" "(v13_0)";
|
||||||
|
:ivl_delay_selection "TYPICAL";
|
||||||
|
:vpi_time_precision - 12;
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/system.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_sys.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_textio.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2005_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/va_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2009.vpi";
|
||||||
|
S_0x1012a3bb0 .scope package, "$unit" "$unit" 2 1;
|
||||||
|
.timescale 0 0;
|
||||||
|
S_0x1012a2190 .scope module, "tb_simple_cpu_auto" "tb_simple_cpu_auto" 3 2;
|
||||||
|
.timescale -9 -12;
|
||||||
|
v0x1012a84e0_0 .var "clk", 0 0;
|
||||||
|
v0x1012a8580_0 .var/i "i", 31 0;
|
||||||
|
v0x1012a8620_0 .var "rst", 0 0;
|
||||||
|
v0x1012a86c0_0 .var/i "seed", 31 0;
|
||||||
|
E_0x1012a7830 .event posedge, v0x1012a0d80_0;
|
||||||
|
S_0x1012a2310 .scope module, "dut" "simple_cpu" 3 9, 4 1 0, S_0x1012a2190;
|
||||||
|
.timescale 0 0;
|
||||||
|
.port_info 0 /INPUT 1 "clk";
|
||||||
|
.port_info 1 /INPUT 1 "rst";
|
||||||
|
v0x1012a0d80_0 .net "clk", 0 0, v0x1012a84e0_0; 1 drivers
|
||||||
|
v0x1012a0f70_0 .var "cnt", 3 0;
|
||||||
|
v0x1012a8440_0 .net "rst", 0 0, v0x1012a8620_0; 1 drivers
|
||||||
|
E_0x1012a2680 .event posedge, v0x1012a8440_0, v0x1012a0d80_0;
|
||||||
|
.scope S_0x1012a2310;
|
||||||
|
T_0 ;
|
||||||
|
%wait E_0x1012a2680;
|
||||||
|
%load/vec4 v0x1012a8440_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_0.0, 8;
|
||||||
|
%pushi/vec4 0, 0, 4;
|
||||||
|
%assign/vec4 v0x1012a0f70_0, 0;
|
||||||
|
%jmp T_0.1;
|
||||||
|
T_0.0 ;
|
||||||
|
%load/vec4 v0x1012a0f70_0;
|
||||||
|
%addi 1, 0, 4;
|
||||||
|
%assign/vec4 v0x1012a0f70_0, 0;
|
||||||
|
T_0.1 ;
|
||||||
|
%jmp T_0;
|
||||||
|
.thread T_0;
|
||||||
|
.scope S_0x1012a2190;
|
||||||
|
T_1 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x1012a84e0_0, 0, 1;
|
||||||
|
T_1.0 ;
|
||||||
|
%delay 5000, 0;
|
||||||
|
%load/vec4 v0x1012a84e0_0;
|
||||||
|
%inv;
|
||||||
|
%store/vec4 v0x1012a84e0_0, 0, 1;
|
||||||
|
%jmp T_1.0;
|
||||||
|
T_1.1 ;
|
||||||
|
%end;
|
||||||
|
.thread T_1;
|
||||||
|
.scope S_0x1012a2190;
|
||||||
|
T_2 ;
|
||||||
|
%pushi/vec4 20260419, 0, 32;
|
||||||
|
%store/vec4 v0x1012a86c0_0, 0, 32;
|
||||||
|
%vpi_call/w 3 18 "$dumpfile", "simple_cpu_auto.vcd" {0 0 0};
|
||||||
|
%vpi_call/w 3 19 "$dumpvars", 32'sb00000000000000000000000000000000, S_0x1012a2190 {0 0 0};
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%store/vec4 v0x1012a8620_0, 0, 1;
|
||||||
|
%pushi/vec4 2, 0, 32;
|
||||||
|
T_2.0 %dup/vec4;
|
||||||
|
%cmpi/s 0, 0, 32;
|
||||||
|
%jmp/1xz T_2.1, 5;
|
||||||
|
%jmp/1 T_2.1, 4;
|
||||||
|
%subi 1, 0, 32;
|
||||||
|
%wait E_0x1012a7830;
|
||||||
|
%jmp T_2.0;
|
||||||
|
T_2.1 ;
|
||||||
|
%pop/vec4 1;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x1012a8620_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 32;
|
||||||
|
%store/vec4 v0x1012a8580_0, 0, 32;
|
||||||
|
T_2.2 ; Top of for-loop
|
||||||
|
%load/vec4 v0x1012a8580_0;
|
||||||
|
%cmpi/s 8, 0, 32;
|
||||||
|
%jmp/0xz T_2.3, 5;
|
||||||
|
%wait E_0x1012a7830;
|
||||||
|
T_2.4 ; for-loop step statement
|
||||||
|
%load/vec4 v0x1012a8580_0;
|
||||||
|
%addi 1, 0, 32;
|
||||||
|
%store/vec4 v0x1012a8580_0, 0, 32;
|
||||||
|
%jmp T_2.2;
|
||||||
|
T_2.3 ; for-loop exit label
|
||||||
|
%delay 1000, 0;
|
||||||
|
%vpi_call/w 3 27 "$finish" {0 0 0};
|
||||||
|
%end;
|
||||||
|
.thread T_2;
|
||||||
|
# The file index is used to find the file name in the following table.
|
||||||
|
:file_names 5;
|
||||||
|
"N/A";
|
||||||
|
"<interactive>";
|
||||||
|
"-";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_batch/simple_cpu/tb_simple_cpu_auto.v";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/simple_cpu/simple_cpu.v";
|
||||||
2
auto_batch/simple_cpu/sim_output.log
Normal file
2
auto_batch/simple_cpu/sim_output.log
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VCD info: dumpfile simple_cpu_auto.vcd opened for output.
|
||||||
|
/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_batch/simple_cpu/tb_simple_cpu_auto.v:27: $finish called at 96000 (1ps)
|
||||||
90
auto_batch/simple_cpu/simple_cpu_auto.vcd
Normal file
90
auto_batch/simple_cpu/simple_cpu_auto.vcd
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
$date
|
||||||
|
Sun Apr 19 14:24:11 2026
|
||||||
|
$end
|
||||||
|
$version
|
||||||
|
Icarus Verilog
|
||||||
|
$end
|
||||||
|
$timescale
|
||||||
|
1ps
|
||||||
|
$end
|
||||||
|
$scope module tb_simple_cpu_auto $end
|
||||||
|
$var reg 1 ! clk $end
|
||||||
|
$var reg 1 " rst $end
|
||||||
|
$var integer 32 # i [31:0] $end
|
||||||
|
$var integer 32 $ seed [31:0] $end
|
||||||
|
$scope module dut $end
|
||||||
|
$var wire 1 ! clk $end
|
||||||
|
$var wire 1 " rst $end
|
||||||
|
$var reg 4 % cnt [3:0] $end
|
||||||
|
$upscope $end
|
||||||
|
$upscope $end
|
||||||
|
$enddefinitions $end
|
||||||
|
$comment Show the parameter values. $end
|
||||||
|
$dumpall
|
||||||
|
$end
|
||||||
|
#0
|
||||||
|
$dumpvars
|
||||||
|
b0 %
|
||||||
|
b1001101010010011001000011 $
|
||||||
|
bx #
|
||||||
|
1"
|
||||||
|
0!
|
||||||
|
$end
|
||||||
|
#5000
|
||||||
|
1!
|
||||||
|
#10000
|
||||||
|
0!
|
||||||
|
#15000
|
||||||
|
b1 %
|
||||||
|
b0 #
|
||||||
|
0"
|
||||||
|
1!
|
||||||
|
#20000
|
||||||
|
0!
|
||||||
|
#25000
|
||||||
|
b10 %
|
||||||
|
b1 #
|
||||||
|
1!
|
||||||
|
#30000
|
||||||
|
0!
|
||||||
|
#35000
|
||||||
|
b11 %
|
||||||
|
b10 #
|
||||||
|
1!
|
||||||
|
#40000
|
||||||
|
0!
|
||||||
|
#45000
|
||||||
|
b100 %
|
||||||
|
b11 #
|
||||||
|
1!
|
||||||
|
#50000
|
||||||
|
0!
|
||||||
|
#55000
|
||||||
|
b101 %
|
||||||
|
b100 #
|
||||||
|
1!
|
||||||
|
#60000
|
||||||
|
0!
|
||||||
|
#65000
|
||||||
|
b110 %
|
||||||
|
b101 #
|
||||||
|
1!
|
||||||
|
#70000
|
||||||
|
0!
|
||||||
|
#75000
|
||||||
|
b111 %
|
||||||
|
b110 #
|
||||||
|
1!
|
||||||
|
#80000
|
||||||
|
0!
|
||||||
|
#85000
|
||||||
|
b1000 %
|
||||||
|
b111 #
|
||||||
|
1!
|
||||||
|
#90000
|
||||||
|
0!
|
||||||
|
#95000
|
||||||
|
b1001 %
|
||||||
|
b1000 #
|
||||||
|
1!
|
||||||
|
#96000
|
||||||
29
auto_batch/simple_cpu/tb_simple_cpu_auto.v
Normal file
29
auto_batch/simple_cpu/tb_simple_cpu_auto.v
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
module tb_simple_cpu_auto;
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
integer i;
|
||||||
|
integer seed;
|
||||||
|
|
||||||
|
simple_cpu dut (.clk(clk), .rst(rst));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
forever #5 clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
seed = 20260419;
|
||||||
|
$dumpfile("simple_cpu_auto.vcd");
|
||||||
|
$dumpvars(0, tb_simple_cpu_auto);
|
||||||
|
rst = 1'b1;
|
||||||
|
repeat (2) @(posedge clk);
|
||||||
|
rst = 1'b0;
|
||||||
|
for (i = 0; i < 8; i = i + 1) begin
|
||||||
|
@(posedge clk);
|
||||||
|
end
|
||||||
|
#1;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
100
auto_out_cli/auto_sim.out
Normal file
100
auto_out_cli/auto_sim.out
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#! /opt/homebrew/Cellar/icarus-verilog/13.0/bin/vvp
|
||||||
|
:ivl_version "13.0 (stable)" "(v13_0)";
|
||||||
|
:ivl_delay_selection "TYPICAL";
|
||||||
|
:vpi_time_precision - 12;
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/system.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_sys.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_textio.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2005_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/va_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2009.vpi";
|
||||||
|
S_0x1016b4c00 .scope package, "$unit" "$unit" 2 1;
|
||||||
|
.timescale 0 0;
|
||||||
|
S_0x1016b3190 .scope module, "tb_simple_cpu_auto" "tb_simple_cpu_auto" 3 2;
|
||||||
|
.timescale -9 -12;
|
||||||
|
v0x1016b97d0_0 .var "clk", 0 0;
|
||||||
|
v0x1016b9870_0 .var/i "i", 31 0;
|
||||||
|
v0x1016b9910_0 .var "rst", 0 0;
|
||||||
|
v0x1016b99b0_0 .var/i "seed", 31 0;
|
||||||
|
E_0x1016b8ca0 .event posedge, v0x1016b4d80_0;
|
||||||
|
S_0x1016b3310 .scope module, "dut" "simple_cpu" 3 9, 4 1 0, S_0x1016b3190;
|
||||||
|
.timescale 0 0;
|
||||||
|
.port_info 0 /INPUT 1 "clk";
|
||||||
|
.port_info 1 /INPUT 1 "rst";
|
||||||
|
v0x1016b4d80_0 .net "clk", 0 0, v0x1016b97d0_0; 1 drivers
|
||||||
|
v0x1016b0f20_0 .var "cnt", 3 0;
|
||||||
|
v0x1016b0fd0_0 .net "rst", 0 0, v0x1016b9910_0; 1 drivers
|
||||||
|
E_0x1016b3ac0 .event posedge, v0x1016b0fd0_0, v0x1016b4d80_0;
|
||||||
|
.scope S_0x1016b3310;
|
||||||
|
T_0 ;
|
||||||
|
%wait E_0x1016b3ac0;
|
||||||
|
%load/vec4 v0x1016b0fd0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_0.0, 8;
|
||||||
|
%pushi/vec4 0, 0, 4;
|
||||||
|
%assign/vec4 v0x1016b0f20_0, 0;
|
||||||
|
%jmp T_0.1;
|
||||||
|
T_0.0 ;
|
||||||
|
%load/vec4 v0x1016b0f20_0;
|
||||||
|
%addi 1, 0, 4;
|
||||||
|
%assign/vec4 v0x1016b0f20_0, 0;
|
||||||
|
T_0.1 ;
|
||||||
|
%jmp T_0;
|
||||||
|
.thread T_0;
|
||||||
|
.scope S_0x1016b3190;
|
||||||
|
T_1 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x1016b97d0_0, 0, 1;
|
||||||
|
T_1.0 ;
|
||||||
|
%delay 5000, 0;
|
||||||
|
%load/vec4 v0x1016b97d0_0;
|
||||||
|
%inv;
|
||||||
|
%store/vec4 v0x1016b97d0_0, 0, 1;
|
||||||
|
%jmp T_1.0;
|
||||||
|
T_1.1 ;
|
||||||
|
%end;
|
||||||
|
.thread T_1;
|
||||||
|
.scope S_0x1016b3190;
|
||||||
|
T_2 ;
|
||||||
|
%pushi/vec4 20260419, 0, 32;
|
||||||
|
%store/vec4 v0x1016b99b0_0, 0, 32;
|
||||||
|
%vpi_call/w 3 18 "$dumpfile", "simple_cpu_auto.vcd" {0 0 0};
|
||||||
|
%vpi_call/w 3 19 "$dumpvars", 32'sb00000000000000000000000000000000, S_0x1016b3190 {0 0 0};
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%store/vec4 v0x1016b9910_0, 0, 1;
|
||||||
|
%pushi/vec4 2, 0, 32;
|
||||||
|
T_2.0 %dup/vec4;
|
||||||
|
%cmpi/s 0, 0, 32;
|
||||||
|
%jmp/1xz T_2.1, 5;
|
||||||
|
%jmp/1 T_2.1, 4;
|
||||||
|
%subi 1, 0, 32;
|
||||||
|
%wait E_0x1016b8ca0;
|
||||||
|
%jmp T_2.0;
|
||||||
|
T_2.1 ;
|
||||||
|
%pop/vec4 1;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x1016b9910_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 32;
|
||||||
|
%store/vec4 v0x1016b9870_0, 0, 32;
|
||||||
|
T_2.2 ; Top of for-loop
|
||||||
|
%load/vec4 v0x1016b9870_0;
|
||||||
|
%cmpi/s 12, 0, 32;
|
||||||
|
%jmp/0xz T_2.3, 5;
|
||||||
|
%wait E_0x1016b8ca0;
|
||||||
|
T_2.4 ; for-loop step statement
|
||||||
|
%load/vec4 v0x1016b9870_0;
|
||||||
|
%addi 1, 0, 32;
|
||||||
|
%store/vec4 v0x1016b9870_0, 0, 32;
|
||||||
|
%jmp T_2.2;
|
||||||
|
T_2.3 ; for-loop exit label
|
||||||
|
%delay 1000, 0;
|
||||||
|
%vpi_call/w 3 27 "$finish" {0 0 0};
|
||||||
|
%end;
|
||||||
|
.thread T_2;
|
||||||
|
# The file index is used to find the file name in the following table.
|
||||||
|
:file_names 5;
|
||||||
|
"N/A";
|
||||||
|
"<interactive>";
|
||||||
|
"-";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_out_cli/tb_simple_cpu_auto.v";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/simple_cpu/simple_cpu.v";
|
||||||
2
auto_out_cli/sim_output.log
Normal file
2
auto_out_cli/sim_output.log
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VCD info: dumpfile simple_cpu_auto.vcd opened for output.
|
||||||
|
/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_out_cli/tb_simple_cpu_auto.v:27: $finish called at 136000 (1ps)
|
||||||
114
auto_out_cli/simple_cpu_auto.vcd
Normal file
114
auto_out_cli/simple_cpu_auto.vcd
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
$date
|
||||||
|
Sun Apr 19 14:11:55 2026
|
||||||
|
$end
|
||||||
|
$version
|
||||||
|
Icarus Verilog
|
||||||
|
$end
|
||||||
|
$timescale
|
||||||
|
1ps
|
||||||
|
$end
|
||||||
|
$scope module tb_simple_cpu_auto $end
|
||||||
|
$var reg 1 ! clk $end
|
||||||
|
$var reg 1 " rst $end
|
||||||
|
$var integer 32 # i [31:0] $end
|
||||||
|
$var integer 32 $ seed [31:0] $end
|
||||||
|
$scope module dut $end
|
||||||
|
$var wire 1 ! clk $end
|
||||||
|
$var wire 1 " rst $end
|
||||||
|
$var reg 4 % cnt [3:0] $end
|
||||||
|
$upscope $end
|
||||||
|
$upscope $end
|
||||||
|
$enddefinitions $end
|
||||||
|
$comment Show the parameter values. $end
|
||||||
|
$dumpall
|
||||||
|
$end
|
||||||
|
#0
|
||||||
|
$dumpvars
|
||||||
|
b0 %
|
||||||
|
b1001101010010011001000011 $
|
||||||
|
bx #
|
||||||
|
1"
|
||||||
|
0!
|
||||||
|
$end
|
||||||
|
#5000
|
||||||
|
1!
|
||||||
|
#10000
|
||||||
|
0!
|
||||||
|
#15000
|
||||||
|
b1 %
|
||||||
|
b0 #
|
||||||
|
0"
|
||||||
|
1!
|
||||||
|
#20000
|
||||||
|
0!
|
||||||
|
#25000
|
||||||
|
b10 %
|
||||||
|
b1 #
|
||||||
|
1!
|
||||||
|
#30000
|
||||||
|
0!
|
||||||
|
#35000
|
||||||
|
b11 %
|
||||||
|
b10 #
|
||||||
|
1!
|
||||||
|
#40000
|
||||||
|
0!
|
||||||
|
#45000
|
||||||
|
b100 %
|
||||||
|
b11 #
|
||||||
|
1!
|
||||||
|
#50000
|
||||||
|
0!
|
||||||
|
#55000
|
||||||
|
b101 %
|
||||||
|
b100 #
|
||||||
|
1!
|
||||||
|
#60000
|
||||||
|
0!
|
||||||
|
#65000
|
||||||
|
b110 %
|
||||||
|
b101 #
|
||||||
|
1!
|
||||||
|
#70000
|
||||||
|
0!
|
||||||
|
#75000
|
||||||
|
b111 %
|
||||||
|
b110 #
|
||||||
|
1!
|
||||||
|
#80000
|
||||||
|
0!
|
||||||
|
#85000
|
||||||
|
b1000 %
|
||||||
|
b111 #
|
||||||
|
1!
|
||||||
|
#90000
|
||||||
|
0!
|
||||||
|
#95000
|
||||||
|
b1001 %
|
||||||
|
b1000 #
|
||||||
|
1!
|
||||||
|
#100000
|
||||||
|
0!
|
||||||
|
#105000
|
||||||
|
b1010 %
|
||||||
|
b1001 #
|
||||||
|
1!
|
||||||
|
#110000
|
||||||
|
0!
|
||||||
|
#115000
|
||||||
|
b1011 %
|
||||||
|
b1010 #
|
||||||
|
1!
|
||||||
|
#120000
|
||||||
|
0!
|
||||||
|
#125000
|
||||||
|
b1100 %
|
||||||
|
b1011 #
|
||||||
|
1!
|
||||||
|
#130000
|
||||||
|
0!
|
||||||
|
#135000
|
||||||
|
b1101 %
|
||||||
|
b1100 #
|
||||||
|
1!
|
||||||
|
#136000
|
||||||
29
auto_out_cli/tb_simple_cpu_auto.v
Normal file
29
auto_out_cli/tb_simple_cpu_auto.v
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
module tb_simple_cpu_auto;
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
integer i;
|
||||||
|
integer seed;
|
||||||
|
|
||||||
|
simple_cpu dut (.clk(clk), .rst(rst));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
forever #5 clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
seed = 20260419;
|
||||||
|
$dumpfile("simple_cpu_auto.vcd");
|
||||||
|
$dumpvars(0, tb_simple_cpu_auto);
|
||||||
|
rst = 1'b1;
|
||||||
|
repeat (2) @(posedge clk);
|
||||||
|
rst = 1'b0;
|
||||||
|
for (i = 0; i < 12; i = i + 1) begin
|
||||||
|
@(posedge clk);
|
||||||
|
end
|
||||||
|
#1;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
100
auto_out_simple_cpu/auto_sim.out
Normal file
100
auto_out_simple_cpu/auto_sim.out
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#! /opt/homebrew/Cellar/icarus-verilog/13.0/bin/vvp
|
||||||
|
:ivl_version "13.0 (stable)" "(v13_0)";
|
||||||
|
:ivl_delay_selection "TYPICAL";
|
||||||
|
:vpi_time_precision - 12;
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/system.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_sys.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_textio.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2005_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/va_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2009.vpi";
|
||||||
|
S_0x1031a0ba0 .scope package, "$unit" "$unit" 2 1;
|
||||||
|
.timescale 0 0;
|
||||||
|
S_0x10319f1c0 .scope module, "tb_simple_cpu_auto" "tb_simple_cpu_auto" 3 2;
|
||||||
|
.timescale -9 -12;
|
||||||
|
v0x1031a5580_0 .var "clk", 0 0;
|
||||||
|
v0x1031a5620_0 .var/i "i", 31 0;
|
||||||
|
v0x1031a56c0_0 .var "rst", 0 0;
|
||||||
|
v0x1031a5760_0 .var/i "seed", 31 0;
|
||||||
|
E_0x1031a49f0 .event posedge, v0x1031a0d20_0;
|
||||||
|
S_0x10319f340 .scope module, "dut" "simple_cpu" 3 9, 4 1 0, S_0x10319f1c0;
|
||||||
|
.timescale 0 0;
|
||||||
|
.port_info 0 /INPUT 1 "clk";
|
||||||
|
.port_info 1 /INPUT 1 "rst";
|
||||||
|
v0x1031a0d20_0 .net "clk", 0 0, v0x1031a5580_0; 1 drivers
|
||||||
|
v0x10319cf30_0 .var "cnt", 3 0;
|
||||||
|
v0x10319cfe0_0 .net "rst", 0 0, v0x1031a56c0_0; 1 drivers
|
||||||
|
E_0x10319faf0 .event posedge, v0x10319cfe0_0, v0x1031a0d20_0;
|
||||||
|
.scope S_0x10319f340;
|
||||||
|
T_0 ;
|
||||||
|
%wait E_0x10319faf0;
|
||||||
|
%load/vec4 v0x10319cfe0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_0.0, 8;
|
||||||
|
%pushi/vec4 0, 0, 4;
|
||||||
|
%assign/vec4 v0x10319cf30_0, 0;
|
||||||
|
%jmp T_0.1;
|
||||||
|
T_0.0 ;
|
||||||
|
%load/vec4 v0x10319cf30_0;
|
||||||
|
%addi 1, 0, 4;
|
||||||
|
%assign/vec4 v0x10319cf30_0, 0;
|
||||||
|
T_0.1 ;
|
||||||
|
%jmp T_0;
|
||||||
|
.thread T_0;
|
||||||
|
.scope S_0x10319f1c0;
|
||||||
|
T_1 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x1031a5580_0, 0, 1;
|
||||||
|
T_1.0 ;
|
||||||
|
%delay 5000, 0;
|
||||||
|
%load/vec4 v0x1031a5580_0;
|
||||||
|
%inv;
|
||||||
|
%store/vec4 v0x1031a5580_0, 0, 1;
|
||||||
|
%jmp T_1.0;
|
||||||
|
T_1.1 ;
|
||||||
|
%end;
|
||||||
|
.thread T_1;
|
||||||
|
.scope S_0x10319f1c0;
|
||||||
|
T_2 ;
|
||||||
|
%pushi/vec4 20260419, 0, 32;
|
||||||
|
%store/vec4 v0x1031a5760_0, 0, 32;
|
||||||
|
%vpi_call/w 3 18 "$dumpfile", "simple_cpu_auto.vcd" {0 0 0};
|
||||||
|
%vpi_call/w 3 19 "$dumpvars", 32'sb00000000000000000000000000000000, S_0x10319f1c0 {0 0 0};
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%store/vec4 v0x1031a56c0_0, 0, 1;
|
||||||
|
%pushi/vec4 2, 0, 32;
|
||||||
|
T_2.0 %dup/vec4;
|
||||||
|
%cmpi/s 0, 0, 32;
|
||||||
|
%jmp/1xz T_2.1, 5;
|
||||||
|
%jmp/1 T_2.1, 4;
|
||||||
|
%subi 1, 0, 32;
|
||||||
|
%wait E_0x1031a49f0;
|
||||||
|
%jmp T_2.0;
|
||||||
|
T_2.1 ;
|
||||||
|
%pop/vec4 1;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x1031a56c0_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 32;
|
||||||
|
%store/vec4 v0x1031a5620_0, 0, 32;
|
||||||
|
T_2.2 ; Top of for-loop
|
||||||
|
%load/vec4 v0x1031a5620_0;
|
||||||
|
%cmpi/s 20, 0, 32;
|
||||||
|
%jmp/0xz T_2.3, 5;
|
||||||
|
%wait E_0x1031a49f0;
|
||||||
|
T_2.4 ; for-loop step statement
|
||||||
|
%load/vec4 v0x1031a5620_0;
|
||||||
|
%addi 1, 0, 32;
|
||||||
|
%store/vec4 v0x1031a5620_0, 0, 32;
|
||||||
|
%jmp T_2.2;
|
||||||
|
T_2.3 ; for-loop exit label
|
||||||
|
%delay 1000, 0;
|
||||||
|
%vpi_call/w 3 27 "$finish" {0 0 0};
|
||||||
|
%end;
|
||||||
|
.thread T_2;
|
||||||
|
# The file index is used to find the file name in the following table.
|
||||||
|
:file_names 5;
|
||||||
|
"N/A";
|
||||||
|
"<interactive>";
|
||||||
|
"-";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_out_simple_cpu/tb_simple_cpu_auto.v";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/simple_cpu/simple_cpu.v";
|
||||||
2
auto_out_simple_cpu/sim_output.log
Normal file
2
auto_out_simple_cpu/sim_output.log
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VCD info: dumpfile simple_cpu_auto.vcd opened for output.
|
||||||
|
/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_out_simple_cpu/tb_simple_cpu_auto.v:27: $finish called at 216000 (1ps)
|
||||||
162
auto_out_simple_cpu/simple_cpu_auto.vcd
Normal file
162
auto_out_simple_cpu/simple_cpu_auto.vcd
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
$date
|
||||||
|
Sun Apr 19 14:11:03 2026
|
||||||
|
$end
|
||||||
|
$version
|
||||||
|
Icarus Verilog
|
||||||
|
$end
|
||||||
|
$timescale
|
||||||
|
1ps
|
||||||
|
$end
|
||||||
|
$scope module tb_simple_cpu_auto $end
|
||||||
|
$var reg 1 ! clk $end
|
||||||
|
$var reg 1 " rst $end
|
||||||
|
$var integer 32 # i [31:0] $end
|
||||||
|
$var integer 32 $ seed [31:0] $end
|
||||||
|
$scope module dut $end
|
||||||
|
$var wire 1 ! clk $end
|
||||||
|
$var wire 1 " rst $end
|
||||||
|
$var reg 4 % cnt [3:0] $end
|
||||||
|
$upscope $end
|
||||||
|
$upscope $end
|
||||||
|
$enddefinitions $end
|
||||||
|
$comment Show the parameter values. $end
|
||||||
|
$dumpall
|
||||||
|
$end
|
||||||
|
#0
|
||||||
|
$dumpvars
|
||||||
|
b0 %
|
||||||
|
b1001101010010011001000011 $
|
||||||
|
bx #
|
||||||
|
1"
|
||||||
|
0!
|
||||||
|
$end
|
||||||
|
#5000
|
||||||
|
1!
|
||||||
|
#10000
|
||||||
|
0!
|
||||||
|
#15000
|
||||||
|
b1 %
|
||||||
|
b0 #
|
||||||
|
0"
|
||||||
|
1!
|
||||||
|
#20000
|
||||||
|
0!
|
||||||
|
#25000
|
||||||
|
b10 %
|
||||||
|
b1 #
|
||||||
|
1!
|
||||||
|
#30000
|
||||||
|
0!
|
||||||
|
#35000
|
||||||
|
b11 %
|
||||||
|
b10 #
|
||||||
|
1!
|
||||||
|
#40000
|
||||||
|
0!
|
||||||
|
#45000
|
||||||
|
b100 %
|
||||||
|
b11 #
|
||||||
|
1!
|
||||||
|
#50000
|
||||||
|
0!
|
||||||
|
#55000
|
||||||
|
b101 %
|
||||||
|
b100 #
|
||||||
|
1!
|
||||||
|
#60000
|
||||||
|
0!
|
||||||
|
#65000
|
||||||
|
b110 %
|
||||||
|
b101 #
|
||||||
|
1!
|
||||||
|
#70000
|
||||||
|
0!
|
||||||
|
#75000
|
||||||
|
b111 %
|
||||||
|
b110 #
|
||||||
|
1!
|
||||||
|
#80000
|
||||||
|
0!
|
||||||
|
#85000
|
||||||
|
b1000 %
|
||||||
|
b111 #
|
||||||
|
1!
|
||||||
|
#90000
|
||||||
|
0!
|
||||||
|
#95000
|
||||||
|
b1001 %
|
||||||
|
b1000 #
|
||||||
|
1!
|
||||||
|
#100000
|
||||||
|
0!
|
||||||
|
#105000
|
||||||
|
b1010 %
|
||||||
|
b1001 #
|
||||||
|
1!
|
||||||
|
#110000
|
||||||
|
0!
|
||||||
|
#115000
|
||||||
|
b1011 %
|
||||||
|
b1010 #
|
||||||
|
1!
|
||||||
|
#120000
|
||||||
|
0!
|
||||||
|
#125000
|
||||||
|
b1100 %
|
||||||
|
b1011 #
|
||||||
|
1!
|
||||||
|
#130000
|
||||||
|
0!
|
||||||
|
#135000
|
||||||
|
b1101 %
|
||||||
|
b1100 #
|
||||||
|
1!
|
||||||
|
#140000
|
||||||
|
0!
|
||||||
|
#145000
|
||||||
|
b1110 %
|
||||||
|
b1101 #
|
||||||
|
1!
|
||||||
|
#150000
|
||||||
|
0!
|
||||||
|
#155000
|
||||||
|
b1111 %
|
||||||
|
b1110 #
|
||||||
|
1!
|
||||||
|
#160000
|
||||||
|
0!
|
||||||
|
#165000
|
||||||
|
b0 %
|
||||||
|
b1111 #
|
||||||
|
1!
|
||||||
|
#170000
|
||||||
|
0!
|
||||||
|
#175000
|
||||||
|
b1 %
|
||||||
|
b10000 #
|
||||||
|
1!
|
||||||
|
#180000
|
||||||
|
0!
|
||||||
|
#185000
|
||||||
|
b10 %
|
||||||
|
b10001 #
|
||||||
|
1!
|
||||||
|
#190000
|
||||||
|
0!
|
||||||
|
#195000
|
||||||
|
b11 %
|
||||||
|
b10010 #
|
||||||
|
1!
|
||||||
|
#200000
|
||||||
|
0!
|
||||||
|
#205000
|
||||||
|
b100 %
|
||||||
|
b10011 #
|
||||||
|
1!
|
||||||
|
#210000
|
||||||
|
0!
|
||||||
|
#215000
|
||||||
|
b101 %
|
||||||
|
b10100 #
|
||||||
|
1!
|
||||||
|
#216000
|
||||||
29
auto_out_simple_cpu/tb_simple_cpu_auto.v
Normal file
29
auto_out_simple_cpu/tb_simple_cpu_auto.v
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
module tb_simple_cpu_auto;
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
integer i;
|
||||||
|
integer seed;
|
||||||
|
|
||||||
|
simple_cpu dut (.clk(clk), .rst(rst));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
forever #5 clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
seed = 20260419;
|
||||||
|
$dumpfile("simple_cpu_auto.vcd");
|
||||||
|
$dumpvars(0, tb_simple_cpu_auto);
|
||||||
|
rst = 1'b1;
|
||||||
|
repeat (2) @(posedge clk);
|
||||||
|
rst = 1'b0;
|
||||||
|
for (i = 0; i < 20; i = i + 1) begin
|
||||||
|
@(posedge clk);
|
||||||
|
end
|
||||||
|
#1;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
100
auto_single_check/auto_sim.out
Normal file
100
auto_single_check/auto_sim.out
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#! /opt/homebrew/Cellar/icarus-verilog/13.0/bin/vvp
|
||||||
|
:ivl_version "13.0 (stable)" "(v13_0)";
|
||||||
|
:ivl_delay_selection "TYPICAL";
|
||||||
|
:vpi_time_precision - 12;
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/system.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_sys.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_textio.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2005_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/va_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2009.vpi";
|
||||||
|
S_0x103563610 .scope package, "$unit" "$unit" 2 1;
|
||||||
|
.timescale 0 0;
|
||||||
|
S_0x103561a80 .scope module, "tb_simple_cpu_auto" "tb_simple_cpu_auto" 3 2;
|
||||||
|
.timescale -9 -12;
|
||||||
|
v0x103568520_0 .var "clk", 0 0;
|
||||||
|
v0x1035685c0_0 .var/i "i", 31 0;
|
||||||
|
v0x103568660_0 .var "rst", 0 0;
|
||||||
|
v0x103568700_0 .var/i "seed", 31 0;
|
||||||
|
E_0x103567970 .event posedge, v0x10355ecc0_0;
|
||||||
|
S_0x103561c00 .scope module, "dut" "simple_cpu" 3 9, 4 1 0, S_0x103561a80;
|
||||||
|
.timescale 0 0;
|
||||||
|
.port_info 0 /INPUT 1 "clk";
|
||||||
|
.port_info 1 /INPUT 1 "rst";
|
||||||
|
v0x10355ecc0_0 .net "clk", 0 0, v0x103568520_0; 1 drivers
|
||||||
|
v0x10355ee70_0 .var "cnt", 3 0;
|
||||||
|
v0x10355ef10_0 .net "rst", 0 0, v0x103568660_0; 1 drivers
|
||||||
|
E_0x103562110 .event posedge, v0x10355ef10_0, v0x10355ecc0_0;
|
||||||
|
.scope S_0x103561c00;
|
||||||
|
T_0 ;
|
||||||
|
%wait E_0x103562110;
|
||||||
|
%load/vec4 v0x10355ef10_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_0.0, 8;
|
||||||
|
%pushi/vec4 0, 0, 4;
|
||||||
|
%assign/vec4 v0x10355ee70_0, 0;
|
||||||
|
%jmp T_0.1;
|
||||||
|
T_0.0 ;
|
||||||
|
%load/vec4 v0x10355ee70_0;
|
||||||
|
%addi 1, 0, 4;
|
||||||
|
%assign/vec4 v0x10355ee70_0, 0;
|
||||||
|
T_0.1 ;
|
||||||
|
%jmp T_0;
|
||||||
|
.thread T_0;
|
||||||
|
.scope S_0x103561a80;
|
||||||
|
T_1 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x103568520_0, 0, 1;
|
||||||
|
T_1.0 ;
|
||||||
|
%delay 5000, 0;
|
||||||
|
%load/vec4 v0x103568520_0;
|
||||||
|
%inv;
|
||||||
|
%store/vec4 v0x103568520_0, 0, 1;
|
||||||
|
%jmp T_1.0;
|
||||||
|
T_1.1 ;
|
||||||
|
%end;
|
||||||
|
.thread T_1;
|
||||||
|
.scope S_0x103561a80;
|
||||||
|
T_2 ;
|
||||||
|
%pushi/vec4 20260419, 0, 32;
|
||||||
|
%store/vec4 v0x103568700_0, 0, 32;
|
||||||
|
%vpi_call/w 3 18 "$dumpfile", "simple_cpu_auto.vcd" {0 0 0};
|
||||||
|
%vpi_call/w 3 19 "$dumpvars", 32'sb00000000000000000000000000000000, S_0x103561a80 {0 0 0};
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%store/vec4 v0x103568660_0, 0, 1;
|
||||||
|
%pushi/vec4 2, 0, 32;
|
||||||
|
T_2.0 %dup/vec4;
|
||||||
|
%cmpi/s 0, 0, 32;
|
||||||
|
%jmp/1xz T_2.1, 5;
|
||||||
|
%jmp/1 T_2.1, 4;
|
||||||
|
%subi 1, 0, 32;
|
||||||
|
%wait E_0x103567970;
|
||||||
|
%jmp T_2.0;
|
||||||
|
T_2.1 ;
|
||||||
|
%pop/vec4 1;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x103568660_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 32;
|
||||||
|
%store/vec4 v0x1035685c0_0, 0, 32;
|
||||||
|
T_2.2 ; Top of for-loop
|
||||||
|
%load/vec4 v0x1035685c0_0;
|
||||||
|
%cmpi/s 6, 0, 32;
|
||||||
|
%jmp/0xz T_2.3, 5;
|
||||||
|
%wait E_0x103567970;
|
||||||
|
T_2.4 ; for-loop step statement
|
||||||
|
%load/vec4 v0x1035685c0_0;
|
||||||
|
%addi 1, 0, 32;
|
||||||
|
%store/vec4 v0x1035685c0_0, 0, 32;
|
||||||
|
%jmp T_2.2;
|
||||||
|
T_2.3 ; for-loop exit label
|
||||||
|
%delay 1000, 0;
|
||||||
|
%vpi_call/w 3 27 "$finish" {0 0 0};
|
||||||
|
%end;
|
||||||
|
.thread T_2;
|
||||||
|
# The file index is used to find the file name in the following table.
|
||||||
|
:file_names 5;
|
||||||
|
"N/A";
|
||||||
|
"<interactive>";
|
||||||
|
"-";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_single_check/tb_simple_cpu_auto.v";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/simple_cpu/simple_cpu.v";
|
||||||
2
auto_single_check/sim_output.log
Normal file
2
auto_single_check/sim_output.log
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VCD info: dumpfile simple_cpu_auto.vcd opened for output.
|
||||||
|
/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_single_check/tb_simple_cpu_auto.v:27: $finish called at 76000 (1ps)
|
||||||
78
auto_single_check/simple_cpu_auto.vcd
Normal file
78
auto_single_check/simple_cpu_auto.vcd
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
$date
|
||||||
|
Sun Apr 19 14:20:32 2026
|
||||||
|
$end
|
||||||
|
$version
|
||||||
|
Icarus Verilog
|
||||||
|
$end
|
||||||
|
$timescale
|
||||||
|
1ps
|
||||||
|
$end
|
||||||
|
$scope module tb_simple_cpu_auto $end
|
||||||
|
$var reg 1 ! clk $end
|
||||||
|
$var reg 1 " rst $end
|
||||||
|
$var integer 32 # i [31:0] $end
|
||||||
|
$var integer 32 $ seed [31:0] $end
|
||||||
|
$scope module dut $end
|
||||||
|
$var wire 1 ! clk $end
|
||||||
|
$var wire 1 " rst $end
|
||||||
|
$var reg 4 % cnt [3:0] $end
|
||||||
|
$upscope $end
|
||||||
|
$upscope $end
|
||||||
|
$enddefinitions $end
|
||||||
|
$comment Show the parameter values. $end
|
||||||
|
$dumpall
|
||||||
|
$end
|
||||||
|
#0
|
||||||
|
$dumpvars
|
||||||
|
b0 %
|
||||||
|
b1001101010010011001000011 $
|
||||||
|
bx #
|
||||||
|
1"
|
||||||
|
0!
|
||||||
|
$end
|
||||||
|
#5000
|
||||||
|
1!
|
||||||
|
#10000
|
||||||
|
0!
|
||||||
|
#15000
|
||||||
|
b1 %
|
||||||
|
b0 #
|
||||||
|
0"
|
||||||
|
1!
|
||||||
|
#20000
|
||||||
|
0!
|
||||||
|
#25000
|
||||||
|
b10 %
|
||||||
|
b1 #
|
||||||
|
1!
|
||||||
|
#30000
|
||||||
|
0!
|
||||||
|
#35000
|
||||||
|
b11 %
|
||||||
|
b10 #
|
||||||
|
1!
|
||||||
|
#40000
|
||||||
|
0!
|
||||||
|
#45000
|
||||||
|
b100 %
|
||||||
|
b11 #
|
||||||
|
1!
|
||||||
|
#50000
|
||||||
|
0!
|
||||||
|
#55000
|
||||||
|
b101 %
|
||||||
|
b100 #
|
||||||
|
1!
|
||||||
|
#60000
|
||||||
|
0!
|
||||||
|
#65000
|
||||||
|
b110 %
|
||||||
|
b101 #
|
||||||
|
1!
|
||||||
|
#70000
|
||||||
|
0!
|
||||||
|
#75000
|
||||||
|
b111 %
|
||||||
|
b110 #
|
||||||
|
1!
|
||||||
|
#76000
|
||||||
29
auto_single_check/tb_simple_cpu_auto.v
Normal file
29
auto_single_check/tb_simple_cpu_auto.v
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
module tb_simple_cpu_auto;
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
integer i;
|
||||||
|
integer seed;
|
||||||
|
|
||||||
|
simple_cpu dut (.clk(clk), .rst(rst));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
forever #5 clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
seed = 20260419;
|
||||||
|
$dumpfile("simple_cpu_auto.vcd");
|
||||||
|
$dumpvars(0, tb_simple_cpu_auto);
|
||||||
|
rst = 1'b1;
|
||||||
|
repeat (2) @(posedge clk);
|
||||||
|
rst = 1'b0;
|
||||||
|
for (i = 0; i < 6; i = i + 1) begin
|
||||||
|
@(posedge clk);
|
||||||
|
end
|
||||||
|
#1;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
91
auto_single_launcher/auto_sim.out
Normal file
91
auto_single_launcher/auto_sim.out
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#! /opt/homebrew/Cellar/icarus-verilog/13.0/bin/vvp
|
||||||
|
:ivl_version "13.0 (stable)" "(v13_0)";
|
||||||
|
:ivl_delay_selection "TYPICAL";
|
||||||
|
:vpi_time_precision - 12;
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/system.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_sys.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/vhdl_textio.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2005_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/va_math.vpi";
|
||||||
|
:vpi_module "/opt/homebrew/Cellar/icarus-verilog/13.0/lib/ivl/v2009.vpi";
|
||||||
|
S_0x10301bbe0 .scope package, "$unit" "$unit" 2 1;
|
||||||
|
.timescale 0 0;
|
||||||
|
S_0x10301a190 .scope module, "tb_simple_cpu_auto" "tb_simple_cpu_auto" 3 2;
|
||||||
|
.timescale -9 -12;
|
||||||
|
v0x103020870_0 .var "clk", 0 0;
|
||||||
|
v0x103020910_0 .var/i "i", 31 0;
|
||||||
|
v0x1030209b0_0 .var "rst", 0 0;
|
||||||
|
v0x103020a50_0 .var/i "seed", 31 0;
|
||||||
|
E_0x10301fd30 .event posedge, v0x103018d80_0;
|
||||||
|
S_0x10301a310 .scope module, "dut" "simple_cpu" 3 9, 4 1 0, S_0x10301a190;
|
||||||
|
.timescale 0 0;
|
||||||
|
.port_info 0 /INPUT 1 "clk";
|
||||||
|
.port_info 1 /INPUT 1 "rst";
|
||||||
|
v0x103018d80_0 .net "clk", 0 0, v0x103020870_0; 1 drivers
|
||||||
|
v0x103018f30_0 .var "cnt", 3 0;
|
||||||
|
v0x103018fe0_0 .net "rst", 0 0, v0x1030209b0_0; 1 drivers
|
||||||
|
E_0x10301a680 .event posedge, v0x103018fe0_0, v0x103018d80_0;
|
||||||
|
.scope S_0x10301a310;
|
||||||
|
T_0 ;
|
||||||
|
%wait E_0x10301a680;
|
||||||
|
%load/vec4 v0x103018fe0_0;
|
||||||
|
%flag_set/vec4 8;
|
||||||
|
%jmp/0xz T_0.0, 8;
|
||||||
|
%pushi/vec4 0, 0, 4;
|
||||||
|
%assign/vec4 v0x103018f30_0, 0;
|
||||||
|
%jmp T_0.1;
|
||||||
|
T_0.0 ;
|
||||||
|
%load/vec4 v0x103018f30_0;
|
||||||
|
%addi 1, 0, 4;
|
||||||
|
%assign/vec4 v0x103018f30_0, 0;
|
||||||
|
T_0.1 ;
|
||||||
|
%jmp T_0;
|
||||||
|
.thread T_0;
|
||||||
|
.scope S_0x10301a190;
|
||||||
|
T_1 ;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x103020870_0, 0, 1;
|
||||||
|
T_1.0 ;
|
||||||
|
%delay 5000, 0;
|
||||||
|
%load/vec4 v0x103020870_0;
|
||||||
|
%inv;
|
||||||
|
%store/vec4 v0x103020870_0, 0, 1;
|
||||||
|
%jmp T_1.0;
|
||||||
|
T_1.1 ;
|
||||||
|
%end;
|
||||||
|
.thread T_1;
|
||||||
|
.scope S_0x10301a190;
|
||||||
|
T_2 ;
|
||||||
|
%pushi/vec4 20260419, 0, 32;
|
||||||
|
%store/vec4 v0x103020a50_0, 0, 32;
|
||||||
|
%vpi_call/w 3 18 "$dumpfile", "simple_cpu_auto.vcd" {0 0 0};
|
||||||
|
%vpi_call/w 3 19 "$dumpvars", 32'sb00000000000000000000000000000000, S_0x10301a190 {0 0 0};
|
||||||
|
%pushi/vec4 1, 0, 1;
|
||||||
|
%store/vec4 v0x1030209b0_0, 0, 1;
|
||||||
|
%wait E_0x10301fd30;
|
||||||
|
%pushi/vec4 0, 0, 1;
|
||||||
|
%store/vec4 v0x1030209b0_0, 0, 1;
|
||||||
|
%pushi/vec4 0, 0, 32;
|
||||||
|
%store/vec4 v0x103020910_0, 0, 32;
|
||||||
|
T_2.0 ; Top of for-loop
|
||||||
|
%load/vec4 v0x103020910_0;
|
||||||
|
%cmpi/s 4, 0, 32;
|
||||||
|
%jmp/0xz T_2.1, 5;
|
||||||
|
%wait E_0x10301fd30;
|
||||||
|
T_2.2 ; for-loop step statement
|
||||||
|
%load/vec4 v0x103020910_0;
|
||||||
|
%addi 1, 0, 32;
|
||||||
|
%store/vec4 v0x103020910_0, 0, 32;
|
||||||
|
%jmp T_2.0;
|
||||||
|
T_2.1 ; for-loop exit label
|
||||||
|
%delay 1000, 0;
|
||||||
|
%vpi_call/w 3 27 "$finish" {0 0 0};
|
||||||
|
%end;
|
||||||
|
.thread T_2;
|
||||||
|
# The file index is used to find the file name in the following table.
|
||||||
|
:file_names 5;
|
||||||
|
"N/A";
|
||||||
|
"<interactive>";
|
||||||
|
"-";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_single_launcher/tb_simple_cpu_auto.v";
|
||||||
|
"/Users/ningyedong/Coding/Verilog-Learn/simple_cpu/simple_cpu.v";
|
||||||
2
auto_single_launcher/sim_output.log
Normal file
2
auto_single_launcher/sim_output.log
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VCD info: dumpfile simple_cpu_auto.vcd opened for output.
|
||||||
|
/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/auto_single_launcher/tb_simple_cpu_auto.v:27: $finish called at 46000 (1ps)
|
||||||
62
auto_single_launcher/simple_cpu_auto.vcd
Normal file
62
auto_single_launcher/simple_cpu_auto.vcd
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
$date
|
||||||
|
Sun Apr 19 14:24:36 2026
|
||||||
|
$end
|
||||||
|
$version
|
||||||
|
Icarus Verilog
|
||||||
|
$end
|
||||||
|
$timescale
|
||||||
|
1ps
|
||||||
|
$end
|
||||||
|
$scope module tb_simple_cpu_auto $end
|
||||||
|
$var reg 1 ! clk $end
|
||||||
|
$var reg 1 " rst $end
|
||||||
|
$var integer 32 # i [31:0] $end
|
||||||
|
$var integer 32 $ seed [31:0] $end
|
||||||
|
$scope module dut $end
|
||||||
|
$var wire 1 ! clk $end
|
||||||
|
$var wire 1 " rst $end
|
||||||
|
$var reg 4 % cnt [3:0] $end
|
||||||
|
$upscope $end
|
||||||
|
$upscope $end
|
||||||
|
$enddefinitions $end
|
||||||
|
$comment Show the parameter values. $end
|
||||||
|
$dumpall
|
||||||
|
$end
|
||||||
|
#0
|
||||||
|
$dumpvars
|
||||||
|
b0 %
|
||||||
|
b1001101010010011001000011 $
|
||||||
|
bx #
|
||||||
|
1"
|
||||||
|
0!
|
||||||
|
$end
|
||||||
|
#5000
|
||||||
|
b1 %
|
||||||
|
b0 #
|
||||||
|
0"
|
||||||
|
1!
|
||||||
|
#10000
|
||||||
|
0!
|
||||||
|
#15000
|
||||||
|
b10 %
|
||||||
|
b1 #
|
||||||
|
1!
|
||||||
|
#20000
|
||||||
|
0!
|
||||||
|
#25000
|
||||||
|
b11 %
|
||||||
|
b10 #
|
||||||
|
1!
|
||||||
|
#30000
|
||||||
|
0!
|
||||||
|
#35000
|
||||||
|
b100 %
|
||||||
|
b11 #
|
||||||
|
1!
|
||||||
|
#40000
|
||||||
|
0!
|
||||||
|
#45000
|
||||||
|
b101 %
|
||||||
|
b100 #
|
||||||
|
1!
|
||||||
|
#46000
|
||||||
29
auto_single_launcher/tb_simple_cpu_auto.v
Normal file
29
auto_single_launcher/tb_simple_cpu_auto.v
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
module tb_simple_cpu_auto;
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst;
|
||||||
|
integer i;
|
||||||
|
integer seed;
|
||||||
|
|
||||||
|
simple_cpu dut (.clk(clk), .rst(rst));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
forever #5 clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
seed = 20260419;
|
||||||
|
$dumpfile("simple_cpu_auto.vcd");
|
||||||
|
$dumpvars(0, tb_simple_cpu_auto);
|
||||||
|
rst = 1'b1;
|
||||||
|
repeat (1) @(posedge clk);
|
||||||
|
rst = 1'b0;
|
||||||
|
for (i = 0; i < 4; i = i + 1) begin
|
||||||
|
@(posedge clk);
|
||||||
|
end
|
||||||
|
#1;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
52
examples/and_gate_spec.json
Normal file
52
examples/and_gate_spec.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"kind": "truth_table",
|
||||||
|
"inputs": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
"f"
|
||||||
|
],
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"name": "00",
|
||||||
|
"in": {
|
||||||
|
"a": 0,
|
||||||
|
"b": 0
|
||||||
|
},
|
||||||
|
"out": {
|
||||||
|
"f": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01",
|
||||||
|
"in": {
|
||||||
|
"a": 0,
|
||||||
|
"b": 1
|
||||||
|
},
|
||||||
|
"out": {
|
||||||
|
"f": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "10",
|
||||||
|
"in": {
|
||||||
|
"a": 1,
|
||||||
|
"b": 0
|
||||||
|
},
|
||||||
|
"out": {
|
||||||
|
"f": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "11",
|
||||||
|
"in": {
|
||||||
|
"a": 1,
|
||||||
|
"b": 1
|
||||||
|
},
|
||||||
|
"out": {
|
||||||
|
"f": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
59
examples/github_cpu_validation/README.md
Normal file
59
examples/github_cpu_validation/README.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# GitHub CPU Validation Example (SLWChipVerify)
|
||||||
|
|
||||||
|
这个 example 演示如何对热门开源 CPU 项目执行 SLWChipVerify 一键验证流程,并生成可复现结果。
|
||||||
|
|
||||||
|
## 已验证项目(按 GitHub Stars)
|
||||||
|
|
||||||
|
- `riscv-mcu/e203_hbirdv2`(约 1811 stars)
|
||||||
|
- 目录:`rtl`
|
||||||
|
- 顶层:`e203_cpu_top`
|
||||||
|
- `ultraembedded/riscv`(约 1703 stars)
|
||||||
|
- 目录:`core/riscv`
|
||||||
|
- 顶层:`riscv_core`
|
||||||
|
- `ZipCPU/zipcpu`(约 1535 stars)
|
||||||
|
- 目录:`rtl`
|
||||||
|
- 顶层:`zipcore`
|
||||||
|
|
||||||
|
> Stars 基于 2026-04-19 采样,后续会变化。
|
||||||
|
|
||||||
|
## 前置要求
|
||||||
|
|
||||||
|
- Python 3.9+
|
||||||
|
- `git`
|
||||||
|
- `iverilog`
|
||||||
|
- `vvp`
|
||||||
|
- 可访问 GitHub 网络
|
||||||
|
|
||||||
|
## 一键复现
|
||||||
|
|
||||||
|
在安装目录执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 slwchipverify/examples/github_cpu_validation/run_github_cpu_validation.py
|
||||||
|
```
|
||||||
|
|
||||||
|
运行后会:
|
||||||
|
|
||||||
|
- 自动克隆/更新目标仓库到临时目录
|
||||||
|
- 对每个项目调用 `slwchipverify/one_click_verify.py`
|
||||||
|
- 在 `slwchipverify/examples/github_cpu_validation/results/` 下生成:
|
||||||
|
- 每个项目的 TB / 波形 / 仿真日志 / run.log
|
||||||
|
- 汇总报告 `github_cpu_validation_summary.json`
|
||||||
|
|
||||||
|
## 打包 example
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./slwchipverify/examples/github_cpu_validation/package_example.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
会在 `slwchipverify/examples/github_cpu_validation/dist/` 下生成 zip 包,包含:
|
||||||
|
|
||||||
|
- `projects.json`
|
||||||
|
- `run_github_cpu_validation.py`
|
||||||
|
- 本说明文档
|
||||||
|
- `results/` 中的汇总报告与验证产物
|
||||||
|
|
||||||
|
## 说明
|
||||||
|
|
||||||
|
- 该 example 重点验证工具在真实开源 CPU 仓库上的可用性(冒烟级编译+仿真+波形)。
|
||||||
|
- 对于特别复杂的 SoC 工程,可能仍需定制宏定义、文件筛选或更细粒度目录选择。
|
||||||
BIN
examples/github_cpu_validation/dist/github_cpu_validation_example_20260419_143919.zip
vendored
Normal file
BIN
examples/github_cpu_validation/dist/github_cpu_validation_example_20260419_143919.zip
vendored
Normal file
Binary file not shown.
24
examples/github_cpu_validation/package_example.sh
Executable file
24
examples/github_cpu_validation/package_example.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# 获取脚本目录并定义输出路径。
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
DIST_DIR="$SCRIPT_DIR/dist"
|
||||||
|
STAMP="$(date +%Y%m%d_%H%M%S)"
|
||||||
|
ARCHIVE="$DIST_DIR/slwchipverify_github_cpu_validation_example_${STAMP}.zip"
|
||||||
|
|
||||||
|
# 创建打包输出目录。
|
||||||
|
mkdir -p "$DIST_DIR"
|
||||||
|
|
||||||
|
# 仅按白名单打包关键文件与结果目录,避免把无关文件一起打进去。
|
||||||
|
(
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
zip -r "$ARCHIVE" \
|
||||||
|
README.md \
|
||||||
|
projects.json \
|
||||||
|
run_github_cpu_validation.py \
|
||||||
|
results \
|
||||||
|
>/dev/null
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "SLWChipVerify 示例已打包: $ARCHIVE"
|
||||||
32
examples/github_cpu_validation/projects.json
Normal file
32
examples/github_cpu_validation/projects.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "ultraembedded_riscv_core",
|
||||||
|
"repo": "https://github.com/ultraembedded/riscv.git",
|
||||||
|
"stars": 1703,
|
||||||
|
"rtl_subdir": "core/riscv",
|
||||||
|
"top": "riscv_core",
|
||||||
|
"cycles": 12,
|
||||||
|
"period": 10,
|
||||||
|
"reset_cycles": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zipcpu",
|
||||||
|
"repo": "https://github.com/ZipCPU/zipcpu.git",
|
||||||
|
"stars": 1535,
|
||||||
|
"rtl_subdir": "rtl",
|
||||||
|
"top": "zipcore",
|
||||||
|
"cycles": 12,
|
||||||
|
"period": 10,
|
||||||
|
"reset_cycles": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "e203_cpu_top_fullrtl",
|
||||||
|
"repo": "https://github.com/riscv-mcu/e203_hbirdv2.git",
|
||||||
|
"stars": 1811,
|
||||||
|
"rtl_subdir": "rtl",
|
||||||
|
"top": "e203_cpu_top",
|
||||||
|
"cycles": 8,
|
||||||
|
"period": 10,
|
||||||
|
"reset_cycles": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
33386
examples/github_cpu_validation/results/e203_cpu_top_fullrtl/auto_sim.out
Normal file
33386
examples/github_cpu_validation/results/e203_cpu_top_fullrtl/auto_sim.out
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
|||||||
|
COMMAND:
|
||||||
|
/opt/anaconda3/bin/python /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/one_click_verify.py --dir /private/tmp/chipverify_github_cpu_example/clones/e203_cpu_top_fullrtl/rtl --top e203_cpu_top --out /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl --cycles 8 --period 10 --reset-cycles 2
|
||||||
|
|
||||||
|
STDOUT:
|
||||||
|
=== ChipVerify One-Click Auto Verification ===
|
||||||
|
Found 136 Verilog files
|
||||||
|
Selected top module: e203_cpu_top
|
||||||
|
Top module file: /private/tmp/chipverify_github_cpu_example/clones/e203_cpu_top_fullrtl/rtl/e203/core/e203_cpu_top.v
|
||||||
|
Generated testbench: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/tb_e203_cpu_top_auto.v
|
||||||
|
Simulation completed successfully
|
||||||
|
Simulation binary: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/auto_sim.out
|
||||||
|
Waveform: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/e203_cpu_top_auto.vcd
|
||||||
|
Simulation log: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/sim_output.log
|
||||||
|
Open waveform with: gtkwave /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/e203_cpu_top_auto.vcd
|
||||||
|
|
||||||
|
|
||||||
|
STDERR:
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
VCD info: dumpfile e203_cpu_top_auto.vcd opened for output.
|
||||||
|
/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/tb_e203_cpu_top_auto.v:232: $finish called at 96000 (1ps)
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
module tb_e203_cpu_top_auto;
|
||||||
|
|
||||||
|
reg pc_rtvec;
|
||||||
|
reg dcsr_r;
|
||||||
|
reg dpc_r;
|
||||||
|
reg dscratch_r;
|
||||||
|
reg dbg_mode;
|
||||||
|
reg dbg_halt_r;
|
||||||
|
reg dbg_step_r;
|
||||||
|
reg dbg_ebreakm_r;
|
||||||
|
reg dbg_stopcycle;
|
||||||
|
reg dbg_irq_a;
|
||||||
|
reg core_mhartid;
|
||||||
|
reg ext_irq_a;
|
||||||
|
reg sft_irq_a;
|
||||||
|
reg tmr_irq_a;
|
||||||
|
reg tcm_sd;
|
||||||
|
reg tcm_ds;
|
||||||
|
reg ext2itcm_icb_cmd_valid;
|
||||||
|
reg ext2itcm_icb_cmd_addr;
|
||||||
|
reg ext2itcm_icb_cmd_read;
|
||||||
|
reg ext2itcm_icb_cmd_wdata;
|
||||||
|
reg ext2itcm_icb_cmd_wmask;
|
||||||
|
reg ext2itcm_icb_rsp_ready;
|
||||||
|
reg ext2dtcm_icb_cmd_valid;
|
||||||
|
reg ext2dtcm_icb_cmd_addr;
|
||||||
|
reg ext2dtcm_icb_cmd_read;
|
||||||
|
reg ext2dtcm_icb_cmd_wdata;
|
||||||
|
reg ext2dtcm_icb_cmd_wmask;
|
||||||
|
reg ext2dtcm_icb_rsp_ready;
|
||||||
|
reg ppi_icb_cmd_ready;
|
||||||
|
reg ppi_icb_rsp_valid;
|
||||||
|
reg ppi_icb_rsp_err;
|
||||||
|
reg ppi_icb_rsp_rdata;
|
||||||
|
reg clint_icb_cmd_ready;
|
||||||
|
reg clint_icb_rsp_valid;
|
||||||
|
reg clint_icb_rsp_err;
|
||||||
|
reg clint_icb_rsp_rdata;
|
||||||
|
reg plic_icb_cmd_ready;
|
||||||
|
reg plic_icb_rsp_valid;
|
||||||
|
reg plic_icb_rsp_err;
|
||||||
|
reg plic_icb_rsp_rdata;
|
||||||
|
reg fio_icb_cmd_ready;
|
||||||
|
reg fio_icb_rsp_valid;
|
||||||
|
reg fio_icb_rsp_err;
|
||||||
|
reg fio_icb_rsp_rdata;
|
||||||
|
reg mem_icb_cmd_ready;
|
||||||
|
reg mem_icb_rsp_valid;
|
||||||
|
reg mem_icb_rsp_err;
|
||||||
|
reg mem_icb_rsp_rdata;
|
||||||
|
reg test_mode;
|
||||||
|
reg clk;
|
||||||
|
reg rst_n;
|
||||||
|
wire inspect_pc;
|
||||||
|
wire inspect_dbg_irq;
|
||||||
|
wire inspect_mem_cmd_valid;
|
||||||
|
wire inspect_mem_cmd_ready;
|
||||||
|
wire inspect_mem_rsp_valid;
|
||||||
|
wire inspect_mem_rsp_ready;
|
||||||
|
wire inspect_core_clk;
|
||||||
|
wire core_csr_clk;
|
||||||
|
wire core_wfi;
|
||||||
|
wire tm_stop;
|
||||||
|
wire dbg_irq_r;
|
||||||
|
wire cmt_dpc;
|
||||||
|
wire cmt_dpc_ena;
|
||||||
|
wire cmt_dcause;
|
||||||
|
wire cmt_dcause_ena;
|
||||||
|
wire wr_dcsr_ena;
|
||||||
|
wire wr_dpc_ena;
|
||||||
|
wire wr_dscratch_ena;
|
||||||
|
wire wr_csr_nxt;
|
||||||
|
wire ext2itcm_icb_cmd_ready;
|
||||||
|
wire ext2itcm_icb_rsp_valid;
|
||||||
|
wire ext2itcm_icb_rsp_err;
|
||||||
|
wire ext2itcm_icb_rsp_rdata;
|
||||||
|
wire ext2dtcm_icb_cmd_ready;
|
||||||
|
wire ext2dtcm_icb_rsp_valid;
|
||||||
|
wire ext2dtcm_icb_rsp_err;
|
||||||
|
wire ext2dtcm_icb_rsp_rdata;
|
||||||
|
wire ppi_icb_cmd_valid;
|
||||||
|
wire ppi_icb_cmd_addr;
|
||||||
|
wire ppi_icb_cmd_read;
|
||||||
|
wire ppi_icb_cmd_wdata;
|
||||||
|
wire ppi_icb_cmd_wmask;
|
||||||
|
wire ppi_icb_rsp_ready;
|
||||||
|
wire clint_icb_cmd_valid;
|
||||||
|
wire clint_icb_cmd_addr;
|
||||||
|
wire clint_icb_cmd_read;
|
||||||
|
wire clint_icb_cmd_wdata;
|
||||||
|
wire clint_icb_cmd_wmask;
|
||||||
|
wire clint_icb_rsp_ready;
|
||||||
|
wire plic_icb_cmd_valid;
|
||||||
|
wire plic_icb_cmd_addr;
|
||||||
|
wire plic_icb_cmd_read;
|
||||||
|
wire plic_icb_cmd_wdata;
|
||||||
|
wire plic_icb_cmd_wmask;
|
||||||
|
wire plic_icb_rsp_ready;
|
||||||
|
wire fio_icb_cmd_valid;
|
||||||
|
wire fio_icb_cmd_addr;
|
||||||
|
wire fio_icb_cmd_read;
|
||||||
|
wire fio_icb_cmd_wdata;
|
||||||
|
wire fio_icb_cmd_wmask;
|
||||||
|
wire fio_icb_rsp_ready;
|
||||||
|
wire mem_icb_cmd_valid;
|
||||||
|
wire mem_icb_cmd_addr;
|
||||||
|
wire mem_icb_cmd_read;
|
||||||
|
wire mem_icb_cmd_wdata;
|
||||||
|
wire mem_icb_cmd_wmask;
|
||||||
|
wire mem_icb_rsp_ready;
|
||||||
|
integer i;
|
||||||
|
integer seed;
|
||||||
|
|
||||||
|
e203_cpu_top dut (.inspect_pc(inspect_pc), .inspect_dbg_irq(inspect_dbg_irq), .inspect_mem_cmd_valid(inspect_mem_cmd_valid), .inspect_mem_cmd_ready(inspect_mem_cmd_ready), .inspect_mem_rsp_valid(inspect_mem_rsp_valid), .inspect_mem_rsp_ready(inspect_mem_rsp_ready), .inspect_core_clk(inspect_core_clk), .core_csr_clk(core_csr_clk), .core_wfi(core_wfi), .tm_stop(tm_stop), .pc_rtvec(pc_rtvec), .dbg_irq_r(dbg_irq_r), .cmt_dpc(cmt_dpc), .cmt_dpc_ena(cmt_dpc_ena), .cmt_dcause(cmt_dcause), .cmt_dcause_ena(cmt_dcause_ena), .wr_dcsr_ena(wr_dcsr_ena), .wr_dpc_ena(wr_dpc_ena), .wr_dscratch_ena(wr_dscratch_ena), .wr_csr_nxt(wr_csr_nxt), .dcsr_r(dcsr_r), .dpc_r(dpc_r), .dscratch_r(dscratch_r), .dbg_mode(dbg_mode), .dbg_halt_r(dbg_halt_r), .dbg_step_r(dbg_step_r), .dbg_ebreakm_r(dbg_ebreakm_r), .dbg_stopcycle(dbg_stopcycle), .dbg_irq_a(dbg_irq_a), .core_mhartid(core_mhartid), .ext_irq_a(ext_irq_a), .sft_irq_a(sft_irq_a), .tmr_irq_a(tmr_irq_a), .tcm_sd(tcm_sd), .tcm_ds(tcm_ds), .ext2itcm_icb_cmd_valid(ext2itcm_icb_cmd_valid), .ext2itcm_icb_cmd_ready(ext2itcm_icb_cmd_ready), .ext2itcm_icb_cmd_addr(ext2itcm_icb_cmd_addr), .ext2itcm_icb_cmd_read(ext2itcm_icb_cmd_read), .ext2itcm_icb_cmd_wdata(ext2itcm_icb_cmd_wdata), .ext2itcm_icb_cmd_wmask(ext2itcm_icb_cmd_wmask), .ext2itcm_icb_rsp_valid(ext2itcm_icb_rsp_valid), .ext2itcm_icb_rsp_ready(ext2itcm_icb_rsp_ready), .ext2itcm_icb_rsp_err(ext2itcm_icb_rsp_err), .ext2itcm_icb_rsp_rdata(ext2itcm_icb_rsp_rdata), .ext2dtcm_icb_cmd_valid(ext2dtcm_icb_cmd_valid), .ext2dtcm_icb_cmd_ready(ext2dtcm_icb_cmd_ready), .ext2dtcm_icb_cmd_addr(ext2dtcm_icb_cmd_addr), .ext2dtcm_icb_cmd_read(ext2dtcm_icb_cmd_read), .ext2dtcm_icb_cmd_wdata(ext2dtcm_icb_cmd_wdata), .ext2dtcm_icb_cmd_wmask(ext2dtcm_icb_cmd_wmask), .ext2dtcm_icb_rsp_valid(ext2dtcm_icb_rsp_valid), .ext2dtcm_icb_rsp_ready(ext2dtcm_icb_rsp_ready), .ext2dtcm_icb_rsp_err(ext2dtcm_icb_rsp_err), .ext2dtcm_icb_rsp_rdata(ext2dtcm_icb_rsp_rdata), .ppi_icb_cmd_valid(ppi_icb_cmd_valid), .ppi_icb_cmd_ready(ppi_icb_cmd_ready), .ppi_icb_cmd_addr(ppi_icb_cmd_addr), .ppi_icb_cmd_read(ppi_icb_cmd_read), .ppi_icb_cmd_wdata(ppi_icb_cmd_wdata), .ppi_icb_cmd_wmask(ppi_icb_cmd_wmask), .ppi_icb_rsp_valid(ppi_icb_rsp_valid), .ppi_icb_rsp_ready(ppi_icb_rsp_ready), .ppi_icb_rsp_err(ppi_icb_rsp_err), .ppi_icb_rsp_rdata(ppi_icb_rsp_rdata), .clint_icb_cmd_valid(clint_icb_cmd_valid), .clint_icb_cmd_ready(clint_icb_cmd_ready), .clint_icb_cmd_addr(clint_icb_cmd_addr), .clint_icb_cmd_read(clint_icb_cmd_read), .clint_icb_cmd_wdata(clint_icb_cmd_wdata), .clint_icb_cmd_wmask(clint_icb_cmd_wmask), .clint_icb_rsp_valid(clint_icb_rsp_valid), .clint_icb_rsp_ready(clint_icb_rsp_ready), .clint_icb_rsp_err(clint_icb_rsp_err), .clint_icb_rsp_rdata(clint_icb_rsp_rdata), .plic_icb_cmd_valid(plic_icb_cmd_valid), .plic_icb_cmd_ready(plic_icb_cmd_ready), .plic_icb_cmd_addr(plic_icb_cmd_addr), .plic_icb_cmd_read(plic_icb_cmd_read), .plic_icb_cmd_wdata(plic_icb_cmd_wdata), .plic_icb_cmd_wmask(plic_icb_cmd_wmask), .plic_icb_rsp_valid(plic_icb_rsp_valid), .plic_icb_rsp_ready(plic_icb_rsp_ready), .plic_icb_rsp_err(plic_icb_rsp_err), .plic_icb_rsp_rdata(plic_icb_rsp_rdata), .fio_icb_cmd_valid(fio_icb_cmd_valid), .fio_icb_cmd_ready(fio_icb_cmd_ready), .fio_icb_cmd_addr(fio_icb_cmd_addr), .fio_icb_cmd_read(fio_icb_cmd_read), .fio_icb_cmd_wdata(fio_icb_cmd_wdata), .fio_icb_cmd_wmask(fio_icb_cmd_wmask), .fio_icb_rsp_valid(fio_icb_rsp_valid), .fio_icb_rsp_ready(fio_icb_rsp_ready), .fio_icb_rsp_err(fio_icb_rsp_err), .fio_icb_rsp_rdata(fio_icb_rsp_rdata), .mem_icb_cmd_valid(mem_icb_cmd_valid), .mem_icb_cmd_ready(mem_icb_cmd_ready), .mem_icb_cmd_addr(mem_icb_cmd_addr), .mem_icb_cmd_read(mem_icb_cmd_read), .mem_icb_cmd_wdata(mem_icb_cmd_wdata), .mem_icb_cmd_wmask(mem_icb_cmd_wmask), .mem_icb_rsp_valid(mem_icb_rsp_valid), .mem_icb_rsp_ready(mem_icb_rsp_ready), .mem_icb_rsp_err(mem_icb_rsp_err), .mem_icb_rsp_rdata(mem_icb_rsp_rdata), .test_mode(test_mode), .clk(clk), .rst_n(rst_n));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 1'b0;
|
||||||
|
forever #5 clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
seed = 20260419;
|
||||||
|
$dumpfile("e203_cpu_top_auto.vcd");
|
||||||
|
$dumpvars(0, tb_e203_cpu_top_auto);
|
||||||
|
pc_rtvec = 1'd0;
|
||||||
|
dcsr_r = 1'd0;
|
||||||
|
dpc_r = 1'd0;
|
||||||
|
dscratch_r = 1'd0;
|
||||||
|
dbg_mode = 1'd0;
|
||||||
|
dbg_halt_r = 1'd0;
|
||||||
|
dbg_step_r = 1'd0;
|
||||||
|
dbg_ebreakm_r = 1'd0;
|
||||||
|
dbg_stopcycle = 1'd0;
|
||||||
|
dbg_irq_a = 1'd0;
|
||||||
|
core_mhartid = 1'd0;
|
||||||
|
ext_irq_a = 1'd0;
|
||||||
|
sft_irq_a = 1'd0;
|
||||||
|
tmr_irq_a = 1'd0;
|
||||||
|
tcm_sd = 1'd0;
|
||||||
|
tcm_ds = 1'd0;
|
||||||
|
ext2itcm_icb_cmd_valid = 1'd0;
|
||||||
|
ext2itcm_icb_cmd_addr = 1'd0;
|
||||||
|
ext2itcm_icb_cmd_read = 1'd0;
|
||||||
|
ext2itcm_icb_cmd_wdata = 1'd0;
|
||||||
|
ext2itcm_icb_cmd_wmask = 1'd0;
|
||||||
|
ext2itcm_icb_rsp_ready = 1'd0;
|
||||||
|
ext2dtcm_icb_cmd_valid = 1'd0;
|
||||||
|
ext2dtcm_icb_cmd_addr = 1'd0;
|
||||||
|
ext2dtcm_icb_cmd_read = 1'd0;
|
||||||
|
ext2dtcm_icb_cmd_wdata = 1'd0;
|
||||||
|
ext2dtcm_icb_cmd_wmask = 1'd0;
|
||||||
|
ext2dtcm_icb_rsp_ready = 1'd0;
|
||||||
|
ppi_icb_cmd_ready = 1'd0;
|
||||||
|
ppi_icb_rsp_valid = 1'd0;
|
||||||
|
ppi_icb_rsp_err = 1'd0;
|
||||||
|
ppi_icb_rsp_rdata = 1'd0;
|
||||||
|
clint_icb_cmd_ready = 1'd0;
|
||||||
|
clint_icb_rsp_valid = 1'd0;
|
||||||
|
clint_icb_rsp_err = 1'd0;
|
||||||
|
clint_icb_rsp_rdata = 1'd0;
|
||||||
|
plic_icb_cmd_ready = 1'd0;
|
||||||
|
plic_icb_rsp_valid = 1'd0;
|
||||||
|
plic_icb_rsp_err = 1'd0;
|
||||||
|
plic_icb_rsp_rdata = 1'd0;
|
||||||
|
fio_icb_cmd_ready = 1'd0;
|
||||||
|
fio_icb_rsp_valid = 1'd0;
|
||||||
|
fio_icb_rsp_err = 1'd0;
|
||||||
|
fio_icb_rsp_rdata = 1'd0;
|
||||||
|
mem_icb_cmd_ready = 1'd0;
|
||||||
|
mem_icb_rsp_valid = 1'd0;
|
||||||
|
mem_icb_rsp_err = 1'd0;
|
||||||
|
mem_icb_rsp_rdata = 1'd0;
|
||||||
|
test_mode = 1'd0;
|
||||||
|
rst_n = 1'b0;
|
||||||
|
repeat (2) @(posedge clk);
|
||||||
|
rst_n = 1'b1;
|
||||||
|
for (i = 0; i < 8; i = i + 1) begin
|
||||||
|
@(negedge clk);
|
||||||
|
pc_rtvec = $random(seed) & 1'd1;
|
||||||
|
dcsr_r = $random(seed) & 1'd1;
|
||||||
|
dpc_r = $random(seed) & 1'd1;
|
||||||
|
dscratch_r = $random(seed) & 1'd1;
|
||||||
|
dbg_mode = $random(seed) & 1'd1;
|
||||||
|
dbg_halt_r = $random(seed) & 1'd1;
|
||||||
|
dbg_step_r = $random(seed) & 1'd1;
|
||||||
|
dbg_ebreakm_r = $random(seed) & 1'd1;
|
||||||
|
dbg_stopcycle = $random(seed) & 1'd1;
|
||||||
|
dbg_irq_a = $random(seed) & 1'd1;
|
||||||
|
core_mhartid = $random(seed) & 1'd1;
|
||||||
|
ext_irq_a = $random(seed) & 1'd1;
|
||||||
|
sft_irq_a = $random(seed) & 1'd1;
|
||||||
|
tmr_irq_a = $random(seed) & 1'd1;
|
||||||
|
tcm_sd = $random(seed) & 1'd1;
|
||||||
|
tcm_ds = $random(seed) & 1'd1;
|
||||||
|
ext2itcm_icb_cmd_valid = $random(seed) & 1'd1;
|
||||||
|
ext2itcm_icb_cmd_addr = $random(seed) & 1'd1;
|
||||||
|
ext2itcm_icb_cmd_read = $random(seed) & 1'd1;
|
||||||
|
ext2itcm_icb_cmd_wdata = $random(seed) & 1'd1;
|
||||||
|
ext2itcm_icb_cmd_wmask = $random(seed) & 1'd1;
|
||||||
|
ext2itcm_icb_rsp_ready = $random(seed) & 1'd1;
|
||||||
|
ext2dtcm_icb_cmd_valid = $random(seed) & 1'd1;
|
||||||
|
ext2dtcm_icb_cmd_addr = $random(seed) & 1'd1;
|
||||||
|
ext2dtcm_icb_cmd_read = $random(seed) & 1'd1;
|
||||||
|
ext2dtcm_icb_cmd_wdata = $random(seed) & 1'd1;
|
||||||
|
ext2dtcm_icb_cmd_wmask = $random(seed) & 1'd1;
|
||||||
|
ext2dtcm_icb_rsp_ready = $random(seed) & 1'd1;
|
||||||
|
ppi_icb_cmd_ready = $random(seed) & 1'd1;
|
||||||
|
ppi_icb_rsp_valid = $random(seed) & 1'd1;
|
||||||
|
ppi_icb_rsp_err = $random(seed) & 1'd1;
|
||||||
|
ppi_icb_rsp_rdata = $random(seed) & 1'd1;
|
||||||
|
clint_icb_cmd_ready = $random(seed) & 1'd1;
|
||||||
|
clint_icb_rsp_valid = $random(seed) & 1'd1;
|
||||||
|
clint_icb_rsp_err = $random(seed) & 1'd1;
|
||||||
|
clint_icb_rsp_rdata = $random(seed) & 1'd1;
|
||||||
|
plic_icb_cmd_ready = $random(seed) & 1'd1;
|
||||||
|
plic_icb_rsp_valid = $random(seed) & 1'd1;
|
||||||
|
plic_icb_rsp_err = $random(seed) & 1'd1;
|
||||||
|
plic_icb_rsp_rdata = $random(seed) & 1'd1;
|
||||||
|
fio_icb_cmd_ready = $random(seed) & 1'd1;
|
||||||
|
fio_icb_rsp_valid = $random(seed) & 1'd1;
|
||||||
|
fio_icb_rsp_err = $random(seed) & 1'd1;
|
||||||
|
fio_icb_rsp_rdata = $random(seed) & 1'd1;
|
||||||
|
mem_icb_cmd_ready = $random(seed) & 1'd1;
|
||||||
|
mem_icb_rsp_valid = $random(seed) & 1'd1;
|
||||||
|
mem_icb_rsp_err = $random(seed) & 1'd1;
|
||||||
|
mem_icb_rsp_rdata = $random(seed) & 1'd1;
|
||||||
|
test_mode = $random(seed) & 1'd1;
|
||||||
|
@(posedge clk);
|
||||||
|
end
|
||||||
|
#1;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"timestamp_utc": "2026-04-19T06:38:06.434830+00:00",
|
||||||
|
"repo_root": "/Users/ningyedong/Coding/Verilog-Learn",
|
||||||
|
"workspace": "/private/tmp/chipverify_github_cpu_example",
|
||||||
|
"projects_file": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/projects.json",
|
||||||
|
"tool": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/one_click_verify.py",
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"name": "ultraembedded_riscv_core",
|
||||||
|
"repo": "https://github.com/ultraembedded/riscv.git",
|
||||||
|
"stars": 1703,
|
||||||
|
"status": "pass",
|
||||||
|
"clone_status": "updated",
|
||||||
|
"rtl_dir": "/private/tmp/chipverify_github_cpu_example/clones/ultraembedded_riscv_core/core/riscv",
|
||||||
|
"top": "riscv_core",
|
||||||
|
"return_code": 0,
|
||||||
|
"artifacts": {
|
||||||
|
"tb": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/tb_riscv_core_auto.v",
|
||||||
|
"vcd": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/riscv_core_auto.vcd",
|
||||||
|
"sim_log": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/sim_output.log",
|
||||||
|
"sim_binary": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/auto_sim.out",
|
||||||
|
"run_log": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/run.log"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zipcpu",
|
||||||
|
"repo": "https://github.com/ZipCPU/zipcpu.git",
|
||||||
|
"stars": 1535,
|
||||||
|
"status": "pass",
|
||||||
|
"clone_status": "cloned",
|
||||||
|
"rtl_dir": "/private/tmp/chipverify_github_cpu_example/clones/zipcpu/rtl",
|
||||||
|
"top": "zipcore",
|
||||||
|
"return_code": 0,
|
||||||
|
"artifacts": {
|
||||||
|
"tb": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/tb_zipcore_auto.v",
|
||||||
|
"vcd": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/zipcore_auto.vcd",
|
||||||
|
"sim_log": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/sim_output.log",
|
||||||
|
"sim_binary": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/auto_sim.out",
|
||||||
|
"run_log": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/run.log"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "e203_cpu_top_fullrtl",
|
||||||
|
"repo": "https://github.com/riscv-mcu/e203_hbirdv2.git",
|
||||||
|
"stars": 1811,
|
||||||
|
"status": "pass",
|
||||||
|
"clone_status": "cloned",
|
||||||
|
"rtl_dir": "/private/tmp/chipverify_github_cpu_example/clones/e203_cpu_top_fullrtl/rtl",
|
||||||
|
"top": "e203_cpu_top",
|
||||||
|
"return_code": 0,
|
||||||
|
"artifacts": {
|
||||||
|
"tb": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/tb_e203_cpu_top_auto.v",
|
||||||
|
"vcd": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/e203_cpu_top_auto.vcd",
|
||||||
|
"sim_log": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/sim_output.log",
|
||||||
|
"sim_binary": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/auto_sim.out",
|
||||||
|
"run_log": "/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/e203_cpu_top_fullrtl/run.log"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pass_count": 3,
|
||||||
|
"fail_count": 0,
|
||||||
|
"total": 3
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
|||||||
|
COMMAND:
|
||||||
|
/opt/anaconda3/bin/python /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/one_click_verify.py --dir /private/tmp/chipverify_github_cpu_example/clones/ultraembedded_riscv_core/core/riscv --top riscv_core --out /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core --cycles 12 --period 10 --reset-cycles 2
|
||||||
|
|
||||||
|
STDOUT:
|
||||||
|
=== ChipVerify One-Click Auto Verification ===
|
||||||
|
Found 18 Verilog files
|
||||||
|
Selected top module: riscv_core
|
||||||
|
Top module file: /private/tmp/chipverify_github_cpu_example/clones/ultraembedded_riscv_core/core/riscv/riscv_core.v
|
||||||
|
Generated testbench: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/tb_riscv_core_auto.v
|
||||||
|
Simulation completed successfully
|
||||||
|
Simulation binary: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/auto_sim.out
|
||||||
|
Waveform: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/riscv_core_auto.vcd
|
||||||
|
Simulation log: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/sim_output.log
|
||||||
|
Open waveform with: gtkwave /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/riscv_core_auto.vcd
|
||||||
|
|
||||||
|
|
||||||
|
STDERR:
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
VCD info: dumpfile riscv_core_auto.vcd opened for output.
|
||||||
|
/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/ultraembedded_riscv_core/tb_riscv_core_auto.v:71: $finish called at 12000 (1ps)
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
module tb_riscv_core_auto;
|
||||||
|
|
||||||
|
reg clk_i;
|
||||||
|
reg rst_i;
|
||||||
|
reg [31:0] mem_d_data_rd_i;
|
||||||
|
reg mem_d_accept_i;
|
||||||
|
reg mem_d_ack_i;
|
||||||
|
reg mem_d_error_i;
|
||||||
|
reg [10:0] mem_d_resp_tag_i;
|
||||||
|
reg mem_i_accept_i;
|
||||||
|
reg mem_i_valid_i;
|
||||||
|
reg mem_i_error_i;
|
||||||
|
reg [31:0] mem_i_inst_i;
|
||||||
|
reg intr_i;
|
||||||
|
reg [31:0] reset_vector_i;
|
||||||
|
reg [31:0] cpu_id_i;
|
||||||
|
wire [31:0] mem_d_addr_o;
|
||||||
|
wire [31:0] mem_d_data_wr_o;
|
||||||
|
wire mem_d_rd_o;
|
||||||
|
wire [3:0] mem_d_wr_o;
|
||||||
|
wire mem_d_cacheable_o;
|
||||||
|
wire [10:0] mem_d_req_tag_o;
|
||||||
|
wire mem_d_invalidate_o;
|
||||||
|
wire mem_d_writeback_o;
|
||||||
|
wire mem_d_flush_o;
|
||||||
|
wire mem_i_rd_o;
|
||||||
|
wire mem_i_flush_o;
|
||||||
|
wire mem_i_invalidate_o;
|
||||||
|
wire [31:0] mem_i_pc_o;
|
||||||
|
integer i;
|
||||||
|
integer seed;
|
||||||
|
integer vec;
|
||||||
|
|
||||||
|
riscv_core dut (.clk_i(clk_i), .rst_i(rst_i), .mem_d_data_rd_i(mem_d_data_rd_i), .mem_d_accept_i(mem_d_accept_i), .mem_d_ack_i(mem_d_ack_i), .mem_d_error_i(mem_d_error_i), .mem_d_resp_tag_i(mem_d_resp_tag_i), .mem_i_accept_i(mem_i_accept_i), .mem_i_valid_i(mem_i_valid_i), .mem_i_error_i(mem_i_error_i), .mem_i_inst_i(mem_i_inst_i), .intr_i(intr_i), .reset_vector_i(reset_vector_i), .cpu_id_i(cpu_id_i), .mem_d_addr_o(mem_d_addr_o), .mem_d_data_wr_o(mem_d_data_wr_o), .mem_d_rd_o(mem_d_rd_o), .mem_d_wr_o(mem_d_wr_o), .mem_d_cacheable_o(mem_d_cacheable_o), .mem_d_req_tag_o(mem_d_req_tag_o), .mem_d_invalidate_o(mem_d_invalidate_o), .mem_d_writeback_o(mem_d_writeback_o), .mem_d_flush_o(mem_d_flush_o), .mem_i_rd_o(mem_i_rd_o), .mem_i_flush_o(mem_i_flush_o), .mem_i_invalidate_o(mem_i_invalidate_o), .mem_i_pc_o(mem_i_pc_o));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
seed = 20260419;
|
||||||
|
$dumpfile("riscv_core_auto.vcd");
|
||||||
|
$dumpvars(0, tb_riscv_core_auto);
|
||||||
|
clk_i = 1'd0;
|
||||||
|
mem_d_data_rd_i = 32'd0;
|
||||||
|
mem_d_accept_i = 1'd0;
|
||||||
|
mem_d_ack_i = 1'd0;
|
||||||
|
mem_d_error_i = 1'd0;
|
||||||
|
mem_d_resp_tag_i = 11'd0;
|
||||||
|
mem_i_accept_i = 1'd0;
|
||||||
|
mem_i_valid_i = 1'd0;
|
||||||
|
mem_i_error_i = 1'd0;
|
||||||
|
mem_i_inst_i = 32'd0;
|
||||||
|
intr_i = 1'd0;
|
||||||
|
reset_vector_i = 32'd0;
|
||||||
|
cpu_id_i = 32'd0;
|
||||||
|
rst_i = 1'b1;
|
||||||
|
for (i = 0; i < 12; i = i + 1) begin
|
||||||
|
clk_i = $random(seed) & 1'd1;
|
||||||
|
mem_d_data_rd_i = $random(seed) & 32'd4294967295;
|
||||||
|
mem_d_accept_i = $random(seed) & 1'd1;
|
||||||
|
mem_d_ack_i = $random(seed) & 1'd1;
|
||||||
|
mem_d_error_i = $random(seed) & 1'd1;
|
||||||
|
mem_d_resp_tag_i = $random(seed) & 11'd2047;
|
||||||
|
mem_i_accept_i = $random(seed) & 1'd1;
|
||||||
|
mem_i_valid_i = $random(seed) & 1'd1;
|
||||||
|
mem_i_error_i = $random(seed) & 1'd1;
|
||||||
|
mem_i_inst_i = $random(seed) & 32'd4294967295;
|
||||||
|
intr_i = $random(seed) & 1'd1;
|
||||||
|
reset_vector_i = $random(seed) & 32'd4294967295;
|
||||||
|
cpu_id_i = $random(seed) & 32'd4294967295;
|
||||||
|
#1;
|
||||||
|
end
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
8918
examples/github_cpu_validation/results/zipcpu/auto_sim.out
Normal file
8918
examples/github_cpu_validation/results/zipcpu/auto_sim.out
Normal file
File diff suppressed because it is too large
Load Diff
17
examples/github_cpu_validation/results/zipcpu/run.log
Normal file
17
examples/github_cpu_validation/results/zipcpu/run.log
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
COMMAND:
|
||||||
|
/opt/anaconda3/bin/python /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/one_click_verify.py --dir /private/tmp/chipverify_github_cpu_example/clones/zipcpu/rtl --top zipcore --out /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu --cycles 12 --period 10 --reset-cycles 2
|
||||||
|
|
||||||
|
STDOUT:
|
||||||
|
=== ChipVerify One-Click Auto Verification ===
|
||||||
|
Found 51 Verilog files
|
||||||
|
Selected top module: zipcore
|
||||||
|
Top module file: /private/tmp/chipverify_github_cpu_example/clones/zipcpu/rtl/core/zipcore.v
|
||||||
|
Generated testbench: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/tb_zipcore_auto.v
|
||||||
|
Simulation completed successfully
|
||||||
|
Simulation binary: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/auto_sim.out
|
||||||
|
Waveform: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/zipcore_auto.vcd
|
||||||
|
Simulation log: /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/sim_output.log
|
||||||
|
Open waveform with: gtkwave /Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/zipcore_auto.vcd
|
||||||
|
|
||||||
|
|
||||||
|
STDERR:
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
VCD info: dumpfile zipcore_auto.vcd opened for output.
|
||||||
|
/Users/ningyedong/Coding/Verilog-Learn/slwchipverify/examples/github_cpu_validation/results/zipcpu/tb_zipcore_auto.v:106: $finish called at 136000 (1ps)
|
||||||
108
examples/github_cpu_validation/results/zipcpu/tb_zipcore_auto.v
Normal file
108
examples/github_cpu_validation/results/zipcpu/tb_zipcore_auto.v
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
module tb_zipcore_auto;
|
||||||
|
|
||||||
|
reg i_clk;
|
||||||
|
reg i_reset;
|
||||||
|
reg i_interrupt;
|
||||||
|
reg i_halt;
|
||||||
|
reg i_clear_cache;
|
||||||
|
reg [4:0] i_dbg_wreg;
|
||||||
|
reg i_dbg_we;
|
||||||
|
reg [31:0] i_dbg_data;
|
||||||
|
reg [4:0] i_dbg_rreg;
|
||||||
|
reg i_pf_valid;
|
||||||
|
reg i_pf_illegal;
|
||||||
|
reg [31:0] i_pf_instruction;
|
||||||
|
reg i_pf_instruction_pc;
|
||||||
|
reg i_mem_busy;
|
||||||
|
reg i_mem_rdbusy;
|
||||||
|
reg i_mem_pipe_stalled;
|
||||||
|
reg i_mem_valid;
|
||||||
|
reg i_bus_err;
|
||||||
|
reg [4:0] i_mem_wreg;
|
||||||
|
reg [31:0] i_mem_result;
|
||||||
|
wire o_clken;
|
||||||
|
wire o_dbg_stall;
|
||||||
|
wire [31:0] o_dbg_reg;
|
||||||
|
wire [2:0] o_dbg_cc;
|
||||||
|
wire o_break;
|
||||||
|
wire o_pf_new_pc;
|
||||||
|
wire o_clear_icache;
|
||||||
|
wire o_pf_ready;
|
||||||
|
wire o_pf_request_address;
|
||||||
|
wire o_clear_dcache;
|
||||||
|
wire o_mem_ce;
|
||||||
|
wire o_bus_lock;
|
||||||
|
wire [2:0] o_mem_op;
|
||||||
|
wire [31:0] o_mem_addr;
|
||||||
|
wire [31:0] o_mem_data;
|
||||||
|
wire o_mem_lock_pc;
|
||||||
|
wire [4:0] o_mem_reg;
|
||||||
|
wire o_op_stall;
|
||||||
|
wire o_pf_stall;
|
||||||
|
wire o_i_count;
|
||||||
|
wire [31:0] o_debug;
|
||||||
|
wire o_prof_stb;
|
||||||
|
wire o_prof_addr;
|
||||||
|
wire [31:0] o_prof_ticks;
|
||||||
|
integer i;
|
||||||
|
integer seed;
|
||||||
|
|
||||||
|
zipcore dut (.i_clk(i_clk), .i_reset(i_reset), .i_interrupt(i_interrupt), .o_clken(o_clken), .i_halt(i_halt), .i_clear_cache(i_clear_cache), .i_dbg_wreg(i_dbg_wreg), .i_dbg_we(i_dbg_we), .i_dbg_data(i_dbg_data), .i_dbg_rreg(i_dbg_rreg), .o_dbg_stall(o_dbg_stall), .o_dbg_reg(o_dbg_reg), .o_dbg_cc(o_dbg_cc), .o_break(o_break), .o_pf_new_pc(o_pf_new_pc), .o_clear_icache(o_clear_icache), .o_pf_ready(o_pf_ready), .o_pf_request_address(o_pf_request_address), .i_pf_valid(i_pf_valid), .i_pf_illegal(i_pf_illegal), .i_pf_instruction(i_pf_instruction), .i_pf_instruction_pc(i_pf_instruction_pc), .o_clear_dcache(o_clear_dcache), .o_mem_ce(o_mem_ce), .o_bus_lock(o_bus_lock), .o_mem_op(o_mem_op), .o_mem_addr(o_mem_addr), .o_mem_data(o_mem_data), .o_mem_lock_pc(o_mem_lock_pc), .o_mem_reg(o_mem_reg), .i_mem_busy(i_mem_busy), .i_mem_rdbusy(i_mem_rdbusy), .i_mem_pipe_stalled(i_mem_pipe_stalled), .i_mem_valid(i_mem_valid), .i_bus_err(i_bus_err), .i_mem_wreg(i_mem_wreg), .i_mem_result(i_mem_result), .o_op_stall(o_op_stall), .o_pf_stall(o_pf_stall), .o_i_count(o_i_count), .o_debug(o_debug), .o_prof_stb(o_prof_stb), .o_prof_addr(o_prof_addr), .o_prof_ticks(o_prof_ticks));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
i_clk = 1'b0;
|
||||||
|
forever #5 i_clk = ~i_clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
seed = 20260419;
|
||||||
|
$dumpfile("zipcore_auto.vcd");
|
||||||
|
$dumpvars(0, tb_zipcore_auto);
|
||||||
|
i_interrupt = 1'd0;
|
||||||
|
i_halt = 1'd0;
|
||||||
|
i_clear_cache = 1'd0;
|
||||||
|
i_dbg_wreg = 5'd0;
|
||||||
|
i_dbg_we = 1'd0;
|
||||||
|
i_dbg_data = 32'd0;
|
||||||
|
i_dbg_rreg = 5'd0;
|
||||||
|
i_pf_valid = 1'd0;
|
||||||
|
i_pf_illegal = 1'd0;
|
||||||
|
i_pf_instruction = 32'd0;
|
||||||
|
i_pf_instruction_pc = 1'd0;
|
||||||
|
i_mem_busy = 1'd0;
|
||||||
|
i_mem_rdbusy = 1'd0;
|
||||||
|
i_mem_pipe_stalled = 1'd0;
|
||||||
|
i_mem_valid = 1'd0;
|
||||||
|
i_bus_err = 1'd0;
|
||||||
|
i_mem_wreg = 5'd0;
|
||||||
|
i_mem_result = 32'd0;
|
||||||
|
i_reset = 1'b1;
|
||||||
|
repeat (2) @(posedge i_clk);
|
||||||
|
i_reset = 1'b0;
|
||||||
|
for (i = 0; i < 12; i = i + 1) begin
|
||||||
|
@(negedge i_clk);
|
||||||
|
i_interrupt = $random(seed) & 1'd1;
|
||||||
|
i_halt = $random(seed) & 1'd1;
|
||||||
|
i_clear_cache = $random(seed) & 1'd1;
|
||||||
|
i_dbg_wreg = $random(seed) & 5'd31;
|
||||||
|
i_dbg_we = $random(seed) & 1'd1;
|
||||||
|
i_dbg_data = $random(seed) & 32'd4294967295;
|
||||||
|
i_dbg_rreg = $random(seed) & 5'd31;
|
||||||
|
i_pf_valid = $random(seed) & 1'd1;
|
||||||
|
i_pf_illegal = $random(seed) & 1'd1;
|
||||||
|
i_pf_instruction = $random(seed) & 32'd4294967295;
|
||||||
|
i_pf_instruction_pc = $random(seed) & 1'd1;
|
||||||
|
i_mem_busy = $random(seed) & 1'd1;
|
||||||
|
i_mem_rdbusy = $random(seed) & 1'd1;
|
||||||
|
i_mem_pipe_stalled = $random(seed) & 1'd1;
|
||||||
|
i_mem_valid = $random(seed) & 1'd1;
|
||||||
|
i_bus_err = $random(seed) & 1'd1;
|
||||||
|
i_mem_wreg = $random(seed) & 5'd31;
|
||||||
|
i_mem_result = $random(seed) & 32'd4294967295;
|
||||||
|
@(posedge i_clk);
|
||||||
|
end
|
||||||
|
#1;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
2208
examples/github_cpu_validation/results/zipcpu/zipcore_auto.vcd
Normal file
2208
examples/github_cpu_validation/results/zipcpu/zipcore_auto.vcd
Normal file
File diff suppressed because it is too large
Load Diff
249
examples/github_cpu_validation/run_github_cpu_validation.py
Normal file
249
examples/github_cpu_validation/run_github_cpu_validation.py
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""验证 SLWChipVerify 在热门开源 GitHub CPU 项目上的一键流程可用性。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProjectCase:
|
||||||
|
"""描述一个待验证项目的仓库信息与仿真参数。"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
repo: str
|
||||||
|
stars: int
|
||||||
|
rtl_subdir: str
|
||||||
|
top: str
|
||||||
|
cycles: int
|
||||||
|
period: int
|
||||||
|
reset_cycles: int
|
||||||
|
|
||||||
|
|
||||||
|
def _run(cmd: list[str], cwd: Path | None = None) -> subprocess.CompletedProcess[str]:
|
||||||
|
"""执行外部命令并收集标准输出/标准错误。"""
|
||||||
|
|
||||||
|
return subprocess.run(
|
||||||
|
cmd,
|
||||||
|
cwd=str(cwd) if cwd is not None else None,
|
||||||
|
text=True,
|
||||||
|
capture_output=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_cases(path: Path) -> list[ProjectCase]:
|
||||||
|
"""从 JSON 文件加载项目清单。"""
|
||||||
|
|
||||||
|
raw: list[dict[str, Any]] = json.loads(path.read_text(encoding="utf-8"))
|
||||||
|
cases: list[ProjectCase] = []
|
||||||
|
for item in raw:
|
||||||
|
cases.append(
|
||||||
|
ProjectCase(
|
||||||
|
name=str(item["name"]),
|
||||||
|
repo=str(item["repo"]),
|
||||||
|
stars=int(item["stars"]),
|
||||||
|
rtl_subdir=str(item["rtl_subdir"]),
|
||||||
|
top=str(item["top"]),
|
||||||
|
cycles=int(item["cycles"]),
|
||||||
|
period=int(item["period"]),
|
||||||
|
reset_cycles=int(item["reset_cycles"]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return cases
|
||||||
|
|
||||||
|
|
||||||
|
def _clone_or_update(case: ProjectCase, clone_dir: Path) -> tuple[bool, str]:
|
||||||
|
"""克隆仓库,或对已有仓库做浅拉取并硬重置到远端最新状态。"""
|
||||||
|
|
||||||
|
if clone_dir.exists() and (clone_dir / ".git").exists():
|
||||||
|
fetch = _run(["git", "-C", str(clone_dir), "fetch", "--depth", "1", "origin"])
|
||||||
|
if fetch.returncode != 0:
|
||||||
|
return False, fetch.stderr.strip() or fetch.stdout.strip()
|
||||||
|
reset = _run(["git", "-C", str(clone_dir), "reset", "--hard", "origin/HEAD"])
|
||||||
|
if reset.returncode != 0:
|
||||||
|
return False, reset.stderr.strip() or reset.stdout.strip()
|
||||||
|
return True, "updated"
|
||||||
|
|
||||||
|
if clone_dir.exists():
|
||||||
|
shutil.rmtree(clone_dir)
|
||||||
|
clone_dir.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
clone = _run(["git", "clone", "--depth", "1", case.repo, str(clone_dir)])
|
||||||
|
if clone.returncode != 0:
|
||||||
|
return False, clone.stderr.strip() or clone.stdout.strip()
|
||||||
|
return True, "cloned"
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
"""按项目列表逐个执行一键验证,并输出汇总 JSON 报告。"""
|
||||||
|
|
||||||
|
script_dir = Path(__file__).resolve().parent
|
||||||
|
default_repo_root = script_dir.parents[2]
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="运行 SLWChipVerify GitHub CPU 验证示例")
|
||||||
|
parser.add_argument(
|
||||||
|
"--repo-root",
|
||||||
|
default=str(default_repo_root),
|
||||||
|
help="Path to Verilog-Learn repository root",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--projects",
|
||||||
|
default=str(script_dir / "projects.json"),
|
||||||
|
help="Path to project case JSON",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--workspace",
|
||||||
|
default="/tmp/slwchipverify_github_cpu_example",
|
||||||
|
help="Temporary clone workspace",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--results-dir",
|
||||||
|
default=str(script_dir / "results"),
|
||||||
|
help="Directory to store per-case outputs and summary",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--python",
|
||||||
|
default=sys.executable,
|
||||||
|
help="Python executable used to run one_click_verify.py",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
repo_root = Path(args.repo_root).expanduser().resolve()
|
||||||
|
one_click = repo_root / "slwchipverify" / "one_click_verify.py"
|
||||||
|
if not one_click.exists():
|
||||||
|
print(
|
||||||
|
f"[SLWChipVerify][github-example] ERROR: one_click script not found: {one_click}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
project_file = Path(args.projects).expanduser().resolve()
|
||||||
|
cases = _load_cases(project_file)
|
||||||
|
|
||||||
|
workspace = Path(args.workspace).expanduser().resolve()
|
||||||
|
clones_dir = workspace / "clones"
|
||||||
|
results_dir = Path(args.results_dir).expanduser().resolve()
|
||||||
|
results_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
summary: dict[str, Any] = {
|
||||||
|
"timestamp_utc": datetime.now(timezone.utc).isoformat(),
|
||||||
|
"repo_root": str(repo_root),
|
||||||
|
"workspace": str(workspace),
|
||||||
|
"projects_file": str(project_file),
|
||||||
|
"tool": str(one_click),
|
||||||
|
"results": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
pass_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
|
||||||
|
for case in cases:
|
||||||
|
print(f"\\n=== Case: {case.name} ===")
|
||||||
|
clone_dir = clones_dir / case.name
|
||||||
|
ok, clone_msg = _clone_or_update(case, clone_dir)
|
||||||
|
if not ok:
|
||||||
|
fail_count += 1
|
||||||
|
result = {
|
||||||
|
"name": case.name,
|
||||||
|
"repo": case.repo,
|
||||||
|
"stars": case.stars,
|
||||||
|
"status": "fail",
|
||||||
|
"stage": "clone",
|
||||||
|
"error": clone_msg,
|
||||||
|
}
|
||||||
|
summary["results"].append(result)
|
||||||
|
print(f"[SLWChipVerify][github-example] FAIL clone: {clone_msg}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
target_dir = (clone_dir / case.rtl_subdir).resolve()
|
||||||
|
out_dir = results_dir / case.name
|
||||||
|
if out_dir.exists():
|
||||||
|
shutil.rmtree(out_dir)
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
args.python,
|
||||||
|
str(one_click),
|
||||||
|
"--dir",
|
||||||
|
str(target_dir),
|
||||||
|
"--top",
|
||||||
|
case.top,
|
||||||
|
"--out",
|
||||||
|
str(out_dir),
|
||||||
|
"--cycles",
|
||||||
|
str(case.cycles),
|
||||||
|
"--period",
|
||||||
|
str(case.period),
|
||||||
|
"--reset-cycles",
|
||||||
|
str(case.reset_cycles),
|
||||||
|
]
|
||||||
|
run = _run(cmd)
|
||||||
|
|
||||||
|
run_log = out_dir / "run.log"
|
||||||
|
run_log.write_text(
|
||||||
|
"COMMAND:\n"
|
||||||
|
+ " ".join(cmd)
|
||||||
|
+ "\n\nSTDOUT:\n"
|
||||||
|
+ run.stdout
|
||||||
|
+ "\n\nSTDERR:\n"
|
||||||
|
+ run.stderr,
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
artifacts = {
|
||||||
|
"tb": str(next(iter(sorted(out_dir.glob("tb_*_auto.v"))), "")),
|
||||||
|
"vcd": str(next(iter(sorted(out_dir.glob("*_auto.vcd"))), "")),
|
||||||
|
"sim_log": str(out_dir / "sim_output.log") if (out_dir / "sim_output.log").exists() else "",
|
||||||
|
"sim_binary": str(out_dir / "auto_sim.out") if (out_dir / "auto_sim.out").exists() else "",
|
||||||
|
"run_log": str(run_log),
|
||||||
|
}
|
||||||
|
|
||||||
|
status = "pass" if run.returncode == 0 else "fail"
|
||||||
|
if status == "pass":
|
||||||
|
pass_count += 1
|
||||||
|
print("[SLWChipVerify][github-example] PASS")
|
||||||
|
else:
|
||||||
|
fail_count += 1
|
||||||
|
print(f"[SLWChipVerify][github-example] FAIL (exit={run.returncode})")
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"name": case.name,
|
||||||
|
"repo": case.repo,
|
||||||
|
"stars": case.stars,
|
||||||
|
"status": status,
|
||||||
|
"clone_status": clone_msg,
|
||||||
|
"rtl_dir": str(target_dir),
|
||||||
|
"top": case.top,
|
||||||
|
"return_code": run.returncode,
|
||||||
|
"artifacts": artifacts,
|
||||||
|
}
|
||||||
|
if status == "fail":
|
||||||
|
result["error_tail"] = (run.stderr or run.stdout)[-2000:]
|
||||||
|
summary["results"].append(result)
|
||||||
|
|
||||||
|
summary["pass_count"] = pass_count
|
||||||
|
summary["fail_count"] = fail_count
|
||||||
|
summary["total"] = len(cases)
|
||||||
|
|
||||||
|
summary_path = results_dir / "github_cpu_validation_summary.json"
|
||||||
|
summary_path.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||||
|
|
||||||
|
print("\\n=== Summary ===")
|
||||||
|
print(f"Passed: {pass_count}")
|
||||||
|
print(f"Failed: {fail_count}")
|
||||||
|
print(f"Summary: {summary_path}")
|
||||||
|
|
||||||
|
return 0 if fail_count == 0 else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
16
examples/seq_auto_demo.v
Normal file
16
examples/seq_auto_demo.v
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// SLWChipVerify 时序验证示例:
|
||||||
|
// 在时钟上升沿把输入 din 锁存到输出 q;复位时清零 q。
|
||||||
|
module seq_auto_demo(
|
||||||
|
input clk,
|
||||||
|
input rst,
|
||||||
|
input [1:0] din,
|
||||||
|
output reg [1:0] q
|
||||||
|
);
|
||||||
|
// 该寄存器逻辑用于演示自动时序激励与断言检查。
|
||||||
|
always @(posedge clk or posedge rst) begin
|
||||||
|
if (rst)
|
||||||
|
q <= 2'd0;
|
||||||
|
else
|
||||||
|
q <= din;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
61
examples/seq_auto_demo_spec.json
Normal file
61
examples/seq_auto_demo_spec.json
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"kind": "sequential",
|
||||||
|
"clock": {
|
||||||
|
"name": "clk",
|
||||||
|
"period_ns": 10,
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"reset": {
|
||||||
|
"name": "rst",
|
||||||
|
"active": 1,
|
||||||
|
"cycles": 1
|
||||||
|
},
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "din",
|
||||||
|
"width": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "q",
|
||||||
|
"width": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max_cycles": 5,
|
||||||
|
"auto_stimulus": {
|
||||||
|
"enabled": true,
|
||||||
|
"start_cycle": 0,
|
||||||
|
"mode": "binary_count"
|
||||||
|
},
|
||||||
|
"assertions": [
|
||||||
|
{
|
||||||
|
"name": "rst_cycle0",
|
||||||
|
"cycle": 0,
|
||||||
|
"expect": {
|
||||||
|
"q": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "q_cycle1",
|
||||||
|
"cycle": 1,
|
||||||
|
"expect": {
|
||||||
|
"q": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "q_cycle2",
|
||||||
|
"cycle": 2,
|
||||||
|
"expect": {
|
||||||
|
"q": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "q_cycle3",
|
||||||
|
"cycle": 3,
|
||||||
|
"expect": {
|
||||||
|
"q": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
51
examples/seq_random_demo_spec.json
Normal file
51
examples/seq_random_demo_spec.json
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"kind": "sequential",
|
||||||
|
"clock": {
|
||||||
|
"name": "clk",
|
||||||
|
"period_ns": 10,
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"reset": {
|
||||||
|
"name": "rst",
|
||||||
|
"active": 1,
|
||||||
|
"cycles": 1
|
||||||
|
},
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "din",
|
||||||
|
"width": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "q",
|
||||||
|
"width": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max_cycles": 8,
|
||||||
|
"auto_stimulus": {
|
||||||
|
"enabled": true,
|
||||||
|
"start_cycle": 0,
|
||||||
|
"mode": "random",
|
||||||
|
"seed": 20260419
|
||||||
|
},
|
||||||
|
"assertions": [
|
||||||
|
{
|
||||||
|
"name": "reset_cycle0",
|
||||||
|
"cycle": 0,
|
||||||
|
"expect": {
|
||||||
|
"q": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "q_range_guard",
|
||||||
|
"cycle": 1,
|
||||||
|
"expect": {
|
||||||
|
"q": {
|
||||||
|
"op": "le",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
44
examples/simple_cpu_eventually_spec.json
Normal file
44
examples/simple_cpu_eventually_spec.json
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"kind": "sequential",
|
||||||
|
"clock": {
|
||||||
|
"name": "clk",
|
||||||
|
"period_ns": 10,
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"reset": {
|
||||||
|
"name": "rst",
|
||||||
|
"active": 1,
|
||||||
|
"cycles": 2
|
||||||
|
},
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [],
|
||||||
|
"observes": [
|
||||||
|
{
|
||||||
|
"name": "cnt",
|
||||||
|
"expr": "dut.cnt",
|
||||||
|
"width": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max_cycles": 7,
|
||||||
|
"auto_stimulus": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"assertions": [
|
||||||
|
{
|
||||||
|
"name": "count_cycle2",
|
||||||
|
"cycle": 2,
|
||||||
|
"expect": {
|
||||||
|
"cnt": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eventually_cnt_3",
|
||||||
|
"cycle": 2,
|
||||||
|
"mode": "eventually",
|
||||||
|
"within": 2,
|
||||||
|
"expect": {
|
||||||
|
"cnt": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
63
examples/simple_cpu_seq_spec.json
Normal file
63
examples/simple_cpu_seq_spec.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"kind": "sequential",
|
||||||
|
"clock": {
|
||||||
|
"name": "clk",
|
||||||
|
"period_ns": 10,
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"reset": {
|
||||||
|
"name": "rst",
|
||||||
|
"active": 1,
|
||||||
|
"cycles": 2
|
||||||
|
},
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [],
|
||||||
|
"observes": [
|
||||||
|
{
|
||||||
|
"name": "cnt",
|
||||||
|
"expr": "dut.cnt",
|
||||||
|
"width": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max_cycles": 6,
|
||||||
|
"auto_stimulus": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"assertions": [
|
||||||
|
{
|
||||||
|
"name": "reset_cycle0",
|
||||||
|
"cycle": 0,
|
||||||
|
"expect": {
|
||||||
|
"cnt": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "reset_cycle1",
|
||||||
|
"cycle": 1,
|
||||||
|
"expect": {
|
||||||
|
"cnt": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "count_cycle2",
|
||||||
|
"cycle": 2,
|
||||||
|
"expect": {
|
||||||
|
"cnt": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "count_cycle3",
|
||||||
|
"cycle": 3,
|
||||||
|
"expect": {
|
||||||
|
"cnt": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "count_cycle4",
|
||||||
|
"cycle": 4,
|
||||||
|
"expect": {
|
||||||
|
"cnt": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
71
examples/simple_cpu_window_modes_spec.json
Normal file
71
examples/simple_cpu_window_modes_spec.json
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"kind": "sequential",
|
||||||
|
"clock": {
|
||||||
|
"name": "clk",
|
||||||
|
"period_ns": 10,
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"reset": {
|
||||||
|
"name": "rst",
|
||||||
|
"active": 1,
|
||||||
|
"cycles": 2
|
||||||
|
},
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [],
|
||||||
|
"observes": [
|
||||||
|
{
|
||||||
|
"name": "cnt",
|
||||||
|
"expr": "dut.cnt",
|
||||||
|
"width": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"max_cycles": 7,
|
||||||
|
"auto_stimulus": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"assertions": [
|
||||||
|
{
|
||||||
|
"name": "cycle_cnt_1",
|
||||||
|
"cycle": 2,
|
||||||
|
"expect": {
|
||||||
|
"cnt": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "always_cnt_ge_1",
|
||||||
|
"cycle": 2,
|
||||||
|
"mode": "always",
|
||||||
|
"within": 2,
|
||||||
|
"expect": {
|
||||||
|
"cnt": {
|
||||||
|
"op": "ge",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "never_cnt_eq_0",
|
||||||
|
"cycle": 2,
|
||||||
|
"mode": "never",
|
||||||
|
"within": 2,
|
||||||
|
"expect": {
|
||||||
|
"cnt": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "until_cnt_lt_4_until_4",
|
||||||
|
"cycle": 2,
|
||||||
|
"mode": "until",
|
||||||
|
"within": 3,
|
||||||
|
"expect": {
|
||||||
|
"cnt": {
|
||||||
|
"op": "lt",
|
||||||
|
"value": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"until_expect": {
|
||||||
|
"cnt": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
881
one_click_verify.py
Normal file
881
one_click_verify.py
Normal file
@@ -0,0 +1,881 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""SLWChipVerify 一键自动验证脚本。
|
||||||
|
|
||||||
|
功能概览:
|
||||||
|
1) 交互式输入工程目录与仿真参数
|
||||||
|
2) 自动扫描 Verilog 文件并解析模块关系
|
||||||
|
3) 自动或手动选择顶层模块
|
||||||
|
4) 自动生成冒烟测试 testbench
|
||||||
|
5) 调用 iverilog/vvp 进行编译与仿真
|
||||||
|
6) 自动输出 VCD 波形
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
KEYWORDS = {
|
||||||
|
"module",
|
||||||
|
"endmodule",
|
||||||
|
"if",
|
||||||
|
"else",
|
||||||
|
"case",
|
||||||
|
"endcase",
|
||||||
|
"for",
|
||||||
|
"while",
|
||||||
|
"always",
|
||||||
|
"initial",
|
||||||
|
"assign",
|
||||||
|
"wire",
|
||||||
|
"reg",
|
||||||
|
"logic",
|
||||||
|
"input",
|
||||||
|
"output",
|
||||||
|
"inout",
|
||||||
|
"function",
|
||||||
|
"task",
|
||||||
|
"begin",
|
||||||
|
"end",
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_PREFIX = "[SLWChipVerify][one-click]"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PortInfo:
|
||||||
|
"""端口信息:名称、方向、位宽。"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
direction: str
|
||||||
|
width: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ModuleInfo:
|
||||||
|
"""模块信息:模块名、来源文件和端口列表。"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
file_path: Path
|
||||||
|
ports: list[PortInfo]
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_comments(text: str) -> str:
|
||||||
|
"""去除 Verilog 的行注释与块注释,便于后续正则解析。"""
|
||||||
|
|
||||||
|
text = re.sub(r"/\*.*?\*/", "", text, flags=re.S)
|
||||||
|
text = re.sub(r"//.*", "", text)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_int_value(expr: str) -> int | None:
|
||||||
|
"""把十进制或十六进制数字文本解析为整数。"""
|
||||||
|
|
||||||
|
value = expr.strip()
|
||||||
|
if re.fullmatch(r"\d+", value):
|
||||||
|
return int(value)
|
||||||
|
if value.lower().startswith("0x"):
|
||||||
|
try:
|
||||||
|
return int(value, 16)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_range_width(range_text: str | None) -> int:
|
||||||
|
"""从位宽范围文本(如 [7:0])计算位宽。"""
|
||||||
|
|
||||||
|
if not range_text:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
m = re.match(r"\[\s*([^:]+)\s*:\s*([^\]]+)\s*\]", range_text)
|
||||||
|
if not m:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
msb = _parse_int_value(m.group(1))
|
||||||
|
lsb = _parse_int_value(m.group(2))
|
||||||
|
if msb is None or lsb is None:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return abs(msb - lsb) + 1
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_header_ports(header_text: str) -> dict[str, PortInfo]:
|
||||||
|
"""解析模块头部端口声明,返回端口名到端口信息的映射。"""
|
||||||
|
|
||||||
|
parts = [part.strip() for part in header_text.replace("\n", " ").split(",")]
|
||||||
|
names: dict[str, PortInfo] = {}
|
||||||
|
current_direction = "input"
|
||||||
|
current_width = 1
|
||||||
|
|
||||||
|
for part in parts:
|
||||||
|
if not part:
|
||||||
|
continue
|
||||||
|
|
||||||
|
direction_match = re.search(r"\b(input|output|inout)\b", part)
|
||||||
|
width_match = re.search(r"\[[^\]]+\]", part)
|
||||||
|
|
||||||
|
if direction_match:
|
||||||
|
current_direction = direction_match.group(1).lower()
|
||||||
|
if width_match is None:
|
||||||
|
current_width = 1
|
||||||
|
|
||||||
|
if width_match:
|
||||||
|
current_width = _parse_range_width(width_match.group(0))
|
||||||
|
|
||||||
|
tokens = re.findall(r"[A-Za-z_][A-Za-z0-9_]*", part)
|
||||||
|
name: str | None = None
|
||||||
|
for token in reversed(tokens):
|
||||||
|
if token.lower() not in {
|
||||||
|
"input",
|
||||||
|
"output",
|
||||||
|
"inout",
|
||||||
|
"wire",
|
||||||
|
"reg",
|
||||||
|
"logic",
|
||||||
|
"signed",
|
||||||
|
"unsigned",
|
||||||
|
}:
|
||||||
|
name = token
|
||||||
|
break
|
||||||
|
|
||||||
|
if name:
|
||||||
|
names[name] = PortInfo(
|
||||||
|
name=name,
|
||||||
|
direction=current_direction,
|
||||||
|
width=current_width,
|
||||||
|
)
|
||||||
|
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_names_from_decl(decl_text: str) -> list[str]:
|
||||||
|
"""从 input/output 声明片段中提取端口名列表。"""
|
||||||
|
|
||||||
|
cleaned = re.sub(r"\[[^\]]+\]", " ", decl_text)
|
||||||
|
cleaned = re.sub(r"\b(?:reg|wire|logic|signed|unsigned)\b", " ", cleaned)
|
||||||
|
names: list[str] = []
|
||||||
|
|
||||||
|
for part in cleaned.split(","):
|
||||||
|
candidate = part.strip()
|
||||||
|
if not candidate:
|
||||||
|
continue
|
||||||
|
candidate = candidate.split("=")[0].strip()
|
||||||
|
if not candidate:
|
||||||
|
continue
|
||||||
|
m = re.match(r"([A-Za-z_][A-Za-z0-9_]*)", candidate)
|
||||||
|
if m:
|
||||||
|
names.append(m.group(1))
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_ports_from_body(body_text: str) -> dict[str, PortInfo]:
|
||||||
|
"""解析模块体中的端口声明,补全方向与位宽信息。"""
|
||||||
|
|
||||||
|
port_map: dict[str, PortInfo] = {}
|
||||||
|
decl_re = re.compile(r"^\s*(input|output|inout)\b([^;]*);", re.M)
|
||||||
|
|
||||||
|
for m in decl_re.finditer(body_text):
|
||||||
|
direction = m.group(1).lower()
|
||||||
|
rest = m.group(2)
|
||||||
|
width_match = re.search(r"\[[^\]]+\]", rest)
|
||||||
|
width = _parse_range_width(width_match.group(0) if width_match else None)
|
||||||
|
names = _extract_names_from_decl(rest)
|
||||||
|
for name in names:
|
||||||
|
port_map[name] = PortInfo(name=name, direction=direction, width=width)
|
||||||
|
|
||||||
|
return port_map
|
||||||
|
|
||||||
|
|
||||||
|
def parse_modules_from_file(file_path: Path) -> list[ModuleInfo]:
|
||||||
|
"""从单个 Verilog 文件中提取模块及其端口定义。"""
|
||||||
|
|
||||||
|
text = file_path.read_text(encoding="utf-8", errors="ignore")
|
||||||
|
text_no_comments = _strip_comments(text)
|
||||||
|
|
||||||
|
module_re = re.compile(
|
||||||
|
r"\bmodule\s+([A-Za-z_][A-Za-z0-9_]*)\s*"
|
||||||
|
r"(?:#\s*\(.*?\)\s*)?"
|
||||||
|
r"\((.*?)\)\s*;",
|
||||||
|
re.S,
|
||||||
|
)
|
||||||
|
|
||||||
|
modules: list[ModuleInfo] = []
|
||||||
|
pos = 0
|
||||||
|
while True:
|
||||||
|
m = module_re.search(text_no_comments, pos)
|
||||||
|
if not m:
|
||||||
|
break
|
||||||
|
|
||||||
|
module_name = m.group(1)
|
||||||
|
header_port_map = _parse_header_ports(m.group(2))
|
||||||
|
header_ports = list(header_port_map.keys())
|
||||||
|
|
||||||
|
body_start = m.end()
|
||||||
|
end_match = re.search(r"\bendmodule\b", text_no_comments[body_start:], re.S)
|
||||||
|
if not end_match:
|
||||||
|
break
|
||||||
|
|
||||||
|
body_end = body_start + end_match.start()
|
||||||
|
body_text = text_no_comments[body_start:body_end]
|
||||||
|
|
||||||
|
port_map = _parse_ports_from_body(body_text)
|
||||||
|
|
||||||
|
ordered_ports: list[PortInfo] = []
|
||||||
|
seen = set()
|
||||||
|
for name in header_ports:
|
||||||
|
if name in port_map:
|
||||||
|
ordered_ports.append(port_map[name])
|
||||||
|
elif name in header_port_map:
|
||||||
|
ordered_ports.append(header_port_map[name])
|
||||||
|
else:
|
||||||
|
ordered_ports.append(PortInfo(name=name, direction="input", width=1))
|
||||||
|
seen.add(name)
|
||||||
|
|
||||||
|
for name in sorted(port_map.keys()):
|
||||||
|
if name not in seen:
|
||||||
|
ordered_ports.append(port_map[name])
|
||||||
|
|
||||||
|
modules.append(ModuleInfo(name=module_name, file_path=file_path, ports=ordered_ports))
|
||||||
|
|
||||||
|
pos = body_end + len("endmodule")
|
||||||
|
|
||||||
|
return modules
|
||||||
|
|
||||||
|
|
||||||
|
def collect_modules(verilog_files: list[Path]) -> list[ModuleInfo]:
|
||||||
|
"""汇总多个 Verilog 文件中的全部模块定义。"""
|
||||||
|
|
||||||
|
modules: list[ModuleInfo] = []
|
||||||
|
for file_path in verilog_files:
|
||||||
|
modules.extend(parse_modules_from_file(file_path))
|
||||||
|
return modules
|
||||||
|
|
||||||
|
|
||||||
|
def find_instantiated_modules(verilog_files: list[Path], module_names: set[str]) -> set[str]:
|
||||||
|
"""扫描实例化关系,找出被其他模块实例化过的模块名。"""
|
||||||
|
|
||||||
|
instantiated: set[str] = set()
|
||||||
|
|
||||||
|
for file_path in verilog_files:
|
||||||
|
text = _strip_comments(file_path.read_text(encoding="utf-8", errors="ignore"))
|
||||||
|
for module_name in module_names:
|
||||||
|
pattern = re.compile(
|
||||||
|
rf"\b{re.escape(module_name)}\b\s*(?:#\s*\(.*?\)\s*)?[A-Za-z_][A-Za-z0-9_]*\s*\(",
|
||||||
|
re.S,
|
||||||
|
)
|
||||||
|
if pattern.search(text):
|
||||||
|
instantiated.add(module_name)
|
||||||
|
|
||||||
|
return instantiated
|
||||||
|
|
||||||
|
|
||||||
|
def _is_clock_name(name: str) -> bool:
|
||||||
|
"""判断端口名是否像时钟信号。"""
|
||||||
|
|
||||||
|
low = name.lower()
|
||||||
|
return low == "clk" or "clock" in low or low.endswith("_clk")
|
||||||
|
|
||||||
|
|
||||||
|
def _is_reset_name(name: str) -> bool:
|
||||||
|
"""判断端口名是否像复位信号。"""
|
||||||
|
|
||||||
|
low = name.lower()
|
||||||
|
return (
|
||||||
|
low == "rst"
|
||||||
|
or low == "reset"
|
||||||
|
or "reset" in low
|
||||||
|
or low.startswith("rst")
|
||||||
|
or low.endswith("_rst")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_active_low_reset(name: str) -> bool:
|
||||||
|
"""根据命名习惯判断复位是否更可能为低有效。"""
|
||||||
|
|
||||||
|
low = name.lower()
|
||||||
|
return (
|
||||||
|
low.endswith("_n")
|
||||||
|
or low.endswith("n")
|
||||||
|
or "resetn" in low
|
||||||
|
or "rstn" in low
|
||||||
|
or low.startswith("nreset")
|
||||||
|
or low.startswith("nrst")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _sv_width(width: int) -> str:
|
||||||
|
"""把位宽整数转换为 SystemVerilog 声明片段。"""
|
||||||
|
|
||||||
|
if width <= 1:
|
||||||
|
return ""
|
||||||
|
return f"[{width - 1}:0] "
|
||||||
|
|
||||||
|
|
||||||
|
def _mask(width: int) -> int:
|
||||||
|
"""生成指定位宽的按位掩码。"""
|
||||||
|
|
||||||
|
if width <= 0:
|
||||||
|
return 0
|
||||||
|
return (1 << width) - 1
|
||||||
|
|
||||||
|
|
||||||
|
def _format_half_period(period_ns: int) -> str:
|
||||||
|
"""将半周期延时格式化为紧凑字符串。"""
|
||||||
|
|
||||||
|
half = period_ns / 2.0
|
||||||
|
text = f"{half:.6f}".rstrip("0").rstrip(".")
|
||||||
|
return text or "0"
|
||||||
|
|
||||||
|
|
||||||
|
def _is_testbench_module_name(name: str) -> bool:
|
||||||
|
"""判断模块名是否呈现 testbench 命名特征。"""
|
||||||
|
|
||||||
|
low = name.lower()
|
||||||
|
return (
|
||||||
|
low == "tb"
|
||||||
|
or low.startswith("tb_")
|
||||||
|
or low.endswith("_tb")
|
||||||
|
or "test" in low
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _top_candidates(
|
||||||
|
modules: list[ModuleInfo],
|
||||||
|
instantiated: set[str],
|
||||||
|
) -> list[ModuleInfo]:
|
||||||
|
"""根据实例化关系和命名特征推断顶层候选模块。"""
|
||||||
|
|
||||||
|
name_map = {m.name: m for m in modules}
|
||||||
|
roots = sorted([m.name for m in modules if m.name not in instantiated])
|
||||||
|
|
||||||
|
if roots:
|
||||||
|
non_tb_roots = [name for name in roots if not _is_testbench_module_name(name)]
|
||||||
|
if non_tb_roots:
|
||||||
|
return [name_map[name] for name in non_tb_roots]
|
||||||
|
|
||||||
|
non_tb_modules = [m for m in modules if not _is_testbench_module_name(m.name)]
|
||||||
|
if non_tb_modules:
|
||||||
|
max_ports = max(len(m.ports) for m in non_tb_modules)
|
||||||
|
likely_tops = [m for m in non_tb_modules if len(m.ports) == max_ports]
|
||||||
|
return sorted(likely_tops, key=lambda m: m.name)
|
||||||
|
|
||||||
|
return [name_map[name] for name in roots]
|
||||||
|
|
||||||
|
non_tb_modules = [m for m in modules if not _is_testbench_module_name(m.name)]
|
||||||
|
if non_tb_modules:
|
||||||
|
max_ports = max(len(m.ports) for m in non_tb_modules)
|
||||||
|
likely_tops = [m for m in non_tb_modules if len(m.ports) == max_ports]
|
||||||
|
return sorted(likely_tops, key=lambda m: m.name)
|
||||||
|
|
||||||
|
max_ports = max(len(m.ports) for m in modules)
|
||||||
|
likely_tops = [m for m in modules if len(m.ports) == max_ports]
|
||||||
|
return sorted(likely_tops, key=lambda m: m.name)
|
||||||
|
|
||||||
|
|
||||||
|
def _pick_top_module(
|
||||||
|
modules: list[ModuleInfo],
|
||||||
|
specified_top: str | None,
|
||||||
|
instantiated: set[str],
|
||||||
|
) -> ModuleInfo:
|
||||||
|
"""确定最终顶层模块:优先使用命令行指定,否则自动推断或交互选择。"""
|
||||||
|
|
||||||
|
name_map = {m.name: m for m in modules}
|
||||||
|
if specified_top:
|
||||||
|
if specified_top not in name_map:
|
||||||
|
available = ", ".join(sorted(name_map))
|
||||||
|
raise ChipVerifyError(
|
||||||
|
f"Top module {specified_top!r} not found. Available modules: {available}"
|
||||||
|
)
|
||||||
|
return name_map[specified_top]
|
||||||
|
|
||||||
|
candidates = _top_candidates(modules, instantiated)
|
||||||
|
if len(candidates) == 1:
|
||||||
|
return candidates[0]
|
||||||
|
|
||||||
|
print("Detected multiple top candidates:")
|
||||||
|
for idx, module in enumerate(candidates, start=1):
|
||||||
|
print(f" {idx}. {module.name}")
|
||||||
|
selected = input("Choose top module index (default 1): ").strip()
|
||||||
|
if not selected:
|
||||||
|
return candidates[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
index = int(selected)
|
||||||
|
if index < 1 or index > len(candidates):
|
||||||
|
raise ValueError
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ChipVerifyError("Invalid top module selection index") from exc
|
||||||
|
|
||||||
|
return candidates[index - 1]
|
||||||
|
|
||||||
|
|
||||||
|
def generate_testbench(
|
||||||
|
top_module: ModuleInfo,
|
||||||
|
output_dir: Path,
|
||||||
|
cycles: int,
|
||||||
|
period_ns: int,
|
||||||
|
reset_cycles: int,
|
||||||
|
) -> tuple[Path, Path]:
|
||||||
|
"""按顶层模块端口自动生成冒烟测试 testbench 与波形路径。"""
|
||||||
|
|
||||||
|
tb_name = f"tb_{top_module.name}_auto"
|
||||||
|
tb_path = output_dir / f"{tb_name}.v"
|
||||||
|
vcd_path = output_dir / f"{top_module.name}_auto.vcd"
|
||||||
|
|
||||||
|
inputs = [p for p in top_module.ports if p.direction == "input"]
|
||||||
|
outputs = [p for p in top_module.ports if p.direction in {"output", "inout"}]
|
||||||
|
|
||||||
|
clock_port = next((p for p in inputs if p.width == 1 and _is_clock_name(p.name)), None)
|
||||||
|
reset_port = next((p for p in inputs if p.width == 1 and _is_reset_name(p.name)), None)
|
||||||
|
|
||||||
|
skip_names = {
|
||||||
|
clock_port.name if clock_port else "",
|
||||||
|
reset_port.name if reset_port else "",
|
||||||
|
}
|
||||||
|
non_special_inputs = [p for p in inputs if p.name not in skip_names]
|
||||||
|
|
||||||
|
lines: list[str] = []
|
||||||
|
lines.append("`timescale 1ns/1ps")
|
||||||
|
lines.append(f"module {tb_name};")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
for p in inputs:
|
||||||
|
lines.append(f" reg {_sv_width(p.width)}{p.name};")
|
||||||
|
for p in outputs:
|
||||||
|
lines.append(f" wire {_sv_width(p.width)}{p.name};")
|
||||||
|
|
||||||
|
lines.append(" integer i;")
|
||||||
|
lines.append(" integer seed;")
|
||||||
|
if not clock_port and non_special_inputs:
|
||||||
|
lines.append(" integer vec;")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
conn = ", ".join([f".{p.name}({p.name})" for p in top_module.ports])
|
||||||
|
lines.append(f" {top_module.name} dut ({conn});")
|
||||||
|
|
||||||
|
if clock_port:
|
||||||
|
half_text = _format_half_period(period_ns)
|
||||||
|
lines.append("")
|
||||||
|
lines.append(" initial begin")
|
||||||
|
lines.append(f" {clock_port.name} = 1'b0;")
|
||||||
|
lines.append(f" forever #{half_text} {clock_port.name} = ~{clock_port.name};")
|
||||||
|
lines.append(" end")
|
||||||
|
|
||||||
|
lines.append("")
|
||||||
|
lines.append(" initial begin")
|
||||||
|
lines.append(" seed = 20260419;")
|
||||||
|
lines.append(f" $dumpfile(\"{vcd_path.name}\");")
|
||||||
|
lines.append(f" $dumpvars(0, {tb_name});")
|
||||||
|
|
||||||
|
for p in non_special_inputs:
|
||||||
|
lines.append(f" {p.name} = {p.width}'d0;")
|
||||||
|
|
||||||
|
if reset_port:
|
||||||
|
active = 0 if _is_active_low_reset(reset_port.name) else 1
|
||||||
|
inactive = 1 - active
|
||||||
|
lines.append(f" {reset_port.name} = 1'b{active};")
|
||||||
|
|
||||||
|
if clock_port:
|
||||||
|
if reset_port:
|
||||||
|
lines.append(f" repeat ({reset_cycles}) @(posedge {clock_port.name});")
|
||||||
|
lines.append(f" {reset_port.name} = 1'b{inactive};")
|
||||||
|
|
||||||
|
lines.append(f" for (i = 0; i < {cycles}; i = i + 1) begin")
|
||||||
|
if non_special_inputs:
|
||||||
|
lines.append(f" @(negedge {clock_port.name});")
|
||||||
|
for p in non_special_inputs:
|
||||||
|
mask = _mask(p.width)
|
||||||
|
lines.append(f" {p.name} = $random(seed) & {p.width}'d{mask};")
|
||||||
|
lines.append(f" @(posedge {clock_port.name});")
|
||||||
|
lines.append(" end")
|
||||||
|
lines.append(" #1;")
|
||||||
|
else:
|
||||||
|
total_width = sum(p.width for p in non_special_inputs)
|
||||||
|
if total_width == 0:
|
||||||
|
lines.append(f" #{max(1, cycles)};")
|
||||||
|
elif total_width <= 8:
|
||||||
|
max_cases = 1 << total_width
|
||||||
|
lines.append(f" for (vec = 0; vec < {max_cases}; vec = vec + 1) begin")
|
||||||
|
bit_offset = 0
|
||||||
|
for p in non_special_inputs:
|
||||||
|
mask = _mask(p.width)
|
||||||
|
lines.append(
|
||||||
|
f" {p.name} = (vec >> {bit_offset}) & {p.width}'d{mask};"
|
||||||
|
)
|
||||||
|
bit_offset += p.width
|
||||||
|
lines.append(" #1;")
|
||||||
|
lines.append(" end")
|
||||||
|
else:
|
||||||
|
lines.append(f" for (i = 0; i < {cycles}; i = i + 1) begin")
|
||||||
|
for p in non_special_inputs:
|
||||||
|
mask = _mask(p.width)
|
||||||
|
lines.append(f" {p.name} = $random(seed) & {p.width}'d{mask};")
|
||||||
|
lines.append(" #1;")
|
||||||
|
lines.append(" end")
|
||||||
|
|
||||||
|
lines.append(" $finish;")
|
||||||
|
lines.append(" end")
|
||||||
|
lines.append("endmodule")
|
||||||
|
|
||||||
|
tb_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
return tb_path, vcd_path
|
||||||
|
|
||||||
|
|
||||||
|
def run_simulation(
|
||||||
|
verilog_files: list[Path],
|
||||||
|
tb_path: Path,
|
||||||
|
output_dir: Path,
|
||||||
|
tb_module_name: str,
|
||||||
|
) -> tuple[Path, str]:
|
||||||
|
"""调用 iverilog/vvp 完成编译与仿真,返回可执行文件和仿真输出。"""
|
||||||
|
|
||||||
|
if shutil.which("iverilog") is None:
|
||||||
|
raise ChipVerifyError("iverilog not found in PATH")
|
||||||
|
if shutil.which("vvp") is None:
|
||||||
|
raise ChipVerifyError("vvp not found in PATH")
|
||||||
|
|
||||||
|
sim_out = output_dir / "auto_sim.out"
|
||||||
|
|
||||||
|
include_dirs = sorted({str(p.parent.resolve()) for p in verilog_files})
|
||||||
|
include_args: list[str] = []
|
||||||
|
for inc in include_dirs:
|
||||||
|
include_args.extend(["-I", inc])
|
||||||
|
|
||||||
|
compile_cmd = [
|
||||||
|
"iverilog",
|
||||||
|
"-g2012",
|
||||||
|
"-gno-assertions",
|
||||||
|
*include_args,
|
||||||
|
"-s",
|
||||||
|
tb_module_name,
|
||||||
|
"-o",
|
||||||
|
str(sim_out),
|
||||||
|
*[str(p) for p in verilog_files],
|
||||||
|
str(tb_path),
|
||||||
|
]
|
||||||
|
|
||||||
|
compile_result = subprocess.run(
|
||||||
|
compile_cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
cwd=output_dir,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if compile_result.returncode != 0:
|
||||||
|
message = compile_result.stderr.strip() or compile_result.stdout.strip()
|
||||||
|
raise ChipVerifyError(f"Compile failed:\n{message}")
|
||||||
|
|
||||||
|
sim_cmd = ["vvp", str(sim_out)]
|
||||||
|
sim_result = subprocess.run(
|
||||||
|
sim_cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
cwd=output_dir,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if sim_result.returncode != 0:
|
||||||
|
message = sim_result.stderr.strip() or sim_result.stdout.strip()
|
||||||
|
raise ChipVerifyError(f"Simulation failed:\n{message}")
|
||||||
|
|
||||||
|
return sim_out, sim_result.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_name(value: str) -> str:
|
||||||
|
"""把模块名清洗为可用作目录名的安全字符串。"""
|
||||||
|
|
||||||
|
txt = re.sub(r"[^A-Za-z0-9_.-]", "_", value)
|
||||||
|
txt = txt.strip("._")
|
||||||
|
return txt or "module"
|
||||||
|
|
||||||
|
|
||||||
|
def _unique_module_dir(base_dir: Path, module_name: str, used: set[str]) -> Path:
|
||||||
|
"""为批量模式生成不冲突的模块输出目录。"""
|
||||||
|
|
||||||
|
stem = _sanitize_name(module_name)
|
||||||
|
candidate = stem
|
||||||
|
counter = 2
|
||||||
|
while candidate in used:
|
||||||
|
candidate = f"{stem}_{counter}"
|
||||||
|
counter += 1
|
||||||
|
used.add(candidate)
|
||||||
|
return base_dir / candidate
|
||||||
|
|
||||||
|
|
||||||
|
def run_single_module(
|
||||||
|
top_module: ModuleInfo,
|
||||||
|
verilog_files: list[Path],
|
||||||
|
output_dir: Path,
|
||||||
|
cycles: int,
|
||||||
|
period: int,
|
||||||
|
reset_cycles: int,
|
||||||
|
) -> dict[str, str | int | None]:
|
||||||
|
"""执行单个顶层模块的一键验证流程,并返回结构化结果。"""
|
||||||
|
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
result: dict[str, str | int | None] = {
|
||||||
|
"module": top_module.name,
|
||||||
|
"module_file": str(top_module.file_path),
|
||||||
|
"output_dir": str(output_dir),
|
||||||
|
"status": "fail",
|
||||||
|
"tb_path": None,
|
||||||
|
"sim_binary": None,
|
||||||
|
"sim_log": None,
|
||||||
|
"vcd_path": None,
|
||||||
|
"error": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
tb_path, vcd_path = generate_testbench(
|
||||||
|
top_module=top_module,
|
||||||
|
output_dir=output_dir,
|
||||||
|
cycles=cycles,
|
||||||
|
period_ns=period,
|
||||||
|
reset_cycles=reset_cycles,
|
||||||
|
)
|
||||||
|
result["tb_path"] = str(tb_path)
|
||||||
|
result["vcd_path"] = str(vcd_path)
|
||||||
|
|
||||||
|
sim_out, sim_stdout = run_simulation(
|
||||||
|
verilog_files,
|
||||||
|
tb_path,
|
||||||
|
output_dir,
|
||||||
|
tb_module_name=tb_path.stem,
|
||||||
|
)
|
||||||
|
sim_log = output_dir / "sim_output.log"
|
||||||
|
sim_log.write_text(sim_stdout, encoding="utf-8")
|
||||||
|
|
||||||
|
result["sim_binary"] = str(sim_out)
|
||||||
|
result["sim_log"] = str(sim_log)
|
||||||
|
result["status"] = "pass"
|
||||||
|
return result
|
||||||
|
except ChipVerifyError as exc:
|
||||||
|
result["error"] = str(exc)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def write_batch_summary(path: Path, report: dict[str, object]) -> None:
|
||||||
|
"""写出批量模式汇总报告。"""
|
||||||
|
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _prompt_yes_no(prompt: str, default: bool) -> bool:
|
||||||
|
"""读取 Y/N 交互输入,并处理默认值。"""
|
||||||
|
|
||||||
|
default_text = "Y/n" if default else "y/N"
|
||||||
|
raw = input(f"{prompt} [{default_text}]: ").strip().lower()
|
||||||
|
if not raw:
|
||||||
|
return default
|
||||||
|
return raw in {"y", "yes", "1", "true"}
|
||||||
|
|
||||||
|
|
||||||
|
def _prompt_with_default(prompt: str, default: str) -> str:
|
||||||
|
"""读取字符串输入,若为空则回退默认值。"""
|
||||||
|
|
||||||
|
user = input(f"{prompt} [{default}]: ").strip()
|
||||||
|
return user or default
|
||||||
|
|
||||||
|
|
||||||
|
def _prompt_int(prompt: str, default: int, min_value: int = 0) -> int:
|
||||||
|
"""读取整数输入并校验最小值约束。"""
|
||||||
|
|
||||||
|
raw = _prompt_with_default(prompt, str(default))
|
||||||
|
try:
|
||||||
|
value = int(raw)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ChipVerifyError(f"Invalid integer input for {prompt}") from exc
|
||||||
|
if value < min_value:
|
||||||
|
raise ChipVerifyError(f"{prompt} must be >= {min_value}")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def find_verilog_files(project_dir: Path) -> list[Path]:
|
||||||
|
"""递归扫描工程目录中的 .v 源文件。"""
|
||||||
|
|
||||||
|
files = sorted([p for p in project_dir.rglob("*.v") if p.is_file()])
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
class ChipVerifyError(Exception):
|
||||||
|
"""一键验证流程的统一异常类型。"""
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
"""一键流程入口:参数解析、模块选择、仿真执行与结果输出。"""
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="One-click Verilog auto testbench + simulation + waveform generator"
|
||||||
|
)
|
||||||
|
parser.add_argument("--dir", help="Verilog project directory")
|
||||||
|
parser.add_argument("--top", help="Top module name (optional)")
|
||||||
|
parser.add_argument("--out", help="Output directory")
|
||||||
|
parser.add_argument("--cycles", type=int, help="Simulation cycles")
|
||||||
|
parser.add_argument("--period", type=int, help="Clock period (ns)")
|
||||||
|
parser.add_argument("--reset-cycles", type=int, help="Reset hold cycles")
|
||||||
|
parser.add_argument(
|
||||||
|
"--batch",
|
||||||
|
action="store_true",
|
||||||
|
help="Run all detected top candidates and produce a consolidated summary report",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--batch-report",
|
||||||
|
help="Optional path of consolidated batch summary JSON",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
print("=== SLWChipVerify One-Click Auto Verification ===")
|
||||||
|
|
||||||
|
cwd = Path.cwd()
|
||||||
|
project_dir_txt = args.dir or _prompt_with_default("Enter Verilog directory", str(cwd))
|
||||||
|
project_dir = Path(project_dir_txt).expanduser().resolve()
|
||||||
|
if not project_dir.exists() or not project_dir.is_dir():
|
||||||
|
raise ChipVerifyError(f"Directory not found: {project_dir}")
|
||||||
|
|
||||||
|
output_dir = Path(
|
||||||
|
args.out
|
||||||
|
or _prompt_with_default("Output directory", str(project_dir / "slwchipverify_auto"))
|
||||||
|
).expanduser().resolve()
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
cycles = args.cycles if args.cycles is not None else _prompt_int("Simulation cycles", 40, 1)
|
||||||
|
period = args.period if args.period is not None else _prompt_int("Clock period (ns)", 10, 1)
|
||||||
|
reset_cycles = (
|
||||||
|
args.reset_cycles
|
||||||
|
if args.reset_cycles is not None
|
||||||
|
else _prompt_int("Reset hold cycles", 2, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
verilog_files = find_verilog_files(project_dir)
|
||||||
|
if not verilog_files:
|
||||||
|
raise ChipVerifyError(f"No .v files found in {project_dir}")
|
||||||
|
|
||||||
|
print(f"Found {len(verilog_files)} Verilog files")
|
||||||
|
modules = collect_modules(verilog_files)
|
||||||
|
if not modules:
|
||||||
|
raise ChipVerifyError("No module declarations found in Verilog files")
|
||||||
|
|
||||||
|
module_names = {m.name for m in modules}
|
||||||
|
instantiated = find_instantiated_modules(verilog_files, module_names)
|
||||||
|
|
||||||
|
batch_mode = args.batch
|
||||||
|
if not args.batch and not args.top:
|
||||||
|
candidate_count = len(_top_candidates(modules, instantiated))
|
||||||
|
if candidate_count > 1:
|
||||||
|
batch_mode = _prompt_yes_no(
|
||||||
|
f"Detected {candidate_count} top candidates. Run in batch mode",
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if batch_mode:
|
||||||
|
if args.top:
|
||||||
|
selected_modules = [_pick_top_module(modules, args.top, instantiated)]
|
||||||
|
else:
|
||||||
|
selected_modules = _top_candidates(modules, instantiated)
|
||||||
|
|
||||||
|
print(f"Batch mode enabled, total candidates: {len(selected_modules)}")
|
||||||
|
for module in selected_modules:
|
||||||
|
print(f" - {module.name} ({module.file_path})")
|
||||||
|
|
||||||
|
used_dir_names: set[str] = set()
|
||||||
|
batch_results: list[dict[str, str | int | None]] = []
|
||||||
|
passed = 0
|
||||||
|
failed = 0
|
||||||
|
|
||||||
|
for module in selected_modules:
|
||||||
|
module_output_dir = _unique_module_dir(output_dir, module.name, used_dir_names)
|
||||||
|
if module_output_dir.exists():
|
||||||
|
shutil.rmtree(module_output_dir)
|
||||||
|
module_output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
print(f"\n[batch] Running module: {module.name}")
|
||||||
|
result = run_single_module(
|
||||||
|
top_module=module,
|
||||||
|
verilog_files=verilog_files,
|
||||||
|
output_dir=module_output_dir,
|
||||||
|
cycles=cycles,
|
||||||
|
period=period,
|
||||||
|
reset_cycles=reset_cycles,
|
||||||
|
)
|
||||||
|
batch_results.append(result)
|
||||||
|
|
||||||
|
if result["status"] == "pass":
|
||||||
|
passed += 1
|
||||||
|
print(f"[batch] PASS: {module.name}")
|
||||||
|
print(f"[batch] TB: {result['tb_path']}")
|
||||||
|
print(f"[batch] VCD: {result['vcd_path']}")
|
||||||
|
else:
|
||||||
|
failed += 1
|
||||||
|
print(f"[batch] FAIL: {module.name}")
|
||||||
|
print(f"[batch] Error: {result['error']}")
|
||||||
|
|
||||||
|
summary_report = {
|
||||||
|
"timestamp": datetime.now().isoformat(timespec="seconds"),
|
||||||
|
"project_dir": str(project_dir),
|
||||||
|
"output_dir": str(output_dir),
|
||||||
|
"total_candidates": len(selected_modules),
|
||||||
|
"passed": passed,
|
||||||
|
"failed": failed,
|
||||||
|
"results": batch_results,
|
||||||
|
}
|
||||||
|
|
||||||
|
batch_report_path = (
|
||||||
|
Path(args.batch_report).expanduser().resolve()
|
||||||
|
if args.batch_report
|
||||||
|
else output_dir / "batch_summary.json"
|
||||||
|
)
|
||||||
|
write_batch_summary(batch_report_path, summary_report)
|
||||||
|
|
||||||
|
print("\nBatch run completed")
|
||||||
|
print(f"Batch summary: {batch_report_path}")
|
||||||
|
print(f"Passed: {passed}, Failed: {failed}, Total: {len(selected_modules)}")
|
||||||
|
|
||||||
|
return 0 if failed == 0 else 1
|
||||||
|
|
||||||
|
top_module = _pick_top_module(modules, args.top, instantiated)
|
||||||
|
print(f"Selected top module: {top_module.name}")
|
||||||
|
print(f"Top module file: {top_module.file_path}")
|
||||||
|
|
||||||
|
tb_path, vcd_path = generate_testbench(
|
||||||
|
top_module=top_module,
|
||||||
|
output_dir=output_dir,
|
||||||
|
cycles=cycles,
|
||||||
|
period_ns=period,
|
||||||
|
reset_cycles=reset_cycles,
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Generated testbench: {tb_path}")
|
||||||
|
sim_out, sim_stdout = run_simulation(
|
||||||
|
verilog_files,
|
||||||
|
tb_path,
|
||||||
|
output_dir,
|
||||||
|
tb_module_name=tb_path.stem,
|
||||||
|
)
|
||||||
|
|
||||||
|
sim_log = output_dir / "sim_output.log"
|
||||||
|
sim_log.write_text(sim_stdout, encoding="utf-8")
|
||||||
|
|
||||||
|
print("Simulation completed successfully")
|
||||||
|
print(f"Simulation binary: {sim_out}")
|
||||||
|
print(f"Waveform: {vcd_path}")
|
||||||
|
print(f"Simulation log: {sim_log}")
|
||||||
|
print(f"Open waveform with: gtkwave {vcd_path}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
raise SystemExit(main())
|
||||||
|
except ChipVerifyError as exc:
|
||||||
|
print(f"{LOG_PREFIX} ERROR: {exc}", file=sys.stderr)
|
||||||
|
raise SystemExit(2) from exc
|
||||||
8
one_click_verify.sh
Executable file
8
one_click_verify.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# 解析当前脚本所在目录,确保从任意路径调用都能找到 Python 主脚本。
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# 透传全部命令行参数给一键验证入口。
|
||||||
|
python3 "$SCRIPT_DIR/one_click_verify.py" "$@"
|
||||||
1940
slwchipverify.py
Normal file
1940
slwchipverify.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user