nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: amaranth-lang/amaranth
Pull request #1449 (2024-07-19 01:12:26)

hdl: truncate init value read from `Signal(...).init`
We have a warning saying that the init value will be truncated to fit the signal's shape (suppressed for common patterns of 0 and -1), but the introspected via `.init` value is, confusingly, not truncated.

whitequark: I consider this PR a bugfix and tagged it for a backport to 0.5.1.

Patch:
diff --git a/amaranth/hdl/_ast.py b/amaranth/hdl/_ast.py
index a490707..1293f87 100644
--- a/amaranth/hdl/_ast.py
+++ b/amaranth/hdl/_ast.py
@@ -2052,7 +2052,7 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
                             .format(orig_init, shape),
                     category=SyntaxWarning,
                     stacklevel=2)
-        self._init = init.value
+        self._init = Const(init.value, shape).value
         self._reset_less = bool(reset_less)
 
         if isinstance(orig_shape, range) and orig_init is not None and orig_init not in orig_shape:
diff --git a/docs/changes.rst b/docs/changes.rst
index 15f776b..38b0c97 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -47,6 +47,20 @@ Standard library changes
 * Removed: (deprecated in 0.5) :mod:`amaranth.lib.coding`. (`RFC 63`_)
 
 
+Version 0.5.1
+=============
+
+
+Standard library changes
+------------------------
+
+.. currentmodule:: amaranth.lib
+
+* Added: views of :class:`amaranth.lib.data.ArrayLayout` can be indexed with negative integers or slices.
+* Added: :py:`len()` works on views of :class:`amaranth.lib.data.ArrayLayout`.
+* Added: views of :class:`amaranth.lib.data.ArrayLayout` are iterable.
+
+
 Version 0.5
 ===========
 


Test patch:
diff --git a/tests/test_hdl_ast.py b/tests/test_hdl_ast.py
index d74ad77..85d9ecf 100644
--- a/tests/test_hdl_ast.py
+++ b/tests/test_hdl_ast.py
@@ -1245,6 +1245,14 @@ class SignalTestCase(FHDLTestCase):
                 r"^Initial value -2 will be truncated to the signal shape signed\(1\)$"):
             Signal(signed(1), init=-2)
 
+    def test_init_truncated(self):
+        s1 = Signal(unsigned(2), init=-1)
+        self.assertEqual(s1.init, 0b11)
+        with warnings.catch_warnings():
+            warnings.filterwarnings(action="ignore", category=SyntaxWarning)
+            s2 = Signal(signed(2), init=-33)
+            self.assertEqual(s2.init, -1)
+
     def test_init_wrong_fencepost(self):
         with self.assertRaisesRegex(SyntaxError,
                 r"^Initial value 10 equals the non-inclusive end of the signal shape "
Repository: amaranth-lang/amaranth
Pull request #1452 (2024-07-19 02:22:40)

hdl: truncate init value read from `Signal(...).init` [0.5 backport]
See https://github.com/amaranth-lang/amaranth/pull/1449.


Patch:
diff --git a/amaranth/hdl/_ast.py b/amaranth/hdl/_ast.py
index 8278302..9c992d5 100644
--- a/amaranth/hdl/_ast.py
+++ b/amaranth/hdl/_ast.py
@@ -2082,7 +2082,7 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
                             .format(orig_init, shape),
                     category=SyntaxWarning,
                     stacklevel=2)
-        self._init = init.value
+        self._init = Const(init.value, shape).value
         self._reset_less = bool(reset_less)
 
         if isinstance(orig_shape, range) and orig_init is not None and orig_init not in orig_shape:
diff --git a/docs/changes.rst b/docs/changes.rst
index 4c2ff5f..93d44fb 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -18,6 +18,20 @@ Documentation for past releases of the Amaranth language and toolchain is availa
 * `Amaranth 0.3 <https://amaranth-lang.org/docs/amaranth/v0.3/>`_
 
 
+Version 0.5.1
+=============
+
+
+Standard library changes
+------------------------
+
+.. currentmodule:: amaranth.lib
+
+* Added: views of :class:`amaranth.lib.data.ArrayLayout` can be indexed with negative integers or slices.
+* Added: :py:`len()` works on views of :class:`amaranth.lib.data.ArrayLayout`.
+* Added: views of :class:`amaranth.lib.data.ArrayLayout` are iterable.
+
+
 Version 0.5
 ===========
 


