| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright 2020-2022 F4PGA Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import argparse |
| import os |
| import shlex |
| import re |
| |
| from v2x import vlog_to_model |
| from v2x import vlog_to_pbtype |
| from v2x.xmlinc import xmlinc |
| |
| from vtr_xml_utils.convert import vtr_stylize_xml |
| |
| # ============================================================================= |
| |
| |
| MODULE_RE = re.compile(r"^module\s+(?P<module>[\w$]+)") |
| |
| |
| def make_xml_for_docs(vlog_src, output_dir): |
| """ |
| Recursively runs V2X (model and pb_type) on the given Verilog file and |
| all its includes. Output XMLs are stylized using vtr-xml-utils. |
| |
| The Verilog file is copied to the output_dir. All include paths are |
| updated so that they reflect the module hierarchy. It is assumed that |
| a Verilog file is named <module>.sim.v. Next V2X is run followed by XML |
| stylization using vtr-xml-utils. |
| |
| The process continues recursively (depth-first). |
| """ |
| |
| # Make absolute paths |
| vlog_src = os.path.abspath(vlog_src) |
| output_dir = os.path.abspath(output_dir) |
| |
| # Make the output directory |
| os.makedirs(output_dir, exist_ok=True) |
| |
| # Copy the verilog file, rearrange includes |
| vlog_title = os.path.basename(vlog_src) |
| vlog_dir = os.path.dirname(vlog_src) |
| vlog_dst = os.path.join(output_dir, vlog_title) |
| |
| # Scan it for dependencies (includes), rewrite them |
| # Try also to guess the top-level module name (assuming that there is only |
| # one module per file!). |
| vlog_deps = [] |
| vlog_top = None |
| |
| with open(vlog_src, "r") as fsrc, open(vlog_dst, "w") as fdst: |
| for src_line in fsrc: |
| line = src_line.strip() |
| |
| # Modify included file path |
| if line.startswith("`include"): |
| fields = shlex.split(line) |
| assert len(fields) == 2, fields |
| |
| dep_src = fields[1] |
| dep_mod = os.path.basename(dep_src).split(".")[0] |
| dep_dst = "./{f}/{f}.sim.v".format(f=dep_mod) |
| |
| vlog_deps.append((dep_src, dep_dst,)) |
| |
| dst_line = "`include \"{}\"\n".format(dep_dst) |
| |
| # Pass unchanges |
| else: |
| dst_line = src_line |
| |
| # Detect top-level module |
| # FIXME: This is crude and assumes the syntax "module <name>...". |
| # Both the keyword and module name must be in the same line |
| match = MODULE_RE.match(line) |
| if vlog_top is None and match is not None: |
| vlog_top = match.group("module") |
| |
| # Write the line |
| fdst.write(dst_line) |
| |
| # Process dependencies recursively first |
| for dep_src, dep_dst in vlog_deps: |
| dep_file = os.path.join(vlog_dir, dep_src) |
| dep_dir = os.path.join(output_dir, os.path.dirname(dep_dst)) |
| |
| make_xml_for_docs(dep_file, dep_dir) |
| |
| # Run V2X, but only if an XML is not already provided |
| vlog_mod = vlog_title.split(".")[0] |
| |
| model_file = os.path.join(output_dir, vlog_mod + ".model.xml") |
| pbtype_file = os.path.join(output_dir, vlog_mod + ".pb_type.xml") |
| |
| if not os.path.isfile(model_file): |
| xml = vlog_to_model.vlog_to_model([vlog_dst], None, vlog_top, model_file) |
| with open(model_file, "w") as fp: |
| fp.write(xml) |
| |
| xml = vtr_stylize_xml(model_file) |
| with open(model_file, "w") as fp: |
| fp.write(xml) |
| |
| if not os.path.isfile(pbtype_file): |
| xml = vlog_to_pbtype.vlog_to_pbtype([vlog_dst], pbtype_file, vlog_top) |
| with open(pbtype_file, "w") as fp: |
| fp.write(xml) |
| |
| xml = vtr_stylize_xml(pbtype_file) |
| with open(pbtype_file, "w") as fp: |
| fp.write(xml) |
| |
| |
| # ============================================================================= |
| |
| |
| if __name__ == "__main__": |
| |
| parser = argparse.ArgumentParser() |
| |
| parser.add_argument( |
| "verilog", |
| help="Input Verilog file" |
| ) |
| parser.add_argument( |
| "path", |
| help="Output path" |
| ) |
| |
| args = parser.parse_args() |
| make_xml_for_docs(args.verilog, args.path) |