from abc import ABCMeta, abstractmethod from pathlib import Path from power_fv.build import sby __all__ = ["PowerFVCheck"] class PowerFVCheckMeta(ABCMeta): all_checks = {} def __new__(metacls, clsname, bases, namespace, name=None, **kwargs): if name is not None: if name in metacls.all_checks: raise NameError("Check {!r} already exists".format(name)) namespace["name"] = name cls = ABCMeta.__new__(metacls, clsname, bases, namespace, **kwargs) if name is not None: metacls.all_checks[name] = cls cls.name = name return cls @classmethod def find(cls, *name): for check_name, check_cls in cls.all_checks.items(): assert isinstance(check_name, tuple) if len(name) > len(check_name): continue if name == check_name[:len(name)]: yield check_name, check_cls class PowerFVCheck(metaclass=PowerFVCheckMeta): @classmethod def add_check_arguments(cls, parser): parser.add_argument( "name", metavar="NAME", type=str, help="name of the check") parser.add_argument( "--depth", type=int, default=15, help="depth of the BMC, in clock cycles (default: %(default)s)") parser.add_argument( "--skip", type=int, default=None, help="skip the specified number of clock cycles (default: DEPTH-1))") @classmethod def add_build_arguments(cls, parser): parser.add_argument( "--build-dir", type=Path, default=Path("./build"), help="output directory (default: %(default)s)") def __init__(self, *, depth, skip, core, **kwargs): self.depth = depth self.skip = skip if skip is not None else depth - 1 self.core = core self.dut = core.wrapper(**kwargs) @abstractmethod def testbench(self): raise NotImplementedError def build(self, *, build_dir, **kwargs): platform = sby.SymbiYosysPlatform() self.core.add_files(platform, self.dut, **kwargs) top = self.testbench() build_dir = build_dir / top.name overrides = {key: str(value) for key, value in kwargs.items()} overrides["depth"] = str(self.depth) overrides["skip"] = str(self.skip) return platform.build(top, name=top.name, build_dir=build_dir, **overrides)