Test patch:
diff --git a/tests/test_hdl_ast.py b/tests/test_hdl_ast.py
index add1471..f4ae006 100644
--- a/tests/test_hdl_ast.py
+++ b/tests/test_hdl_ast.py
@@ -1264,6 +1264,14 @@ class SignalTestCase(FHDLTestCase):
                 r"^Initial value -2 will be truncated to the signal shape signed\(1\)$"):
             Signal(signed(1), init=-2)
 
+    def test_init_truncated(self):
+        s1 = Signal(unsigned(2), init=-1)
+        self.assertEqual(s1.init, 0b11)
+        with warnings.catch_warnings():
+            warnings.filterwarnings(action="ignore", category=SyntaxWarning)
+            s2 = Signal(signed(2), init=-33)
+            self.assertEqual(s2.init, -1)
+
     def test_init_wrong_fencepost(self):
         with self.assertRaisesRegex(SyntaxError,
                 r"^Initial value 10 equals the non-inclusive end of the signal shape "
Repository: amaranth-lang/amaranth
Pull request #1477 (2024-08-06 21:08:50)

Implement RFC 71: `EnumView.matches`.


Patch:
diff --git a/amaranth/lib/enum.py b/amaranth/lib/enum.py
index d9d415b..af90ed2 100644
--- a/amaranth/lib/enum.py
+++ b/amaranth/lib/enum.py
@@ -313,6 +313,22 @@ class EnumView(ValueCastable):
                             "the same enum type")
         return self.as_value() != other.as_value()
 
+    def matches(self, *patterns):
+        """Pattern matching.
+
+        Matches against a set of patterns, which must be values of the same enum as the view.
+
+        Returns
+        -------
+        :class:`Value`, :py:`unsigned(1)`
+        """
+        patterns = tuple(patterns)
+        for pattern in patterns:
+            if not isinstance(pattern, self.__enum):
+                raise TypeError(f"a pattern must be an enum value of the same type as the "
+                                f"EnumView ({self.__enum.__qualname__}), not {pattern!r}")
+        return self.__target.matches(*patterns)
+
     def __repr__(self):
         return f"{type(self).__qualname__}({self.__enum.__qualname__}, {self.__target!r})"
 
diff --git a/docs/changes.rst b/docs/changes.rst
index 2ff57b3..279f85f 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -23,6 +23,14 @@ Version 0.6.0 (unreleased)
 ==========================
 
 
+Implemented RFCs
+----------------
+
+.. _RFC 71: https://amaranth-lang.org/rfcs/0071-enumview-matches.html
+
+* `RFC 71`_: ``EnumView.matches``
+
+
 Language changes
 ----------------
 
@@ -45,6 +53,7 @@ Standard library changes
 .. currentmodule:: amaranth.lib
 
 * Added: :py:`payload_init=` argument in :class:`amaranth.lib.stream.Signature`.
+* Added: :meth:`enum.EnumView.matches`. (`RFC 71`_)
 * Changed: (deprecated in 0.5.1) providing :meth:`io.PortLike.__add__` is now mandatory. (`RFC 69`_)
 * Removed: (deprecated in 0.5.0) :mod:`amaranth.lib.coding`. (`RFC 63`_)
 


Test patch:
diff --git a/tests/test_lib_enum.py b/tests/test_lib_enum.py
index 1cfe1e2..80a5293 100644
--- a/tests/test_lib_enum.py
+++ b/tests/test_lib_enum.py
@@ -147,6 +147,7 @@ class EnumTestCase(FHDLTestCase):
         class EnumB(Enum, shape=signed(4)):
             C = 0
             D = 5
+            E = 7
         a = Signal(EnumA)
         b = Signal(EnumB)
         c = Signal(EnumA)
@@ -201,6 +202,16 @@ class EnumTestCase(FHDLTestCase):
         self.assertRepr(a == EnumA.B, "(== (sig a) (const 4'sd-3))")
         self.assertRepr(EnumA.B == a, "(== (sig a) (const 4'sd-3))")
         self.assertRepr(a != EnumA.B, "(!= (sig a) (const 4'sd-3))")
