feat: 集成 waveform_trace 波形调试工具
新增功能: - waveformTracer.ts: 调用 waveform_trace.exe 的工具实现 - toolExecutor.ts: 添加 waveform_trace 工具分发 - types/api.ts: 添加 WaveformTraceArgs 类型定义 工具源码 (tools/waveform_trace/src/): - AST 解析 + BFS 信号追踪 - VCD 波形解析 - 修复通用 testbench 支持 配置文件: - .gitignore: 排除 exe 和打包产物 - .vscodeignore: 发布时排除源码 - build.bat/build.sh: 打包脚本
This commit is contained in:
144
tools/waveform_trace/src/graph_builder.py
Normal file
144
tools/waveform_trace/src/graph_builder.py
Normal file
@ -0,0 +1,144 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Author : Chia-Tung (Mark) Ho, NVIDIA
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import os
|
||||
from optparse import OptionParser
|
||||
|
||||
# 优先使用本地修改过的 pyverilog(包含 toplogic_tree_traverse 方法)
|
||||
_local_path = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, _local_path)
|
||||
|
||||
from pyverilog.vparser.parser import parse
|
||||
from io import StringIO
|
||||
import networkx as nx
|
||||
# importing matplotlib.pyplot
|
||||
import matplotlib.pyplot as plt
|
||||
import re
|
||||
|
||||
# create graph from ast str
|
||||
# directed graph from networkX
|
||||
def create_graph_from_ast(ast, display=False, display_signal_only=False):
|
||||
graph = nx.DiGraph()
|
||||
ast.toplogic_tree_traverse(network_G=graph, rvalue=False, lvalue=False)
|
||||
if not display and not display_signal_only:
|
||||
return graph
|
||||
# Print out nodes with attributes
|
||||
nodes_to_display = []
|
||||
edges_to_display = []
|
||||
print("Nodes:")
|
||||
for node, attrs in graph.nodes(data=True):
|
||||
if display_signal_only and (not re.match("^Assign", node) and not re.match("^Always", node) and not re.match("^Module", node)):
|
||||
nodes_to_display.append(node)
|
||||
print(f"Node {node}: {attrs}")
|
||||
|
||||
# Print out edges with attributes
|
||||
print("\nEdges:")
|
||||
for src, dst, attrs in graph.edges(data=True):
|
||||
if display_signal_only and src in nodes_to_display and dst in nodes_to_display:
|
||||
edges_to_display.append((src, dst))
|
||||
print(f"Edge {src} to {dst}: {attrs}")
|
||||
|
||||
# displaying graphs
|
||||
plt.figure(figsize=(18, 16)) # Set the figure size
|
||||
pos = nx.spring_layout(graph, k=1.0)
|
||||
if display_signal_only:
|
||||
subgraph = graph.subgraph(nodes_to_display)
|
||||
# subgraph.add_edges_from(edges_to_display)
|
||||
else:
|
||||
subgraph = graph
|
||||
|
||||
nx.draw_networkx(subgraph, pos, with_labels=True) # Draw the graph without labels
|
||||
|
||||
# Add node labels
|
||||
# node_labels = nx.get_node_attributes(graph, 'label')
|
||||
# nx.draw_networkx_labels(graph, pos, labels=node_labels)
|
||||
|
||||
# edge labels
|
||||
edge_labels = nx.get_edge_attributes(subgraph, 'lines')
|
||||
nx.draw_networkx_edge_labels(
|
||||
subgraph, pos,
|
||||
edge_labels=edge_labels,
|
||||
font_color='blue'
|
||||
)
|
||||
# plt.axis('off')
|
||||
plt.show()
|
||||
return graph
|
||||
|
||||
def get_ast_structure_str(ast):
|
||||
normal_stdout = sys.stdout
|
||||
# put the string output to a string buffer
|
||||
result = StringIO()
|
||||
sys.stdout = result
|
||||
|
||||
# traverse the ast
|
||||
ast.show(buf=sys.stdout)
|
||||
|
||||
# Redirect std output to the normal mode
|
||||
sys.stdout = normal_stdout
|
||||
|
||||
# Get the result out
|
||||
ast_str = result.getvalue()
|
||||
# print('ast str = ', ast_str, '\n ast end')
|
||||
return ast_str
|
||||
|
||||
def generate_top_logic_graph(filelist: list[str]):
|
||||
for f in filelist:
|
||||
if not os.path.exists(f):
|
||||
raise IOError("file not found: " + f)
|
||||
|
||||
ast, directives = parse(filelist,
|
||||
preprocess_include=[],
|
||||
preprocess_define=[])
|
||||
|
||||
# ast_str = get_ast_structure_str(ast)
|
||||
return create_graph_from_ast(ast, display=False, display_signal_only=False)
|
||||
|
||||
def main():
|
||||
INFO = "Verilog code parser"
|
||||
VERSION = pyverilog.__version__
|
||||
USAGE = "Usage: python example_parser.py file ..."
|
||||
|
||||
def showVersion():
|
||||
print(INFO)
|
||||
print(VERSION)
|
||||
print(USAGE)
|
||||
sys.exit()
|
||||
|
||||
optparser = OptionParser()
|
||||
optparser.add_option("-v", "--version", action="store_true", dest="showversion",
|
||||
default=False, help="Show the version")
|
||||
optparser.add_option("-I", "--include", dest="include", action="append",
|
||||
default=[], help="Include path")
|
||||
optparser.add_option("-D", dest="define", action="append",
|
||||
default=[], help="Macro Definition")
|
||||
(options, args) = optparser.parse_args()
|
||||
|
||||
filelist = args
|
||||
# print(filelist)
|
||||
if options.showversion:
|
||||
showVersion()
|
||||
|
||||
for f in filelist:
|
||||
if not os.path.exists(f):
|
||||
raise IOError("file not found: " + f)
|
||||
|
||||
if len(filelist) == 0:
|
||||
showVersion()
|
||||
|
||||
ast, directives = parse(filelist,
|
||||
preprocess_include=options.include,
|
||||
preprocess_define=options.define)
|
||||
|
||||
# ast_str = get_ast_structure_str(ast)
|
||||
create_graph_from_ast(ast, display_signal_only=True, display=True)
|
||||
ast.show(attrnames=True)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user