hdl: remove deprecated Sample, Past, Stable, Rose, Fell.

This commit is contained in:
Catherine 2023-11-25 00:38:33 +00:00
parent 475b0f35dd
commit 750cbbc3c7
13 changed files with 23 additions and 328 deletions

View file

@ -1,2 +1,4 @@
from .hdl.ast import AnyConst, AnySeq, Assert, Assume, Cover
from .hdl.ast import Past, Stable, Rose, Fell, Initial
from .hdl.ast import AnyConst, AnySeq, Initial, Assert, Assume, Cover
__all__ = ["AnyConst", "AnySeq", "Initial", "Assert", "Assume", "Cover"]

View file

@ -398,12 +398,6 @@ class _ValueCompiler(xfrm.ValueVisitor):
def on_ResetSignal(self, value):
raise NotImplementedError # :nocov:
def on_Sample(self, value):
raise NotImplementedError # :nocov:
def on_Initial(self, value):
raise NotImplementedError # :nocov:
def on_Cat(self, value):
return "{{ {} }}".format(" ".join(reversed([self(o) for o in value.parts])))
@ -498,6 +492,13 @@ class _RHSValueCompiler(_ValueCompiler):
self.s.anys[value] = res
return res
def on_Initial(self, value):
res = self.s.rtlil.wire(width=1, src=_src(value.src_loc))
self.s.rtlil.cell("$initstate", ports={
"\\Y": res,
}, src=_src(value.src_loc))
return res
def on_Signal(self, value):
wire_curr, wire_next = self.s.resolve(value)
return wire_curr
@ -646,6 +647,9 @@ class _LHSValueCompiler(_ValueCompiler):
def on_AnySeq(self, value):
raise TypeError # :nocov:
def on_Initial(self, value):
raise TypeError # :nocov:
def on_Operator(self, value):
if value.operator in ("u", "s"):
# These operators are transparent on the LHS.

View file

@ -9,7 +9,7 @@ import jinja2
from .. import __version__
from .._toolchain import *
from ..hdl import *
from ..hdl.xfrm import SampleLowerer, DomainLowerer
from ..hdl.xfrm import DomainLowerer
from ..lib.cdc import ResetSynchronizer
from ..back import rtlil, verilog
from .res import *
@ -143,7 +143,6 @@ class Platform(ResourceManager, metaclass=ABCMeta):
self._prepared = True
fragment = Fragment.get(elaboratable, self)
fragment = SampleLowerer()(fragment)
fragment._propagate_domains(self.create_missing_domain, platform=self)
fragment = DomainLowerer()(fragment)

View file