+        self.assertRepr(b.matches(EnumB.C, EnumB.D), """
+            (r| (cat
+                (== (sig b) (const 1'd0))
+                (== (sig b) (const 3'd5))
+            ))
+        """)
+        with self.assertRaisesRegex(TypeError,
+                r"^a pattern must be an enum value of the same type as the EnumView \(.*EnumB\), "
+                r"not .*$"):
+            b.matches(EnumA.A)
 
     def test_flag_view(self):
         class FlagA(Flag, shape=unsigned(4)):
Repository: amaranth-lang/amaranth
Pull request #1487 (2024-08-24 20:54:49)

sim: fix infinite recursion on non-`Signal` values.


Patch:
diff --git a/amaranth/sim/pysim.py b/amaranth/sim/pysim.py
index d89ddca..2d9562d 100644
--- a/amaranth/sim/pysim.py
+++ b/amaranth/sim/pysim.py
@@ -269,24 +269,22 @@ class _VCDWriter:
             self.gtkw_save.treeopen("top")
 
             def traverse_traces(traces):
-                if isinstance(traces, Signal):
-                    for name in self.gtkw_signal_names[traces]:
-                        self.gtkw_save.trace(name)
-                elif isinstance(traces, data.View):
+                if isinstance(traces, data.View):
                     with self.gtkw_save.group("view"):
-                        trace = Value.cast(traces)
+                        traverse_traces(Value.cast(traces))
+                elif isinstance(traces, ValueLike):
+                    trace = Value.cast(traces)
+                    if isinstance(traces, MemoryData._Row):
+                        for name in self.gtkw_memory_names[traces._memory][traces._index]:
+                            self.gtkw_save.trace(name)
+                    else:
                         for trace_signal in trace._rhs_signals():
                             for name in self.gtkw_signal_names[trace_signal]:
                                 self.gtkw_save.trace(name)
-                elif isinstance(traces, ValueLike):
-                    traverse_traces(Value.cast(traces))
                 elif isinstance(traces, MemoryData):
                     for row_names in self.gtkw_memory_names[traces]:
                         for name in row_names:
                             self.gtkw_save.trace(name)
-                elif isinstance(traces, MemoryData._Row):
-                    for name in self.gtkw_memory_names[traces._memory][traces._index]:
-                        self.gtkw_save.trace(name)
                 elif hasattr(traces, "signature") and isinstance(traces.signature, wiring.Signature):
                     with self.gtkw_save.group("interface"):
                         for _, _, member in traces.signature.flatten(traces):


Test patch:
diff --git a/tests/test_sim.py b/tests/test_sim.py
index deb305d..419e504 100644
--- a/tests/test_sim.py
+++ b/tests/test_sim.py
@@ -1468,6 +1468,10 @@ class SimulatorTracesTestCase(FHDLTestCase):
         a = Signal()
         self.assertDef(a, [a])
 
+    def test_expr(self):
+        a = Signal()
+        self.assertDef(a + 3, [a])
+
     def test_list(self):
         a = Signal()
         self.assertDef([a], [a])
Repository: amaranth-lang/amaranth
Pull request #1494 (2024-08-26 21:54:41)

sim: fix memory writes with signed shapes.

wanda-phi: > LGTM. Should we backport?

definitely; was just waiting on review for 0.6 versions of these PRs first

Patch:
diff --git a/amaranth/sim/pysim.py b/amaranth/sim/pysim.py
index 2d9562d..ddfb8ad 100644
--- a/amaranth/sim/pysim.py
+++ b/amaranth/sim/pysim.py
@@ -392,10 +392,11 @@ class _PyMemoryChange:
 
 
 class _PyMemoryState(BaseMemoryState):
-    __slots__ = ("memory", "data", "write_queue", "wakers", "pending")
+    __slots__ = ("memory", "shape", "data", "write_queue", "wakers", "pending")
 
     def __init__(self, memory, pending):
         self.memory  = memory
+        self.shape   = Shape.cast(memory.shape)
         self.pending = pending
         self.wakers  = list()
         self.reset()
