blob: 4d474938aceffcb47fe160f2e1677e3e1e6c49ef [file] [edit]
#!/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)