@ -20,7 +20,7 @@ __all__ = [
"Array", "ArrayProxy",
"Signal", "ClockSignal", "ResetSignal",
"ValueCastable", "ValueLike",
"Sample", "Past", "Stable", "Rose", "Fell", "Initial",
"Initial",
"Statement", "Switch",
"Property", "Assign", "Assert", "Assume", "Cover",
"ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict", "SignalSet",
@ -1549,70 +1549,6 @@ class ValueLike(metaclass=_ValueLikeMeta):
raise TypeError("ValueLike is an abstract class and cannot be constructed")
# TODO(amaranth-0.5): remove
@final
class Sample(Value):
"""Value from the past.
A ``Sample`` of an expression is equal to the value of the expression ``clocks`` clock edges
of the ``domain`` clock back. If that moment is before the beginning of time, it is equal
to the value of the expression calculated as if each signal had its reset value.
"""
@deprecated("instead of using `Sample`, create a register explicitly")
def __init__(self, expr, clocks, domain, *, src_loc_at=0):
super().__init__(src_loc_at=1 + src_loc_at)
self.value = Value.cast(expr)
self.clocks = int(clocks)
self.domain = domain
if not isinstance(self.value, (Const, Signal, ClockSignal, ResetSignal, Initial)):
raise TypeError("Sampled value must be a signal or a constant, not {!r}"
.format(self.value))
if self.clocks < 0:
raise ValueError("Cannot sample a value {} cycles in the future"
.format(-self.clocks))
if not (self.domain is None or isinstance(self.domain, str)):
raise TypeError("Domain name must be a string or None, not {!r}"
.format(self.domain))
def shape(self):
return self.value.shape()
def _rhs_signals(self):
return SignalSet((self,))
def __repr__(self):
return "(sample {!r} @ {}[{}])".format(
self.value, "<default>" if self.domain is None else self.domain, self.clocks)
# TODO(amaranth-0.5): remove
@deprecated("instead of using `Past`, create a register explicitly")
def Past(expr, clocks=1, domain=None):
with _ignore_deprecated():
return Sample(expr, clocks, domain)
# TODO(amaranth-0.5): remove
@deprecated("instead of using `Stable`, create registers and comparisons explicitly")
def Stable(expr, clocks=0, domain=None):
with _ignore_deprecated():
return Sample(expr, clocks + 1, domain) == Sample(expr, clocks, domain)
# TODO(amaranth-0.5): remove
@deprecated("instead of using `Rose`, create registers and comparisons explicitly")
def Rose(expr, clocks=0, domain=None):
with _ignore_deprecated():
return ~Sample(expr, clocks + 1, domain) & Sample(expr, clocks, domain)
# TODO(amaranth-0.5): remove
@deprecated("instead of using `Fell`, create registers and comparisons explicitly")
def Fell(expr, clocks=0, domain=None):
with _ignore_deprecated():
return Sample(expr, clocks + 1, domain) & ~Sample(expr, clocks, domain)
@final
class Initial(Value):
"""Start indicator, for model checking.
@ -1626,7 +1562,7 @@ class Initial(Value):
return Shape(1)
def _rhs_signals(self):
return SignalSet((self,))
return SignalSet()
def __repr__(self):
return "(initial)"
@ -1895,8 +1831,6 @@ class ValueKey:
elif isinstance(self.value, ArrayProxy):
self._hash = hash((ValueKey(self.value.index),
tuple(ValueKey(e) for e in self.value._iter_as_values())))
elif isinstance(self.value, Sample):
self._hash = hash((ValueKey(self.value.value), self.value.clocks, self.value.domain))
elif isinstance(self.value, Initial):
self._hash = 0
else: # :nocov:
@ -1942,10 +1876,6 @@ class ValueKey:
all(ValueKey(a) == ValueKey(b)
for a, b in zip(self.value._iter_as_values(),
other.value._iter_as_values())))
elif isinstance(self.value, Sample):
return (ValueKey(self.value.value) == ValueKey(other.value.value) and
self.value.clocks == other.value.clocks and
self.value.domain == self.value.domain)
elif isinstance(self.value, Initial):
return True
else: # :nocov:

View file

@ -491,7 +491,6 @@ class Module(_ModuleBuilderRoot, Elaboratable):
.format(domain_name(domain)))
stmt._MustUse__used = True
stmt = SampleDomainInjector(domain)(stmt)
for signal in stmt._lhs_signals():
if signal not in self._driving:
@ -539,8 +538,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
fragment.add_subfragment(Fragment.get(self._named_submodules[name], platform), name)
for submodule in self._anon_submodules:
fragment.add_subfragment(Fragment.get(submodule, platform), None)
statements = SampleDomainInjector("sync")(self._statements)
fragment.add_statements(statements)
fragment.add_statements(self._statements)
for signal, domain in self._driving.items():
fragment.add_driver(signal, domain)
fragment.add_domains(self._domains.values())

View file

@ -506,11 +506,10 @@ class Fragment:
self.add_ports(sig, dir="i")
def prepare(self, ports=None, missing_domain=lambda name: ClockDomain(name)):
from .xfrm import SampleLowerer, DomainLowerer
from .xfrm import DomainLowerer
fragment = SampleLowerer()(self)
new_domains = fragment._propagate_domains(missing_domain)
fragment = DomainLowerer()(fragment)
new_domains = self._propagate_domains(missing_domain)
fragment = DomainLowerer()(self)
if ports is None:
fragment._propagate_ports(ports=(), all_undef_as_ports=True)
else:

View file

@ -15,7 +15,6 @@ __all__ = ["ValueVisitor", "ValueTransformer",
"FragmentTransformer",
"TransformedElaboratable",
"DomainCollector", "DomainRenamer", "DomainLowerer",
"SampleDomainInjector", "SampleLowerer",
"SwitchCleaner", "LHSGroupAnalyzer", "LHSGroupFilter",
"ResetInserter", "EnableInserter"]
@ -65,10 +64,6 @@ class ValueVisitor(metaclass=ABCMeta):
def on_ArrayProxy(self, value):
pass # :nocov:
@abstractmethod
def on_Sample(self, value):
pass # :nocov:
@abstractmethod
def on_Initial(self, value):
pass # :nocov:
@ -103,8 +98,6 @@ class ValueVisitor(metaclass=ABCMeta):
new_value = self.on_Cat(value)
elif type(value) is ArrayProxy:
new_value = self.on_ArrayProxy(value)
elif type(value) is Sample:
new_value = self.on_Sample(value)
elif type(value) is Initial:
new_value = self.on_Initial(value)
else:
@ -153,9 +146,6 @@ class ValueTransformer(ValueVisitor):
return ArrayProxy([self.on_value(elem) for elem in value._iter_as_values()],
self.on_value(value.index))
def on_Sample(self, value):
return Sample(self.on_value(value.value), value.clocks, value.domain)
def on_Initial(self, value):
return value
@ -369,9 +359,6 @@ class DomainCollector(ValueVisitor, StatementVisitor):
self.on_value(elem)
self.on_value(value.index)
def on_Sample(self, value):
self.on_value(value.value)
def on_Initial(self, value):
pass
@ -509,81 +496,6 @@ class DomainLowerer(FragmentTransformer, ValueTransformer, StatementTransformer)
return new_fragment
class SampleDomainInjector(ValueTransformer, StatementTransformer):
def __init__(self, domain):
self.domain = domain
@_ignore_deprecated
def on_Sample(self, value):
if value.domain is not None:
return value
return Sample(value.value, value.clocks, self.domain)
def __call__(self, stmts):
return self.on_statement(stmts)
class SampleLowerer(FragmentTransformer, ValueTransformer, StatementTransformer):
def __init__(self):
self.initial = None
self.sample_cache = None
self.sample_stmts = None
def _name_reset(self, value):
if isinstance(value, Const):
return f"c${value.value}", value.value
elif isinstance(value, Signal):
return f"s${value.name}", value.reset
elif isinstance(value, ClockSignal):
return "clk", 0
elif isinstance(value, ResetSignal):
return "rst", 1
elif isinstance(value, Initial):
return "init", 0 # Past(Initial()) produces 0, 1, 0, 0, ...
else:
raise NotImplementedError # :nocov:
@_ignore_deprecated
def on_Sample(self, value):
if value in self.sample_cache:
return self.sample_cache[value]
sampled_value = self.on_value(value.value)
if value.clocks == 0:
sample = sampled_value
else:
assert value.domain is not None
sampled_name, sampled_reset = self._name_reset(value.value)
name = f"$sample${sampled_name}${value.domain}${value.clocks}"
sample = Signal.like(value.value, name=name, reset_less=True, reset=sampled_reset)
sample.attrs["amaranth.sample_reg"] = True
prev_sample = self.on_Sample(Sample(sampled_value, value.clocks - 1, value.domain))
if value.domain not in self.sample_stmts:
self.sample_stmts[value.domain] = []
self.sample_stmts[value.domain].append(sample.eq(prev_sample))
self.sample_cache[value] = sample
return sample
def on_Initial(self, value):
if self.initial is None:
self.initial = Signal(name="init")
return self.initial
def map_statements(self, fragment, new_fragment):
self.initial = None
self.sample_cache = ValueDict()
self.sample_stmts = OrderedDict()
new_fragment.add_statements(map(self.on_statement, fragment.statements))
for domain, stmts in self.sample_stmts.items():
new_fragment.add_statements(stmts)
for stmt in stmts:
new_fragment.add_driver(stmt.lhs, domain)
if self.initial is not None:
new_fragment.add_subfragment(Instance("$initstate", o_Y=self.initial))
class SwitchCleaner(StatementVisitor):
def on_ignore(self, stmt):
return stmt

View file

@ -103,9 +103,6 @@ class _ValueCompiler(ValueVisitor, _Compiler):
def on_AnySeq(self, value):
raise NotImplementedError # :nocov:
def on_Sample(self, value):
raise NotImplementedError # :nocov:
def on_Initial(self, value):
raise NotImplementedError # :nocov:

View file

@ -13,6 +13,7 @@ Language changes
.. currentmodule:: amaranth.hdl
* Removed: (deprecated in 0.4) :meth:`Const.normalize`. (`RFC 5`_)
* Removed: (deprecated in 0.4) :class:`ast.Sample`, :class:`ast.Past`, :class:`ast.Stable`, :class:`ast.Rose`, :class:`ast.Fell`.
Standard library changes

View file

@ -894,7 +894,7 @@ class CatTestCase(FHDLTestCase):
def test_cast(self):
c = Cat(1, 0)
self.assertEqual(repr(c), "(cat (const 1'd1) (const 1'd0))")
def test_str_wrong(self):
with self.assertRaisesRegex(TypeError,
r"^Object 'foo' cannot be converted to an Amaranth value$"):
@ -1382,39 +1382,6 @@ class ValueLikeTestCase(FHDLTestCase):
self.assertFalse(isinstance(EnumD.A, ValueLike))
class SampleTestCase(FHDLTestCase):
@_ignore_deprecated
def test_const(self):
s = Sample(1, 1, "sync")
self.assertEqual(s.shape(), unsigned(1))
@_ignore_deprecated
def test_signal(self):
s1 = Sample(Signal(2), 1, "sync")
self.assertEqual(s1.shape(), unsigned(2))
s2 = Sample(ClockSignal(), 1, "sync")
s3 = Sample(ResetSignal(), 1, "sync")
@_ignore_deprecated
def test_wrong_value_operator(self):
with self.assertRaisesRegex(TypeError,
(r"^Sampled value must be a signal or a constant, not "
r"\(\+ \(sig \$signal\) \(const 1'd1\)\)$")):
Sample(Signal() + 1, 1, "sync")
@_ignore_deprecated
def test_wrong_clocks_neg(self):
with self.assertRaisesRegex(ValueError,
r"^Cannot sample a value 1 cycles in the future$"):
Sample(Signal(), -1, "sync")
@_ignore_deprecated
def test_wrong_domain(self):
with self.assertRaisesRegex(TypeError,
r"^Domain name must be a string or None, not 0$"):
Sample(Signal(), 1, 0)
class InitialTestCase(FHDLTestCase):
def test_initial(self):
i = Initial()

View file

@ -133,25 +133,6 @@ class DSLTestCase(FHDLTestCase):
)
""")
@_ignore_deprecated
def test_sample_domain(self):
m = Module()
i = Signal()
o1 = Signal()
o2 = Signal()
o3 = Signal()
m.d.sync += o1.eq(Past(i))
m.d.pix += o2.eq(Past(i))
m.d.pix += o3.eq(Past(i, domain="sync"))
f = m.elaborate(platform=None)
self.assertRepr(f.statements, """
(
(eq (sig o1) (sample (sig i) @ sync[1]))
(eq (sig o2) (sample (sig i) @ pix[1]))
(eq (sig o3) (sample (sig i) @ sync[1]))
)
""")
def test_If(self):
m = Module()
with m.If(self.s1):