@@ -419,6 +420,11 @@ class _PyMemoryState(BaseMemoryState):
                 self.write_queue[addr] = self.data[addr]
             if mask is not None:
                 value = (value & mask) | (self.write_queue[addr] & ~mask)
+            if self.shape.signed:
+                if value & (1 << (self.shape.width - 1)):
+                    value |= -1 << (self.shape.width)
+                else:
+                    value &= (1 << (self.shape.width)) - 1
             self.write_queue[addr] = value
             self.pending.add(self)
 


Test patch:
diff --git a/tests/test_sim.py b/tests/test_sim.py
index 419e504..45e84d1 100644
--- a/tests/test_sim.py
+++ b/tests/test_sim.py
@@ -1418,15 +1418,20 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
         mem1 = MemoryData(shape=8, depth=4, init=[1, 2, 3])
         mem2 = MemoryData(shape=MyEnum, depth=4, init=[MyEnum.A, MyEnum.B, MyEnum.C])
         mem3 = MemoryData(shape=data.StructLayout({"a": signed(3), "b": 2}), depth=4, init=[{"a": 2, "b": 1}])
+        mem4 = MemoryData(shape=signed(8), depth=4, init=[1, -2, 3])
 
         async def testbench(ctx):
             await ctx.delay(1e-6)
             ctx.set(mem1[0], 4)
             ctx.set(mem2[3], MyEnum.C)
             ctx.set(mem3[2], {"a": -1, "b": 2})
+            ctx.set(mem4[1][4:], 0)
+            ctx.set(mem4[3][7], 1)
             await ctx.delay(1e-6)
+            self.assertEqual(ctx.get(mem4[1]), 0xe)
+            self.assertEqual(ctx.get(mem4[3]), -128)
 
-        with self.assertSimulation(Module(), traces=[mem1, mem2, mem3]) as sim:
+        with self.assertSimulation(Module(), traces=[mem1, mem2, mem3, mem4]) as sim:
             sim.add_testbench(testbench)
 
     def test_multiple_modules(self):
Repository: amaranth-lang/amaranth
Pull request #1499 (2024-08-29 19:13:47)

build.res: allow `dir="-"` when requesting resources with subsignals.
This makes the only non-deprecated way to do I/O not be a pain when subsignals are involved.

(thanks @meithecatte for the report)

wanda-phi: > LGTM, but I propose a 0.5 backport as well as a small note in changelog. Yes, it's a deprecated API and all that, but it's important enough that we should highlight it.

added a changelog entry; 0.5 backport already done

I don't think it's a deprecated API though? I mean, unless we want to play google...
whitequark: > I don't think it's a deprecated API though? I mean, unless we want to play google...

I mean, basically all of the functionality of `platform.request` is expressly recommended to be bypassed in the `lib.io` docs. That would make it deprecated in my view.

Patch:
diff --git a/amaranth/build/res.py b/amaranth/build/res.py
index f6d3c93..effe44e 100644
--- a/amaranth/build/res.py
+++ b/amaranth/build/res.py
@@ -152,7 +152,8 @@ class ResourceManager:
 
         def merge_options(subsignal, dir, xdr):
             if isinstance(subsignal.ios[0], Subsignal):
-                if dir is None:
+                orig_dir = dir
+                if dir is None or dir == "-":
                     dir = dict()
                 if xdr is None:
                     xdr = dict()
@@ -165,7 +166,7 @@ class ResourceManager:
                                     "has subsignals"
                                     .format(xdr, subsignal))
                 for sub in subsignal.ios:
-                    sub_dir = dir.get(sub.name, None)
+                    sub_dir = "-" if orig_dir == "-" else dir.get(sub.name, None)
                     sub_xdr = xdr.get(sub.name, None)
                     dir[sub.name], xdr[sub.name] = merge_options(sub, sub_dir, sub_xdr)
             else:
diff --git a/docs/changes.rst b/docs/changes.rst
index 17564df..d39197d 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -80,6 +80,14 @@ Standard library changes
 * Added: constants of :class:`amaranth.lib.data.ArrayLayout` are iterable.
 
 
