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
|
from nmigen.cli import main
|
||||||
|
|
||||||
|
|
||||||
class ALU:
|
class ALU(Elaboratable):
|
||||||
def __init__(self, width):
|
def __init__(self, width):
|
||||||
self.sel = Signal(2)
|
self.sel = Signal(2)
|
||||||
self.a = Signal(width)
|
self.a = Signal(width)
|
||||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
||||||
from nmigen.cli import main
|
from nmigen.cli import main
|
||||||
|
|
||||||
|
|
||||||
class Adder:
|
class Adder(Elaboratable):
|
||||||
def __init__(self, width):
|
def __init__(self, width):
|
||||||
self.a = Signal(width)
|
self.a = Signal(width)
|
||||||
self.b = Signal(width)
|
self.b = Signal(width)
|
||||||
|
@ -14,7 +14,7 @@ class Adder:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class Subtractor:
|
class Subtractor(Elaboratable):
|
||||||
def __init__(self, width):
|
def __init__(self, width):
|
||||||
self.a = Signal(width)
|
self.a = Signal(width)
|
||||||
self.b = Signal(width)
|
self.b = Signal(width)
|
||||||
|
@ -26,7 +26,7 @@ class Subtractor:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class ALU:
|
class ALU(Elaboratable):
|
||||||
def __init__(self, width):
|
def __init__(self, width):
|
||||||
self.op = Signal()
|
self.op = Signal()
|
||||||
self.a = Signal(width)
|
self.a = Signal(width)
|
||||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
||||||
from nmigen.cli import main
|
from nmigen.cli import main
|
||||||
|
|
||||||
|
|
||||||
class ClockDivisor:
|
class ClockDivisor(Elaboratable):
|
||||||
def __init__(self, factor):
|
def __init__(self, factor):
|
||||||
self.v = Signal(factor)
|
self.v = Signal(factor)
|
||||||
self.o = Signal()
|
self.o = Signal()
|
||||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
||||||
from nmigen.cli import main, pysim
|
from nmigen.cli import main, pysim
|
||||||
|
|
||||||
|
|
||||||
class Counter:
|
class Counter(Elaboratable):
|
||||||
def __init__(self, width):
|
def __init__(self, width):
|
||||||
self.v = Signal(width, reset=2**width-1)
|
self.v = Signal(width, reset=2**width-1)
|
||||||
self.o = Signal()
|
self.o = Signal()
|
||||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
||||||
from nmigen.back import rtlil, verilog, pysim
|
from nmigen.back import rtlil, verilog, pysim
|
||||||
|
|
||||||
|
|
||||||
class Counter:
|
class Counter(Elaboratable):
|
||||||
def __init__(self, width):
|
def __init__(self, width):
|
||||||
self.v = Signal(width, reset=2**width-1)
|
self.v = Signal(width, reset=2**width-1)
|
||||||
self.o = Signal()
|
self.o = Signal()
|
||||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
||||||
from nmigen.cli import main
|
from nmigen.cli import main
|
||||||
|
|
||||||
|
|
||||||
class UARTReceiver:
|
class UARTReceiver(Elaboratable):
|
||||||
def __init__(self, divisor):
|
def __init__(self, divisor):
|
||||||
self.divisor = divisor
|
self.divisor = divisor
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from nmigen import *
|
||||||
from nmigen.cli import main
|
from nmigen.cli import main
|
||||||
|
|
||||||
|
|
||||||
class GPIO:
|
class GPIO(Elaboratable):
|
||||||
def __init__(self, pins, bus):
|
def __init__(self, pins, bus):
|
||||||
self.pins = pins
|
self.pins = pins
|
||||||
self.bus = bus
|
self.bus = bus
|
||||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
||||||
from nmigen.cli import main
|
from nmigen.cli import main
|
||||||
|
|
||||||
|
|
||||||
class System:
|
class System(Elaboratable):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.adr = Signal(16)
|
self.adr = Signal(16)
|
||||||
self.dat_r = Signal(8)
|
self.dat_r = Signal(8)
|
||||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
||||||
from nmigen.cli import main
|
from nmigen.cli import main
|
||||||
|
|
||||||
|
|
||||||
class RegisterFile:
|
class RegisterFile(Elaboratable):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.adr = Signal(4)
|
self.adr = Signal(4)
|
||||||
self.dat_r = Signal(8)
|
self.dat_r = Signal(8)
|
||||||
|
|
|
@ -2,7 +2,7 @@ from nmigen import *
|
||||||
from nmigen.cli import main
|
from nmigen.cli import main
|
||||||
|
|
||||||
|
|
||||||
class ParMux:
|
class ParMux(Elaboratable):
|
||||||
def __init__(self, width):
|
def __init__(self, width):
|
||||||
self.s = Signal(3)
|
self.s = Signal(3)
|
||||||
self.a = Signal(width)
|
self.a = Signal(width)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from .hdl.ast import Value, Const, C, Mux, Cat, Repl, Array, Signal, ClockSignal, ResetSignal
|
from .hdl.ast import Value, Const, C, Mux, Cat, Repl, Array, Signal, ClockSignal, ResetSignal
|
||||||
from .hdl.dsl import Module
|
from .hdl.dsl import Module
|
||||||
from .hdl.cd import ClockDomain
|
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.mem import Memory
|
||||||
from .hdl.rec import Record
|
from .hdl.rec import Record
|
||||||
from .hdl.xfrm import ResetInserter, CEInserter
|
from .hdl.xfrm import ResetInserter, CEInserter
|
||||||
|
|
|
@ -9,7 +9,7 @@ from .ir import *
|
||||||
from .xfrm import *
|
from .xfrm import *
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Module", "SyntaxError", "SyntaxWarning"]
|
__all__ = ["SyntaxError", "SyntaxWarning", "Module"]
|
||||||
|
|
||||||
|
|
||||||
class SyntaxError(Exception):
|
class SyntaxError(Exception):
|
||||||
|
@ -109,7 +109,7 @@ class FSM:
|
||||||
return self.state == self.encoding[name]
|
return self.state == self.encoding[name]
|
||||||
|
|
||||||
|
|
||||||
class Module(_ModuleBuilderRoot):
|
class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
_ModuleBuilderRoot.__init__(self, self, depth=0)
|
_ModuleBuilderRoot.__init__(self, self, depth=0)
|
||||||
self.submodules = _ModuleBuilderSubmodules(self)
|
self.submodules = _ModuleBuilderSubmodules(self)
|
||||||
|
|
|
@ -1,12 +1,30 @@
|
||||||
import warnings
|
from abc import ABCMeta, abstractmethod
|
||||||
from collections import defaultdict, OrderedDict
|
from collections import defaultdict, OrderedDict
|
||||||
|
import warnings
|
||||||
|
import traceback
|
||||||
|
import sys
|
||||||
|
|
||||||
from ..tools import *
|
from ..tools import *
|
||||||
from .ast import *
|
from .ast import *
|
||||||
from .cd 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):
|
class DriverConflict(UserWarning):
|
||||||
|
@ -16,13 +34,22 @@ class DriverConflict(UserWarning):
|
||||||
class Fragment:
|
class Fragment:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(obj, platform):
|
def get(obj, platform):
|
||||||
if isinstance(obj, Fragment):
|
while True:
|
||||||
return obj
|
if isinstance(obj, Fragment):
|
||||||
elif hasattr(obj, "elaborate"):
|
return obj
|
||||||
frag = obj.elaborate(platform)
|
elif isinstance(obj, Elaboratable):
|
||||||
else:
|
obj._Elaboratable__used = True
|
||||||
raise AttributeError("Object '{!r}' cannot be elaborated".format(obj))
|
obj = obj.elaborate(platform)
|
||||||
return Fragment.get(frag, 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):
|
def __init__(self):
|
||||||
self.ports = SignalDict()
|
self.ports = SignalDict()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from .. import tracer
|
from .. import tracer
|
||||||
from .ast import *
|
from .ast import *
|
||||||
from .ir import Instance
|
from .ir import Elaboratable, Instance
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Memory", "ReadPort", "WritePort", "DummyPort"]
|
__all__ = ["Memory", "ReadPort", "WritePort", "DummyPort"]
|
||||||
|
@ -70,7 +70,7 @@ class Memory:
|
||||||
return self._array[index]
|
return self._array[index]
|
||||||
|
|
||||||
|
|
||||||
class ReadPort:
|
class ReadPort(Elaboratable):
|
||||||
def __init__(self, memory, domain, synchronous, transparent):
|
def __init__(self, memory, domain, synchronous, transparent):
|
||||||
self.memory = memory
|
self.memory = memory
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
|
@ -135,7 +135,7 @@ class ReadPort:
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|
||||||
class WritePort:
|
class WritePort(Elaboratable):
|
||||||
def __init__(self, memory, domain, priority, granularity):
|
def __init__(self, memory, domain, priority, granularity):
|
||||||
self.memory = memory
|
self.memory = memory
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
|
|
|
@ -292,7 +292,7 @@ class FragmentTransformer:
|
||||||
raise AttributeError("Object '{!r}' cannot be elaborated".format(value))
|
raise AttributeError("Object '{!r}' cannot be elaborated".format(value))
|
||||||
|
|
||||||
|
|
||||||
class TransformedElaboratable:
|
class TransformedElaboratable(Elaboratable):
|
||||||
def __init__(self, elaboratable):
|
def __init__(self, elaboratable):
|
||||||
assert hasattr(elaboratable, "elaborate")
|
assert hasattr(elaboratable, "elaborate")
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from .. import *
|
||||||
__all__ = ["MultiReg", "ResetSynchronizer"]
|
__all__ = ["MultiReg", "ResetSynchronizer"]
|
||||||
|
|
||||||
|
|
||||||
class MultiReg:
|
class MultiReg(Elaboratable):
|
||||||
"""Resynchronise a signal to a different clock domain.
|
"""Resynchronise a signal to a different clock domain.
|
||||||
|
|
||||||
Consists of a chain of flip-flops. Eliminates metastabilities at the output, but provides
|
Consists of a chain of flip-flops. Eliminates metastabilities at the output, but provides
|
||||||
|
@ -69,7 +69,7 @@ class MultiReg:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class ResetSynchronizer:
|
class ResetSynchronizer(Elaboratable):
|
||||||
def __init__(self, arst, domain="sync", n=2):
|
def __init__(self, arst, domain="sync", n=2):
|
||||||
self.arst = arst
|
self.arst = arst
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
|
|
|
@ -10,7 +10,7 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class Encoder:
|
class Encoder(Elaboratable):
|
||||||
"""Encode one-hot to binary.
|
"""Encode one-hot to binary.
|
||||||
|
|
||||||
If one bit in ``i`` is asserted, ``n`` is low and ``o`` indicates the asserted bit.
|
If one bit in ``i`` is asserted, ``n`` is low and ``o`` indicates the asserted bit.
|
||||||
|
@ -48,7 +48,7 @@ class Encoder:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class PriorityEncoder:
|
class PriorityEncoder(Elaboratable):
|
||||||
"""Priority encode requests to binary.
|
"""Priority encode requests to binary.
|
||||||
|
|
||||||
If any bit in ``i`` is asserted, ``n`` is low and ``o`` indicates the least significant
|
If any bit in ``i`` is asserted, ``n`` is low and ``o`` indicates the least significant
|
||||||
|
@ -85,7 +85,7 @@ class PriorityEncoder:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class Decoder:
|
class Decoder(Elaboratable):
|
||||||
"""Decode binary to one-hot.
|
"""Decode binary to one-hot.
|
||||||
|
|
||||||
If ``n`` is low, only the ``i``th bit in ``o`` is asserted.
|
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.
|
"""Encode binary to Gray code.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
@ -157,7 +157,7 @@ class GrayEncoder:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class GrayDecoder:
|
class GrayDecoder(Elaboratable):
|
||||||
"""Decode Gray code to binary.
|
"""Decode Gray code to binary.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
|
|
@ -102,7 +102,7 @@ def _decr(signal, modulo):
|
||||||
return Mux(signal == 0, modulo - 1, signal - 1)
|
return Mux(signal == 0, modulo - 1, signal - 1)
|
||||||
|
|
||||||
|
|
||||||
class SyncFIFO(FIFOInterface):
|
class SyncFIFO(Elaboratable, FIFOInterface):
|
||||||
__doc__ = FIFOInterface._doc_template.format(
|
__doc__ = FIFOInterface._doc_template.format(
|
||||||
description="""
|
description="""
|
||||||
Synchronous first in, first out queue.
|
Synchronous first in, first out queue.
|
||||||
|
@ -209,7 +209,7 @@ class SyncFIFO(FIFOInterface):
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class SyncFIFOBuffered(FIFOInterface):
|
class SyncFIFOBuffered(Elaboratable, FIFOInterface):
|
||||||
__doc__ = FIFOInterface._doc_template.format(
|
__doc__ = FIFOInterface._doc_template.format(
|
||||||
description="""
|
description="""
|
||||||
Buffered synchronous first in, first out queue.
|
Buffered synchronous first in, first out queue.
|
||||||
|
@ -265,7 +265,7 @@ class SyncFIFOBuffered(FIFOInterface):
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class AsyncFIFO(FIFOInterface):
|
class AsyncFIFO(Elaboratable, FIFOInterface):
|
||||||
__doc__ = FIFOInterface._doc_template.format(
|
__doc__ = FIFOInterface._doc_template.format(
|
||||||
description="""
|
description="""
|
||||||
Asynchronous first in, first out queue.
|
Asynchronous first in, first out queue.
|
||||||
|
@ -361,7 +361,7 @@ class AsyncFIFO(FIFOInterface):
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class AsyncFIFOBuffered(FIFOInterface):
|
class AsyncFIFOBuffered(Elaboratable, FIFOInterface):
|
||||||
__doc__ = FIFOInterface._doc_template.format(
|
__doc__ = FIFOInterface._doc_template.format(
|
||||||
description="""
|
description="""
|
||||||
Buffered asynchronous first in, first out queue.
|
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):
|
def __init__(self):
|
||||||
self.s1 = Signal()
|
self.s1 = Signal()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from .tools import *
|
from .tools import *
|
||||||
from ..hdl.ast import *
|
from ..hdl.ast import *
|
||||||
from ..hdl.dsl import *
|
from ..hdl.dsl import *
|
||||||
|
from ..hdl.ir import *
|
||||||
from ..back.pysim import *
|
from ..back.pysim import *
|
||||||
from ..lib.coding import *
|
from ..lib.coding import *
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ class DecoderTestCase(FHDLTestCase):
|
||||||
sim.run()
|
sim.run()
|
||||||
|
|
||||||
|
|
||||||
class ReversibleSpec:
|
class ReversibleSpec(Elaboratable):
|
||||||
def __init__(self, encoder_cls, decoder_cls, args):
|
def __init__(self, encoder_cls, decoder_cls, args):
|
||||||
self.encoder_cls = encoder_cls
|
self.encoder_cls = encoder_cls
|
||||||
self.decoder_cls = decoder_cls
|
self.decoder_cls = decoder_cls
|
||||||
|
@ -99,7 +100,7 @@ class ReversibleSpec:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class HammingDistanceSpec:
|
class HammingDistanceSpec(Elaboratable):
|
||||||
def __init__(self, distance, encoder_cls, args):
|
def __init__(self, distance, encoder_cls, args):
|
||||||
self.distance = distance
|
self.distance = distance
|
||||||
self.encoder_cls = encoder_cls
|
self.encoder_cls = encoder_cls
|
||||||
|
|
|
@ -45,7 +45,7 @@ class FIFOSmokeTestCase(FHDLTestCase):
|
||||||
self.assertAsyncFIFOWorks(AsyncFIFOBuffered(width=8, depth=3))
|
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.
|
Non-synthesizable first-in first-out queue, implemented naively as a chain of registers.
|
||||||
"""
|
"""
|
||||||
|
@ -104,7 +104,7 @@ class FIFOModel(FIFOInterface):
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class FIFOModelEquivalenceSpec:
|
class FIFOModelEquivalenceSpec(Elaboratable):
|
||||||
"""
|
"""
|
||||||
The first-in first-out queue model equivalence specification: for any inputs and control
|
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,
|
signals, the behavior of the implementation under test exactly matches the ideal model,
|
||||||
|
@ -148,7 +148,7 @@ class FIFOModelEquivalenceSpec:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
class FIFOContractSpec:
|
class FIFOContractSpec(Elaboratable):
|
||||||
"""
|
"""
|
||||||
The first-in first-out queue contract specification: if two elements are written to the queue
|
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
|
consecutively, they must be read out consecutively at some later point, no matter all other
|
||||||
|
|
Loading…
Reference in a new issue