View file

@ -210,54 +210,6 @@ class DomainLowererTestCase(FHDLTestCase):
DomainLowerer()(f)
class SampleLowererTestCase(FHDLTestCase):
def setUp(self):
self.i = Signal()
self.o1 = Signal()
self.o2 = Signal()
self.o3 = Signal()
@_ignore_deprecated
def test_lower_signal(self):
f = Fragment()
f.add_statements(
self.o1.eq(Sample(self.i, 2, "sync")),
self.o2.eq(Sample(self.i, 1, "sync")),
self.o3.eq(Sample(self.i, 1, "pix")),
)
f = SampleLowerer()(f)
self.assertRepr(f.statements, """
(
(eq (sig o1) (sig $sample$s$i$sync$2))
(eq (sig o2) (sig $sample$s$i$sync$1))
(eq (sig o3) (sig $sample$s$i$pix$1))
(eq (sig $sample$s$i$sync$1) (sig i))
(eq (sig $sample$s$i$sync$2) (sig $sample$s$i$sync$1))
(eq (sig $sample$s$i$pix$1) (sig i))
)
""")
self.assertEqual(len(f.drivers["sync"]), 2)
self.assertEqual(len(f.drivers["pix"]), 1)
@_ignore_deprecated
def test_lower_const(self):
f = Fragment()
f.add_statements(
self.o1.eq(Sample(1, 2, "sync")),
)
f = SampleLowerer()(f)
self.assertRepr(f.statements, """
(
(eq (sig o1) (sig $sample$c$1$sync$2))
(eq (sig $sample$c$1$sync$1) (const 1'd1))
(eq (sig $sample$c$1$sync$2) (sig $sample$c$1$sync$1))
)
""")
self.assertEqual(len(f.drivers["sync"]), 2)
class SwitchCleanerTestCase(FHDLTestCase):
def test_clean(self):
a = Signal()

