_yosys: translate Yosys warnings to Python warnings.
authorwhitequark <whitequark@whitequark.org>
Thu, 11 Jun 2020 16:12:52 +0000 (16:12 +0000)
committerwhitequark <whitequark@whitequark.org>
Thu, 11 Jun 2020 16:12:52 +0000 (16:12 +0000)
This isn't used yet (the only Yosys warning we can get is useless),
but will be handy for CXXRTL.

nmigen/_yosys.py
nmigen/back/verilog.py

index 913d5cc8303d38a0f36c6adfc7bcef1d8623c11f..dcade81a7a3fc941a10ee00841fa3d7315ef3b5d 100644 (file)
@@ -2,6 +2,7 @@ import os
 import sys
 import re
 import subprocess
+import warnings
 try:
     from importlib import metadata as importlib_metadata # py3.8+ stdlib
 except ImportError:
@@ -20,6 +21,10 @@ class YosysError(Exception):
     pass
 
 
+class YosysWarning(Warning):
+    pass
+
+
 class YosysBinary:
     @classmethod
     def available(cls):
@@ -72,6 +77,16 @@ class YosysBinary:
         """
         raise NotImplementedError
 
+    @classmethod
+    def _process_result(cls, returncode, stdout, stderr, ignore_warnings, src_loc_at):
+        if returncode:
+            raise YosysError(stderr.strip())
+        if not ignore_warnings:
+            for match in re.finditer(r"(?ms:^Warning: (.+)\n$)", stderr):
+                message = match.group(1).replace("\n", " ")
+                warnings.warn(message, YosysWarning, stacklevel=3 + src_loc_at)
+        return stdout
+
 
 class _BuiltinYosys(YosysBinary):
     YOSYS_PACKAGE = "nmigen_yosys"
@@ -93,15 +108,12 @@ class _BuiltinYosys(YosysBinary):
         return (int(match[1]), int(match[2]), int(match[3] or 0))
 
     @classmethod
-    def run(cls, args, stdin=""):
+    def run(cls, args, stdin="", *, ignore_warnings=False, src_loc_at=0):
         popen = subprocess.Popen([sys.executable, "-m", cls.YOSYS_PACKAGE, *args],
             stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
             encoding="utf-8")
         stdout, stderr = popen.communicate(stdin)
-        if popen.returncode:
-            raise YosysError(stderr.strip())
-        else:
-            return stdout
+        return cls._process_result(popen.returncode, stdout, stderr, ignore_warnings, src_loc_at)
 
 
 class _SystemYosys(YosysBinary):
@@ -118,7 +130,7 @@ class _SystemYosys(YosysBinary):
         return (int(match[1]), int(match[2]), int(match[3] or 0))
 
     @classmethod
-    def run(cls, args, stdin=""):
+    def run(cls, args, stdin="", *, ignore_warnings=False, src_loc_at=0):
         popen = subprocess.Popen([require_tool(cls.YOSYS_BINARY), *args],
             stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
             encoding="utf-8")
@@ -129,10 +141,7 @@ class _SystemYosys(YosysBinary):
         #
         # This is not ideal, but Verific license conditions rule out any other solution.
         stdout = re.sub(r"\A(-- .+\n|\n)*", "", stdout)
-        if popen.returncode:
-            raise YosysError(stderr.strip())
-        else:
-            return stdout
+        return cls._process_result(popen.returncode, stdout, stderr, ignore_warnings, src_loc_at)
 
 
 def find_yosys(requirement):
index 19a91e41dd9fb35883c56b634c4fc91c1343b29b..cb684ab57e666ae4decf7fb654abed3be49d5aaa 100644 (file)
@@ -38,7 +38,11 @@ write_verilog -norename {write_verilog_opts}
         prune="# " if yosys_version < (0, 9, 231) else "",
         attr_map=" ".join(attr_map),
         write_verilog_opts=" ".join(write_verilog_opts),
-    ))
+    ),
+    # At the moment, Yosys always shows a warning indicating that not all processes can be
+    # translated to Verilog. We carefully emit only the processes that *can* be translated, and
+    # squash this warning. Once Yosys' write_verilog pass is fixed, we should remove this.
+    ignore_warnings=True)
 
 
 def convert_fragment(*args, strip_internal_attrs=False, **kwargs):