# # 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()