+Platform integration changes
+----------------------------
+
+.. currentmodule:: amaranth.vendor
+
+* Added: :meth:`Platform.request` accepts :py:`dir="-"` for resources with subsignals.
+
+
 Version 0.5.1
 =============
 


Test patch:
diff --git a/tests/test_build_res.py b/tests/test_build_res.py
index a0dbc55..758cdce 100644
--- a/tests/test_build_res.py
+++ b/tests/test_build_res.py
@@ -84,6 +84,12 @@ class ResourceManagerTestCase(FHDLTestCase):
         scl_buffer._MustUse__silence = True
         sda_buffer._MustUse__silence = True
 
+    def test_request_subsignal_dash(self):
+        with _ignore_deprecated():
+            i2c = self.cm.request("i2c", 0, dir="-")
+        self.assertIsInstance(i2c.sda, SingleEndedPort)
+        self.assertIsInstance(i2c.scl, SingleEndedPort)
+
     def test_request_tristate(self):
         with _ignore_deprecated():
             i2c = self.cm.request("i2c", 0)
Repository: amaranth-lang/amaranth
Pull request #1500 (2024-08-29 19:14:02)

build.res: allow `dir="-"` when requesting resources with subsignals. [0.5 backport]
This makes the only non-deprecated way to do I/O not be a pain when subsignals are involved.


Patch:
diff --git a/amaranth/build/res.py b/amaranth/build/res.py
index f6d3c93..effe44e 100644
--- a/amaranth/build/res.py
+++ b/amaranth/build/res.py
@@ -152,7 +152,8 @@ class ResourceManager:
 
         def merge_options(subsignal, dir, xdr):
             if isinstance(subsignal.ios[0], Subsignal):
-                if dir is None:
+                orig_dir = dir
+                if dir is None or dir == "-":
                     dir = dict()
                 if xdr is None:
                     xdr = dict()
@@ -165,7 +166,7 @@ class ResourceManager:
                                     "has subsignals"
                                     .format(xdr, subsignal))
                 for sub in subsignal.ios:
-                    sub_dir = dir.get(sub.name, None)
+                    sub_dir = "-" if orig_dir == "-" else dir.get(sub.name, None)
                     sub_xdr = xdr.get(sub.name, None)
                     dir[sub.name], xdr[sub.name] = merge_options(sub, sub_dir, sub_xdr)
             else:
diff --git a/docs/changes.rst b/docs/changes.rst
index ed2145c..033daa2 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -33,6 +33,14 @@ Standard library changes
 * Added: constants of :class:`amaranth.lib.data.ArrayLayout` are iterable.
 
 
+Platform integration changes
+----------------------------
+
+.. currentmodule:: amaranth.vendor
+
+* Added: :meth:`Platform.request` accepts :py:`dir="-"` for resources with subsignals.
+
+
 Version 0.5.1
 =============
 


Test patch:
diff --git a/tests/test_build_res.py b/tests/test_build_res.py
index a0dbc55..758cdce 100644
--- a/tests/test_build_res.py
+++ b/tests/test_build_res.py
@@ -84,6 +84,12 @@ class ResourceManagerTestCase(FHDLTestCase):
         scl_buffer._MustUse__silence = True
         sda_buffer._MustUse__silence = True
 
+    def test_request_subsignal_dash(self):
+        with _ignore_deprecated():
+            i2c = self.cm.request("i2c", 0, dir="-")
+        self.assertIsInstance(i2c.sda, SingleEndedPort)
+        self.assertIsInstance(i2c.scl, SingleEndedPort)
+
     def test_request_tristate(self):
         with _ignore_deprecated():
             i2c = self.cm.request("i2c", 0)
Repository: amaranth-lang/amaranth
Pull request #1514 (2024-09-19 09:02:35)

hdl._nir: AssignmentList: fix comb_edges_to
Only include the condition of a assignment in the comb edges if the assignment assigns to the bit in question.


Patch:
diff --git a/amaranth/hdl/_nir.py b/amaranth/hdl/_nir.py
index 2cd4d28..c15247a 100644
--- a/amaranth/hdl/_nir.py
+++ b/amaranth/hdl/_nir.py
@@ -921,8 +921,8 @@ class AssignmentList(Cell):
     def comb_edges_to(self, bit):
         yield (self.default[bit], self.src_loc)
         for assign in self.assignments:
