From 86d14f584e724cfca3dc8b4457b1a01b4ebc7243 Mon Sep 17 00:00:00 2001 From: Wanda Date: Sat, 13 Jan 2024 12:35:52 +0100 Subject: [PATCH] Implement RFC 39: Change semantics of no-argument `m.Case()`. --- amaranth/hdl/ast.py | 2 +- amaranth/hdl/dsl.py | 20 ++++++++++++++++---- docs/changes.rst | 6 ++++++ tests/test_hdl_ast.py | 2 +- tests/test_hdl_dsl.py | 3 +-- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/amaranth/hdl/ast.py b/amaranth/hdl/ast.py index 957d71b..26be377 100644 --- a/amaranth/hdl/ast.py +++ b/amaranth/hdl/ast.py @@ -542,7 +542,7 @@ class Value(metaclass=ABCMeta): continue matches.append(self == pattern) if not matches: - return Const(1) + return Const(0) elif len(matches) == 1: return matches[0] else: diff --git a/amaranth/hdl/dsl.py b/amaranth/hdl/dsl.py index e23d161..2da0dac 100644 --- a/amaranth/hdl/dsl.py +++ b/amaranth/hdl/dsl.py @@ -343,19 +343,31 @@ class Module(_ModuleBuilderRoot, Elaboratable): yield self._flush_ctrl() # If none of the provided cases can possibly be true, omit this branch completely. - # This needs to be differentiated from no cases being provided in the first place, - # which means the branch will always match. # Likewise, omit this branch if another branch with this exact set of patterns already # exists (since otherwise we'd overwrite the previous branch's slot in the dict). - if not (patterns and not new_patterns) and new_patterns not in switch_data["cases"]: + if new_patterns and new_patterns not in switch_data["cases"]: switch_data["cases"][new_patterns] = self._statements switch_data["case_src_locs"][new_patterns] = src_loc finally: self._ctrl_context = "Switch" self._statements = _outer_case + @contextmanager def Default(self): - return self.Case() + self._check_context("Default", context="Switch") + src_loc = tracer.get_src_loc(src_loc_at=1) + switch_data = self._get_ctrl("Switch") + try: + _outer_case, self._statements = self._statements, [] + self._ctrl_context = None + yield + self._flush_ctrl() + if () not in switch_data["cases"]: + switch_data["cases"][()] = self._statements + switch_data["case_src_locs"][()] = src_loc + finally: + self._ctrl_context = "Switch" + self._statements = _outer_case @contextmanager def FSM(self, reset=None, domain="sync", name="fsm"): diff --git a/docs/changes.rst b/docs/changes.rst index dac65f2..8735cee 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -15,6 +15,8 @@ Migrating from version 0.4 Apply the following changes to code written against Amaranth 0.4 to migrate it to version 0.5: +* Replace uses of ``m.Case()`` with no patterns with ``m.Default()`` +* Replace uses of ``Value.matches()`` with no patterns with ``Const(1)`` * Update uses of :func:`amaranth.utils.log2_int(need_pow2=False)` to :func:`amaranth.utils.ceil_log2` * Update uses of :func:`amaranth.utils.log2_int(need_pow2=True)` to :func:`amaranth.utils.exact_log2` @@ -23,8 +25,10 @@ Implemented RFCs ---------------- .. _RFC 17: https://amaranth-lang.org/rfcs/0017-remove-log2-int.html +.. _RFC 39: https://amaranth-lang.org/rfcs/0039-empty-case.html * `RFC 17`_: Remove ``log2_int`` +* `RFC 39`_: Change semantics of no-argument ``m.Case()`` Language changes @@ -34,6 +38,8 @@ Language changes * Added: :class:`ast.Slice` objects have been made const-castable. * Added: :func:`amaranth.utils.ceil_log2`, :func:`amaranth.utils.exact_log2`. (`RFC 17`_) +* Changed: ``m.Case()`` with no patterns is never active instead of always active. (`RFC 39`_) +* Changed: ``Value.matches()`` with no patterns is ``Const(0)`` instead of ``Const(1)``. (`RFC 39`_) * Deprecated: :func:`amaranth.utils.log2_int`. (`RFC 17`_) * 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`. diff --git a/tests/test_hdl_ast.py b/tests/test_hdl_ast.py index 2d9ef2a..4e52c6b 100644 --- a/tests/test_hdl_ast.py +++ b/tests/test_hdl_ast.py @@ -682,7 +682,7 @@ class OperatorTestCase(FHDLTestCase): def test_matches(self): s = Signal(4) - self.assertRepr(s.matches(), "(const 1'd1)") + self.assertRepr(s.matches(), "(const 1'd0)") self.assertRepr(s.matches(1), """ (== (sig s) (const 1'd1)) """) diff --git a/tests/test_hdl_dsl.py b/tests/test_hdl_dsl.py index 9558ef6..dddae25 100644 --- a/tests/test_hdl_dsl.py +++ b/tests/test_hdl_dsl.py @@ -366,7 +366,7 @@ class DSLTestCase(FHDLTestCase): ) """) - def test_Switch_default_Case(self): + def test_Switch_empty_Case(self): m = Module() with m.Switch(self.w1): with m.Case(3): @@ -378,7 +378,6 @@ class DSLTestCase(FHDLTestCase): ( (switch (sig w1) (case 0011 (eq (sig c1) (const 1'd1))) - (default (eq (sig c2) (const 1'd1))) ) ) """)