File: vl_hier_graph

package info (click to toggle)
verilator 5.038-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 162,552 kB
  • sloc: cpp: 139,204; python: 20,931; ansic: 10,222; yacc: 6,000; lex: 1,925; makefile: 1,260; sh: 494; perl: 282; fortran: 22
file content (153 lines) | stat: -rwxr-xr-x 6,024 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python3
# -*- Python -*- See copyright, etc below
# pylint: disable=C0103,C0114,C0115,C0115,C0116,C0209,R0914
######################################################################

import argparse
import json
import os
import subprocess
from tempfile import NamedTemporaryFile
# from pprint import pprint, pformat

#######################################################################


class VlHierGraph:

    def __init__(
            self,
            verilator_args,  # presently all verilator options are passed-thru
            debug=0,
            output_filename='graph.dot'):  # output filename
        self.debug = debug
        self.next_vertex_number = 0
        self.addr_to_number = {}

        with NamedTemporaryFile() as tree_temp, NamedTemporaryFile() as meta_temp:
            vargs = [
                '--json-only-output',
                tree_temp.name,
                '--json-only-meta-output',
                meta_temp.name,
                '--bbox-sys',  # Parse some stuff can't translate
                '--bbox-unsup',
                '--prefix vljson'
            ]
            vargs += verilator_args
            self.run_verilator(vargs)
            self.tree = json.load(tree_temp)
            self.meta = json.load(meta_temp)

        with open(output_filename, "w", encoding="utf8") as fh:
            # For more serious purposes, use the python graphviz package instead
            fh.write("digraph {\n")
            fh.write("  dpi=300;\n")
            fh.write("  order=LR;\n")
            fh.write("  node [fontsize=8 shape=\"box\" margin=0.01 width=0 height=0]")
            fh.write("  edge [fontsize=6]")
            # Find cells
            modules = self.flatten(self.tree, lambda n: n['type'] == "MODULE")
            top_module = True
            for mod in modules:
                # origNames are before parameterization, name if after
                mod_number = self.addr_to_vertex_number(mod['addr'])
                fh.write("  n%d [label=\"%s\"" % (mod_number, mod['name']))
                if top_module:
                    fh.write(" color=\"red\" rank=1")
                    top_module = False
                fh.write("];\n")

                instances = self.flatten(mod, lambda n: n['type'] == "CELL")
                for inst in instances:
                    def_number = self.addr_to_vertex_number(inst['modp'])
                    fh.write("  n%d->n%d [label=\"%s\"];\n" %
                             (mod_number, def_number, inst['name']))

            fh.write("}\n")

    def flatten(self, node, accept_cb=lambda n: True):
        """Flatten tree to list using DFS.
        accept_cb(node) should return True for nodes you want to save"""
        arr = []
        self.dfs(node, lambda n: accept_cb(n) and arr.append(n))
        return arr

    def dfs(self, node, node_cb):
        """Traverse given tree using DFS and apply node_cb(node) on each one"""
        node_cb(node)
        for _, v in node.items():
            if isinstance(v, list):  # childlist
                for child in v:
                    self.dfs(child, node_cb)

    def addr_to_vertex_number(self, name):
        if name not in self.addr_to_number:
            self.next_vertex_number += 1
            self.addr_to_number[name] = self.next_vertex_number
        return self.addr_to_number[name]

    def run_verilator(self, vargs):
        """Run Verilator command, check errors"""
        if os.getenv("VERILATOR_ROOT"):
            command = os.getenv("VERILATOR_ROOT") + "/bin/verilator"
        else:
            command = "verilator"
        command += ' ' + ' '.join(vargs)
        if self.debug:
            print("\t%s " % command)
        status = subprocess.call(command, shell=True)
        if status != 0:
            raise RuntimeError("Command failed running Verilator with '" + command + "', stopped")


#######################################################################

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        allow_abbrev=False,
        formatter_class=argparse.RawTextHelpFormatter,
        description="""Example of using Verilator JSON output to create a .dot file showing the
design module hierarchy.

Example usage:
     vl_hier_graph -f input.vc top.v -o graph.dot
     dot -Tpdf -o graph.pdf graph.dot
""",
        epilog="""All other arguments are pass-thru to Verilator: e.g.:

  +define+<var>=<value>      Set preprocessor define
  -F <file>                  Parse options from a file, relatively
  -f <file>                  Parse options from a file
  -G<name>=<value>           Overwrite toplevel parameter
  +incdir+<dir>              Directory to search for includes
  +libext+<ext>+[ext]...     Extensions for finding modules
  -v <filename>              Verilog library
  -y <dir>                   Directory to search for modules

This file ONLY is placed under the Creative Commons Public Domain, for
any use, without warranty, 2019 by Wilson Snyder.
SPDX-License-Identifier: CC0-1.0
""")
    parser.add_argument('-debug', '--debug', action='store_const', const=9, help='enable debug')
    parser.add_argument('-o',
                        '--o',
                        action='store',
                        metavar='filename',
                        required=True,
                        help='output filename')
    (args, rem) = parser.parse_known_args()

    print("NOTE: vl_hier_graph is only an example starting point for writing your own tool.")
    # That is:
    # 1. We will accept basic patches
    # 2. We are not expecting to make this globally useful. (e.g. we don't cleanup obj_dir)
    # 3. "make install" will not install this.
    # 4. This has not had production-worthy validation.

    fc = VlHierGraph(output_filename=args.o, debug=args.debug, verilator_args=rem)

######################################################################
# Local Variables:
# compile-command: "./vl_hier_graph -h ; VERILATOR_ROOT=$V4 ./vl_hier_graph +define+thru top.v"
# End: