build.run: implement SSH remote builds using Paramiko.
[nmigen.git] / nmigen / test / utils.py
1 import os
2 import re
3 import shutil
4 import subprocess
5 import textwrap
6 import traceback
7 import unittest
8 import warnings
9 from contextlib import contextmanager
10
11 from ..hdl.ast import *
12 from ..hdl.ir import *
13 from ..back import rtlil
14 from .._toolchain import require_tool
15
16
17 __all__ = ["FHDLTestCase"]
18
19
20 class FHDLTestCase(unittest.TestCase):
21 def assertRepr(self, obj, repr_str):
22 if isinstance(obj, list):
23 obj = Statement.cast(obj)
24 def prepare_repr(repr_str):
25 repr_str = re.sub(r"\s+", " ", repr_str)
26 repr_str = re.sub(r"\( (?=\()", "(", repr_str)
27 repr_str = re.sub(r"\) (?=\))", ")", repr_str)
28 return repr_str.strip()
29 self.assertEqual(prepare_repr(repr(obj)), prepare_repr(repr_str))
30
31 def assertFormal(self, spec, mode="bmc", depth=1):
32 caller, *_ = traceback.extract_stack(limit=2)
33 spec_root, _ = os.path.splitext(caller.filename)
34 spec_dir = os.path.dirname(spec_root)
35 spec_name = "{}_{}".format(
36 os.path.basename(spec_root).replace("test_", "spec_"),
37 caller.name.replace("test_", "")
38 )
39
40 # The sby -f switch seems not fully functional when sby is reading from stdin.
41 if os.path.exists(os.path.join(spec_dir, spec_name)):
42 shutil.rmtree(os.path.join(spec_dir, spec_name))
43
44 if mode == "hybrid":
45 # A mix of BMC and k-induction, as per personal communication with Clifford Wolf.
46 script = "setattr -unset init w:* a:nmigen.sample_reg %d"
47 mode = "bmc"
48 else:
49 script = ""
50
51 config = textwrap.dedent("""\
52 [options]
53 mode {mode}
54 depth {depth}
55 wait on
56
57 [engines]
58 smtbmc
59
60 [script]
61 read_ilang top.il
62 prep
63 {script}
64
65 [file top.il]
66 {rtlil}
67 """).format(
68 mode=mode,
69 depth=depth,
70 script=script,
71 rtlil=rtlil.convert(Fragment.get(spec, platform="formal"))
72 )
73 with subprocess.Popen([require_tool("sby"), "-f", "-d", spec_name], cwd=spec_dir,
74 universal_newlines=True,
75 stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc:
76 stdout, stderr = proc.communicate(config)
77 if proc.returncode != 0:
78 self.fail("Formal verification failed:\n" + stdout)