Skip to content

(Merge after 0.4 is released) Remove deprecated Sample, Past, Stable, Rose, Fell #960

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions amaranth/asserts.py
Original file line number Diff line number Diff line change
@@ -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"]
16 changes: 10 additions & 6 deletions amaranth/back/rtlil.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,6 @@ def on_ClockSignal(self, value):
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])))

Expand Down Expand Up @@ -498,6 +492,13 @@ def on_AnySeq(self, value):
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
Expand Down Expand Up @@ -646,6 +647,9 @@ def on_AnyConst(self, value):
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.
Expand Down
3 changes: 1 addition & 2 deletions amaranth/build/plat.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
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 *
Expand Down Expand Up @@ -143,7 +143,6 @@ def prepare(self, elaboratable, name="top", **kwargs):
self._prepared = True

fragment = Fragment.get(elaboratable, self)
fragment = SampleLowerer()(fragment)
fragment._propagate_domains(self.create_missing_domain, platform=self)
fragment = DomainLowerer()(fragment)

Expand Down
74 changes: 2 additions & 72 deletions amaranth/hdl/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"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",
Expand Down Expand Up @@ -1549,70 +1549,6 @@ def __new__(cls, *args, **kwargs):
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.
Expand All @@ -1626,7 +1562,7 @@ def shape(self):
return Shape(1)

def _rhs_signals(self):
return SignalSet((self,))
return SignalSet()

def __repr__(self):
return "(initial)"
Expand Down Expand Up @@ -1895,8 +1831,6 @@ def __init__(self, value):
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:
Expand Down Expand Up @@ -1942,10 +1876,6 @@ def __eq__(self, other):
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:
Expand Down
4 changes: 1 addition & 3 deletions amaranth/hdl/dsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,6 @@ def domain_name(domain):
.format(domain_name(domain)))

stmt._MustUse__used = True
stmt = SampleDomainInjector(domain)(stmt)

for signal in stmt._lhs_signals():
if signal not in self._driving:
Expand Down Expand Up @@ -539,8 +538,7 @@ def elaborate(self, platform):
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())
Expand Down
7 changes: 3 additions & 4 deletions amaranth/hdl/ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,11 +506,10 @@ def lca_of(fragu, fragv):
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:
Expand Down
88 changes: 0 additions & 88 deletions amaranth/hdl/xfrm.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"FragmentTransformer",
"TransformedElaboratable",
"DomainCollector", "DomainRenamer", "DomainLowerer",
"SampleDomainInjector", "SampleLowerer",
"SwitchCleaner", "LHSGroupAnalyzer", "LHSGroupFilter",
"ResetInserter", "EnableInserter"]

Expand Down Expand Up @@ -65,10 +64,6 @@ def on_Cat(self, value):
def on_ArrayProxy(self, value):
pass # :nocov:

@abstractmethod
def on_Sample(self, value):
pass # :nocov:

@abstractmethod
def on_Initial(self, value):
pass # :nocov:
Expand Down Expand Up @@ -103,8 +98,6 @@ def on_value(self, value):
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:
Expand Down Expand Up @@ -153,9 +146,6 @@ def on_ArrayProxy(self, value):
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

Expand Down Expand Up @@ -369,9 +359,6 @@ def on_ArrayProxy(self, value):
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

Expand Down Expand Up @@ -509,81 +496,6 @@ def on_fragment(self, 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):
def on_ignore(self, stmt):
return stmt
Expand Down
3 changes: 0 additions & 3 deletions amaranth/sim/_pyrtl.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,6 @@ def on_AnyConst(self, value):
def on_AnySeq(self, value):
raise NotImplementedError # :nocov:

def on_Sample(self, value):
raise NotImplementedError # :nocov:

def on_Initial(self, value):
raise NotImplementedError # :nocov:

Expand Down
1 change: 1 addition & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 1 addition & 34 deletions tests/test_hdl_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ def test_repr(self):
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$"):
Expand Down Expand Up @@ -1382,39 +1382,6 @@ class EnumD(Enum):
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()
Expand Down
Loading