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 AnyConst, AnySeq, Initial, Assert, Assume, Cover
from .hdl.ast import Past, Stable, Rose, Fell, Initial
__all__ = ["AnyConst", "AnySeq", "Initial", "Assert", "Assume", "Cover"]

View file

@ -398,12 +398,6 @@ class _ValueCompiler(xfrm.ValueVisitor):
def on_ResetSignal(self, value): def on_ResetSignal(self, value):
raise NotImplementedError # :nocov: raise NotImplementedError # :nocov:
def on_Sample(self, value):
raise NotImplementedError # :nocov:
def on_Initial(self, value):
raise NotImplementedError # :nocov:
def on_Cat(self, value): def on_Cat(self, value):
return "{{ {} }}".format(" ".join(reversed([self(o) for o in value.parts]))) return "{{ {} }}".format(" ".join(reversed([self(o) for o in value.parts])))
@ -498,6 +492,13 @@ class _RHSValueCompiler(_ValueCompiler):
self.s.anys[value] = res self.s.anys[value] = res
return 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): def on_Signal(self, value):
wire_curr, wire_next = self.s.resolve(value) wire_curr, wire_next = self.s.resolve(value)
return wire_curr return wire_curr
@ -646,6 +647,9 @@ class _LHSValueCompiler(_ValueCompiler):
def on_AnySeq(self, value): def on_AnySeq(self, value):
raise TypeError # :nocov: raise TypeError # :nocov:
def on_Initial(self, value):
raise TypeError # :nocov:
def on_Operator(self, value): def on_Operator(self, value):
if value.operator in ("u", "s"): if value.operator in ("u", "s"):
# These operators are transparent on the LHS. # These operators are transparent on the LHS.

View file

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

View file

