From: whitequark Date: Sun, 13 Oct 2019 18:53:38 +0000 (+0000) Subject: {,_}tools→{,_}utils X-Git-Tag: v0.1rc1~5 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=2f9dab361f5aa255f4e5c372dc3ac2da9916aaf5;p=nmigen.git {,_}tools→{,_}utils In context of nMigen, "tools" means "parts of toolchain", so it is confusing to have a completely unrelated module also called "tools". --- diff --git a/nmigen/_tools.py b/nmigen/_tools.py deleted file mode 100644 index a62153d..0000000 --- a/nmigen/_tools.py +++ /dev/null @@ -1,84 +0,0 @@ -import contextlib -import functools -import warnings -from collections import OrderedDict -from collections.abc import Iterable -from contextlib import contextmanager - -from .tools import * - - -__all__ = ["flatten", "union" , "log2_int", "bits_for", "memoize", "final", "deprecated"] - - -def flatten(i): - for e in i: - if isinstance(e, Iterable): - yield from flatten(e) - else: - yield e - - -def union(i, start=None): - r = start - for e in i: - if r is None: - r = e - else: - r |= e - return r - - -def memoize(f): - memo = OrderedDict() - @functools.wraps(f) - def g(*args): - if args not in memo: - memo[args] = f(*args) - return memo[args] - return g - - -def final(cls): - def init_subclass(): - raise TypeError("Subclassing {}.{} is not supported" - .format(cls.__module__, cls.__name__)) - cls.__init_subclass__ = init_subclass - return cls - - -def deprecated(message, stacklevel=2): - def decorator(f): - @functools.wraps(f) - def wrapper(*args, **kwargs): - warnings.warn(message, DeprecationWarning, stacklevel=stacklevel) - return f(*args, **kwargs) - return wrapper - return decorator - - -def _ignore_deprecated(f=None): - if f is None: - @contextlib.contextmanager - def context_like(): - with warnings.catch_warnings(): - warnings.filterwarnings(action="ignore", category=DeprecationWarning) - yield - return context_like() - else: - @functools.wraps(f) - def decorator_like(*args, **kwargs): - with warnings.catch_warnings(): - warnings.filterwarnings(action="ignore", category=DeprecationWarning) - f(*args, **kwargs) - return decorator_like - - -def extend(cls): - def decorator(f): - if isinstance(f, property): - name = f.fget.__name__ - else: - name = f.__name__ - setattr(cls, name, f) - return decorator diff --git a/nmigen/_utils.py b/nmigen/_utils.py new file mode 100644 index 0000000..85c8cdb --- /dev/null +++ b/nmigen/_utils.py @@ -0,0 +1,84 @@ +import contextlib +import functools +import warnings +from collections import OrderedDict +from collections.abc import Iterable +from contextlib import contextmanager + +from .utils import * + + +__all__ = ["flatten", "union" , "log2_int", "bits_for", "memoize", "final", "deprecated"] + + +def flatten(i): + for e in i: + if isinstance(e, Iterable): + yield from flatten(e) + else: + yield e + + +def union(i, start=None): + r = start + for e in i: + if r is None: + r = e + else: + r |= e + return r + + +def memoize(f): + memo = OrderedDict() + @functools.wraps(f) + def g(*args): + if args not in memo: + memo[args] = f(*args) + return memo[args] + return g + + +def final(cls): + def init_subclass(): + raise TypeError("Subclassing {}.{} is not supported" + .format(cls.__module__, cls.__name__)) + cls.__init_subclass__ = init_subclass + return cls + + +def deprecated(message, stacklevel=2): + def decorator(f): + @functools.wraps(f) + def wrapper(*args, **kwargs): + warnings.warn(message, DeprecationWarning, stacklevel=stacklevel) + return f(*args, **kwargs) + return wrapper + return decorator + + +def _ignore_deprecated(f=None): + if f is None: + @contextlib.contextmanager + def context_like(): + with warnings.catch_warnings(): + warnings.filterwarnings(action="ignore", category=DeprecationWarning) + yield + return context_like() + else: + @functools.wraps(f) + def decorator_like(*args, **kwargs): + with warnings.catch_warnings(): + warnings.filterwarnings(action="ignore", category=DeprecationWarning) + f(*args, **kwargs) + return decorator_like + + +def extend(cls): + def decorator(f): + if isinstance(f, property): + name = f.fget.__name__ + else: + name = f.__name__ + setattr(cls, name, f) + return decorator diff --git a/nmigen/back/pysim.py b/nmigen/back/pysim.py index 1ef8132..dc51e7c 100644 --- a/nmigen/back/pysim.py +++ b/nmigen/back/pysim.py @@ -6,7 +6,7 @@ from bitarray import bitarray from vcd import VCDWriter from vcd.gtkw import GTKWSave -from .._tools import flatten +from .._utils import flatten from ..hdl.ast import * from ..hdl.ir import * from ..hdl.xfrm import ValueVisitor, StatementVisitor diff --git a/nmigen/back/rtlil.py b/nmigen/back/rtlil.py index 2dfed0e..5a847c5 100644 --- a/nmigen/back/rtlil.py +++ b/nmigen/back/rtlil.py @@ -3,7 +3,7 @@ import textwrap from collections import defaultdict, OrderedDict from contextlib import contextmanager -from .._tools import bits_for, flatten +from .._utils import bits_for, flatten from ..hdl import ast, rec, ir, mem, xfrm diff --git a/nmigen/compat/fhdl/bitcontainer.py b/nmigen/compat/fhdl/bitcontainer.py index b52887d..648b129 100644 --- a/nmigen/compat/fhdl/bitcontainer.py +++ b/nmigen/compat/fhdl/bitcontainer.py @@ -1,19 +1,19 @@ -from ... import tools +from ... import utils from ...hdl import ast -from ..._tools import deprecated +from ..._utils import deprecated __all__ = ["log2_int", "bits_for", "value_bits_sign"] -@deprecated("instead of `log2_int`, use `nmigen.tools.log2_int`") +@deprecated("instead of `log2_int`, use `nmigen.utils.log2_int`") def log2_int(n, need_pow2=True): - return tools.log2_int(n, need_pow2) + return utils.log2_int(n, need_pow2) -@deprecated("instead of `bits_for`, use `nmigen.tools.bits_for`") +@deprecated("instead of `bits_for`, use `nmigen.utils.bits_for`") def bits_for(n, require_sign_bit=False): - return tools.bits_for(n, require_sign_bit) + return utils.bits_for(n, require_sign_bit) @deprecated("instead of `value_bits_sign(v)`, use `v.shape()`") diff --git a/nmigen/compat/fhdl/decorators.py b/nmigen/compat/fhdl/decorators.py index 83f39b0..e61ccb5 100644 --- a/nmigen/compat/fhdl/decorators.py +++ b/nmigen/compat/fhdl/decorators.py @@ -2,7 +2,7 @@ from ...hdl.ast import * from ...hdl.xfrm import ResetInserter as NativeResetInserter from ...hdl.xfrm import EnableInserter as NativeEnableInserter from ...hdl.xfrm import DomainRenamer as NativeDomainRenamer -from ..._tools import deprecated +from ..._utils import deprecated __all__ = ["ResetInserter", "CEInserter", "ClockDomainsRenamer"] diff --git a/nmigen/compat/fhdl/module.py b/nmigen/compat/fhdl/module.py index 55dc96c..c403a5e 100644 --- a/nmigen/compat/fhdl/module.py +++ b/nmigen/compat/fhdl/module.py @@ -1,6 +1,6 @@ from collections.abc import Iterable -from ..._tools import flatten, deprecated +from ..._utils import flatten, deprecated from ...hdl import dsl, ir diff --git a/nmigen/compat/fhdl/specials.py b/nmigen/compat/fhdl/specials.py index 2a49ffc..93111c2 100644 --- a/nmigen/compat/fhdl/specials.py +++ b/nmigen/compat/fhdl/specials.py @@ -1,6 +1,6 @@ import warnings -from ..._tools import deprecated, extend +from ..._utils import deprecated, extend from ...hdl.ast import * from ...hdl.ir import Elaboratable from ...hdl.mem import Memory as NativeMemory diff --git a/nmigen/compat/fhdl/structure.py b/nmigen/compat/fhdl/structure.py index 91aea7f..27f639c 100644 --- a/nmigen/compat/fhdl/structure.py +++ b/nmigen/compat/fhdl/structure.py @@ -1,6 +1,6 @@ from collections import OrderedDict -from ..._tools import deprecated, extend +from ..._utils import deprecated, extend from ...hdl import ast from ...hdl.ast import (DUID, Value, Signal, Mux, Slice as _Slice, Cat, Repl, Const, C, ClockSignal, ResetSignal, diff --git a/nmigen/compat/genlib/cdc.py b/nmigen/compat/genlib/cdc.py index 7bfcd8c..3bdddb1 100644 --- a/nmigen/compat/genlib/cdc.py +++ b/nmigen/compat/genlib/cdc.py @@ -1,6 +1,6 @@ import warnings -from ..._tools import deprecated +from ..._utils import deprecated from ...lib.cdc import FFSynchronizer as NativeFFSynchronizer from ...hdl.ast import * from ..fhdl.module import CompatModule diff --git a/nmigen/compat/genlib/fifo.py b/nmigen/compat/genlib/fifo.py index 0e4b55d..5c9bed7 100644 --- a/nmigen/compat/genlib/fifo.py +++ b/nmigen/compat/genlib/fifo.py @@ -1,4 +1,4 @@ -from ..._tools import deprecated, extend +from ..._utils import deprecated, extend from ...lib.fifo import (FIFOInterface as NativeFIFOInterface, SyncFIFO as NativeSyncFIFO, SyncFIFOBuffered as NativeSyncFIFOBuffered, AsyncFIFO as NativeAsyncFIFO, AsyncFIFOBuffered as NativeAsyncFIFOBuffered) diff --git a/nmigen/compat/genlib/fsm.py b/nmigen/compat/genlib/fsm.py index 68b1d0a..fe8f525 100644 --- a/nmigen/compat/genlib/fsm.py +++ b/nmigen/compat/genlib/fsm.py @@ -1,6 +1,6 @@ from collections import OrderedDict -from ..._tools import deprecated, _ignore_deprecated +from ..._utils import deprecated, _ignore_deprecated from ...hdl.xfrm import ValueTransformer, StatementTransformer from ...hdl.ast import * from ..fhdl.module import CompatModule, CompatFinalizeError diff --git a/nmigen/compat/genlib/resetsync.py b/nmigen/compat/genlib/resetsync.py index f7b5c4e..66f0bae 100644 --- a/nmigen/compat/genlib/resetsync.py +++ b/nmigen/compat/genlib/resetsync.py @@ -1,4 +1,4 @@ -from ..._tools import deprecated +from ..._utils import deprecated from ...lib.cdc import ResetSynchronizer as NativeResetSynchronizer diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index 5d616c1..e0314cd 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -8,7 +8,7 @@ from collections.abc import Iterable, MutableMapping, MutableSet, MutableSequenc from enum import Enum from .. import tracer -from .._tools import * +from .._utils import * __all__ = [ diff --git a/nmigen/hdl/dsl.py b/nmigen/hdl/dsl.py index b063482..598d812 100644 --- a/nmigen/hdl/dsl.py +++ b/nmigen/hdl/dsl.py @@ -4,7 +4,7 @@ from contextlib import contextmanager from enum import Enum import warnings -from .._tools import flatten, bits_for, deprecated +from .._utils import flatten, bits_for, deprecated from .. import tracer from .ast import * from .ir import * diff --git a/nmigen/hdl/ir.py b/nmigen/hdl/ir.py index 4232c52..44c3ecd 100644 --- a/nmigen/hdl/ir.py +++ b/nmigen/hdl/ir.py @@ -5,7 +5,7 @@ import warnings import traceback import sys -from .._tools import * +from .._utils import * from .ast import * from .cd import * diff --git a/nmigen/hdl/rec.py b/nmigen/hdl/rec.py index 7448ecd..d6c4686 100644 --- a/nmigen/hdl/rec.py +++ b/nmigen/hdl/rec.py @@ -3,7 +3,7 @@ from collections import OrderedDict from functools import reduce from .. import tracer -from .._tools import union, deprecated +from .._utils import union, deprecated from .ast import * diff --git a/nmigen/hdl/xfrm.py b/nmigen/hdl/xfrm.py index 2d4e639..8cd6936 100644 --- a/nmigen/hdl/xfrm.py +++ b/nmigen/hdl/xfrm.py @@ -2,7 +2,7 @@ from abc import ABCMeta, abstractmethod from collections import OrderedDict from collections.abc import Iterable -from .._tools import flatten, deprecated +from .._utils import flatten, deprecated from .. import tracer from .ast import * from .ast import _StatementList diff --git a/nmigen/lib/cdc.py b/nmigen/lib/cdc.py index 1294fb4..5dc865d 100644 --- a/nmigen/lib/cdc.py +++ b/nmigen/lib/cdc.py @@ -1,4 +1,4 @@ -from .._tools import deprecated +from .._utils import deprecated from .. import * diff --git a/nmigen/lib/fifo.py b/nmigen/lib/fifo.py index 332d703..f3244df 100644 --- a/nmigen/lib/fifo.py +++ b/nmigen/lib/fifo.py @@ -2,7 +2,7 @@ from .. import * from ..asserts import * -from .._tools import log2_int, deprecated +from .._utils import log2_int, deprecated from .coding import GrayEncoder from .cdc import FFSynchronizer diff --git a/nmigen/test/compat/support.py b/nmigen/test/compat/support.py index 22b97cc..8491652 100644 --- a/nmigen/test/compat/support.py +++ b/nmigen/test/compat/support.py @@ -1,4 +1,4 @@ -from ..._tools import _ignore_deprecated +from ..._utils import _ignore_deprecated from ...compat import * from ...compat.fhdl import verilog diff --git a/nmigen/test/compat/test_size.py b/nmigen/test/compat/test_size.py index a739c2b..e3864f9 100644 --- a/nmigen/test/compat/test_size.py +++ b/nmigen/test/compat/test_size.py @@ -1,6 +1,6 @@ import unittest -from ..._tools import _ignore_deprecated +from ..._utils import _ignore_deprecated from ...compat import * diff --git a/nmigen/test/test_build_dsl.py b/nmigen/test/test_build_dsl.py index 1e37d19..a613d12 100644 --- a/nmigen/test/test_build_dsl.py +++ b/nmigen/test/test_build_dsl.py @@ -1,7 +1,7 @@ from collections import OrderedDict from ..build.dsl import * -from .tools import * +from .utils import * class PinsTestCase(FHDLTestCase): diff --git a/nmigen/test/test_build_res.py b/nmigen/test/test_build_res.py index fc111c8..40f4235 100644 --- a/nmigen/test/test_build_res.py +++ b/nmigen/test/test_build_res.py @@ -3,7 +3,7 @@ from ..hdl.rec import * from ..lib.io import * from ..build.dsl import * from ..build.res import * -from .tools import * +from .utils import * class ResourceManagerTestCase(FHDLTestCase): diff --git a/nmigen/test/test_compat.py b/nmigen/test/test_compat.py index d42812c..62e2d92 100644 --- a/nmigen/test/test_compat.py +++ b/nmigen/test/test_compat.py @@ -1,6 +1,6 @@ from ..hdl.ir import Fragment from ..compat import * -from .tools import * +from .utils import * class CompatTestCase(FHDLTestCase): diff --git a/nmigen/test/test_examples.py b/nmigen/test/test_examples.py index e428a5e..94b9377 100644 --- a/nmigen/test/test_examples.py +++ b/nmigen/test/test_examples.py @@ -2,7 +2,7 @@ import sys import subprocess from pathlib import Path -from .tools import * +from .utils import * def example_test(name): diff --git a/nmigen/test/test_hdl_ast.py b/nmigen/test/test_hdl_ast.py index b7e9647..6968910 100644 --- a/nmigen/test/test_hdl_ast.py +++ b/nmigen/test/test_hdl_ast.py @@ -2,7 +2,7 @@ import warnings from enum import Enum from ..hdl.ast import * -from .tools import * +from .utils import * class UnsignedEnum(Enum): diff --git a/nmigen/test/test_hdl_cd.py b/nmigen/test/test_hdl_cd.py index 60edc3b..85bcfc8 100644 --- a/nmigen/test/test_hdl_cd.py +++ b/nmigen/test/test_hdl_cd.py @@ -1,5 +1,5 @@ from ..hdl.cd import * -from .tools import * +from .utils import * class ClockDomainTestCase(FHDLTestCase): diff --git a/nmigen/test/test_hdl_dsl.py b/nmigen/test/test_hdl_dsl.py index 48993dd..85d205c 100644 --- a/nmigen/test/test_hdl_dsl.py +++ b/nmigen/test/test_hdl_dsl.py @@ -4,7 +4,7 @@ from enum import Enum from ..hdl.ast import * from ..hdl.cd import * from ..hdl.dsl import * -from .tools import * +from .utils import * class DSLTestCase(FHDLTestCase): diff --git a/nmigen/test/test_hdl_ir.py b/nmigen/test/test_hdl_ir.py index e51ae4e..6a85ebe 100644 --- a/nmigen/test/test_hdl_ir.py +++ b/nmigen/test/test_hdl_ir.py @@ -4,7 +4,7 @@ from ..hdl.ast import * from ..hdl.cd import * from ..hdl.ir import * from ..hdl.mem import * -from .tools import * +from .utils import * class BadElaboratable(Elaboratable): diff --git a/nmigen/test/test_hdl_mem.py b/nmigen/test/test_hdl_mem.py index 8cf3bb1..1a0a007 100644 --- a/nmigen/test/test_hdl_mem.py +++ b/nmigen/test/test_hdl_mem.py @@ -1,6 +1,6 @@ from ..hdl.ast import * from ..hdl.mem import * -from .tools import * +from .utils import * class MemoryTestCase(FHDLTestCase): diff --git a/nmigen/test/test_hdl_rec.py b/nmigen/test/test_hdl_rec.py index 2e84107..dd9dd40 100644 --- a/nmigen/test/test_hdl_rec.py +++ b/nmigen/test/test_hdl_rec.py @@ -2,7 +2,7 @@ from enum import Enum from ..hdl.ast import * from ..hdl.rec import * -from .tools import * +from .utils import * class UnsignedEnum(Enum): diff --git a/nmigen/test/test_hdl_xfrm.py b/nmigen/test/test_hdl_xfrm.py index d67dc6f..c2e826a 100644 --- a/nmigen/test/test_hdl_xfrm.py +++ b/nmigen/test/test_hdl_xfrm.py @@ -3,7 +3,7 @@ from ..hdl.cd import * from ..hdl.ir import * from ..hdl.xfrm import * from ..hdl.mem import * -from .tools import * +from .utils import * class DomainRenamerTestCase(FHDLTestCase): diff --git a/nmigen/test/test_lib_cdc.py b/nmigen/test/test_lib_cdc.py index 23ebed5..1a394ea 100644 --- a/nmigen/test/test_lib_cdc.py +++ b/nmigen/test/test_lib_cdc.py @@ -1,4 +1,4 @@ -from .tools import * +from .utils import * from ..hdl import * from ..back.pysim import * from ..lib.cdc import * diff --git a/nmigen/test/test_lib_coding.py b/nmigen/test/test_lib_coding.py index 6052c9a..874b87c 100644 --- a/nmigen/test/test_lib_coding.py +++ b/nmigen/test/test_lib_coding.py @@ -1,4 +1,4 @@ -from .tools import * +from .utils import * from ..hdl import * from ..asserts import * from ..back.pysim import * diff --git a/nmigen/test/test_lib_fifo.py b/nmigen/test/test_lib_fifo.py index 2398f8f..26c5391 100644 --- a/nmigen/test/test_lib_fifo.py +++ b/nmigen/test/test_lib_fifo.py @@ -1,4 +1,4 @@ -from .tools import * +from .utils import * from ..hdl import * from ..asserts import * from ..back.pysim import * diff --git a/nmigen/test/test_lib_io.py b/nmigen/test/test_lib_io.py index 7ae44a3..cb1cd46 100644 --- a/nmigen/test/test_lib_io.py +++ b/nmigen/test/test_lib_io.py @@ -1,4 +1,4 @@ -from .tools import * +from .utils import * from ..hdl import * from ..hdl.rec import * from ..back.pysim import * diff --git a/nmigen/test/test_sim.py b/nmigen/test/test_sim.py index 91fec34..7ae6d97 100644 --- a/nmigen/test/test_sim.py +++ b/nmigen/test/test_sim.py @@ -1,7 +1,7 @@ from contextlib import contextmanager -from .tools import * -from .._tools import flatten, union +from .utils import * +from .._utils import flatten, union from ..hdl.ast import * from ..hdl.cd import * from ..hdl.mem import * diff --git a/nmigen/test/tools.py b/nmigen/test/tools.py deleted file mode 100644 index c3907ce..0000000 --- a/nmigen/test/tools.py +++ /dev/null @@ -1,103 +0,0 @@ -import os -import re -import shutil -import subprocess -import textwrap -import traceback -import unittest -import warnings -from contextlib import contextmanager - -from ..hdl.ast import * -from ..hdl.ir import * -from ..back import rtlil -from .._toolchain import require_tool - - -__all__ = ["FHDLTestCase"] - - -class FHDLTestCase(unittest.TestCase): - def assertRepr(self, obj, repr_str): - if isinstance(obj, list): - obj = Statement.cast(obj) - def prepare_repr(repr_str): - repr_str = re.sub(r"\s+", " ", repr_str) - repr_str = re.sub(r"\( (?=\()", "(", repr_str) - repr_str = re.sub(r"\) (?=\))", ")", repr_str) - return repr_str.strip() - self.assertEqual(prepare_repr(repr(obj)), prepare_repr(repr_str)) - - @contextmanager - def assertRaises(self, exception, msg=None): - with super().assertRaises(exception) as cm: - yield - if msg is not None: - # WTF? unittest.assertRaises is completely broken. - self.assertEqual(str(cm.exception), msg) - - @contextmanager - def assertRaisesRegex(self, exception, regex=None): - with super().assertRaises(exception) as cm: - yield - if regex is not None: - # unittest.assertRaisesRegex also seems broken... - self.assertRegex(str(cm.exception), regex) - - @contextmanager - def assertWarns(self, category, msg=None): - with warnings.catch_warnings(record=True) as warns: - yield - self.assertEqual(len(warns), 1) - self.assertEqual(warns[0].category, category) - if msg is not None: - self.assertEqual(str(warns[0].message), msg) - - def assertFormal(self, spec, mode="bmc", depth=1): - caller, *_ = traceback.extract_stack(limit=2) - spec_root, _ = os.path.splitext(caller.filename) - spec_dir = os.path.dirname(spec_root) - spec_name = "{}_{}".format( - os.path.basename(spec_root).replace("test_", "spec_"), - caller.name.replace("test_", "") - ) - - # The sby -f switch seems not fully functional when sby is reading from stdin. - if os.path.exists(os.path.join(spec_dir, spec_name)): - shutil.rmtree(os.path.join(spec_dir, spec_name)) - - if mode == "hybrid": - # A mix of BMC and k-induction, as per personal communication with Clifford Wolf. - script = "setattr -unset init w:* a:nmigen.sample_reg %d" - mode = "bmc" - else: - script = "" - - config = textwrap.dedent("""\ - [options] - mode {mode} - depth {depth} - wait on - - [engines] - smtbmc - - [script] - read_ilang top.il - prep - {script} - - [file top.il] - {rtlil} - """).format( - mode=mode, - depth=depth, - script=script, - rtlil=rtlil.convert(Fragment.get(spec, platform="formal")) - ) - with subprocess.Popen([require_tool("sby"), "-f", "-d", spec_name], cwd=spec_dir, - universal_newlines=True, - stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc: - stdout, stderr = proc.communicate(config) - if proc.returncode != 0: - self.fail("Formal verification failed:\n" + stdout) diff --git a/nmigen/test/utils.py b/nmigen/test/utils.py new file mode 100644 index 0000000..c3907ce --- /dev/null +++ b/nmigen/test/utils.py @@ -0,0 +1,103 @@ +import os +import re +import shutil +import subprocess +import textwrap +import traceback +import unittest +import warnings +from contextlib import contextmanager + +from ..hdl.ast import * +from ..hdl.ir import * +from ..back import rtlil +from .._toolchain import require_tool + + +__all__ = ["FHDLTestCase"] + + +class FHDLTestCase(unittest.TestCase): + def assertRepr(self, obj, repr_str): + if isinstance(obj, list): + obj = Statement.cast(obj) + def prepare_repr(repr_str): + repr_str = re.sub(r"\s+", " ", repr_str) + repr_str = re.sub(r"\( (?=\()", "(", repr_str) + repr_str = re.sub(r"\) (?=\))", ")", repr_str) + return repr_str.strip() + self.assertEqual(prepare_repr(repr(obj)), prepare_repr(repr_str)) + + @contextmanager + def assertRaises(self, exception, msg=None): + with super().assertRaises(exception) as cm: + yield + if msg is not None: + # WTF? unittest.assertRaises is completely broken. + self.assertEqual(str(cm.exception), msg) + + @contextmanager + def assertRaisesRegex(self, exception, regex=None): + with super().assertRaises(exception) as cm: + yield + if regex is not None: + # unittest.assertRaisesRegex also seems broken... + self.assertRegex(str(cm.exception), regex) + + @contextmanager + def assertWarns(self, category, msg=None): + with warnings.catch_warnings(record=True) as warns: + yield + self.assertEqual(len(warns), 1) + self.assertEqual(warns[0].category, category) + if msg is not None: + self.assertEqual(str(warns[0].message), msg) + + def assertFormal(self, spec, mode="bmc", depth=1): + caller, *_ = traceback.extract_stack(limit=2) + spec_root, _ = os.path.splitext(caller.filename) + spec_dir = os.path.dirname(spec_root) + spec_name = "{}_{}".format( + os.path.basename(spec_root).replace("test_", "spec_"), + caller.name.replace("test_", "") + ) + + # The sby -f switch seems not fully functional when sby is reading from stdin. + if os.path.exists(os.path.join(spec_dir, spec_name)): + shutil.rmtree(os.path.join(spec_dir, spec_name)) + + if mode == "hybrid": + # A mix of BMC and k-induction, as per personal communication with Clifford Wolf. + script = "setattr -unset init w:* a:nmigen.sample_reg %d" + mode = "bmc" + else: + script = "" + + config = textwrap.dedent("""\ + [options] + mode {mode} + depth {depth} + wait on + + [engines] + smtbmc + + [script] + read_ilang top.il + prep + {script} + + [file top.il] + {rtlil} + """).format( + mode=mode, + depth=depth, + script=script, + rtlil=rtlil.convert(Fragment.get(spec, platform="formal")) + ) + with subprocess.Popen([require_tool("sby"), "-f", "-d", spec_name], cwd=spec_dir, + universal_newlines=True, + stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc: + stdout, stderr = proc.communicate(config) + if proc.returncode != 0: + self.fail("Formal verification failed:\n" + stdout) diff --git a/nmigen/tools.py b/nmigen/tools.py deleted file mode 100644 index 227258a..0000000 --- a/nmigen/tools.py +++ /dev/null @@ -1,21 +0,0 @@ -__all__ = ["log2_int", "bits_for"] - - -def log2_int(n, need_pow2=True): - if n == 0: - return 0 - r = (n - 1).bit_length() - if need_pow2 and (1 << r) != n: - raise ValueError("{} is not a power of 2".format(n)) - return r - - -def bits_for(n, require_sign_bit=False): - if n > 0: - r = log2_int(n + 1, False) - else: - require_sign_bit = True - r = log2_int(-n, False) - if require_sign_bit: - r += 1 - return r diff --git a/nmigen/utils.py b/nmigen/utils.py new file mode 100644 index 0000000..227258a --- /dev/null +++ b/nmigen/utils.py @@ -0,0 +1,21 @@ +__all__ = ["log2_int", "bits_for"] + + +def log2_int(n, need_pow2=True): + if n == 0: + return 0 + r = (n - 1).bit_length() + if need_pow2 and (1 << r) != n: + raise ValueError("{} is not a power of 2".format(n)) + return r + + +def bits_for(n, require_sign_bit=False): + if n > 0: + r = log2_int(n + 1, False) + else: + require_sign_bit = True + r = log2_int(-n, False) + if require_sign_bit: + r += 1 + return r