新增功能: - 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: 打包脚本
145 lines
4.4 KiB
Python
145 lines
4.4 KiB
Python
#
|
||
# 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()
|