Merge pull request #31 from antmicro/generating-caravel-diagram

Generating caravel diagram
diff --git a/scripts/create-caravel-diagram.py b/scripts/create-caravel-diagram.py
new file mode 100644
index 0000000..18af64d
--- /dev/null
+++ b/scripts/create-caravel-diagram.py
@@ -0,0 +1,112 @@
+import sys
+import os
+import subprocess
+from pathlib import Path
+import argparse
+from tempfile import mkstemp
+import re
+
+
+def remove_inouts(jsonpath, replacewith='input'):
+    """Replaces inouts with either input or output statements.
+
+    Netlistsvg does not parse inout ports as for now, so they need to be
+    replaced with either input or output to produce a diagram.
+
+    Parameters
+    ----------
+    jsonpath : str
+        Path to JSON file to fix
+    replacewith : str
+        The string to replace 'inout', can be 'input' or 'output'
+    """
+    assert replacewith in ['input', 'output']
+    with open(jsonpath, 'r') as withinouts:
+        lines = withinouts.readlines()
+    with open(jsonpath, 'w') as withoutinouts:
+        for line in lines:
+            withoutinouts.write(re.sub('inout', replacewith, line))
+
+
+def main(argv):
+    parser = argparse.ArgumentParser(argv[0])
+    parser.add_argument(
+        'verilog_rtl_dir',
+        help="Path to the project's verilog/rtl directory",
+        type=Path)
+    parser.add_argument(
+        'output',
+        help="Path to the output SVG file",
+        type=Path)
+    parser.add_argument(
+        '--num-iopads',
+        help='Number of iopads to render',
+        type=int,
+        default=38)
+    parser.add_argument(
+        '--yosys-executable',
+        help='Path to yosys executable',
+        type=Path,
+        default='yosys')
+    parser.add_argument(
+        '--netlistsvg-executable',
+        help='Path to netlistsvg executable',
+        type=Path,
+        default='netlistsvg')
+    parser.add_argument(
+        '--inouts-as',
+        help='To what kind of IO should inout ports be replaced',
+        choices=['input', 'output'],
+        default='input'
+    )
+
+    args = parser.parse_args(argv[1:])
+
+    fd, jsonpath = mkstemp(suffix='-yosys.json')
+    os.close(fd)
+
+    yosyscommand = [
+        f'{str(args.yosys_executable)}',
+        '-p',
+        'read_verilog pads.v defines.v; ' +
+        'read_verilog -lib -overwrite *.v; ' +
+        f'verilog_defines -DMPRJ_IO_PADS={args.num_iopads}; ' +
+        'read_verilog -overwrite caravel.v; ' +
+        'hierarchy -top caravel; ' +
+        'proc; ' +
+        'opt; ' +
+        f'write_json {jsonpath}; '
+    ]
+
+    result = subprocess.run(
+        yosyscommand,
+        cwd=args.verilog_rtl_dir,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.STDOUT
+    )
+
+    exitcode = 0
+    if result.returncode != 0:
+        print(f'Failed to run: {" ".join(yosyscommand)}', file=sys.stderr)
+        print(result.stdout.decode())
+        exitcode = result.returncode
+    else:
+        # TODO once netlistsvg supports inout ports, this should be removed
+        remove_inouts(jsonpath, args.inouts_as)
+        command = f'{args.netlistsvg_executable} {jsonpath} -o {args.output}'
+        result = subprocess.run(
+            command.split(),
+            stdout=subprocess.PIPE,
+            stderr=subprocess.STDOUT
+        )
+        if result.returncode != 0:
+            print(f'Failed to run: {command}', file=sys.stderr)
+            print(result.stdout.decode())
+            exitcode = result.returncode
+
+    os.unlink(jsonpath)
+    sys.exit(exitcode)
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))