hdl.ir: detect elaboratables that are created but not used.
Requres every elaboratable to inherit from Elaboratable, but still accepts ones that do not, with a warning. Fixes #3.
This commit is contained in:
parent
85ae99c1b4
commit
44711b7d08
|
@ -2,7 +2,7 @@ from nmigen import *
|
|||
from nmigen.cli import main
|
||||
|
||||
|
||||
class ALU:
|
||||
class ALU(Elaboratable):
|
||||
def __init__(self, width):
|
||||
self.sel = Signal(2)
|
||||
self.a = Signal(width)
|
||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
|||
from nmigen.cli import main
|
||||
|
||||
|
||||
class Adder:
|
||||
class Adder(Elaboratable):
|
||||
def __init__(self, width):
|
||||
self.a = Signal(width)
|
||||
self.b = Signal(width)
|
||||
|
@ -14,7 +14,7 @@ class Adder:
|
|||
return m
|
||||
|
||||
|
||||
class Subtractor:
|
||||
class Subtractor(Elaboratable):
|
||||
def __init__(self, width):
|
||||
self.a = Signal(width)
|
||||
self.b = Signal(width)
|
||||
|
@ -26,7 +26,7 @@ class Subtractor:
|
|||
return m
|
||||
|
||||
|
||||
class ALU:
|
||||
class ALU(Elaboratable):
|
||||
def __init__(self, width):
|
||||
self.op = Signal()
|
||||
self.a = Signal(width)
|
||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
|||
from nmigen.cli import main
|
||||
|
||||
|
||||
class ClockDivisor:
|
||||
class ClockDivisor(Elaboratable):
|
||||
def __init__(self, factor):
|
||||
self.v = Signal(factor)
|
||||
self.o = Signal()
|
||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
|||
from nmigen.cli import main, pysim
|
||||
|
||||
|
||||
class Counter:
|
||||
class Counter(Elaboratable):
|
||||
def __init__(self, width):
|
||||
self.v = Signal(width, reset=2**width-1)
|
||||
self.o = Signal()
|
||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
|||
from nmigen.back import rtlil, verilog, pysim
|
||||
|
||||
|
||||
class Counter:
|
||||
class Counter(Elaboratable):
|
||||
def __init__(self, width):
|
||||
self.v = Signal(width, reset=2**width-1)
|
||||
self.o = Signal()
|
||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
|||
from nmigen.cli import main
|
||||
|
||||
|
||||
class UARTReceiver:
|
||||
class UARTReceiver(Elaboratable):
|
||||
def __init__(self, divisor):
|
||||
self.divisor = divisor
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from nmigen import *
|
|||
from nmigen.cli import main
|
||||
|
||||
|
||||
class GPIO:
|
||||
class GPIO(Elaboratable):
|
||||
def __init__(self, pins, bus):
|
||||
self.pins = pins
|
||||
self.bus = bus
|
||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
|||
from nmigen.cli import main
|
||||
|
||||
|
||||
class System:
|
||||
class System(Elaboratable):
|
||||
def __init__(self):
|
||||
self.adr = Signal(16)
|
||||
self.dat_r = Signal(8)
|
||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
|||
from nmigen.cli import main
|
||||
|
||||
|
||||
class RegisterFile:
|
||||
class RegisterFile(Elaboratable):
|
||||
def __init__(self):
|
||||
self.adr = Signal(4)
|
||||
self.dat_r = Signal(8)
|
||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
|||
from nmigen.cli import main
|
||||
|
||||
|
||||
class ParMux:
|
||||
class ParMux(Elaboratable):
|
||||
def __init__(self, width):
|
||||
self.s = Signal(3)
|
||||
self.a = Signal(width)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from .hdl.ast import Value, Const, C, Mux, Cat, Repl, Array, Signal, ClockSignal, ResetSignal
|
||||
from .hdl.dsl import Module
|
||||
from .hdl.cd import ClockDomain
|
||||
from .hdl.ir import Fragment, Instance
|
||||
from .hdl.ir import Elaboratable, Fragment, Instance
|
||||
from .hdl.mem import Memory
|
||||
from .hdl.rec import Record
|
||||
from .hdl.xfrm import ResetInserter, CEInserter
|
||||
|
|
|
@ -9,7 +9,7 @@ from .ir import *
|
|||
from .xfrm import *
|
||||
|
||||
|
||||
__all__ = ["Module", "SyntaxError", "SyntaxWarning"]
|
||||
__all__ = ["SyntaxError", "SyntaxWarning", "Module"]
|
||||
|
||||
|
||||
class SyntaxError(Exception):
|
||||
|
@ -109,7 +109,7 @@ class FSM:
|
|||
return self.state == self.encoding[name]
|
||||
|
||||
|
||||
class Module(_ModuleBuilderRoot):
|
||||
class Module(_ModuleBuilderRoot, Elaboratable):
|
||||
def __init__(self):
|
||||
_ModuleBuilderRoot.__init__(self, self, depth=0)
|
||||
self.submodules = _ModuleBuilderSubmodules(self)
|
||||
|
|
|
@ -1,12 +1,30 @@
|
|||
import warnings
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from collections import defaultdict, OrderedDict
|
||||
import warnings
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
from ..tools import *
|
||||
from .ast import *
|
||||
from .cd import *
|
||||
|
||||
|
||||
__all__ = ["Fragment", "Instance", "DriverConflict"]
|
||||
__all__ = ["Elaboratable", "DriverConflict", "Fragment", "Instance"]
|
||||
|
||||
|
||||
class Elaboratable(metaclass=ABCMeta):
|
||||
def __new__(cls, *args, **kwargs):
|
||||
self = super().__new__(cls)
|
||||
self._Elaboratable__traceback = traceback.extract_stack()[:-1]
|
||||
self._Elaboratable__used = False
|
||||
return self
|
||||
|
||||
def __del__(self):
|
||||
if hasattr(self, "_Elaboratable__used") and not self._Elaboratable__used:
|
||||
print("Elaboratable created but never used\n",
|
||||
"Traceback (most recent call last):\n",
|
||||
*traceback.format_list(self._Elaboratable__traceback),
|
||||
file=sys.stderr, sep="")
|
||||
|
||||
|
||||
class DriverConflict(UserWarning):
|
||||
|
@ -16,13 +34,22 @@ class DriverConflict(UserWarning):
|
|||
class Fragment:
|
||||
@staticmethod
|
||||
def get(obj, platform):
|
||||
if isinstance(obj, Fragment):
|
||||
return obj
|
||||
elif hasattr(obj, "elaborate"):
|
||||
frag = obj.elaborate(platform)
|
||||
else:
|
||||
raise AttributeError("Object '{!r}' cannot be elaborated".format(obj))
|
||||
return Fragment.get(frag, platform)
|
||||
while True:
|
||||
if isinstance(obj, Fragment):
|
||||
return obj
|
||||
elif isinstance(obj, Elaboratable):
|
||||
obj._Elaboratable__used = True
|
||||
obj = obj.elaborate(platform)
|
||||
elif hasattr(obj, "elaborate"):
|
||||
warnings.warn(
|
||||
message="Class {!r} is an elaboratable that does not explicitly inherit from "
|
||||
"Elaboratable; doing so would improve diagnostics"
|
||||
.format(type(obj)),
|
||||
category=RuntimeWarning,
|
||||
stacklevel=2)
|
||||
obj = obj.elaborate(platform)
|
||||
else:
|
||||
raise AttributeError("Object '{!r}' cannot be elaborated".format(obj))
|
||||
|
||||
def __init__(self):
|
||||
self.ports = SignalDict()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from .. import tracer
|
||||
from .ast import *
|
||||
from .ir import Instance
|
||||
from .ir import Elaboratable, Instance
|
||||
|
||||
|
||||
__all__ = ["Memory", "ReadPort", "WritePort", "DummyPort"]
|
||||
|
@ -70,7 +70,7 @@ class Memory:
|
|||
return self._array[index]
|
||||
|
||||
|
||||
class ReadPort:
|
||||
class ReadPort(Elaboratable):
|
||||
def __init__(self, memory, domain, synchronous, transparent):
|
||||
self.memory = memory
|
||||
self.domain = domain
|
||||
|
@ -135,7 +135,7 @@ class ReadPort:
|
|||
return f
|
||||
|
||||
|
||||
class WritePort:
|
||||
class WritePort(Elaboratable):
|
||||
def __init__(self, memory, domain, priority, granularity):
|
||||
self.memory = memory
|
||||
self.domain = domain
|
||||
|
|
|
@ -292,7 +292,7 @@ class FragmentTransformer:
|
|||
raise AttributeError("Object '{!r}' cannot be elaborated".format(value))
|
||||
|
||||
|
||||
class TransformedElaboratable:
|
||||
class TransformedElaboratable(Elaboratable):
|
||||
def __init__(self, elaboratable):
|
||||
assert hasattr(elaboratable, "elaborate")
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from .. import *
|
|||
__all__ = ["MultiReg", "ResetSynchronizer"]
|
||||
|
||||
|
||||
class MultiReg:
|
||||
class MultiReg(Elaboratable):
|
||||
"""Resynchronise a signal to a different clock domain.
|
||||
|
||||
Consists of a chain of flip-flops. Eliminates metastabilities at the output, but provides
|
||||
|
@ -69,7 +69,7 @@ class MultiReg:
|
|||
return m
|
||||
|
||||
|
||||
class ResetSynchronizer:
|
||||
class ResetSynchronizer(Elaboratable):
|
||||
def __init__(self, arst, domain="sync", n=2):
|
||||
self.arst = arst
|
||||
self.domain = domain
|
||||
|
|
|
@ -10,7 +10,7 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
class Encoder:
|
||||
class Encoder(Elaboratable):
|
||||
"""Encode one-hot to binary.
|
||||
|
||||
If one bit in ``i`` is asserted, ``n`` is low and ``o`` indicates the asserted bit.
|
||||
|
@ -48,7 +48,7 @@ class Encoder:
|
|||
return m
|
||||
|
||||
|
||||
class PriorityEncoder:
|
||||
class PriorityEncoder(Elaboratable):
|
||||
"""Priority encode requests to binary.
|
||||
|
||||
If any bit in ``i`` is asserted, ``n`` is low and ``o`` indicates the least significant
|
||||
|
@ -85,7 +85,7 @@ class PriorityEncoder:
|
|||
return m
|
||||
|
||||
|
||||
class Decoder:
|
||||
class Decoder(Elaboratable):
|
||||
"""Decode binary to one-hot.
|
||||
|
||||
If ``n`` is low, only the ``i``th bit in ``o`` is asserted.
|
||||
|
@ -130,7 +130,7 @@ class PriorityDecoder(Decoder):
|
|||
"""
|
||||
|
||||
|
||||
class GrayEncoder:
|
||||
class GrayEncoder(Elaboratable):
|
||||
"""Encode binary to Gray code.
|
||||
|
||||
Parameters
|
||||
|
@ -157,7 +157,7 @@ class GrayEncoder:
|
|||
return m
|
||||
|
||||
|
||||
class GrayDecoder:
|
||||
class GrayDecoder(Elaboratable):
|
||||
"""Decode Gray code to binary.
|
||||
|
||||
Parameters
|
||||
|
|
|
@ -102,7 +102,7 @@ def _decr(signal, modulo):
|
|||
return Mux(signal == 0, modulo - 1, signal - 1)
|
||||
|
||||
|
||||
class SyncFIFO(FIFOInterface):
|
||||
class SyncFIFO(Elaboratable, FIFOInterface):
|
||||
__doc__ = FIFOInterface._doc_template.format(
|
||||
description="""
|
||||
Synchronous first in, first out queue.
|
||||
|
@ -209,7 +209,7 @@ class SyncFIFO(FIFOInterface):
|
|||
return m
|
||||
|
||||
|
||||
class SyncFIFOBuffered(FIFOInterface):
|
||||
class SyncFIFOBuffered(Elaboratable, FIFOInterface):
|
||||
__doc__ = FIFOInterface._doc_template.format(
|
||||
description="""
|
||||
Buffered synchronous first in, first out queue.
|
||||
|
@ -265,7 +265,7 @@ class SyncFIFOBuffered(FIFOInterface):
|
|||
return m
|
||||
|
||||
|
||||
class AsyncFIFO(FIFOInterface):
|
||||
class AsyncFIFO(Elaboratable, FIFOInterface):
|
||||
__doc__ = FIFOInterface._doc_template.format(
|
||||
description="""
|
||||
Asynchronous first in, first out queue.
|
||||
|
@ -361,7 +361,7 @@ class AsyncFIFO(FIFOInterface):
|
|||
return m
|
||||
|
||||
|
||||
class AsyncFIFOBuffered(FIFOInterface):
|
||||
class AsyncFIFOBuffered(Elaboratable, FIFOInterface):
|
||||
__doc__ = FIFOInterface._doc_template.format(
|
||||
description="""
|
||||
Buffered asynchronous first in, first out queue.
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
from ..hdl.ir import Elaboratable
|
||||
|
||||
|
||||
# The nMigen testsuite creates a lot of elaboratables that are intentionally unused.
|
||||
# Disable the unused elaboratable check, as in our case it provides nothing but noise.
|
||||
del Elaboratable.__del__
|
|
@ -488,7 +488,7 @@ class CEInserterTestCase(FHDLTestCase):
|
|||
""")
|
||||
|
||||
|
||||
class _MockElaboratable:
|
||||
class _MockElaboratable(Elaboratable):
|
||||
def __init__(self):
|
||||
self.s1 = Signal()
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from .tools import *
|
||||
from ..hdl.ast import *
|
||||
from ..hdl.dsl import *
|
||||
from ..hdl.ir import *
|
||||
from ..back.pysim import *
|
||||
from ..lib.coding import *
|
||||
|
||||
|
@ -82,7 +83,7 @@ class DecoderTestCase(FHDLTestCase):
|
|||
sim.run()
|
||||
|
||||
|
||||
class ReversibleSpec:
|
||||
class ReversibleSpec(Elaboratable):
|
||||
def __init__(self, encoder_cls, decoder_cls, args):
|
||||
self.encoder_cls = encoder_cls
|
||||
self.decoder_cls = decoder_cls
|
||||
|
@ -99,7 +100,7 @@ class ReversibleSpec:
|
|||
return m
|
||||
|
||||
|
||||
class HammingDistanceSpec:
|
||||
class HammingDistanceSpec(Elaboratable):
|
||||
def __init__(self, distance, encoder_cls, args):
|
||||
self.distance = distance
|
||||
self.encoder_cls = encoder_cls
|
||||
|
|
|
@ -45,7 +45,7 @@ class FIFOSmokeTestCase(FHDLTestCase):
|
|||
self.assertAsyncFIFOWorks(AsyncFIFOBuffered(width=8, depth=3))
|
||||
|
||||
|
||||
class FIFOModel(FIFOInterface):
|
||||
class FIFOModel(Elaboratable, FIFOInterface):
|
||||
"""
|
||||
Non-synthesizable first-in first-out queue, implemented naively as a chain of registers.
|
||||
"""
|
||||
|
@ -104,7 +104,7 @@ class FIFOModel(FIFOInterface):
|
|||
return m
|
||||
|
||||
|
||||
class FIFOModelEquivalenceSpec:
|
||||
class FIFOModelEquivalenceSpec(Elaboratable):
|
||||
"""
|
||||
The first-in first-out queue model equivalence specification: for any inputs and control
|
||||
signals, the behavior of the implementation under test exactly matches the ideal model,
|
||||
|
@ -148,7 +148,7 @@ class FIFOModelEquivalenceSpec:
|
|||
return m
|
||||
|
||||
|
||||
class FIFOContractSpec:
|
||||
class FIFOContractSpec(Elaboratable):
|
||||
"""
|
||||
The first-in first-out queue contract specification: if two elements are written to the queue
|
||||
consecutively, they must be read out consecutively at some later point, no matter all other
|
||||
|
|
Loading…
Reference in a new issue