@ -20,7 +20,7 @@ __all__ = [
"Array", "ArrayProxy", "Array", "ArrayProxy",
"Signal", "ClockSignal", "ResetSignal", "Signal", "ClockSignal", "ResetSignal",
"ValueCastable", "ValueLike", "ValueCastable", "ValueLike",
"Sample", "Past", "Stable", "Rose", "Fell", "Initial", "Initial",
"Statement", "Switch", "Statement", "Switch",
"Property", "Assign", "Assert", "Assume", "Cover", "Property", "Assign", "Assert", "Assume", "Cover",
"ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict", "SignalSet", "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") 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 @final
class Initial(Value): class Initial(Value):
"""Start indicator, for model checking. """Start indicator, for model checking.
@ -1626,7 +1562,7 @@ class Initial(Value):
return Shape(1) return Shape(1)
def _rhs_signals(self): def _rhs_signals(self):
return SignalSet((self,)) return SignalSet()
def __repr__(self): def __repr__(self):
return "(initial)" return "(initial)"
@ -1895,8 +1831,6 @@ class ValueKey:
elif isinstance(self.value, ArrayProxy): elif isinstance(self.value, ArrayProxy):
self._hash = hash((ValueKey(self.value.index), self._hash = hash((ValueKey(self.value.index),
tuple(ValueKey(e) for e in self.value._iter_as_values()))) 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): elif isinstance(self.value, Initial):
self._hash = 0 self._hash = 0
else: # :nocov: else: # :nocov:
@ -1942,10 +1876,6 @@ class ValueKey:
all(ValueKey(a) == ValueKey(b) all(ValueKey(a) == ValueKey(b)
for a, b in zip(self.value._iter_as_values(), for a, b in zip(self.value._iter_as_values(),
other.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): elif isinstance(self.value, Initial):
return True return True
else: # :nocov: else: # :nocov:

View file

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

View file

@ -506,11 +506,10 @@ class Fragment:
self.add_ports(sig, dir="i") self.add_ports(sig, dir="i")
def prepare(self, ports=None, missing_domain=lambda name: ClockDomain(name)): 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 = self._propagate_domains(missing_domain)
new_domains = fragment._propagate_domains(missing_domain) fragment = DomainLowerer()(self)
fragment = DomainLowerer()(fragment)
if ports is None: if ports is None:
fragment._propagate_ports(ports=(), all_undef_as_ports=True) fragment._propagate_ports(ports=(), all_undef_as_ports=True)
else: else:

View file

@ -15,7 +15,6 @@ __all__ = ["ValueVisitor", "ValueTransformer",
"FragmentTransformer", "FragmentTransformer",
"TransformedElaboratable", "TransformedElaboratable",
"DomainCollector", "DomainRenamer", "DomainLowerer", "DomainCollector", "DomainRenamer", "DomainLowerer",
"SampleDomainInjector", "SampleLowerer",
"SwitchCleaner", "LHSGroupAnalyzer", "LHSGroupFilter", "SwitchCleaner", "LHSGroupAnalyzer", "LHSGroupFilter",
"ResetInserter", "EnableInserter"] "ResetInserter", "EnableInserter"]
@ -65,10 +64,6 @@ class ValueVisitor(metaclass=ABCMeta):
def on_ArrayProxy(self, value): def on_ArrayProxy(self, value):
pass # :nocov: pass # :nocov:
@abstractmethod
def on_Sample(self, value):
pass # :nocov:
@abstractmethod @abstractmethod
def on_Initial(self, value): def on_Initial(self, value):
pass # :nocov: pass # :nocov:
@ -103,8 +98,6 @@ class ValueVisitor(metaclass=ABCMeta):
new_value = self.on_Cat(value) new_value = self.on_Cat(value)
elif type(value) is ArrayProxy: elif type(value) is ArrayProxy:
new_value = self.on_ArrayProxy(value) new_value = self.on_ArrayProxy(value)
elif type(value) is Sample:
new_value = self.on_Sample(value)
elif type(value) is Initial: elif type(value) is Initial:
new_value = self.on_Initial(value) new_value = self.on_Initial(value)
else: else:
@ -153,9 +146,6 @@ class ValueTransformer(ValueVisitor):
return ArrayProxy([self.on_value(elem) for elem in value._iter_as_values()], return ArrayProxy([self.on_value(elem) for elem in value._iter_as_values()],
self.on_value(value.index)) 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): def on_Initial(self, value):
return value return value
@ -369,9 +359,6 @@ class DomainCollector(ValueVisitor, StatementVisitor):
self.on_value(elem) self.on_value(elem)
self.on_value(value.index) self.on_value(value.index)
def on_Sample(self, value):
self.on_value(value.value)
def on_Initial(self, value): def on_Initial(self, value):
pass pass
@ -509,81 +496,6 @@ class DomainLowerer(FragmentTransformer, ValueTransformer, StatementTransformer)
return new_fragment 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): class SwitchCleaner(StatementVisitor):
def on_ignore(self, stmt): def on_ignore(self, stmt):
return stmt return stmt

View file

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

View file

@ -13,6 +13,7 @@ Language changes
.. currentmodule:: amaranth.hdl .. currentmodule:: amaranth.hdl
* Removed: (deprecated in 0.4) :meth:`Const.normalize`. (`RFC 5`_) * 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 Standard library changes

View file

@ -894,7 +894,7 @@ class CatTestCase(FHDLTestCase):
def test_cast(self): def test_cast(self):
c = Cat(1, 0) c = Cat(1, 0)
self.assertEqual(repr(c), "(cat (const 1'd1) (const 1'd0))") self.assertEqual(repr(c), "(cat (const 1'd1) (const 1'd0))")
def test_str_wrong(self): def test_str_wrong(self):
with self.assertRaisesRegex(TypeError, with self.assertRaisesRegex(TypeError,
r"^Object 'foo' cannot be converted to an Amaranth value$"): r"^Object 'foo' cannot be converted to an Amaranth value$"):
@ -1382,39 +1382,6 @@ class ValueLikeTestCase(FHDLTestCase):
self.assertFalse(isinstance(EnumD.A, ValueLike)) 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): class InitialTestCase(FHDLTestCase):
def test_initial(self): def test_initial(self):
i = Initial() 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): def test_If(self):
m = Module() m = Module()
with m.If(self.s1): with m.If(self.s1):

View file

@ -210,54 +210,6 @@ class DomainLowererTestCase(FHDLTestCase):
DomainLowerer()(f) 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): class SwitchCleanerTestCase(FHDLTestCase):
def test_clean(self): def test_clean(self):
a = Signal() a = Signal()

View file

@ -911,53 +911,6 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
sim.add_clock(1e-6) sim.add_clock(1e-6)
sim.add_sync_process(process) 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): def test_vcd_wrong_nonzero_time(self):
s = Signal() s = Signal()
m = Module() m = Module()