-            yield (assign.cond, assign.src_loc)
             if bit >= assign.start and bit < assign.start + len(assign.value):
+                yield (assign.cond, assign.src_loc)
                 yield (assign.value[bit - assign.start], assign.src_loc)
 
 


Test patch:
diff --git a/tests/test_hdl_ir.py b/tests/test_hdl_ir.py
index 0bacb0c..f515d76 100644
--- a/tests/test_hdl_ir.py
+++ b/tests/test_hdl_ir.py
@@ -3571,6 +3571,30 @@ class CycleTestCase(FHDLTestCase):
                 r"$"):
             build_netlist(Fragment.get(m, None), [])
 
+    def test_assignment_cycle(self):
+        a = Signal(2)
+        m = Module()
+
+        with m.If(a[0]):
+            m.d.comb += a[0].eq(1)
+
+        with self.assertRaisesRegex(CombinationalCycle,
+                r"^Combinational cycle detected, path:\n"
+                r".*test_hdl_ir.py:\d+: cell Matches bit 0\n"
+                r".*test_hdl_ir.py:\d+: signal a bit 0\n"
+                r".*test_hdl_ir.py:\d+: cell AssignmentList bit 0\n"
+                r".*test_hdl_ir.py:\d+: cell PriorityMatch bit 0\n"
+                r"$"):
+            build_netlist(Fragment.get(m, None), [])
+
+        m = Module()
+
+        with m.If(a[0]):
+            m.d.comb += a[1].eq(1)
+
+        # no cycle here, a[1] gets assigned and a[0] gets checked
+        build_netlist(Fragment.get(m, None), [])
+
 
 class DomainLookupTestCase(FHDLTestCase):
     def test_domain_lookup(self):
Repository: amaranth-lang/amaranth
Pull request #1515 (2024-09-19 11:16:24)

hdl._nir: AssignmentList: fix comb_edges_to [0.5 backport]


Patch:
diff --git a/amaranth/hdl/_nir.py b/amaranth/hdl/_nir.py
index 2cd4d28..c15247a 100644
--- a/amaranth/hdl/_nir.py
+++ b/amaranth/hdl/_nir.py
@@ -921,8 +921,8 @@ class AssignmentList(Cell):
     def comb_edges_to(self, bit):
         yield (self.default[bit], self.src_loc)
         for assign in self.assignments:
-            yield (assign.cond, assign.src_loc)
             if bit >= assign.start and bit < assign.start + len(assign.value):
+                yield (assign.cond, assign.src_loc)
                 yield (assign.value[bit - assign.start], assign.src_loc)
 
 


Test patch:
diff --git a/tests/test_hdl_ir.py b/tests/test_hdl_ir.py
index e73a972..4899d18 100644
--- a/tests/test_hdl_ir.py
+++ b/tests/test_hdl_ir.py
@@ -3678,6 +3678,30 @@ class CycleTestCase(FHDLTestCase):
                 r"$"):
             build_netlist(Fragment.get(m, None), [])
 
+    def test_assignment_cycle(self):
+        a = Signal(2)
+        m = Module()
+
+        with m.If(a[0]):
+            m.d.comb += a[0].eq(1)
+
+        with self.assertRaisesRegex(CombinationalCycle,
+                r"^Combinational cycle detected, path:\n"
+                r".*test_hdl_ir.py:\d+: cell Matches bit 0\n"
+                r".*test_hdl_ir.py:\d+: signal a bit 0\n"
+                r".*test_hdl_ir.py:\d+: cell AssignmentList bit 0\n"
+                r".*test_hdl_ir.py:\d+: cell PriorityMatch bit 0\n"
+                r"$"):
+            build_netlist(Fragment.get(m, None), [])
+
+        m = Module()
+
+        with m.If(a[0]):
+            m.d.comb += a[1].eq(1)
+
+        # no cycle here, a[1] gets assigned and a[0] gets checked
+        build_netlist(Fragment.get(m, None), [])
+
 
 class DomainLookupTestCase(FHDLTestCase):
     def test_domain_lookup(self):