基于 slang 的 SystemVerilog 语义导航工具,为 AI Agent 提供 RTL 调试能力
navisv 将 RTL 设计转化为结构化查询,让 AI Agent 能够直接回答:
- 这个信号从哪来?到哪去?
- 这个变量被哪些约束限制?
- 这个 coverpoint 的 bin 定义合理吗?
- 哪些关键信号还没有被验证覆盖? ← navisv 独有的能力
- 路径追踪 - 两点间完整路径,自动识别时序/组合/条件关系
- 验证覆盖缺口检测 - 自动发现未覆盖的高风险信号
- CDC 跨时钟域分析 - 识别跨时钟域路径、同步器缺失
- 约束 / CoverGroup / SVA 查询 - 三者交叉验证
- 多格式可视化 - Graphviz DOT / Mermaid 两重输出,支持 CDC 高亮
- Python API + CLI 双接口 - Agent 可编程、设计师可直接调用
- 🔥 杀手级应用: 验证覆盖缺口检测
- 快速上手
- 3. Python API (推荐 Agent 使用)
- 能力总览
- 功能一览
- 实际效果示例
- API 参考
- CLI 命令速查
- 架构
- 测试状态
- 项目结构
- 环境要求
- 许可证
"我写了这么多 assertion 和 covergroup,到底哪些关键信号还没被覆盖到?"
navisv 用 risk + verify-map 工作流回答这个问题。
python3 examples/verify_coverage_workflow.py top.sv -I ./RTL/工作原理:
Step 1: navisv risk → 找出 critical/high 风险信号 (功能+时序复杂度)
Step 2: navisv verify-map → 检查这些信号有没有 SVA 或 CoverGroup 覆盖
Step 3: 输出待验证清单 → "这 5 个高风险信号完全没有覆盖"
实际输出:
⏱️ Step 1: 风险分析
高风险信号 (24 个):
reg_data_o 综合=88.0 critical 高入度(12), 大Fan-in锥(117)
tx_fifo_rd_en 综合=83.5 critical 中出度(5), 大Fan-in锥(117)
next_state 综合=82.5 critical 高入度(19), 高出度(14)
时序关键路径 (Top 3):
1: uart_rst_n → stop_bit → curr_state → tik_count → parity_bit → uart_tx_o (深度=8)
🔍 Step 2: 覆盖缺口检测
🔴 未覆盖: 0 🟢 双覆盖: 0
📋 Step 3: 高风险信号覆盖状态
✅ 所有高风险信号已覆盖
✅ 工作流完成
覆盖状态图 (verify-map 输出):
🔴 红色节点 = 没有 SVA 也没有 CoverGroup
🟡 黄色节点 = 只有 SVA assertion
🔵 蓝色节点 = 只有 CoverGroup
🟢 绿色节点 = SVA + CoverGroup 双覆盖
查看具体用例: examples/verify_coverage_workflow.py
详细文档: docs/verification_coverage_analysis.md
推荐方式 — 一键编译脚本 (跨平台):
# 编译 slang + slang-netlist (Release 模式, 自动并行)
./scripts/install_dependencies.sh
# 编译完后 source 环境变量
source .navisv_env
# 验证
navisv tools支持的平台:
| 平台 | 包管理器 |
|---|---|
| macOS (arm64 / x86_64) | Homebrew |
| Linux (Ubuntu/Debian) | apt |
| Linux (Fedora/RHEL) | dnf / yum |
| Linux (Arch/Manjaro) | pacman |
| Linux (Alpine) | apk |
| Linux (openSUSE) | zypper |
| Linux (WSL) | 同 Linux |
| Windows (Git Bash / MSYS2) | choco / scoop / winget |
| Windows (Cygwin) | choco / scoop |
脚本会自动:
- 检测平台和 CPU 核心数
- 检测已安装的 C++ 编译器 (clang++ / g++ / MSVC)
- 处理 Windows 的 .exe 后缀差异
- 提示适合当前平台的依赖安装命令
脚本高级用法:
./scripts/install_dependencies.sh --clean # 清理后重新编译
./scripts/install_dependencies.sh --debug # Debug 构建
./scripts/install_dependencies.sh --prefix /opt/navisv/bin # 安装到指定目录
./scripts/install_dependencies.sh --skip-netlist # 只编译 slang
./scripts/install_dependencies.sh --help # 查看所有选项手动方式 (不推荐):
# 前置条件: cmake, C++ 编译器
# macOS: xcode-select --install
# Ubuntu: apt install cmake g++
# Python 依赖
pip install networkx
# 编译 slang (SystemVerilog 前端)
git clone https://github.com/MikePopoloski/slang.git
cd slang && cmake -B build && cmake --build build -j$(nproc)
export NAVISV_SLANG_BIN=$(pwd)/slang # 可执行文件在项目根目录
# 编译 slang-netlist (网表提取, DesignGraph 需要)
# 注意: slang-netlist 依赖 slang, 请先编译 slang
git clone https://github.com/MikePopoloski/slang-netlist.git
cd slang-netlist && cmake -B build && cmake --build build -j$(nproc)
export NAVISV_NETLIST_BIN=$(pwd)/build/tools/driver/slang-netlist# 信号路径追踪: 这个信号从哪来?
python3 cli.py trace design.sv top.src top.dst
# 约束查询: 这个变量被哪些约束限制?
python3 cli.py cvar design.sv pkg.Class.var
# covergroup 检查: bin 和 constraint 一致吗?
python3 cli.py cg-check design.sv pkg.Class.var cg_name cp_name
# 语法检查
python3 cli.py check design.svnavisv 支持生成带信号关系的可视化图,每个节点 = 信号,每个边 = 数据/时钟/条件关系。
# ── DOT 格式(Graphviz,适合生成高清 PNG)────────────────────────
# 1. 风险分析图
navisv dot design.sv -o /tmp/risk.dot
dot -Tpng /tmp/risk.dot -Gbgcolor=white -o /tmp/risk.png
# 2. 验证覆盖图
navisv dot design.sv -s top.fifo -o /tmp/fifo.dot
dot -Tpng /tmp/fifo.dot -Gbgcolor=white -o /tmp/fifo.png
# 3. CDC 高亮(跨时钟域路径用粉红粗边标注)
navisv dot design.sv --cdc-highlight -o /tmp/cdc.dot
# ── Mermaid 格式(可直接粘贴到 GitHub / Typora / 飞书)─────────
navisv mermaid design.sv -o /tmp/design.mmd
# 或直接输出到标准输出
navisv mermaid design.sv -s top.submodule --cdc-highlight
# ── 其他分析命令(均支持 --cdc-highlight)──────────────────────
navisv temporal design.sv --cdc-highlight # 时序关系图
navisv risk design.sv --cdc-highlight # 风险分析图布局选项:
| 参数 | 效果 |
|---|---|
--rankdir LR |
左右排列(默认,输入靠左,输出靠右) |
--rankdir TB |
上下排列(深图/层级结构) |
--subgraph X |
只显示指定子模块(过滤节点数量) |
--cluster-depth N |
模块聚类深度(默认2,即子模块级别) |
--no-legend |
隐藏图例面板 |
图例说明:
| 节点颜色 | 含义 |
|---|---|
| 🔴 红色 | critical (风险≥80) |
| 🟠 橙色 | high (风险≥60) |
| 🟡 黄色 | medium (风险≥40) |
| 🟢 绿色 | low (风险<40) |
| 边样式 | 含义 |
|---|---|
| 🔵 蓝虚线 | 组合逻辑驱动 |
| 🔴 红粗线 | 寄存器时钟驱动 |
| 🟠 橙实线 | 条件控制 |
| ⚡ 粉红粗线 | CDC 跨时钟域路径(需加 --cdc-highlight) |
在真实 UART 控制器项目上测试的图形输出:
uart_controller 全局视图(完整节点,无限制)
风险图 LR — 163KB,Input/Output Port 左右对齐,polyline 柔和折线,正方比例
子模块分析示例(uart_tx 状态机)
# 只看 uart_tx 子模块,带 CDC 高亮
navisv dot design.sv -s uart_controller.uart_tx --cdc-highlight -o /tmp/uart_tx.dot
dot -Tpng /tmp/uart_tx.dot -Gbgcolor=white -o /tmp/uart_tx.png生成命令对照表:
| 命令 | 输出格式 | 适用场景 |
|---|---|---|
navisv dot |
Graphviz DOT | 高清 PNG、专业报告 |
navisv mermaid |
Mermaid | 快速预览、嵌入文档 |
navisv temporal |
DOT + Mermaid | 时序路径分析 |
navisv risk |
DOT + Mermaid | 风险等级分析 |
| 项目 | 节点数 | risk 图大小 | 特点 |
|---|---|---|---|
| UART | ~150 | 163KB | Input/Output Port 左右对齐,正方比例 |
| pipeline | 75 | 21KB | 轻量快速 |
| chipsonar | 115 | 44KB | 中等规模 |
Python API (更灵活):
from navisv import DesignDriver
from navisv.graph.risk_analyzer import RiskAnalyzer
from navisv.graph.graphviz_exporter import export_risk_dot, export_risk_mermaid
dd = DesignDriver(['design.sv'], output_dir='/tmp/navisv_out', include_dirs=['./'])
dd.build()
analyzer = RiskAnalyzer(dd.design_graph, 'top')
analyzer.analyze()
# DOT (Graphviz) - 适合生成高清图
dot = export_risk_dot(
dd.design_graph,
module_prefix='top',
max_nodes=100, max_edges=200,
rankdir='LR', # 布局方向: LR/TB/BT/RL
cdc_highlight=True, # CDC 路径高亮
show_legend=True, # 显示图例
cluster_depth=2, # 模块聚类深度
)
# Mermaid - 适合粘贴到文档
mmd = export_risk_mermaid(
dd.design_graph,
module_prefix='top',
max_nodes=80,
rankdir='LR',
cdc_highlight=True,
)
# 验证覆盖图
from navisv.graph.verify_mapper import VerifyMapper
from navisv.graph.graphviz_exporter import export_verify_dot
mapper = VerifyMapper(dd.design_graph)
vreport = mapper.analyze('top')
dot = export_verify_dot(
dd.design_graph,
module_prefix='top',
max_nodes=100,
verify_report=vreport,
rankdir='LR',
cdc_highlight=True,
show_legend=True,
)| 参数 | 说明 |
|---|---|
module_prefix |
模块前缀,按模块筛选节点 |
max_nodes |
最大节点数,按度数排序裁剪 |
max_edges |
最大边数,按重要性排序裁剪 |
rankdir |
布局方向: LR(左右,默认)/ TB(上下)/ BT/RL |
cdc_highlight |
True 时高亮 CDC 跨时钟域路径(粉红粗边) |
show_legend |
True 时显示图例面板(默认) |
cluster_depth |
模块聚类深度(默认2,子模块级别) |
verify_report |
verify-map 的报告,用于着色覆盖状态 |
from navisv import DesignDriver
dd = DesignDriver(['design.sv'])
dd.build()
# 信号路径
dg = dd.design_graph
dg.trace_full_path('top.src', 'top.dst')
# 约束查询
cg = dd.constraint_graph
cg.get_constraints_for_variable('pkg.Class.var')
# SVA 提取
sva = dd.sva
for a in sva.assertions:
print(f'{a.kind}: {a.expression}')
# UVM 结构
uvm = dd.uvm_tb
uvm.get_port_connections()
# 调用图
call = dd.call_graph
call.to_mermaid()navisv 提供 9 大能力模块,覆盖 RTL 设计分析的完整链路:
| 功能 | CLI 命令 | 说明 |
|---|---|---|
| 信号路径追踪 | trace |
两点间完整路径,含时序和条件 |
| 信号分析 | info |
fan-in/fan-out/条件列表 |
| 约束查询 | cvar / ccons / crel |
变量↔约束关系 |
| CoverGroup 分析 | cg-list / cg-check / cg-quality |
bins 一致性 + 质量评估 |
| SVA 提取 | (Python API) | 从 AST 提取 assert/assume/cover/restrict |
| UVM TB 结构 | (Python API) | 组件层级/Sequence/Port/config_db/plusargs |
| 调用图 | (Python API) | 函数调用图、fork、randomize 标记 |
| SVA 生成 | (Python API) | 从信号关系生成 assert property |
| 编译检查 | check |
快速语法检查 |
| 时序分析 | timing / fanout |
时钟域/CDC 分析 |
| 验证覆盖工作流 | examples/verify_coverage_workflow.py |
🔥 杀手级: risk + verify-map 缺口检测 |
| 时序关系分析 | temporal |
自动发现信号间时序约束 |
| SVA 时序对齐 | sva-align |
检查 SVA 与 RTL 时序一致性 |
| 验证覆盖率地图 | verify-map |
SVA + Coverage 叠加在信号图上 |
| 信号风险分析 | risk |
功能+时序复杂度,风险等级评估 |
| 图形可视化 | -f dot/mermaid |
🔥 信号关系图 + 风险/覆盖状态着色 |
$ python3 cli.py trace uart_controller.sv uart_controller.s_apb_pwdata_i uart_controller.uart_tx.tx_fifo_data_i✅ APB WDATA → TX FIFO path=9 confidence=0.52
路径: s_apb_pwdata_i → apb_interface.s_apb_pwdata_i → ... → uart_tx.tx_fifo_data_i
Agent 用这个能力回答:"这个 FIFO 数据是从 APB 总线的 pwdata 写入的"。
$ python3 cli.py cvar tests/sv/realworld_ethernet.sv ethernet_pkg.packet.ipg变量 ipg 的约束 (4):
packet::C_ipg
expr: ipg inside { 10:50 }
packet_bringup::C_bringup
expr: ipg == 10
packet_small_ipg::C_ipg
expr: ipg inside { 1:10 }
packet_zero_ipg::C_ipg
expr: ipg == 0
Agent 用这个能力回答:"ipg 被 4 个约束限制,基类允许 10-50,子类分别覆盖为 10、1-10、0"。
$ python3 cli.py cg-check tests/sv/covergroup_constraint_check.sv \
cg_check_pkg.dead_bin_cls.data dead_bin_cls.cg cp_datadata 的一致性检查:
⚠️ dead_bin: bin [101:200] 被 constraint 排除, 永远无法 hit
range: [101:200]
⚠️ dead_bin: bin [255:255] 被 constraint 排除, 永远无法 hit
range: [255:255]
⚠️ missing_illegal_bin: constraint 禁止的取值没有标 illegal_bins
forbidden: [101:255]
Agent 用这个能力回答:"你的 covergroup 有 3 个问题:2 个死 bin 永远不会 hit,还有 101-255 的值应该标 illegal_bins"。
$ python3 cli.py --json trace true_condition.sv true_condition.a true_condition.out_if边上带 true_condition 字段:
{
"src": "a", "dst": "out_if",
"true_condition": "!!rst_n && sel == 2'b0"
}Agent 用这个能力回答:"信号 a 只在 rst_n 有效且 sel==0 时才会传递到 out_if"。
$ python3 cli.py cg-quality tests/sv/covergroup_quality.sv \
cg_quality_pkg.data_bad_cls.data data_bad_cls.cg cp_data -t datadata_bad_cls.data 的质量评估 (score=0.50):
⚠️ 缺少极值 bin: 建议添加 bins zero = {0}
⚠️ 缺少极值 bin: 建议添加 bins max = {255}
⚠️ bin 数量较少 (2), 建议细化范围划分
$ python3 cli.py crel tests/sv/realworld_ethernet.sv \
ethernet_pkg.packet.mac_dst_addr ethernet_pkg.packet.mac_src_addr变量关系:
mac_dst_addr <-> mac_src_addr
共享约束 (1):
- C_bringup
from navisv import DesignDriver
dd = DesignDriver(['design.sv'])
dd.build()
sva = dd.sva
for a in sva.assertions:
print(f'{a.kind:15s} clk={a.clock} expr={a.expression}')
if a.disable_condition:
print(f' disable iff ({a.disable_condition})')Assert clk=clk expr: valid |-> ##1 ready
Assume clk=clk expr: valid |-> ##[1:3] ready
Assert clk=clk expr: valid |-> data != 8'd0
disable iff (!rst_n)
Assert clk=clk prop=p_data_stable expr: valid |-> ##1 $stable(data)
Restrict clk=clk expr: data != 8'hFF
Agent 用这个能力回答:“这个模块有哪些 assertion?涉及哪些信号?”
cg = dd.call_graph
# 调用关系
for cls in ['my_pkg.my_seq']:
for m in cg.get_methods(cls):
calls = cg.get_calls_from(m['full_path'])
for c in calls:
flags = []
if c['is_randomize']: flags.append('randomize')
if c['is_constructor']: flags.append('new')
if c['is_super']: flags.append('super')
print(f'{m["name"]} -> {c["callee"]} {flags}')
# fork 块
for f in cg.get_forks('my_pkg.my_seq'):
print(f'fork {[b["callee"] for b in f["branches"]]} {f["join_type"]}')
# 导出
print(cg.to_mermaid())body -> body [super]
body -> do_tag []
do_send -> randomize [randomize]
run_phase -> new [new]
run_phase -> body []
fork ['task_a', 'task_b'] join_any
Agent 用这个能力回答:“这个 sequence 的执行流程是什么?哪里有 randomize?”
以 tests/sv/uvm_full_test.sv 为例,使用真实 uvm_component_utils、uvm_config_db、$value$plusargs:
dd = DesignDriver(['uvm_full_test.sv'])
dd.build()
uvm = dd.uvm_tb
# 组件层级
for c in uvm.get_components():
print(f'{c["name"]:20s} type={c["uvm_type"]}')
# config_db 配置流
for f in uvm.get_config_flows():
print(f'{f["field"]:15s} {f["setter"]} -> {f["getter"]} = {f["value"]}')
# 端口连接
for conn in uvm.get_port_connections():
print(f'{conn["source"]} -> {conn["target"]}')=== 组件 ===
axi_driver type=uvm_driver
axi_monitor type=uvm_monitor
axi_scoreboard type=uvm_scoreboard
axi_agent type=uvm_agent
my_env type=uvm_env
=== 层级 ===
axi_agent -> [drv, mon, sqr]
my_env -> [agt, sb, cov]
=== config_db 配置流 ===
max_retries my_test -> my_driver = 3
sample_count my_test -> my_monitor = 100
=== plusargs ===
test: VERBOSE -> driver
value: RETRY=%d -> max_retries
=== Port Connections ===
drv.seq_item_port -> sqr.seq_item_export
agt.mon.ap -> sb.imp
agt.mon.ap -> cov.imp
分析 OpenTitan 的 UART IP 模块(40 个依赖文件,2290 节点):
from navisv import DesignDriver
import tempfile, os, glob
ot = os.path.expanduser('~/my_dv_proj/opentitan/hw/')
include_dirs = [
ot + 'ip/prim/rtl/',
ot + 'ip/prim_generic/rtl/',
ot + 'ip/tlul/rtl/',
ot + 'ip/uart/rtl/',
ot + 'top_earlgrey/rtl/',
ot + 'top_earlgrey/rtl/autogen/',
ot + 'vendor/lowrisc_ibex/rtl/',
ot + 'ip/prim_xilinx/rtl/',
]
# 自动收集依赖 (OpenTitan 依赖链 40+ 文件)
def collect_deps(top_file, include_dirs):
file_index = {}
for d in include_dirs:
if os.path.isdir(d):
for f in glob.glob(d + '*.sv'):
base = os.path.basename(f).replace('.sv', '')
file_index.setdefault(base, []).append(f)
files = [file_index['prim_flop_macros'][0], top_file]
import re
for _ in range(20):
with tempfile.TemporaryDirectory() as od:
dd = DesignDriver(files, output_dir=od, include_dirs=include_dirs)
dd.build()
errors = [d for d in dd._diagnostics if d['severity'] == 'error']
if not errors:
return files
missing = set()
for d in errors:
m = re.search(r"unknown (?:class or package|module|macro) '(\w+)'", d['message'])
if m: missing.add(m.group(1))
added = False
for name in missing:
if name in file_index:
p = file_index[name][0]
if p not in files:
files.insert(0, p)
added = True
if not added: break
return files
files = collect_deps(ot + 'ip/uart/rtl/uart.sv', include_dirs)
with tempfile.TemporaryDirectory() as od:
dd = DesignDriver(files, output_dir=od, include_dirs=include_dirs)
dd.build()
dg = dd.design_graph
print(f'节点: {len(dg.graph.nodes)}') # 2290
print(f'寄存器: {len(dg.get_registers())}') # 112
# 路径追踪: UART RX → FIFO
r = dg.trace_full_path('uart.cio_rx_i', 'uart.uart_core.rx_fifo_data')
print(f'status={r["status"]} path={len(r["path"])} 跳') # found, 15 跳完整示例见
examples/opentitan_example.py
从 RTL 信号出发,追踪到约束和覆盖组,检查约束空间是否被完整覆盖:
from navisv import DesignDriver
import tempfile
dd = DesignDriver(['examples/e2e_rtl_to_coverage.sv'])
dd.build()
dg = dd.design_graph
cg = dd.constraint_graph
ca = dd._covergroup_analyzer
# 1. 信号分析
loads = dg.get_loads('e2e_coverage_demo.data_in') # 6 个负载
r = dg.trace_path('e2e_coverage_demo.data_in', 'e2e_coverage_demo.data_out')
print(f'路径: {r["success"]} hops={len(r["path"])}') # True, 5 跳
# 2. 约束分析
for cls in cg.get_classes():
for c in cg.get_constraints_in_class(cls['name']):
print(f'{c["name"]}: {c["constraint_body"]}')
# c_data_range: data inside { 0:200 }
# c_mode3_limit: if (op_mode == 2'b11) { data < 100 }
# c_no_zero: data != 8'd0
# 3. 覆盖分析
for cp in ca.get_coverpoints_by_cg('cg_data'):
bins = ca.get_bins('cg_data', cp['name'])
for b in bins:
print(f'{b["name"]}: {b["values"]}')
# low: [1:50], mid: [51:100], high: [101:200], extreme: [201:255]
# 4. 交叉分析: 约束 [0:200] vs bins [0:200] → ✅ 完全覆盖完整示例见
examples/e2e_rtl_to_coverage.py和docs/e2e_workflow.md
仿真发现信号值异常,用 navisv 快速定位根因:
# 调试信号
python3 examples/debug_signal.py debug_demo.pipeline_datafrom navisv import DesignDriver
import tempfile
dd = DesignDriver(['examples/debug_scenario.sv'])
dd.build()
dg = dd.design_graph
signal = 'debug_demo.pipeline_data'
# 向后追踪: 谁影响它?
drivers = dg.get_drivers(signal)
# → [clk, en, mux_out, rst_n]
fanin = dg.get_fanin_cone(signal, depth=5)
# → {clk, data_a, data_b, en, rst_n, sel, mux_out}
# 向前追踪: 它影响谁?
loads = dg.get_loads(signal)
# → [flag, processed]
fanout = dg.get_fanout_cone(signal, depth=5)
# → {flag, processed, result}
# 路径追踪
r = dg.trace_path('debug_demo.data_a', signal)
# data_a → mux_out → pipeline_data (3 跳)
r = dg.trace_path(signal, 'debug_demo.result')
# pipeline_data → processed → result (4 跳)
# 条件分析
cov = dg.get_condition_coverage(signal)
# 条件: rst_n (复位), en (使能)完整示例见
examples/debug_signal.py和docs/debug_workflow.md
from navisv import DesignDriver
dd = DesignDriver(['design.sv'])
dd.build()
dg = dd.design_graph
# 路径追踪
result = dg.trace_full_path('top.src', 'top.dst')
print(result['path']) # 路径节点
print(result['summary']) # 置信度/时钟域/CDC
# 信号信息
info = dg.get_signal_info('top.clk')
print(info['drivers']) # 驱动源
print(info['conditions']) # 条件列表
# 寄存器列表
regs = dg.get_registers()cg = dd.constraint_graph
# Q1: 变量在哪些约束中
cons = cg.get_constraints_for_variable('pkg.Class.var', include_composition=True)
for c in cons:
print(f'{c["class_name"]}::{c["constraint_name"]}')
print(f' body: {c["constraint_body"]}')
print(f' expr: {c["direct_exprs"]}')
print(f' bit: {c["bit_range"]}')
print(f' cond: {c["is_conditional"]}')
# Q2: 约束影响哪些变量
vars = cg.get_variables_in_constraint('pkg.Class.constraint')
# Q3: 变量关系
rel = cg.get_constraint_relationship('pkg.Class.var_a', 'pkg.Class.var_b')
print(rel['shared_constraints'])cg = dd.covergroups
# 列出 covergroup
for info in cg.get_covergroups():
cps = cg.get_coverpoints(info['name'])
for cp in cps:
bins = cg.get_bins(info['name'], cp['name'])
print(f'{cp["name"]}: {[b["name"] for b in bins]}')
# bin-constraint 一致性
issues = cg.check_bin_constraint_consistency('pkg.Class.var', 'cg_name', 'cp_name')
for issue in issues:
print(f'{issue["type"]}: {issue["reason"]}')
# 质量评估
report = cg.check_coverage_quality('pkg.Class.var', 'cg_name', 'cp_name', signal_type='data')sva = dd.sva
# 列出所有 assertion
for a in sva.assertions:
print(f'{a.kind} clk={a.clock} expr={a.expression}')
if a.disable_condition:
print(f' disable iff ({a.disable_condition})')
# 列出 property/sequence 定义
for name, prop in sva.properties.items():
print(f'property {name}')
for name, seq in sva.sequences.items():
print(f'sequence {name}')cg = dd.call_graph
# 方法列表 (含继承)
methods = cg.get_methods('pkg.my_class')
# 调用关系
calls = cg.get_calls_from('pkg.my_class.body')
for c in calls:
print(f'{c["callee"]} super={c["is_super"]} randomize={c["is_randomize"]} new={c["is_constructor"]}')
# fork 块
forks = cg.get_forks('pkg.my_class')
for f in forks:
print(f'fork {f["join_type"]}: {[b["callee"] for b in f["branches"]]}')
# 导出
dot = cg.to_dot()
mermaid = cg.to_mermaid()navisv elk 用 elkjs (Eclipse Layout Kernel 的 JS 实现) 做交互式可视化。输出可以是:
- HTML (默认): 自包含 viewer, 带 bundled elkjs (~1.6MB), 离线可用, file:// 协议能开
- SVG: 静态 SVG, 可贴到文档里
- PNG: SVG → rsvg-convert → 1600px 宽
- JSON: ELK JSON dump (给高级用户调 elkjs 客户端)
# 默认: dataflow view, HTML viewer, 纯数据流 (过滤 CLOCK/RESET/FF-update 边)
navisv elk design.sv
# 输出 PNG (适合贴到 issue / 文档)
navisv elk design.sv -o out.png
# 关闭 FF-update 边过滤 (跟 Stage 2.7 行为一致, 显示全部边)
navisv elk design.sv --no-filter-clock-reset
# 控制流视图 (always_ff / case / if)
navisv elk design.sv --view controlflow
# 模块架构视图
navisv elk design.sv --view modules
# TOP 方向 (上下布局)
navisv elk design.sv -d DOWN -o out.svg打开生成的 .html 后:
- 搜索框: 输入子串 (case-insensitive), 命中节点橙色高亮
- 节点类型过滤器: Port / State / Operator / Literal 复选框, 取消勾选淡出
- CDC toggle 按钮: 开关跨时钟域边高亮 (off 时淡出, on 时红色加粗)
- 点击节点/边: 底部 info 面板显示完整属性 (kind, direction, file, line, timing 等)
$ navisv elk tests/fixtures/elk_counter.sv -o counter.png
✅ ELK 输出: counter.png
view: dataflow
filter_clock_reset: True
direction: RIGHT
children: 10
edges: 11
filtered_edges: 4
orphan_nodes_removed: 1效果:
输入端口 (clk, rst_n, enable) 垂直对齐在最左列, count State 在右, 中间是 operators (!, if, <=, +, merge, merge) 和 4'b0 literal。
| 视图 | CLI flag | elkjs 算法 | 数据来源 | 何时用 |
|---|---|---|---|---|
| dataflow | --view dataflow (默认) |
layered (RIGHT) |
GraphBuilder + operator/literal 节点 |
看信号怎么从 A 流到 B |
| controlflow | --view controlflow |
layered |
关闭 FF 边过滤 | 看 always_ff 触发链 |
| modules | --view modules |
layered |
复用 dataflow | 看模块层级 |
navisv elk <file.sv> [options]
位置参数:
file SV 设计文件
选项:
--view {dataflow,controlflow,modules} 默认: dataflow
--output/-o <path.{html,svg,png,json}> 默认: <basename>.elk.html
--filter-clock-reset/--no-filter-clock-reset 默认: 开 (纯数据流)
--max-nodes <int> 默认: 500
--scope <module.path> 子模块聚焦
--cdc-highlight 高亮跨时钟域
--direction {RIGHT,DOWN,LEFT,UP} 默认: RIGHT
--include/-I <dir> slang include 路径
--preserve-operators/--no-preserve-operators 默认: 开 (Stage 2.5)
from navisv.graph.graph_builder import GraphBuilder
from navisv.graph.elk_exporter import ElkExporter
from navisv.tools.elk_layout import run_layout_and_render
gb = GraphBuilder(ast, nl, ast_json_path='ast.json',
source_files=['design.sv'], preserve_operators=True)
gb.build()
exporter = ElkExporter(
view='dataflow',
filter_clock_reset=True,
cdc_highlight=True,
max_nodes=500,
).from_graph_builder(gb)
# 输出 HTML (1.6MB, 带 bundled elkjs)
exporter.export_html('/tmp/out.html')
# 输出 SVG/PNG (走 run_layout_and_render)
from navisv.tools.elk_layout import run_elk_layout
from navisv.tools.render_svg import render_svg
elk_json = exporter.to_elk_json()
positioned = run_elk_layout(elk_json, direction='RIGHT')
svg = render_svg(positioned, title='my design', subtitle='...')详见 docs/ELKJS_SPEC.md 和 docs/ELKJS_PLAN.md。
# 信号分析
navisv info design.sv top.clk # 信号完整信息
navisv trace design.sv src dst # 路径追踪
navisv fanout design.sv top.clk # fan-out 分析
navisv timing design.sv # 时序报告
# 约束分析
navisv constraints design.sv # 列出所有 class/constraint
navisv constraints design.sv -v # 显示约束体内容
navisv cvar design.sv pkg.Class.var # Q1: 变量在哪些约束中
navisv cvar -c design.sv pkg.Class.var # Q1: 含组合穿透
navisv ccons design.sv pkg.Class.constraint # Q2: 约束影响哪些变量
navisv crel design.sv pkg.Class.a pkg.Class.b # Q3: 变量关系
# CoverGroup 分析
navisv cg-list design.sv # 列出 covergroup/coverpoint/bins
navisv cg-list design.sv -v # 显示 bins 详情
navisv cg-check design.sv var cg cp # bin-constraint 一致性
navisv cg-quality design.sv var cg cp -t data # 质量评估
# 编译检查
navisv check design.sv # 语法检查
navisv check -F filelist.f # filelist 检查
# 交互式可视化 (elkjs)
navisv elk design.sv # HTML viewer (默认 dataflow)
navisv elk design.sv -o out.png # PNG (rsvg-convert)
navisv elk design.sv -o out.svg --direction DOWN # SVG, 上下布局
navisv elk design.sv --view controlflow # 控制流视图
navisv elk design.sv --view modules # 模块架构视图
navisv elk design.sv --no-filter-clock-reset # 显示全部边 (含 FF-update)
navisv elk design.sv --cdc-highlight # 高亮跨时钟域
# 通用
navisv --json <command> # JSON 输出User / AI Agent
↓
DesignDriver # 统一入口
↓
┌─────────────────────────────────────────┐
│ Parsers │
│ ├── ast_parser.py (信号/条件) │
│ ├── netlist_parser.py (网表) │
│ ├── constraint_parser.py (约束) │
│ └── covergroup_parser.py (覆盖) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Graph Layer │
│ ├── design_graph.py (信号/路径) │
│ ├── constraint_graph.py (约束查询) │
│ └── covergroup_analyzer.py (覆盖分析)│
└─────────────────────────────────────────┘
↓
slang / slang-netlist
DesignGraph: NetworkX MultiDiGraph
- 节点: Port / State / Net
- 边: drives / controls +
true_condition+edge_kind+condition
ConstraintGraph: NetworkX MultiDiGraph
- 节点: Class / Variable / Constraint
- 边: has_var / has_constraint / binds / inherits / member_of
| 测试集 | 数量 | 状态 |
|---|---|---|
| ConstraintGraph | 43 | ✅ |
| CoverGroup 解析 | 33 | ✅ |
| bin-constraint 一致性 | 12 | ✅ |
| coverage 质量评估 | 9 | ✅ |
| true_condition | 9 | ✅ |
| SVA Parser | 17 | ✅ |
| SVA Generator | 6 | ✅ |
| CallGraph | 17 | ✅ |
| DesignGraph (UART) | 10/10 | ✅ |
| DesignGraph (benchmark) | 75/79 | ✅ (94%) |
| UVM config_db + plusargs | 17 | ✅ |
| UVM TB 静态结构 | 26 | ✅ |
| 总计 | 212 | ✅ |
| 测试集 | 通过率 | 说明 |
|---|---|---|
| UART (10 路径) | 10/10 (100%) | 含 FSM、组合逻辑、拼接表达式 |
| sv-trace benchmarks (79 路径) | 75/79 (94%) | 剩余 4 个是 pipeline 跨域路径 |
navisv/
├── cli.py # CLI 入口
├── README.md # 本文件
├── FEATURE_PLAN.md # 功能规划
├── docs/
│ └── covergroup_design.md # CoverGroup 设计方案
├── navisv/
│ ├── config.py # 配置
│ ├── drivers/
│ │ └── design_driver.py # 统一入口
│ ├── parsers/
│ │ ├── ast_parser.py # AST 解析
│ │ ├── netlist_parser.py # 网表解析
│ │ ├── constraint_parser.py # 约束解析
│ │ └── covergroup_parser.py # 覆盖解析
│ └── graph/
│ ├── design_graph.py # 信号/路径查询
│ ├── constraint_graph.py # 约束查询
│ ├── covergroup_analyzer.py # 覆盖分析
│ └── graph_builder.py # 图构建 + true_condition
└── tests/
├── sv/ # 测试用 SV 文件
├── test_constraint_graph.py # 43 测试
├── test_covergroup.py # 33 测试
├── test_cg_constraint_check.py # 12 测试
├── test_cg_quality.py # 9 测试
└── test_true_condition.py # 9 测试
- Python 3.9+
- networkx
- slang (编译好的二进制)
- slang-netlist (编译好的二进制)
本项目采用 MIT 许可证 - 详见 LICENSE 文件。
MIT License
Copyright (c) 2026 方浩博 (Fang Haobo)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