View file

@ -911,53 +911,6 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
sim.add_clock(1e-6)
sim.add_sync_process(process)
@_ignore_deprecated
def test_sample_helpers(self):
m = Module()
s = Signal(2)
def mk(x):
y = Signal.like(x)
m.d.comb += y.eq(x)
return y
p0, r0, f0, s0 = mk(Past(s, 0)), mk(Rose(s)), mk(Fell(s)), mk(Stable(s))
p1, r1, f1, s1 = mk(Past(s)), mk(Rose(s, 1)), mk(Fell(s, 1)), mk(Stable(s, 1))
p2, r2, f2, s2 = mk(Past(s, 2)), mk(Rose(s, 2)), mk(Fell(s, 2)), mk(Stable(s, 2))
p3, r3, f3, s3 = mk(Past(s, 3)), mk(Rose(s, 3)), mk(Fell(s, 3)), mk(Stable(s, 3))
with self.assertSimulation(m) as sim:
def process_gen():
yield s.eq(0b10)
yield
yield
yield s.eq(0b01)
yield
def process_check():
yield
yield
yield
self.assertEqual((yield p0), 0b01)
self.assertEqual((yield p1), 0b10)
self.assertEqual((yield p2), 0b10)
self.assertEqual((yield p3), 0b00)
self.assertEqual((yield s0), 0b0)
self.assertEqual((yield s1), 0b1)
self.assertEqual((yield s2), 0b0)
self.assertEqual((yield s3), 0b1)
self.assertEqual((yield r0), 0b01)
self.assertEqual((yield r1), 0b00)
self.assertEqual((yield r2), 0b10)
self.assertEqual((yield r3), 0b00)
self.assertEqual((yield f0), 0b10)
self.assertEqual((yield f1), 0b00)
self.assertEqual((yield f2), 0b00)
self.assertEqual((yield f3), 0b00)
sim.add_clock(1e-6)
sim.add_sync_process(process_gen)
sim.add_sync_process(process_check)
def test_vcd_wrong_nonzero_time(self):
s = Signal()
m = Module()