hdl: appropriately rename tests. NFC.
authorwhitequark <whitequark@whitequark.org>
Sat, 15 Dec 2018 16:13:53 +0000 (16:13 +0000)
committerwhitequark <whitequark@whitequark.org>
Sat, 15 Dec 2018 16:13:53 +0000 (16:13 +0000)
nmigen/test/test_fhdl_ast.py [deleted file]
nmigen/test/test_fhdl_cd.py [deleted file]
nmigen/test/test_fhdl_dsl.py [deleted file]
nmigen/test/test_fhdl_ir.py [deleted file]
nmigen/test/test_fhdl_xfrm.py [deleted file]
nmigen/test/test_hdl_ast.py [new file with mode: 0644]
nmigen/test/test_hdl_cd.py [new file with mode: 0644]
nmigen/test/test_hdl_dsl.py [new file with mode: 0644]
nmigen/test/test_hdl_ir.py [new file with mode: 0644]
nmigen/test/test_hdl_xfrm.py [new file with mode: 0644]

diff --git a/nmigen/test/test_fhdl_ast.py b/nmigen/test/test_fhdl_ast.py
deleted file mode 100644 (file)
index 0fde4c6..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
-from ..hdl.ast import *
-from .tools import *
-
-
-class ValueTestCase(FHDLTestCase):
-    def test_wrap(self):
-        self.assertIsInstance(Value.wrap(0), Const)
-        self.assertIsInstance(Value.wrap(True), Const)
-        c = Const(0)
-        self.assertIs(Value.wrap(c), c)
-        with self.assertRaises(TypeError):
-            Value.wrap("str")
-
-    def test_bool(self):
-        with self.assertRaises(TypeError):
-            if Const(0):
-                pass
-
-    def test_len(self):
-        self.assertEqual(len(Const(10)), 4)
-
-    def test_getitem_int(self):
-        s1 = Const(10)[0]
-        self.assertIsInstance(s1, Slice)
-        self.assertEqual(s1.start, 0)
-        self.assertEqual(s1.end, 1)
-        s2 = Const(10)[-1]
-        self.assertIsInstance(s2, Slice)
-        self.assertEqual(s2.start, 3)
-        self.assertEqual(s2.end, 4)
-        with self.assertRaises(IndexError):
-            Const(10)[5]
-
-    def test_getitem_slice(self):
-        s1 = Const(10)[1:3]
-        self.assertIsInstance(s1, Slice)
-        self.assertEqual(s1.start, 1)
-        self.assertEqual(s1.end, 3)
-        s2 = Const(10)[1:-2]
-        self.assertIsInstance(s2, Slice)
-        self.assertEqual(s2.start, 1)
-        self.assertEqual(s2.end, 2)
-        s3 = Const(31)[::2]
-        self.assertIsInstance(s3, Cat)
-        self.assertIsInstance(s3.operands[0], Slice)
-        self.assertEqual(s3.operands[0].start, 0)
-        self.assertEqual(s3.operands[0].end, 1)
-        self.assertIsInstance(s3.operands[1], Slice)
-        self.assertEqual(s3.operands[1].start, 2)
-        self.assertEqual(s3.operands[1].end, 3)
-        self.assertIsInstance(s3.operands[2], Slice)
-        self.assertEqual(s3.operands[2].start, 4)
-        self.assertEqual(s3.operands[2].end, 5)
-
-    def test_getitem_wrong(self):
-        with self.assertRaises(TypeError):
-            Const(31)["str"]
-
-
-class ConstTestCase(FHDLTestCase):
-    def test_shape(self):
-        self.assertEqual(Const(0).shape(),   (1, False))
-        self.assertEqual(Const(1).shape(),   (1, False))
-        self.assertEqual(Const(10).shape(),  (4, False))
-        self.assertEqual(Const(-10).shape(), (5, True))
-
-        self.assertEqual(Const(1, 4).shape(),          (4, False))
-        self.assertEqual(Const(1, (4, True)).shape(),  (4, True))
-        self.assertEqual(Const(0, (0, False)).shape(), (0, False))
-
-    def test_shape_bad(self):
-        with self.assertRaises(TypeError):
-            Const(1, -1)
-
-    def test_normalization(self):
-        self.assertEqual(Const(0b10110, (5, True)).value, -10)
-
-    def test_value(self):
-        self.assertEqual(Const(10).value, 10)
-
-    def test_repr(self):
-        self.assertEqual(repr(Const(10)),  "(const 4'd10)")
-        self.assertEqual(repr(Const(-10)), "(const 5'sd-10)")
-
-    def test_hash(self):
-        with self.assertRaises(TypeError):
-            hash(Const(0))
-
-
-class OperatorTestCase(FHDLTestCase):
-    def test_bool(self):
-        v = Const(0, 4).bool()
-        self.assertEqual(repr(v), "(b (const 4'd0))")
-        self.assertEqual(v.shape(), (1, False))
-
-    def test_invert(self):
-        v = ~Const(0, 4)
-        self.assertEqual(repr(v), "(~ (const 4'd0))")
-        self.assertEqual(v.shape(), (4, False))
-
-    def test_neg(self):
-        v1 = -Const(0, (4, False))
-        self.assertEqual(repr(v1), "(- (const 4'd0))")
-        self.assertEqual(v1.shape(), (5, True))
-        v2 = -Const(0, (4, True))
-        self.assertEqual(repr(v2), "(- (const 4'sd0))")
-        self.assertEqual(v2.shape(), (4, True))
-
-    def test_add(self):
-        v1 = Const(0, (4, False)) + Const(0, (6, False))
-        self.assertEqual(repr(v1), "(+ (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (7, False))
-        v2 = Const(0, (4, True)) + Const(0, (6, True))
-        self.assertEqual(v2.shape(), (7, True))
-        v3 = Const(0, (4, True)) + Const(0, (4, False))
-        self.assertEqual(v3.shape(), (6, True))
-        v4 = Const(0, (4, False)) + Const(0, (4, True))
-        self.assertEqual(v4.shape(), (6, True))
-        v5 = 10 + Const(0, 4)
-        self.assertEqual(v5.shape(), (5, False))
-
-    def test_sub(self):
-        v1 = Const(0, (4, False)) - Const(0, (6, False))
-        self.assertEqual(repr(v1), "(- (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (7, False))
-        v2 = Const(0, (4, True)) - Const(0, (6, True))
-        self.assertEqual(v2.shape(), (7, True))
-        v3 = Const(0, (4, True)) - Const(0, (4, False))
-        self.assertEqual(v3.shape(), (6, True))
-        v4 = Const(0, (4, False)) - Const(0, (4, True))
-        self.assertEqual(v4.shape(), (6, True))
-        v5 = 10 - Const(0, 4)
-        self.assertEqual(v5.shape(), (5, False))
-
-    def test_mul(self):
-        v1 = Const(0, (4, False)) * Const(0, (6, False))
-        self.assertEqual(repr(v1), "(* (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (10, False))
-        v2 = Const(0, (4, True)) * Const(0, (6, True))
-        self.assertEqual(v2.shape(), (9, True))
-        v3 = Const(0, (4, True)) * Const(0, (4, False))
-        self.assertEqual(v3.shape(), (8, True))
-        v5 = 10 * Const(0, 4)
-        self.assertEqual(v5.shape(), (8, False))
-
-    def test_and(self):
-        v1 = Const(0, (4, False)) & Const(0, (6, False))
-        self.assertEqual(repr(v1), "(& (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (6, False))
-        v2 = Const(0, (4, True)) & Const(0, (6, True))
-        self.assertEqual(v2.shape(), (6, True))
-        v3 = Const(0, (4, True)) & Const(0, (4, False))
-        self.assertEqual(v3.shape(), (5, True))
-        v4 = Const(0, (4, False)) & Const(0, (4, True))
-        self.assertEqual(v4.shape(), (5, True))
-        v5 = 10 & Const(0, 4)
-        self.assertEqual(v5.shape(), (4, False))
-
-    def test_or(self):
-        v1 = Const(0, (4, False)) | Const(0, (6, False))
-        self.assertEqual(repr(v1), "(| (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (6, False))
-        v2 = Const(0, (4, True)) | Const(0, (6, True))
-        self.assertEqual(v2.shape(), (6, True))
-        v3 = Const(0, (4, True)) | Const(0, (4, False))
-        self.assertEqual(v3.shape(), (5, True))
-        v4 = Const(0, (4, False)) | Const(0, (4, True))
-        self.assertEqual(v4.shape(), (5, True))
-        v5 = 10 | Const(0, 4)
-        self.assertEqual(v5.shape(), (4, False))
-
-    def test_xor(self):
-        v1 = Const(0, (4, False)) ^ Const(0, (6, False))
-        self.assertEqual(repr(v1), "(^ (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (6, False))
-        v2 = Const(0, (4, True)) ^ Const(0, (6, True))
-        self.assertEqual(v2.shape(), (6, True))
-        v3 = Const(0, (4, True)) ^ Const(0, (4, False))
-        self.assertEqual(v3.shape(), (5, True))
-        v4 = Const(0, (4, False)) ^ Const(0, (4, True))
-        self.assertEqual(v4.shape(), (5, True))
-        v5 = 10 ^ Const(0, 4)
-        self.assertEqual(v5.shape(), (4, False))
-
-    def test_shl(self):
-        v1 = Const(1, 4) << Const(4)
-        self.assertEqual(repr(v1), "(<< (const 4'd1) (const 3'd4))")
-        self.assertEqual(v1.shape(), (11, False))
-        v2 = Const(1, 4) << Const(-3)
-        self.assertEqual(v2.shape(), (7, False))
-
-    def test_shr(self):
-        v1 = Const(1, 4) >> Const(4)
-        self.assertEqual(repr(v1), "(>> (const 4'd1) (const 3'd4))")
-        self.assertEqual(v1.shape(), (4, False))
-        v2 = Const(1, 4) >> Const(-3)
-        self.assertEqual(v2.shape(), (8, False))
-
-    def test_lt(self):
-        v = Const(0, 4) < Const(0, 6)
-        self.assertEqual(repr(v), "(< (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
-
-    def test_le(self):
-        v = Const(0, 4) <= Const(0, 6)
-        self.assertEqual(repr(v), "(<= (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
-
-    def test_gt(self):
-        v = Const(0, 4) > Const(0, 6)
-        self.assertEqual(repr(v), "(> (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
-
-    def test_ge(self):
-        v = Const(0, 4) >= Const(0, 6)
-        self.assertEqual(repr(v), "(>= (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
-
-    def test_eq(self):
-        v = Const(0, 4) == Const(0, 6)
-        self.assertEqual(repr(v), "(== (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
-
-    def test_ne(self):
-        v = Const(0, 4) != Const(0, 6)
-        self.assertEqual(repr(v), "(!= (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
-
-    def test_mux(self):
-        s  = Const(0)
-        v1 = Mux(s, Const(0, (4, False)), Const(0, (6, False)))
-        self.assertEqual(repr(v1), "(m (const 1'd0) (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (6, False))
-        v2 = Mux(s, Const(0, (4, True)), Const(0, (6, True)))
-        self.assertEqual(v2.shape(), (6, True))
-        v3 = Mux(s, Const(0, (4, True)), Const(0, (4, False)))
-        self.assertEqual(v3.shape(), (5, True))
-        v4 = Mux(s, Const(0, (4, False)), Const(0, (4, True)))
-        self.assertEqual(v4.shape(), (5, True))
-
-    def test_bool(self):
-        v = Const(0).bool()
-        self.assertEqual(repr(v), "(b (const 1'd0))")
-        self.assertEqual(v.shape(), (1, False))
-
-    def test_hash(self):
-        with self.assertRaises(TypeError):
-            hash(Const(0) + Const(0))
-
-
-class SliceTestCase(FHDLTestCase):
-    def test_shape(self):
-        s1 = Const(10)[2]
-        self.assertEqual(s1.shape(), (1, False))
-        s2 = Const(-10)[0:2]
-        self.assertEqual(s2.shape(), (2, False))
-
-    def test_start_end_negative(self):
-        c  = Const(0, 8)
-        s1 = Slice(c, 0, -1)
-        self.assertEqual((s1.start, s1.end), (0, 7))
-        s1 = Slice(c, -4, -1)
-        self.assertEqual((s1.start, s1.end), (4, 7))
-
-    def test_start_end_wrong(self):
-        with self.assertRaises(TypeError):
-            Slice(0, "x", 1)
-        with self.assertRaises(TypeError):
-            Slice(0, 1, "x")
-
-    def test_start_end_out_of_range(self):
-        c = Const(0, 8)
-        with self.assertRaises(IndexError):
-            Slice(c, 10, 12)
-        with self.assertRaises(IndexError):
-            Slice(c, 0, 12)
-        with self.assertRaises(IndexError):
-            Slice(c, 4, 2)
-
-    def test_repr(self):
-        s1 = Const(10)[2]
-        self.assertEqual(repr(s1), "(slice (const 4'd10) 2:3)")
-
-
-class PartTestCase(FHDLTestCase):
-    def setUp(self):
-        self.c = Const(0, 8)
-        self.s = Signal(max=self.c.nbits)
-
-    def test_shape(self):
-        s1 = self.c.part(self.s, 2)
-        self.assertEqual(s1.shape(), (2, False))
-        s2 = self.c.part(self.s, 0)
-        self.assertEqual(s2.shape(), (0, False))
-
-    def test_width_bad(self):
-        with self.assertRaises(TypeError):
-            self.c.part(self.s, -1)
-
-    def test_repr(self):
-        s = self.c.part(self.s, 2)
-        self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2)")
-
-
-class CatTestCase(FHDLTestCase):
-    def test_shape(self):
-        c1 = Cat(Const(10))
-        self.assertEqual(c1.shape(), (4, False))
-        c2 = Cat(Const(10), Const(1))
-        self.assertEqual(c2.shape(), (5, False))
-        c3 = Cat(Const(10), Const(1), Const(0))
-        self.assertEqual(c3.shape(), (6, False))
-
-    def test_repr(self):
-        c1 = Cat(Const(10), Const(1))
-        self.assertEqual(repr(c1), "(cat (const 4'd10) (const 1'd1))")
-
-
-class ReplTestCase(FHDLTestCase):
-    def test_shape(self):
-        s1 = Repl(Const(10), 3)
-        self.assertEqual(s1.shape(), (12, False))
-        s2 = Repl(Const(10), 0)
-        self.assertEqual(s2.shape(), (0, False))
-
-    def test_count_wrong(self):
-        with self.assertRaises(TypeError):
-            Repl(Const(10), -1)
-        with self.assertRaises(TypeError):
-            Repl(Const(10), "str")
-
-    def test_repr(self):
-        s = Repl(Const(10), 3)
-        self.assertEqual(repr(s), "(repl (const 4'd10) 3)")
-
-
-class SignalTestCase(FHDLTestCase):
-    def test_shape(self):
-        s1 = Signal()
-        self.assertEqual(s1.shape(), (1, False))
-        s2 = Signal(2)
-        self.assertEqual(s2.shape(), (2, False))
-        s3 = Signal((2, False))
-        self.assertEqual(s3.shape(), (2, False))
-        s4 = Signal((2, True))
-        self.assertEqual(s4.shape(), (2, True))
-        s5 = Signal(max=16)
-        self.assertEqual(s5.shape(), (4, False))
-        s6 = Signal(min=4, max=16)
-        self.assertEqual(s6.shape(), (4, False))
-        s7 = Signal(min=-4, max=16)
-        self.assertEqual(s7.shape(), (5, True))
-        s8 = Signal(min=-20, max=16)
-        self.assertEqual(s8.shape(), (6, True))
-        s9 = Signal(0)
-        self.assertEqual(s9.shape(), (0, False))
-
-    def test_shape_bad(self):
-        with self.assertRaises(ValueError):
-            Signal(min=10, max=4)
-        with self.assertRaises(ValueError):
-            Signal(2, min=10)
-        with self.assertRaises(TypeError):
-            Signal(-10)
-
-    def test_name(self):
-        s1 = Signal()
-        self.assertEqual(s1.name, "s1")
-        s2 = Signal(name="sig")
-        self.assertEqual(s2.name, "sig")
-
-    def test_reset(self):
-        s1 = Signal(4, reset=0b111, reset_less=True)
-        self.assertEqual(s1.reset, 0b111)
-        self.assertEqual(s1.reset_less, True)
-
-    def test_attrs(self):
-        s1 = Signal()
-        self.assertEqual(s1.attrs, {})
-        s2 = Signal(attrs={"no_retiming": True})
-        self.assertEqual(s2.attrs, {"no_retiming": True})
-
-    def test_repr(self):
-        s1 = Signal()
-        self.assertEqual(repr(s1), "(sig s1)")
-
-    def test_like(self):
-        s1 = Signal.like(Signal(4))
-        self.assertEqual(s1.shape(), (4, False))
-        s2 = Signal.like(Signal(min=-15))
-        self.assertEqual(s2.shape(), (5, True))
-        s3 = Signal.like(Signal(4, reset=0b111, reset_less=True))
-        self.assertEqual(s3.reset, 0b111)
-        self.assertEqual(s3.reset_less, True)
-        s4 = Signal.like(Signal(attrs={"no_retiming": True}))
-        self.assertEqual(s4.attrs, {"no_retiming": True})
-        s5 = Signal.like(Signal(decoder=str))
-        self.assertEqual(s5.decoder, str)
-        s6 = Signal.like(10)
-        self.assertEqual(s6.shape(), (4, False))
-
-
-class ClockSignalTestCase(FHDLTestCase):
-    def test_domain(self):
-        s1 = ClockSignal()
-        self.assertEqual(s1.domain, "sync")
-        s2 = ClockSignal("pix")
-        self.assertEqual(s2.domain, "pix")
-
-        with self.assertRaises(TypeError):
-            ClockSignal(1)
-
-    def test_shape(self):
-        self.assertEqual(ClockSignal().shape(), (1, False))
-
-    def test_repr(self):
-        s1 = ClockSignal()
-        self.assertEqual(repr(s1), "(clk sync)")
-
-
-class ResetSignalTestCase(FHDLTestCase):
-    def test_domain(self):
-        s1 = ResetSignal()
-        self.assertEqual(s1.domain, "sync")
-        s2 = ResetSignal("pix")
-        self.assertEqual(s2.domain, "pix")
-
-        with self.assertRaises(TypeError):
-            ResetSignal(1)
-
-    def test_shape(self):
-        self.assertEqual(ResetSignal().shape(), (1, False))
-
-    def test_repr(self):
-        s1 = ResetSignal()
-        self.assertEqual(repr(s1), "(rst sync)")
diff --git a/nmigen/test/test_fhdl_cd.py b/nmigen/test/test_fhdl_cd.py
deleted file mode 100644 (file)
index 8e7dcdf..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-from ..hdl.cd import *
-from .tools import *
-
-
-class ClockDomainCase(FHDLTestCase):
-    def test_name(self):
-        sync = ClockDomain()
-        self.assertEqual(sync.name, "sync")
-        self.assertEqual(sync.clk.name, "clk")
-        self.assertEqual(sync.rst.name, "rst")
-        pix = ClockDomain()
-        self.assertEqual(pix.name, "pix")
-        self.assertEqual(pix.clk.name, "pix_clk")
-        self.assertEqual(pix.rst.name, "pix_rst")
-        cd_pix = ClockDomain()
-        self.assertEqual(pix.name, "pix")
-        dom = [ClockDomain("foo")][0]
-        self.assertEqual(dom.name, "foo")
-        with self.assertRaises(ValueError,
-                msg="Clock domain name must be specified explicitly"):
-            ClockDomain()
-
-    def test_with_reset(self):
-        pix = ClockDomain()
-        self.assertIsNotNone(pix.clk)
-        self.assertIsNotNone(pix.rst)
-        self.assertFalse(pix.async_reset)
-
-    def test_without_reset(self):
-        pix = ClockDomain(reset_less=True)
-        self.assertIsNotNone(pix.clk)
-        self.assertIsNone(pix.rst)
-        self.assertFalse(pix.async_reset)
-
-    def test_async_reset(self):
-        pix = ClockDomain(async_reset=True)
-        self.assertIsNotNone(pix.clk)
-        self.assertIsNotNone(pix.rst)
-        self.assertTrue(pix.async_reset)
-
-    def test_rename(self):
-        sync = ClockDomain()
-        self.assertEqual(sync.name, "sync")
-        self.assertEqual(sync.clk.name, "clk")
-        self.assertEqual(sync.rst.name, "rst")
-        sync.rename("pix")
-        self.assertEqual(sync.name, "pix")
-        self.assertEqual(sync.clk.name, "pix_clk")
-        self.assertEqual(sync.rst.name, "pix_rst")
-
-    def test_rename_reset_less(self):
-        sync = ClockDomain(reset_less=True)
-        self.assertEqual(sync.name, "sync")
-        self.assertEqual(sync.clk.name, "clk")
-        sync.rename("pix")
-        self.assertEqual(sync.name, "pix")
-        self.assertEqual(sync.clk.name, "pix_clk")
diff --git a/nmigen/test/test_fhdl_dsl.py b/nmigen/test/test_fhdl_dsl.py
deleted file mode 100644 (file)
index f156880..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-from ..hdl.ast import *
-from ..hdl.dsl import *
-from .tools import *
-
-
-class DSLTestCase(FHDLTestCase):
-    def setUp(self):
-        self.s1 = Signal()
-        self.s2 = Signal()
-        self.s3 = Signal()
-        self.c1 = Signal()
-        self.c2 = Signal()
-        self.c3 = Signal()
-        self.w1 = Signal(4)
-
-    def test_d_comb(self):
-        m = Module()
-        m.d.comb += self.c1.eq(1)
-        m._flush()
-        self.assertEqual(m._driving[self.c1], None)
-        self.assertRepr(m._statements, """(
-            (eq (sig c1) (const 1'd1))
-        )""")
-
-    def test_d_sync(self):
-        m = Module()
-        m.d.sync += self.c1.eq(1)
-        m._flush()
-        self.assertEqual(m._driving[self.c1], "sync")
-        self.assertRepr(m._statements, """(
-            (eq (sig c1) (const 1'd1))
-        )""")
-
-    def test_d_pix(self):
-        m = Module()
-        m.d.pix += self.c1.eq(1)
-        m._flush()
-        self.assertEqual(m._driving[self.c1], "pix")
-        self.assertRepr(m._statements, """(
-            (eq (sig c1) (const 1'd1))
-        )""")
-
-    def test_d_index(self):
-        m = Module()
-        m.d["pix"] += self.c1.eq(1)
-        m._flush()
-        self.assertEqual(m._driving[self.c1], "pix")
-        self.assertRepr(m._statements, """(
-            (eq (sig c1) (const 1'd1))
-        )""")
-
-    def test_d_no_conflict(self):
-        m = Module()
-        m.d.comb += self.w1[0].eq(1)
-        m.d.comb += self.w1[1].eq(1)
-
-    def test_d_conflict(self):
-        m = Module()
-        with self.assertRaises(SyntaxError,
-                msg="Driver-driver conflict: trying to drive (sig c1) from d.sync, but it "
-                    "is already driven from d.comb"):
-            m.d.comb += self.c1.eq(1)
-            m.d.sync += self.c1.eq(1)
-
-    def test_d_wrong(self):
-        m = Module()
-        with self.assertRaises(AttributeError,
-                msg="Cannot assign 'd.pix' attribute; did you mean 'd.pix +='?"):
-            m.d.pix = None
-
-    def test_d_asgn_wrong(self):
-        m = Module()
-        with self.assertRaises(SyntaxError,
-                msg="Only assignments may be appended to d.sync"):
-            m.d.sync += Switch(self.s1, {})
-
-    def test_comb_wrong(self):
-        m = Module()
-        with self.assertRaises(AttributeError,
-                msg="'Module' object has no attribute 'comb'; did you mean 'd.comb'?"):
-            m.comb += self.c1.eq(1)
-
-    def test_sync_wrong(self):
-        m = Module()
-        with self.assertRaises(AttributeError,
-                msg="'Module' object has no attribute 'sync'; did you mean 'd.sync'?"):
-            m.sync += self.c1.eq(1)
-
-    def test_attr_wrong(self):
-        m = Module()
-        with self.assertRaises(AttributeError,
-                msg="'Module' object has no attribute 'nonexistentattr'"):
-            m.nonexistentattr
-
-    def test_If(self):
-        m = Module()
-        with m.If(self.s1):
-            m.d.comb += self.c1.eq(1)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (cat (sig s1))
-                (case 1 (eq (sig c1) (const 1'd1)))
-            )
-        )
-        """)
-
-    def test_If_Elif(self):
-        m = Module()
-        with m.If(self.s1):
-            m.d.comb += self.c1.eq(1)
-        with m.Elif(self.s2):
-            m.d.sync += self.c2.eq(0)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (cat (sig s1) (sig s2))
-                (case -1 (eq (sig c1) (const 1'd1)))
-                (case 1- (eq (sig c2) (const 1'd0)))
-            )
-        )
-        """)
-
-    def test_If_Elif_Else(self):
-        m = Module()
-        with m.If(self.s1):
-            m.d.comb += self.c1.eq(1)
-        with m.Elif(self.s2):
-            m.d.sync += self.c2.eq(0)
-        with m.Else():
-            m.d.comb += self.c3.eq(1)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (cat (sig s1) (sig s2))
-                (case -1 (eq (sig c1) (const 1'd1)))
-                (case 1- (eq (sig c2) (const 1'd0)))
-                (case -- (eq (sig c3) (const 1'd1)))
-            )
-        )
-        """)
-
-    def test_If_If(self):
-        m = Module()
-        with m.If(self.s1):
-            m.d.comb += self.c1.eq(1)
-        with m.If(self.s2):
-            m.d.comb += self.c2.eq(1)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (cat (sig s1))
-                (case 1 (eq (sig c1) (const 1'd1)))
-            )
-            (switch (cat (sig s2))
-                (case 1 (eq (sig c2) (const 1'd1)))
-            )
-        )
-        """)
-
-    def test_If_nested_If(self):
-        m = Module()
-        with m.If(self.s1):
-            m.d.comb += self.c1.eq(1)
-            with m.If(self.s2):
-                m.d.comb += self.c2.eq(1)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (cat (sig s1))
-                (case 1 (eq (sig c1) (const 1'd1))
-                    (switch (cat (sig s2))
-                        (case 1 (eq (sig c2) (const 1'd1)))
-                    )
-                )
-            )
-        )
-        """)
-
-    def test_If_dangling_Else(self):
-        m = Module()
-        with m.If(self.s1):
-            m.d.comb += self.c1.eq(1)
-            with m.If(self.s2):
-                m.d.comb += self.c2.eq(1)
-        with m.Else():
-            m.d.comb += self.c3.eq(1)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (cat (sig s1))
-                (case 1
-                    (eq (sig c1) (const 1'd1))
-                    (switch (cat (sig s2))
-                        (case 1 (eq (sig c2) (const 1'd1)))
-                    )
-                )
-                (case -
-                    (eq (sig c3) (const 1'd1))
-                )
-            )
-        )
-        """)
-
-    def test_Elif_wrong(self):
-        m = Module()
-        with self.assertRaises(SyntaxError,
-                msg="Elif without preceding If"):
-            with m.Elif(self.s2):
-                pass
-
-    def test_Else_wrong(self):
-        m = Module()
-        with self.assertRaises(SyntaxError,
-                msg="Else without preceding If/Elif"):
-            with m.Else():
-                pass
-
-    def test_If_wide(self):
-        m = Module()
-        with m.If(self.w1):
-            m.d.comb += self.c1.eq(1)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (cat (b (sig w1)))
-                (case 1 (eq (sig c1) (const 1'd1)))
-            )
-        )
-        """)
-
-    def test_Switch(self):
-        m = Module()
-        with m.Switch(self.w1):
-            with m.Case(3):
-                m.d.comb += self.c1.eq(1)
-            with m.Case("11--"):
-                m.d.comb += self.c2.eq(1)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (sig w1)
-                (case 0011 (eq (sig c1) (const 1'd1)))
-                (case 11-- (eq (sig c2) (const 1'd1)))
-            )
-        )
-        """)
-
-    def test_Switch_default(self):
-        m = Module()
-        with m.Switch(self.w1):
-            with m.Case(3):
-                m.d.comb += self.c1.eq(1)
-            with m.Case():
-                m.d.comb += self.c2.eq(1)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (sig w1)
-                (case 0011 (eq (sig c1) (const 1'd1)))
-                (case ---- (eq (sig c2) (const 1'd1)))
-            )
-        )
-        """)
-
-    def test_Switch_const_test(self):
-        m = Module()
-        with m.Switch(1):
-            with m.Case(1):
-                m.d.comb += self.c1.eq(1)
-        m._flush()
-        self.assertRepr(m._statements, """
-        (
-            (switch (const 1'd1)
-                (case 1 (eq (sig c1) (const 1'd1)))
-            )
-        )
-        """)
-
-    def test_Case_width_wrong(self):
-        m = Module()
-        with m.Switch(self.w1):
-            with self.assertRaises(SyntaxError,
-                    msg="Case value '--' must have the same width as test (which is 4)"):
-                with m.Case("--"):
-                    pass
-
-    def test_Case_outside_Switch_wrong(self):
-        m = Module()
-        with self.assertRaises(SyntaxError,
-                msg="Case is not permitted outside of Switch"):
-            with m.Case():
-                pass
-
-    def test_If_inside_Switch_wrong(self):
-        m = Module()
-        with m.Switch(self.s1):
-            with self.assertRaises(SyntaxError,
-                    msg="If is not permitted inside of Switch"):
-                with m.If(self.s2):
-                    pass
-
-    def test_auto_pop_ctrl(self):
-        m = Module()
-        with m.If(self.w1):
-            m.d.comb += self.c1.eq(1)
-        m.d.comb += self.c2.eq(1)
-        self.assertRepr(m._statements, """
-        (
-            (switch (cat (b (sig w1)))
-                (case 1 (eq (sig c1) (const 1'd1)))
-            )
-            (eq (sig c2) (const 1'd1))
-        )
-        """)
-
-    def test_submodule_anon(self):
-        m1 = Module()
-        m2 = Module()
-        m1.submodules += m2
-        self.assertEqual(m1._submodules, [(m2, None)])
-
-    def test_submodule_anon_multi(self):
-        m1 = Module()
-        m2 = Module()
-        m3 = Module()
-        m1.submodules += m2, m3
-        self.assertEqual(m1._submodules, [(m2, None), (m3, None)])
-
-    def test_submodule_named(self):
-        m1 = Module()
-        m2 = Module()
-        m1.submodules.foo = m2
-        self.assertEqual(m1._submodules, [(m2, "foo")])
-
-    def test_submodule_wrong(self):
-        m = Module()
-        with self.assertRaises(TypeError,
-                msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
-            m.submodules.foo = 1
-        with self.assertRaises(TypeError,
-                msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
-            m.submodules += 1
-
-    def test_lower(self):
-        m1 = Module()
-        m1.d.comb += self.c1.eq(self.s1)
-        m2 = Module()
-        m2.d.comb += self.c2.eq(self.s2)
-        m2.d.sync += self.c3.eq(self.s3)
-        m1.submodules.foo = m2
-
-        f1 = m1.lower(platform=None)
-        self.assertRepr(f1.statements, """
-        (
-            (eq (sig c1) (sig s1))
-        )
-        """)
-        self.assertEqual(f1.drivers, {
-            None: ValueSet((self.c1,))
-        })
-        self.assertEqual(len(f1.subfragments), 1)
-        (f2, f2_name), = f1.subfragments
-        self.assertEqual(f2_name, "foo")
-        self.assertRepr(f2.statements, """
-        (
-            (eq (sig c2) (sig s2))
-            (eq (sig c3) (sig s3))
-        )
-        """)
-        self.assertEqual(f2.drivers, {
-            None: ValueSet((self.c2,)),
-            "sync": ValueSet((self.c3,))
-        })
-        self.assertEqual(len(f2.subfragments), 0)
diff --git a/nmigen/test/test_fhdl_ir.py b/nmigen/test/test_fhdl_ir.py
deleted file mode 100644 (file)
index fcc1b2e..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-from ..hdl.ast import *
-from ..hdl.cd import *
-from ..hdl.ir import *
-from .tools import *
-
-
-class FragmentDriversTestCase(FHDLTestCase):
-    def test_empty(self):
-        f = Fragment()
-        self.assertEqual(list(f.iter_comb()), [])
-        self.assertEqual(list(f.iter_sync()), [])
-
-
-class FragmentPortsTestCase(FHDLTestCase):
-    def setUp(self):
-        self.s1 = Signal()
-        self.s2 = Signal()
-        self.s3 = Signal()
-        self.c1 = Signal()
-        self.c2 = Signal()
-        self.c3 = Signal()
-
-    def test_empty(self):
-        f = Fragment()
-        self.assertEqual(list(f.iter_ports()), [])
-
-        f._propagate_ports(ports=())
-        self.assertEqual(f.ports, ValueDict([]))
-
-    def test_iter_signals(self):
-        f = Fragment()
-        f.add_ports(self.s1, self.s2, kind="io")
-        self.assertEqual(ValueSet((self.s1, self.s2)), f.iter_signals())
-
-    def test_self_contained(self):
-        f = Fragment()
-        f.add_statements(
-            self.c1.eq(self.s1),
-            self.s1.eq(self.c1)
-        )
-
-        f._propagate_ports(ports=())
-        self.assertEqual(f.ports, ValueDict([]))
-
-    def test_infer_input(self):
-        f = Fragment()
-        f.add_statements(
-            self.c1.eq(self.s1)
-        )
-
-        f._propagate_ports(ports=())
-        self.assertEqual(f.ports, ValueDict([
-            (self.s1, "i")
-        ]))
-
-    def test_request_output(self):
-        f = Fragment()
-        f.add_statements(
-            self.c1.eq(self.s1)
-        )
-
-        f._propagate_ports(ports=(self.c1,))
-        self.assertEqual(f.ports, ValueDict([
-            (self.s1, "i"),
-            (self.c1, "o")
-        ]))
-
-    def test_input_in_subfragment(self):
-        f1 = Fragment()
-        f1.add_statements(
-            self.c1.eq(self.s1)
-        )
-        f2 = Fragment()
-        f2.add_statements(
-            self.s1.eq(0)
-        )
-        f1.add_subfragment(f2)
-        f1._propagate_ports(ports=())
-        self.assertEqual(f1.ports, ValueDict())
-        self.assertEqual(f2.ports, ValueDict([
-            (self.s1, "o"),
-        ]))
-
-    def test_input_only_in_subfragment(self):
-        f1 = Fragment()
-        f2 = Fragment()
-        f2.add_statements(
-            self.c1.eq(self.s1)
-        )
-        f1.add_subfragment(f2)
-        f1._propagate_ports(ports=())
-        self.assertEqual(f1.ports, ValueDict([
-            (self.s1, "i"),
-        ]))
-        self.assertEqual(f2.ports, ValueDict([
-            (self.s1, "i"),
-        ]))
-
-    def test_output_from_subfragment(self):
-        f1 = Fragment()
-        f1.add_statements(
-            self.c1.eq(0)
-        )
-        f2 = Fragment()
-        f2.add_statements(
-            self.c2.eq(1)
-        )
-        f1.add_subfragment(f2)
-
-        f1._propagate_ports(ports=(self.c2,))
-        self.assertEqual(f1.ports, ValueDict([
-            (self.c2, "o"),
-        ]))
-        self.assertEqual(f2.ports, ValueDict([
-            (self.c2, "o"),
-        ]))
-
-    def test_input_cd(self):
-        sync = ClockDomain()
-        f = Fragment()
-        f.add_statements(
-            self.c1.eq(self.s1)
-        )
-        f.add_domains(sync)
-        f.add_driver(self.c1, "sync")
-
-        f._propagate_ports(ports=())
-        self.assertEqual(f.ports, ValueDict([
-            (self.s1,  "i"),
-            (sync.clk, "i"),
-            (sync.rst, "i"),
-        ]))
-
-    def test_input_cd_reset_less(self):
-        sync = ClockDomain(reset_less=True)
-        f = Fragment()
-        f.add_statements(
-            self.c1.eq(self.s1)
-        )
-        f.add_domains(sync)
-        f.add_driver(self.c1, "sync")
-
-        f._propagate_ports(ports=())
-        self.assertEqual(f.ports, ValueDict([
-            (self.s1,  "i"),
-            (sync.clk, "i"),
-        ]))
-
-
-class FragmentDomainsTestCase(FHDLTestCase):
-    def test_iter_signals(self):
-        cd1 = ClockDomain()
-        cd2 = ClockDomain(reset_less=True)
-        s1 = Signal()
-        s2 = Signal()
-
-        f = Fragment()
-        f.add_domains(cd1, cd2)
-        f.add_driver(s1, "cd1")
-        self.assertEqual(ValueSet((cd1.clk, cd1.rst, s1)), f.iter_signals())
-        f.add_driver(s2, "cd2")
-        self.assertEqual(ValueSet((cd1.clk, cd1.rst, cd2.clk, s1, s2)), f.iter_signals())
-
-    def test_propagate_up(self):
-        cd = ClockDomain()
-
-        f1 = Fragment()
-        f2 = Fragment()
-        f1.add_subfragment(f2)
-        f2.add_domains(cd)
-
-        f1._propagate_domains_up()
-        self.assertEqual(f1.domains, {"cd": cd})
-
-    def test_domain_conflict(self):
-        cda = ClockDomain("sync")
-        cdb = ClockDomain("sync")
-
-        fa = Fragment()
-        fa.add_domains(cda)
-        fb = Fragment()
-        fb.add_domains(cdb)
-        f = Fragment()
-        f.add_subfragment(fa, "a")
-        f.add_subfragment(fb, "b")
-
-        f._propagate_domains_up()
-        self.assertEqual(f.domains, {"a_sync": cda, "b_sync": cdb})
-        (fa, _), (fb, _) = f.subfragments
-        self.assertEqual(fa.domains, {"a_sync": cda})
-        self.assertEqual(fb.domains, {"b_sync": cdb})
-
-    def test_domain_conflict_anon(self):
-        cda = ClockDomain("sync")
-        cdb = ClockDomain("sync")
-
-        fa = Fragment()
-        fa.add_domains(cda)
-        fb = Fragment()
-        fb.add_domains(cdb)
-        f = Fragment()
-        f.add_subfragment(fa, "a")
-        f.add_subfragment(fb)
-
-        with self.assertRaises(DomainError,
-                msg="Domain 'sync' is defined by subfragments 'a', <unnamed #1> of fragment "
-                    "'top'; it is necessary to either rename subfragment domains explicitly, "
-                    "or give names to subfragments"):
-            f._propagate_domains_up()
-
-    def test_domain_conflict_name(self):
-        cda = ClockDomain("sync")
-        cdb = ClockDomain("sync")
-
-        fa = Fragment()
-        fa.add_domains(cda)
-        fb = Fragment()
-        fb.add_domains(cdb)
-        f = Fragment()
-        f.add_subfragment(fa, "x")
-        f.add_subfragment(fb, "x")
-
-        with self.assertRaises(DomainError,
-                msg="Domain 'sync' is defined by subfragments #0, #1 of fragment 'top', some "
-                    "of which have identical names; it is necessary to either rename subfragment "
-                    "domains explicitly, or give distinct names to subfragments"):
-            f._propagate_domains_up()
-
-    def test_propagate_down(self):
-        cd = ClockDomain()
-
-        f1 = Fragment()
-        f2 = Fragment()
-        f1.add_domains(cd)
-        f1.add_subfragment(f2)
-
-        f1._propagate_domains_down()
-        self.assertEqual(f2.domains, {"cd": cd})
-
-    def test_propagate_down_idempotent(self):
-        cd = ClockDomain()
-
-        f1 = Fragment()
-        f1.add_domains(cd)
-        f2 = Fragment()
-        f2.add_domains(cd)
-        f1.add_subfragment(f2)
-
-        f1._propagate_domains_down()
-        self.assertEqual(f1.domains, {"cd": cd})
-        self.assertEqual(f2.domains, {"cd": cd})
-
-    def test_propagate(self):
-        cd = ClockDomain()
-
-        f1 = Fragment()
-        f2 = Fragment()
-        f1.add_domains(cd)
-        f1.add_subfragment(f2)
-
-        f1._propagate_domains(ensure_sync_exists=False)
-        self.assertEqual(f1.domains, {"cd": cd})
-        self.assertEqual(f2.domains, {"cd": cd})
-
-    def test_propagate_ensure_sync(self):
-        f1 = Fragment()
-        f2 = Fragment()
-        f1.add_subfragment(f2)
-
-        f1._propagate_domains(ensure_sync_exists=True)
-        self.assertEqual(f1.domains.keys(), {"sync"})
-        self.assertEqual(f2.domains.keys(), {"sync"})
-        self.assertEqual(f1.domains["sync"], f2.domains["sync"])
-
-
-class FragmentDriverConflictTestCase(FHDLTestCase):
-    def setUp_self_sub(self):
-        self.s1 = Signal()
-        self.c1 = Signal()
-        self.c2 = Signal()
-
-        self.f1 = Fragment()
-        self.f1.add_statements(self.c1.eq(0))
-        self.f1.add_driver(self.s1)
-        self.f1.add_driver(self.c1, "sync")
-
-        self.f1a = Fragment()
-        self.f1.add_subfragment(self.f1a, "f1a")
-
-        self.f2 = Fragment()
-        self.f2.add_statements(self.c2.eq(1))
-        self.f2.add_driver(self.s1)
-        self.f2.add_driver(self.c2, "sync")
-        self.f1.add_subfragment(self.f2)
-
-        self.f1b = Fragment()
-        self.f1.add_subfragment(self.f1b, "f1b")
-
-        self.f2a = Fragment()
-        self.f2.add_subfragment(self.f2a, "f2a")
-
-    def test_conflict_self_sub(self):
-        self.setUp_self_sub()
-
-        self.f1._resolve_driver_conflicts(mode="silent")
-        self.assertEqual(self.f1.subfragments, [
-            (self.f1a, "f1a"),
-            (self.f1b, "f1b"),
-            (self.f2a, "f2a"),
-        ])
-        self.assertRepr(self.f1.statements, """
-        (
-            (eq (sig c1) (const 1'd0))
-            (eq (sig c2) (const 1'd1))
-        )
-        """)
-        self.assertEqual(self.f1.drivers, {
-            None:   ValueSet((self.s1,)),
-            "sync": ValueSet((self.c1, self.c2)),
-        })
-
-    def test_conflict_self_sub_error(self):
-        self.setUp_self_sub()
-
-        with self.assertRaises(DriverConflict,
-                msg="Signal '(sig s1)' is driven from multiple fragments: top, top.<unnamed #1>"):
-            self.f1._resolve_driver_conflicts(mode="error")
-
-    def test_conflict_self_sub_warning(self):
-        self.setUp_self_sub()
-
-        with self.assertWarns(DriverConflict,
-                msg="Signal '(sig s1)' is driven from multiple fragments: top, top.<unnamed #1>; "
-                    "hierarchy will be flattened"):
-            self.f1._resolve_driver_conflicts(mode="warn")
-
-    def setUp_sub_sub(self):
-        self.s1 = Signal()
-        self.c1 = Signal()
-        self.c2 = Signal()
-
-        self.f1 = Fragment()
-
-        self.f2 = Fragment()
-        self.f2.add_driver(self.s1)
-        self.f2.add_statements(self.c1.eq(0))
-        self.f1.add_subfragment(self.f2)
-
-        self.f3 = Fragment()
-        self.f3.add_driver(self.s1)
-        self.f3.add_statements(self.c2.eq(1))
-        self.f1.add_subfragment(self.f3)
-
-    def test_conflict_sub_sub(self):
-        self.setUp_sub_sub()
-
-        self.f1._resolve_driver_conflicts(mode="silent")
-        self.assertEqual(self.f1.subfragments, [])
-        self.assertRepr(self.f1.statements, """
-        (
-            (eq (sig c1) (const 1'd0))
-            (eq (sig c2) (const 1'd1))
-        )
-        """)
-
-    def setUp_self_subsub(self):
-        self.s1 = Signal()
-        self.c1 = Signal()
-        self.c2 = Signal()
-
-        self.f1 = Fragment()
-        self.f1.add_driver(self.s1)
-
-        self.f2 = Fragment()
-        self.f2.add_statements(self.c1.eq(0))
-        self.f1.add_subfragment(self.f2)
-
-        self.f3 = Fragment()
-        self.f3.add_driver(self.s1)
-        self.f3.add_statements(self.c2.eq(1))
-        self.f2.add_subfragment(self.f3)
-
-    def test_conflict_self_subsub(self):
-        self.setUp_self_subsub()
-
-        self.f1._resolve_driver_conflicts(mode="silent")
-        self.assertEqual(self.f1.subfragments, [])
-        self.assertRepr(self.f1.statements, """
-        (
-            (eq (sig c1) (const 1'd0))
-            (eq (sig c2) (const 1'd1))
-        )
-        """)
diff --git a/nmigen/test/test_fhdl_xfrm.py b/nmigen/test/test_fhdl_xfrm.py
deleted file mode 100644 (file)
index 428bad7..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-from ..hdl.ast import *
-from ..hdl.cd import *
-from ..hdl.ir import *
-from ..hdl.xfrm import *
-from .tools import *
-
-
-class DomainRenamerTestCase(FHDLTestCase):
-    def setUp(self):
-        self.s1 = Signal()
-        self.s2 = Signal()
-        self.s3 = Signal()
-        self.s4 = Signal()
-        self.s5 = Signal()
-        self.c1 = Signal()
-
-    def test_rename_signals(self):
-        f = Fragment()
-        f.add_statements(
-            self.s1.eq(ClockSignal()),
-            ResetSignal().eq(self.s2),
-            self.s3.eq(0),
-            self.s4.eq(ClockSignal("other")),
-            self.s5.eq(ResetSignal("other")),
-        )
-        f.add_driver(self.s1, None)
-        f.add_driver(self.s2, None)
-        f.add_driver(self.s3, "sync")
-
-        f = DomainRenamer("pix")(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s1) (clk pix))
-            (eq (rst pix) (sig s2))
-            (eq (sig s3) (const 1'd0))
-            (eq (sig s4) (clk other))
-            (eq (sig s5) (rst other))
-        )
-        """)
-        self.assertEqual(f.drivers, {
-            None: ValueSet((self.s1, self.s2)),
-            "pix": ValueSet((self.s3,)),
-        })
-
-    def test_rename_multi(self):
-        f = Fragment()
-        f.add_statements(
-            self.s1.eq(ClockSignal()),
-            self.s2.eq(ResetSignal("other")),
-        )
-
-        f = DomainRenamer({"sync": "pix", "other": "pix2"})(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s1) (clk pix))
-            (eq (sig s2) (rst pix2))
-        )
-        """)
-
-    def test_rename_cd(self):
-        cd_sync = ClockDomain()
-        cd_pix  = ClockDomain()
-
-        f = Fragment()
-        f.add_domains(cd_sync, cd_pix)
-
-        f = DomainRenamer("ext")(f)
-        self.assertEqual(cd_sync.name, "ext")
-        self.assertEqual(f.domains, {
-            "ext": cd_sync,
-            "pix": cd_pix,
-        })
-
-    def test_rename_cd_subfragment(self):
-        cd_sync = ClockDomain()
-        cd_pix  = ClockDomain()
-
-        f1 = Fragment()
-        f1.add_domains(cd_sync, cd_pix)
-        f2 = Fragment()
-        f2.add_domains(cd_sync)
-        f1.add_subfragment(f2)
-
-        f1 = DomainRenamer("ext")(f1)
-        self.assertEqual(cd_sync.name, "ext")
-        self.assertEqual(f1.domains, {
-            "ext": cd_sync,
-            "pix": cd_pix,
-        })
-
-
-class DomainLowererTestCase(FHDLTestCase):
-    def setUp(self):
-        self.s = Signal()
-
-    def test_lower_clk(self):
-        sync = ClockDomain()
-        f = Fragment()
-        f.add_statements(
-            self.s.eq(ClockSignal("sync"))
-        )
-
-        f = DomainLowerer({"sync": sync})(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s) (sig clk))
-        )
-        """)
-
-    def test_lower_rst(self):
-        sync = ClockDomain()
-        f = Fragment()
-        f.add_statements(
-            self.s.eq(ResetSignal("sync"))
-        )
-
-        f = DomainLowerer({"sync": sync})(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s) (sig rst))
-        )
-        """)
-
-    def test_lower_rst_reset_less(self):
-        sync = ClockDomain(reset_less=True)
-        f = Fragment()
-        f.add_statements(
-            self.s.eq(ResetSignal("sync", allow_reset_less=True))
-        )
-
-        f = DomainLowerer({"sync": sync})(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s) (const 1'd0))
-        )
-        """)
-
-    def test_lower_wrong_domain(self):
-        sync = ClockDomain()
-        f = Fragment()
-        f.add_statements(
-            self.s.eq(ClockSignal("xxx"))
-        )
-
-        with self.assertRaises(DomainError,
-                msg="Signal (clk xxx) refers to nonexistent domain 'xxx'"):
-            DomainLowerer({"sync": sync})(f)
-
-    def test_lower_wrong_reset_less_domain(self):
-        sync = ClockDomain(reset_less=True)
-        f = Fragment()
-        f.add_statements(
-            self.s.eq(ResetSignal("sync"))
-        )
-
-        with self.assertRaises(DomainError,
-                msg="Signal (rst sync) refers to reset of reset-less domain 'sync'"):
-            DomainLowerer({"sync": sync})(f)
-
-
-class ResetInserterTestCase(FHDLTestCase):
-    def setUp(self):
-        self.s1 = Signal()
-        self.s2 = Signal(reset=1)
-        self.s3 = Signal(reset=1, reset_less=True)
-        self.c1 = Signal()
-
-    def test_reset_default(self):
-        f = Fragment()
-        f.add_statements(
-            self.s1.eq(1)
-        )
-        f.add_driver(self.s1, "sync")
-
-        f = ResetInserter(self.c1)(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s1) (const 1'd1))
-            (switch (sig c1)
-                (case 1 (eq (sig s1) (const 1'd0)))
-            )
-        )
-        """)
-
-    def test_reset_cd(self):
-        f = Fragment()
-        f.add_statements(
-            self.s1.eq(1),
-            self.s2.eq(0),
-        )
-        f.add_domains(ClockDomain("sync"))
-        f.add_driver(self.s1, "sync")
-        f.add_driver(self.s2, "pix")
-
-        f = ResetInserter({"pix": self.c1})(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s1) (const 1'd1))
-            (eq (sig s2) (const 1'd0))
-            (switch (sig c1)
-                (case 1 (eq (sig s2) (const 1'd1)))
-            )
-        )
-        """)
-
-    def test_reset_value(self):
-        f = Fragment()
-        f.add_statements(
-            self.s2.eq(0)
-        )
-        f.add_driver(self.s2, "sync")
-
-        f = ResetInserter(self.c1)(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s2) (const 1'd0))
-            (switch (sig c1)
-                (case 1 (eq (sig s2) (const 1'd1)))
-            )
-        )
-        """)
-
-    def test_reset_less(self):
-        f = Fragment()
-        f.add_statements(
-            self.s3.eq(0)
-        )
-        f.add_driver(self.s3, "sync")
-
-        f = ResetInserter(self.c1)(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s3) (const 1'd0))
-            (switch (sig c1)
-                (case 1 )
-            )
-        )
-        """)
-
-
-class CEInserterTestCase(FHDLTestCase):
-    def setUp(self):
-        self.s1 = Signal()
-        self.s2 = Signal()
-        self.s3 = Signal()
-        self.c1 = Signal()
-
-    def test_ce_default(self):
-        f = Fragment()
-        f.add_statements(
-            self.s1.eq(1)
-        )
-        f.add_driver(self.s1, "sync")
-
-        f = CEInserter(self.c1)(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s1) (const 1'd1))
-            (switch (sig c1)
-                (case 0 (eq (sig s1) (sig s1)))
-            )
-        )
-        """)
-
-    def test_ce_cd(self):
-        f = Fragment()
-        f.add_statements(
-            self.s1.eq(1),
-            self.s2.eq(0),
-        )
-        f.add_driver(self.s1, "sync")
-        f.add_driver(self.s2, "pix")
-
-        f = CEInserter({"pix": self.c1})(f)
-        self.assertRepr(f.statements, """
-        (
-            (eq (sig s1) (const 1'd1))
-            (eq (sig s2) (const 1'd0))
-            (switch (sig c1)
-                (case 0 (eq (sig s2) (sig s2)))
-            )
-        )
-        """)
-
-    def test_ce_subfragment(self):
-        f1 = Fragment()
-        f1.add_statements(
-            self.s1.eq(1)
-        )
-        f1.add_driver(self.s1, "sync")
-
-        f2 = Fragment()
-        f2.add_statements(
-            self.s2.eq(1)
-        )
-        f2.add_driver(self.s2, "sync")
-        f1.add_subfragment(f2)
-
-        f1 = CEInserter(self.c1)(f1)
-        (f2, _), = f1.subfragments
-        self.assertRepr(f1.statements, """
-        (
-            (eq (sig s1) (const 1'd1))
-            (switch (sig c1)
-                (case 0 (eq (sig s1) (sig s1)))
-            )
-        )
-        """)
-        self.assertRepr(f2.statements, """
-        (
-            (eq (sig s2) (const 1'd1))
-            (switch (sig c1)
-                (case 0 (eq (sig s2) (sig s2)))
-            )
-        )
-        """)
diff --git a/nmigen/test/test_hdl_ast.py b/nmigen/test/test_hdl_ast.py
new file mode 100644 (file)
index 0000000..0fde4c6
--- /dev/null
@@ -0,0 +1,436 @@
+from ..hdl.ast import *
+from .tools import *
+
+
+class ValueTestCase(FHDLTestCase):
+    def test_wrap(self):
+        self.assertIsInstance(Value.wrap(0), Const)
+        self.assertIsInstance(Value.wrap(True), Const)
+        c = Const(0)
+        self.assertIs(Value.wrap(c), c)
+        with self.assertRaises(TypeError):
+            Value.wrap("str")
+
+    def test_bool(self):
+        with self.assertRaises(TypeError):
+            if Const(0):
+                pass
+
+    def test_len(self):
+        self.assertEqual(len(Const(10)), 4)
+
+    def test_getitem_int(self):
+        s1 = Const(10)[0]
+        self.assertIsInstance(s1, Slice)
+        self.assertEqual(s1.start, 0)
+        self.assertEqual(s1.end, 1)
+        s2 = Const(10)[-1]
+        self.assertIsInstance(s2, Slice)
+        self.assertEqual(s2.start, 3)
+        self.assertEqual(s2.end, 4)
+        with self.assertRaises(IndexError):
+            Const(10)[5]
+
+    def test_getitem_slice(self):
+        s1 = Const(10)[1:3]
+        self.assertIsInstance(s1, Slice)
+        self.assertEqual(s1.start, 1)
+        self.assertEqual(s1.end, 3)
+        s2 = Const(10)[1:-2]
+        self.assertIsInstance(s2, Slice)
+        self.assertEqual(s2.start, 1)
+        self.assertEqual(s2.end, 2)
+        s3 = Const(31)[::2]
+        self.assertIsInstance(s3, Cat)
+        self.assertIsInstance(s3.operands[0], Slice)
+        self.assertEqual(s3.operands[0].start, 0)
+        self.assertEqual(s3.operands[0].end, 1)
+        self.assertIsInstance(s3.operands[1], Slice)
+        self.assertEqual(s3.operands[1].start, 2)
+        self.assertEqual(s3.operands[1].end, 3)
+        self.assertIsInstance(s3.operands[2], Slice)
+        self.assertEqual(s3.operands[2].start, 4)
+        self.assertEqual(s3.operands[2].end, 5)
+
+    def test_getitem_wrong(self):
+        with self.assertRaises(TypeError):
+            Const(31)["str"]
+
+
+class ConstTestCase(FHDLTestCase):
+    def test_shape(self):
+        self.assertEqual(Const(0).shape(),   (1, False))
+        self.assertEqual(Const(1).shape(),   (1, False))
+        self.assertEqual(Const(10).shape(),  (4, False))
+        self.assertEqual(Const(-10).shape(), (5, True))
+
+        self.assertEqual(Const(1, 4).shape(),          (4, False))
+        self.assertEqual(Const(1, (4, True)).shape(),  (4, True))
+        self.assertEqual(Const(0, (0, False)).shape(), (0, False))
+
+    def test_shape_bad(self):
+        with self.assertRaises(TypeError):
+            Const(1, -1)
+
+    def test_normalization(self):
+        self.assertEqual(Const(0b10110, (5, True)).value, -10)
+
+    def test_value(self):
+        self.assertEqual(Const(10).value, 10)
+
+    def test_repr(self):
+        self.assertEqual(repr(Const(10)),  "(const 4'd10)")
+        self.assertEqual(repr(Const(-10)), "(const 5'sd-10)")
+
+    def test_hash(self):
+        with self.assertRaises(TypeError):
+            hash(Const(0))
+
+
+class OperatorTestCase(FHDLTestCase):
+    def test_bool(self):
+        v = Const(0, 4).bool()
+        self.assertEqual(repr(v), "(b (const 4'd0))")
+        self.assertEqual(v.shape(), (1, False))
+
+    def test_invert(self):
+        v = ~Const(0, 4)
+        self.assertEqual(repr(v), "(~ (const 4'd0))")
+        self.assertEqual(v.shape(), (4, False))
+
+    def test_neg(self):
+        v1 = -Const(0, (4, False))
+        self.assertEqual(repr(v1), "(- (const 4'd0))")
+        self.assertEqual(v1.shape(), (5, True))
+        v2 = -Const(0, (4, True))
+        self.assertEqual(repr(v2), "(- (const 4'sd0))")
+        self.assertEqual(v2.shape(), (4, True))
+
+    def test_add(self):
+        v1 = Const(0, (4, False)) + Const(0, (6, False))
+        self.assertEqual(repr(v1), "(+ (const 4'd0) (const 6'd0))")
+        self.assertEqual(v1.shape(), (7, False))
+        v2 = Const(0, (4, True)) + Const(0, (6, True))
+        self.assertEqual(v2.shape(), (7, True))
+        v3 = Const(0, (4, True)) + Const(0, (4, False))
+        self.assertEqual(v3.shape(), (6, True))
+        v4 = Const(0, (4, False)) + Const(0, (4, True))
+        self.assertEqual(v4.shape(), (6, True))
+        v5 = 10 + Const(0, 4)
+        self.assertEqual(v5.shape(), (5, False))
+
+    def test_sub(self):
+        v1 = Const(0, (4, False)) - Const(0, (6, False))
+        self.assertEqual(repr(v1), "(- (const 4'd0) (const 6'd0))")
+        self.assertEqual(v1.shape(), (7, False))
+        v2 = Const(0, (4, True)) - Const(0, (6, True))
+        self.assertEqual(v2.shape(), (7, True))
+        v3 = Const(0, (4, True)) - Const(0, (4, False))
+        self.assertEqual(v3.shape(), (6, True))
+        v4 = Const(0, (4, False)) - Const(0, (4, True))
+        self.assertEqual(v4.shape(), (6, True))
+        v5 = 10 - Const(0, 4)
+        self.assertEqual(v5.shape(), (5, False))
+
+    def test_mul(self):
+        v1 = Const(0, (4, False)) * Const(0, (6, False))
+        self.assertEqual(repr(v1), "(* (const 4'd0) (const 6'd0))")
+        self.assertEqual(v1.shape(), (10, False))
+        v2 = Const(0, (4, True)) * Const(0, (6, True))
+        self.assertEqual(v2.shape(), (9, True))
+        v3 = Const(0, (4, True)) * Const(0, (4, False))
+        self.assertEqual(v3.shape(), (8, True))
+        v5 = 10 * Const(0, 4)
+        self.assertEqual(v5.shape(), (8, False))
+
+    def test_and(self):
+        v1 = Const(0, (4, False)) & Const(0, (6, False))
+        self.assertEqual(repr(v1), "(& (const 4'd0) (const 6'd0))")
+        self.assertEqual(v1.shape(), (6, False))
+        v2 = Const(0, (4, True)) & Const(0, (6, True))
+        self.assertEqual(v2.shape(), (6, True))
+        v3 = Const(0, (4, True)) & Const(0, (4, False))
+        self.assertEqual(v3.shape(), (5, True))
+        v4 = Const(0, (4, False)) & Const(0, (4, True))
+        self.assertEqual(v4.shape(), (5, True))
+        v5 = 10 & Const(0, 4)
+        self.assertEqual(v5.shape(), (4, False))
+
+    def test_or(self):
+        v1 = Const(0, (4, False)) | Const(0, (6, False))
+        self.assertEqual(repr(v1), "(| (const 4'd0) (const 6'd0))")
+        self.assertEqual(v1.shape(), (6, False))
+        v2 = Const(0, (4, True)) | Const(0, (6, True))
+        self.assertEqual(v2.shape(), (6, True))
+        v3 = Const(0, (4, True)) | Const(0, (4, False))
+        self.assertEqual(v3.shape(), (5, True))
+        v4 = Const(0, (4, False)) | Const(0, (4, True))
+        self.assertEqual(v4.shape(), (5, True))
+        v5 = 10 | Const(0, 4)
+        self.assertEqual(v5.shape(), (4, False))
+
+    def test_xor(self):
+        v1 = Const(0, (4, False)) ^ Const(0, (6, False))
+        self.assertEqual(repr(v1), "(^ (const 4'd0) (const 6'd0))")
+        self.assertEqual(v1.shape(), (6, False))
+        v2 = Const(0, (4, True)) ^ Const(0, (6, True))
+        self.assertEqual(v2.shape(), (6, True))
+        v3 = Const(0, (4, True)) ^ Const(0, (4, False))
+        self.assertEqual(v3.shape(), (5, True))
+        v4 = Const(0, (4, False)) ^ Const(0, (4, True))
+        self.assertEqual(v4.shape(), (5, True))
+        v5 = 10 ^ Const(0, 4)
+        self.assertEqual(v5.shape(), (4, False))
+
+    def test_shl(self):
+        v1 = Const(1, 4) << Const(4)
+        self.assertEqual(repr(v1), "(<< (const 4'd1) (const 3'd4))")
+        self.assertEqual(v1.shape(), (11, False))
+        v2 = Const(1, 4) << Const(-3)
+        self.assertEqual(v2.shape(), (7, False))
+
+    def test_shr(self):
+        v1 = Const(1, 4) >> Const(4)
+        self.assertEqual(repr(v1), "(>> (const 4'd1) (const 3'd4))")
+        self.assertEqual(v1.shape(), (4, False))
+        v2 = Const(1, 4) >> Const(-3)
+        self.assertEqual(v2.shape(), (8, False))
+
+    def test_lt(self):
+        v = Const(0, 4) < Const(0, 6)
+        self.assertEqual(repr(v), "(< (const 4'd0) (const 6'd0))")
+        self.assertEqual(v.shape(), (1, False))
+
+    def test_le(self):
+        v = Const(0, 4) <= Const(0, 6)
+        self.assertEqual(repr(v), "(<= (const 4'd0) (const 6'd0))")
+        self.assertEqual(v.shape(), (1, False))
+
+    def test_gt(self):
+        v = Const(0, 4) > Const(0, 6)
+        self.assertEqual(repr(v), "(> (const 4'd0) (const 6'd0))")
+        self.assertEqual(v.shape(), (1, False))
+
+    def test_ge(self):
+        v = Const(0, 4) >= Const(0, 6)
+        self.assertEqual(repr(v), "(>= (const 4'd0) (const 6'd0))")
+        self.assertEqual(v.shape(), (1, False))
+
+    def test_eq(self):
+        v = Const(0, 4) == Const(0, 6)
+        self.assertEqual(repr(v), "(== (const 4'd0) (const 6'd0))")
+        self.assertEqual(v.shape(), (1, False))
+
+    def test_ne(self):
+        v = Const(0, 4) != Const(0, 6)
+        self.assertEqual(repr(v), "(!= (const 4'd0) (const 6'd0))")
+        self.assertEqual(v.shape(), (1, False))
+
+    def test_mux(self):
+        s  = Const(0)
+        v1 = Mux(s, Const(0, (4, False)), Const(0, (6, False)))
+        self.assertEqual(repr(v1), "(m (const 1'd0) (const 4'd0) (const 6'd0))")
+        self.assertEqual(v1.shape(), (6, False))
+        v2 = Mux(s, Const(0, (4, True)), Const(0, (6, True)))
+        self.assertEqual(v2.shape(), (6, True))
+        v3 = Mux(s, Const(0, (4, True)), Const(0, (4, False)))
+        self.assertEqual(v3.shape(), (5, True))
+        v4 = Mux(s, Const(0, (4, False)), Const(0, (4, True)))
+        self.assertEqual(v4.shape(), (5, True))
+
+    def test_bool(self):
+        v = Const(0).bool()
+        self.assertEqual(repr(v), "(b (const 1'd0))")
+        self.assertEqual(v.shape(), (1, False))
+
+    def test_hash(self):
+        with self.assertRaises(TypeError):
+            hash(Const(0) + Const(0))
+
+
+class SliceTestCase(FHDLTestCase):
+    def test_shape(self):
+        s1 = Const(10)[2]
+        self.assertEqual(s1.shape(), (1, False))
+        s2 = Const(-10)[0:2]
+        self.assertEqual(s2.shape(), (2, False))
+
+    def test_start_end_negative(self):
+        c  = Const(0, 8)
+        s1 = Slice(c, 0, -1)
+        self.assertEqual((s1.start, s1.end), (0, 7))
+        s1 = Slice(c, -4, -1)
+        self.assertEqual((s1.start, s1.end), (4, 7))
+
+    def test_start_end_wrong(self):
+        with self.assertRaises(TypeError):
+            Slice(0, "x", 1)
+        with self.assertRaises(TypeError):
+            Slice(0, 1, "x")
+
+    def test_start_end_out_of_range(self):
+        c = Const(0, 8)
+        with self.assertRaises(IndexError):
+            Slice(c, 10, 12)
+        with self.assertRaises(IndexError):
+            Slice(c, 0, 12)
+        with self.assertRaises(IndexError):
+            Slice(c, 4, 2)
+
+    def test_repr(self):
+        s1 = Const(10)[2]
+        self.assertEqual(repr(s1), "(slice (const 4'd10) 2:3)")
+
+
+class PartTestCase(FHDLTestCase):
+    def setUp(self):
+        self.c = Const(0, 8)
+        self.s = Signal(max=self.c.nbits)
+
+    def test_shape(self):
+        s1 = self.c.part(self.s, 2)
+        self.assertEqual(s1.shape(), (2, False))
+        s2 = self.c.part(self.s, 0)
+        self.assertEqual(s2.shape(), (0, False))
+
+    def test_width_bad(self):
+        with self.assertRaises(TypeError):
+            self.c.part(self.s, -1)
+
+    def test_repr(self):
+        s = self.c.part(self.s, 2)
+        self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2)")
+
+
+class CatTestCase(FHDLTestCase):
+    def test_shape(self):
+        c1 = Cat(Const(10))
+        self.assertEqual(c1.shape(), (4, False))
+        c2 = Cat(Const(10), Const(1))
+        self.assertEqual(c2.shape(), (5, False))
+        c3 = Cat(Const(10), Const(1), Const(0))
+        self.assertEqual(c3.shape(), (6, False))
+
+    def test_repr(self):
+        c1 = Cat(Const(10), Const(1))
+        self.assertEqual(repr(c1), "(cat (const 4'd10) (const 1'd1))")
+
+
+class ReplTestCase(FHDLTestCase):
+    def test_shape(self):
+        s1 = Repl(Const(10), 3)
+        self.assertEqual(s1.shape(), (12, False))
+        s2 = Repl(Const(10), 0)
+        self.assertEqual(s2.shape(), (0, False))
+
+    def test_count_wrong(self):
+        with self.assertRaises(TypeError):
+            Repl(Const(10), -1)
+        with self.assertRaises(TypeError):
+            Repl(Const(10), "str")
+
+    def test_repr(self):
+        s = Repl(Const(10), 3)
+        self.assertEqual(repr(s), "(repl (const 4'd10) 3)")
+
+
+class SignalTestCase(FHDLTestCase):
+    def test_shape(self):
+        s1 = Signal()
+        self.assertEqual(s1.shape(), (1, False))
+        s2 = Signal(2)
+        self.assertEqual(s2.shape(), (2, False))
+        s3 = Signal((2, False))
+        self.assertEqual(s3.shape(), (2, False))
+        s4 = Signal((2, True))
+        self.assertEqual(s4.shape(), (2, True))
+        s5 = Signal(max=16)
+        self.assertEqual(s5.shape(), (4, False))
+        s6 = Signal(min=4, max=16)
+        self.assertEqual(s6.shape(), (4, False))
+        s7 = Signal(min=-4, max=16)
+        self.assertEqual(s7.shape(), (5, True))
+        s8 = Signal(min=-20, max=16)
+        self.assertEqual(s8.shape(), (6, True))
+        s9 = Signal(0)
+        self.assertEqual(s9.shape(), (0, False))
+
+    def test_shape_bad(self):
+        with self.assertRaises(ValueError):
+            Signal(min=10, max=4)
+        with self.assertRaises(ValueError):
+            Signal(2, min=10)
+        with self.assertRaises(TypeError):
+            Signal(-10)
+
+    def test_name(self):
+        s1 = Signal()
+        self.assertEqual(s1.name, "s1")
+        s2 = Signal(name="sig")
+        self.assertEqual(s2.name, "sig")
+
+    def test_reset(self):
+        s1 = Signal(4, reset=0b111, reset_less=True)
+        self.assertEqual(s1.reset, 0b111)
+        self.assertEqual(s1.reset_less, True)
+
+    def test_attrs(self):
+        s1 = Signal()
+        self.assertEqual(s1.attrs, {})
+        s2 = Signal(attrs={"no_retiming": True})
+        self.assertEqual(s2.attrs, {"no_retiming": True})
+
+    def test_repr(self):
+        s1 = Signal()
+        self.assertEqual(repr(s1), "(sig s1)")
+
+    def test_like(self):
+        s1 = Signal.like(Signal(4))
+        self.assertEqual(s1.shape(), (4, False))
+        s2 = Signal.like(Signal(min=-15))
+        self.assertEqual(s2.shape(), (5, True))
+        s3 = Signal.like(Signal(4, reset=0b111, reset_less=True))
+        self.assertEqual(s3.reset, 0b111)
+        self.assertEqual(s3.reset_less, True)
+        s4 = Signal.like(Signal(attrs={"no_retiming": True}))
+        self.assertEqual(s4.attrs, {"no_retiming": True})
+        s5 = Signal.like(Signal(decoder=str))
+        self.assertEqual(s5.decoder, str)
+        s6 = Signal.like(10)
+        self.assertEqual(s6.shape(), (4, False))
+
+
+class ClockSignalTestCase(FHDLTestCase):
+    def test_domain(self):
+        s1 = ClockSignal()
+        self.assertEqual(s1.domain, "sync")
+        s2 = ClockSignal("pix")
+        self.assertEqual(s2.domain, "pix")
+
+        with self.assertRaises(TypeError):
+            ClockSignal(1)
+
+    def test_shape(self):
+        self.assertEqual(ClockSignal().shape(), (1, False))
+
+    def test_repr(self):
+        s1 = ClockSignal()
+        self.assertEqual(repr(s1), "(clk sync)")
+
+
+class ResetSignalTestCase(FHDLTestCase):
+    def test_domain(self):
+        s1 = ResetSignal()
+        self.assertEqual(s1.domain, "sync")
+        s2 = ResetSignal("pix")
+        self.assertEqual(s2.domain, "pix")
+
+        with self.assertRaises(TypeError):
+            ResetSignal(1)
+
+    def test_shape(self):
+        self.assertEqual(ResetSignal().shape(), (1, False))
+
+    def test_repr(self):
+        s1 = ResetSignal()
+        self.assertEqual(repr(s1), "(rst sync)")
diff --git a/nmigen/test/test_hdl_cd.py b/nmigen/test/test_hdl_cd.py
new file mode 100644 (file)
index 0000000..8e7dcdf
--- /dev/null
@@ -0,0 +1,57 @@
+from ..hdl.cd import *
+from .tools import *
+
+
+class ClockDomainCase(FHDLTestCase):
+    def test_name(self):
+        sync = ClockDomain()
+        self.assertEqual(sync.name, "sync")
+        self.assertEqual(sync.clk.name, "clk")
+        self.assertEqual(sync.rst.name, "rst")
+        pix = ClockDomain()
+        self.assertEqual(pix.name, "pix")
+        self.assertEqual(pix.clk.name, "pix_clk")
+        self.assertEqual(pix.rst.name, "pix_rst")
+        cd_pix = ClockDomain()
+        self.assertEqual(pix.name, "pix")
+        dom = [ClockDomain("foo")][0]
+        self.assertEqual(dom.name, "foo")
+        with self.assertRaises(ValueError,
+                msg="Clock domain name must be specified explicitly"):
+            ClockDomain()
+
+    def test_with_reset(self):
+        pix = ClockDomain()
+        self.assertIsNotNone(pix.clk)
+        self.assertIsNotNone(pix.rst)
+        self.assertFalse(pix.async_reset)
+
+    def test_without_reset(self):
+        pix = ClockDomain(reset_less=True)
+        self.assertIsNotNone(pix.clk)
+        self.assertIsNone(pix.rst)
+        self.assertFalse(pix.async_reset)
+
+    def test_async_reset(self):
+        pix = ClockDomain(async_reset=True)
+        self.assertIsNotNone(pix.clk)
+        self.assertIsNotNone(pix.rst)
+        self.assertTrue(pix.async_reset)
+
+    def test_rename(self):
+        sync = ClockDomain()
+        self.assertEqual(sync.name, "sync")
+        self.assertEqual(sync.clk.name, "clk")
+        self.assertEqual(sync.rst.name, "rst")
+        sync.rename("pix")
+        self.assertEqual(sync.name, "pix")
+        self.assertEqual(sync.clk.name, "pix_clk")
+        self.assertEqual(sync.rst.name, "pix_rst")
+
+    def test_rename_reset_less(self):
+        sync = ClockDomain(reset_less=True)
+        self.assertEqual(sync.name, "sync")
+        self.assertEqual(sync.clk.name, "clk")
+        sync.rename("pix")
+        self.assertEqual(sync.name, "pix")
+        self.assertEqual(sync.clk.name, "pix_clk")
diff --git a/nmigen/test/test_hdl_dsl.py b/nmigen/test/test_hdl_dsl.py
new file mode 100644 (file)
index 0000000..f156880
--- /dev/null
@@ -0,0 +1,375 @@
+from ..hdl.ast import *
+from ..hdl.dsl import *
+from .tools import *
+
+
+class DSLTestCase(FHDLTestCase):
+    def setUp(self):
+        self.s1 = Signal()
+        self.s2 = Signal()
+        self.s3 = Signal()
+        self.c1 = Signal()
+        self.c2 = Signal()
+        self.c3 = Signal()
+        self.w1 = Signal(4)
+
+    def test_d_comb(self):
+        m = Module()
+        m.d.comb += self.c1.eq(1)
+        m._flush()
+        self.assertEqual(m._driving[self.c1], None)
+        self.assertRepr(m._statements, """(
+            (eq (sig c1) (const 1'd1))
+        )""")
+
+    def test_d_sync(self):
+        m = Module()
+        m.d.sync += self.c1.eq(1)
+        m._flush()
+        self.assertEqual(m._driving[self.c1], "sync")
+        self.assertRepr(m._statements, """(
+            (eq (sig c1) (const 1'd1))
+        )""")
+
+    def test_d_pix(self):
+        m = Module()
+        m.d.pix += self.c1.eq(1)
+        m._flush()
+        self.assertEqual(m._driving[self.c1], "pix")
+        self.assertRepr(m._statements, """(
+            (eq (sig c1) (const 1'd1))
+        )""")
+
+    def test_d_index(self):
+        m = Module()
+        m.d["pix"] += self.c1.eq(1)
+        m._flush()
+        self.assertEqual(m._driving[self.c1], "pix")
+        self.assertRepr(m._statements, """(
+            (eq (sig c1) (const 1'd1))
+        )""")
+
+    def test_d_no_conflict(self):
+        m = Module()
+        m.d.comb += self.w1[0].eq(1)
+        m.d.comb += self.w1[1].eq(1)
+
+    def test_d_conflict(self):
+        m = Module()
+        with self.assertRaises(SyntaxError,
+                msg="Driver-driver conflict: trying to drive (sig c1) from d.sync, but it "
+                    "is already driven from d.comb"):
+            m.d.comb += self.c1.eq(1)
+            m.d.sync += self.c1.eq(1)
+
+    def test_d_wrong(self):
+        m = Module()
+        with self.assertRaises(AttributeError,
+                msg="Cannot assign 'd.pix' attribute; did you mean 'd.pix +='?"):
+            m.d.pix = None
+
+    def test_d_asgn_wrong(self):
+        m = Module()
+        with self.assertRaises(SyntaxError,
+                msg="Only assignments may be appended to d.sync"):
+            m.d.sync += Switch(self.s1, {})
+
+    def test_comb_wrong(self):
+        m = Module()
+        with self.assertRaises(AttributeError,
+                msg="'Module' object has no attribute 'comb'; did you mean 'd.comb'?"):
+            m.comb += self.c1.eq(1)
+
+    def test_sync_wrong(self):
+        m = Module()
+        with self.assertRaises(AttributeError,
+                msg="'Module' object has no attribute 'sync'; did you mean 'd.sync'?"):
+            m.sync += self.c1.eq(1)
+
+    def test_attr_wrong(self):
+        m = Module()
+        with self.assertRaises(AttributeError,
+                msg="'Module' object has no attribute 'nonexistentattr'"):
+            m.nonexistentattr
+
+    def test_If(self):
+        m = Module()
+        with m.If(self.s1):
+            m.d.comb += self.c1.eq(1)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (cat (sig s1))
+                (case 1 (eq (sig c1) (const 1'd1)))
+            )
+        )
+        """)
+
+    def test_If_Elif(self):
+        m = Module()
+        with m.If(self.s1):
+            m.d.comb += self.c1.eq(1)
+        with m.Elif(self.s2):
+            m.d.sync += self.c2.eq(0)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (cat (sig s1) (sig s2))
+                (case -1 (eq (sig c1) (const 1'd1)))
+                (case 1- (eq (sig c2) (const 1'd0)))
+            )
+        )
+        """)
+
+    def test_If_Elif_Else(self):
+        m = Module()
+        with m.If(self.s1):
+            m.d.comb += self.c1.eq(1)
+        with m.Elif(self.s2):
+            m.d.sync += self.c2.eq(0)
+        with m.Else():
+            m.d.comb += self.c3.eq(1)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (cat (sig s1) (sig s2))
+                (case -1 (eq (sig c1) (const 1'd1)))
+                (case 1- (eq (sig c2) (const 1'd0)))
+                (case -- (eq (sig c3) (const 1'd1)))
+            )
+        )
+        """)
+
+    def test_If_If(self):
+        m = Module()
+        with m.If(self.s1):
+            m.d.comb += self.c1.eq(1)
+        with m.If(self.s2):
+            m.d.comb += self.c2.eq(1)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (cat (sig s1))
+                (case 1 (eq (sig c1) (const 1'd1)))
+            )
+            (switch (cat (sig s2))
+                (case 1 (eq (sig c2) (const 1'd1)))
+            )
+        )
+        """)
+
+    def test_If_nested_If(self):
+        m = Module()
+        with m.If(self.s1):
+            m.d.comb += self.c1.eq(1)
+            with m.If(self.s2):
+                m.d.comb += self.c2.eq(1)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (cat (sig s1))
+                (case 1 (eq (sig c1) (const 1'd1))
+                    (switch (cat (sig s2))
+                        (case 1 (eq (sig c2) (const 1'd1)))
+                    )
+                )
+            )
+        )
+        """)
+
+    def test_If_dangling_Else(self):
+        m = Module()
+        with m.If(self.s1):
+            m.d.comb += self.c1.eq(1)
+            with m.If(self.s2):
+                m.d.comb += self.c2.eq(1)
+        with m.Else():
+            m.d.comb += self.c3.eq(1)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (cat (sig s1))
+                (case 1
+                    (eq (sig c1) (const 1'd1))
+                    (switch (cat (sig s2))
+                        (case 1 (eq (sig c2) (const 1'd1)))
+                    )
+                )
+                (case -
+                    (eq (sig c3) (const 1'd1))
+                )
+            )
+        )
+        """)
+
+    def test_Elif_wrong(self):
+        m = Module()
+        with self.assertRaises(SyntaxError,
+                msg="Elif without preceding If"):
+            with m.Elif(self.s2):
+                pass
+
+    def test_Else_wrong(self):
+        m = Module()
+        with self.assertRaises(SyntaxError,
+                msg="Else without preceding If/Elif"):
+            with m.Else():
+                pass
+
+    def test_If_wide(self):
+        m = Module()
+        with m.If(self.w1):
+            m.d.comb += self.c1.eq(1)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (cat (b (sig w1)))
+                (case 1 (eq (sig c1) (const 1'd1)))
+            )
+        )
+        """)
+
+    def test_Switch(self):
+        m = Module()
+        with m.Switch(self.w1):
+            with m.Case(3):
+                m.d.comb += self.c1.eq(1)
+            with m.Case("11--"):
+                m.d.comb += self.c2.eq(1)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (sig w1)
+                (case 0011 (eq (sig c1) (const 1'd1)))
+                (case 11-- (eq (sig c2) (const 1'd1)))
+            )
+        )
+        """)
+
+    def test_Switch_default(self):
+        m = Module()
+        with m.Switch(self.w1):
+            with m.Case(3):
+                m.d.comb += self.c1.eq(1)
+            with m.Case():
+                m.d.comb += self.c2.eq(1)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (sig w1)
+                (case 0011 (eq (sig c1) (const 1'd1)))
+                (case ---- (eq (sig c2) (const 1'd1)))
+            )
+        )
+        """)
+
+    def test_Switch_const_test(self):
+        m = Module()
+        with m.Switch(1):
+            with m.Case(1):
+                m.d.comb += self.c1.eq(1)
+        m._flush()
+        self.assertRepr(m._statements, """
+        (
+            (switch (const 1'd1)
+                (case 1 (eq (sig c1) (const 1'd1)))
+            )
+        )
+        """)
+
+    def test_Case_width_wrong(self):
+        m = Module()
+        with m.Switch(self.w1):
+            with self.assertRaises(SyntaxError,
+                    msg="Case value '--' must have the same width as test (which is 4)"):
+                with m.Case("--"):
+                    pass
+
+    def test_Case_outside_Switch_wrong(self):
+        m = Module()
+        with self.assertRaises(SyntaxError,
+                msg="Case is not permitted outside of Switch"):
+            with m.Case():
+                pass
+
+    def test_If_inside_Switch_wrong(self):
+        m = Module()
+        with m.Switch(self.s1):
+            with self.assertRaises(SyntaxError,
+                    msg="If is not permitted inside of Switch"):
+                with m.If(self.s2):
+                    pass
+
+    def test_auto_pop_ctrl(self):
+        m = Module()
+        with m.If(self.w1):
+            m.d.comb += self.c1.eq(1)
+        m.d.comb += self.c2.eq(1)
+        self.assertRepr(m._statements, """
+        (
+            (switch (cat (b (sig w1)))
+                (case 1 (eq (sig c1) (const 1'd1)))
+            )
+            (eq (sig c2) (const 1'd1))
+        )
+        """)
+
+    def test_submodule_anon(self):
+        m1 = Module()
+        m2 = Module()
+        m1.submodules += m2
+        self.assertEqual(m1._submodules, [(m2, None)])
+
+    def test_submodule_anon_multi(self):
+        m1 = Module()
+        m2 = Module()
+        m3 = Module()
+        m1.submodules += m2, m3
+        self.assertEqual(m1._submodules, [(m2, None), (m3, None)])
+
+    def test_submodule_named(self):
+        m1 = Module()
+        m2 = Module()
+        m1.submodules.foo = m2
+        self.assertEqual(m1._submodules, [(m2, "foo")])
+
+    def test_submodule_wrong(self):
+        m = Module()
+        with self.assertRaises(TypeError,
+                msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
+            m.submodules.foo = 1
+        with self.assertRaises(TypeError,
+                msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
+            m.submodules += 1
+
+    def test_lower(self):
+        m1 = Module()
+        m1.d.comb += self.c1.eq(self.s1)
+        m2 = Module()
+        m2.d.comb += self.c2.eq(self.s2)
+        m2.d.sync += self.c3.eq(self.s3)
+        m1.submodules.foo = m2
+
+        f1 = m1.lower(platform=None)
+        self.assertRepr(f1.statements, """
+        (
+            (eq (sig c1) (sig s1))
+        )
+        """)
+        self.assertEqual(f1.drivers, {
+            None: ValueSet((self.c1,))
+        })
+        self.assertEqual(len(f1.subfragments), 1)
+        (f2, f2_name), = f1.subfragments
+        self.assertEqual(f2_name, "foo")
+        self.assertRepr(f2.statements, """
+        (
+            (eq (sig c2) (sig s2))
+            (eq (sig c3) (sig s3))
+        )
+        """)
+        self.assertEqual(f2.drivers, {
+            None: ValueSet((self.c2,)),
+            "sync": ValueSet((self.c3,))
+        })
+        self.assertEqual(len(f2.subfragments), 0)
diff --git a/nmigen/test/test_hdl_ir.py b/nmigen/test/test_hdl_ir.py
new file mode 100644 (file)
index 0000000..fcc1b2e
--- /dev/null
@@ -0,0 +1,393 @@
+from ..hdl.ast import *
+from ..hdl.cd import *
+from ..hdl.ir import *
+from .tools import *
+
+
+class FragmentDriversTestCase(FHDLTestCase):
+    def test_empty(self):
+        f = Fragment()
+        self.assertEqual(list(f.iter_comb()), [])
+        self.assertEqual(list(f.iter_sync()), [])
+
+
+class FragmentPortsTestCase(FHDLTestCase):
+    def setUp(self):
+        self.s1 = Signal()
+        self.s2 = Signal()
+        self.s3 = Signal()
+        self.c1 = Signal()
+        self.c2 = Signal()
+        self.c3 = Signal()
+
+    def test_empty(self):
+        f = Fragment()
+        self.assertEqual(list(f.iter_ports()), [])
+
+        f._propagate_ports(ports=())
+        self.assertEqual(f.ports, ValueDict([]))
+
+    def test_iter_signals(self):
+        f = Fragment()
+        f.add_ports(self.s1, self.s2, kind="io")
+        self.assertEqual(ValueSet((self.s1, self.s2)), f.iter_signals())
+
+    def test_self_contained(self):
+        f = Fragment()
+        f.add_statements(
+            self.c1.eq(self.s1),
+            self.s1.eq(self.c1)
+        )
+
+        f._propagate_ports(ports=())
+        self.assertEqual(f.ports, ValueDict([]))
+
+    def test_infer_input(self):
+        f = Fragment()
+        f.add_statements(
+            self.c1.eq(self.s1)
+        )
+
+        f._propagate_ports(ports=())
+        self.assertEqual(f.ports, ValueDict([
+            (self.s1, "i")
+        ]))
+
+    def test_request_output(self):
+        f = Fragment()
+        f.add_statements(
+            self.c1.eq(self.s1)
+        )
+
+        f._propagate_ports(ports=(self.c1,))
+        self.assertEqual(f.ports, ValueDict([
+            (self.s1, "i"),
+            (self.c1, "o")
+        ]))
+
+    def test_input_in_subfragment(self):
+        f1 = Fragment()
+        f1.add_statements(
+            self.c1.eq(self.s1)
+        )
+        f2 = Fragment()
+        f2.add_statements(
+            self.s1.eq(0)
+        )
+        f1.add_subfragment(f2)
+        f1._propagate_ports(ports=())
+        self.assertEqual(f1.ports, ValueDict())
+        self.assertEqual(f2.ports, ValueDict([
+            (self.s1, "o"),
+        ]))
+
+    def test_input_only_in_subfragment(self):
+        f1 = Fragment()
+        f2 = Fragment()
+        f2.add_statements(
+            self.c1.eq(self.s1)
+        )
+        f1.add_subfragment(f2)
+        f1._propagate_ports(ports=())
+        self.assertEqual(f1.ports, ValueDict([
+            (self.s1, "i"),
+        ]))
+        self.assertEqual(f2.ports, ValueDict([
+            (self.s1, "i"),
+        ]))
+
+    def test_output_from_subfragment(self):
+        f1 = Fragment()
+        f1.add_statements(
+            self.c1.eq(0)
+        )
+        f2 = Fragment()
+        f2.add_statements(
+            self.c2.eq(1)
+        )
+        f1.add_subfragment(f2)
+
+        f1._propagate_ports(ports=(self.c2,))
+        self.assertEqual(f1.ports, ValueDict([
+            (self.c2, "o"),
+        ]))
+        self.assertEqual(f2.ports, ValueDict([
+            (self.c2, "o"),
+        ]))
+
+    def test_input_cd(self):
+        sync = ClockDomain()
+        f = Fragment()
+        f.add_statements(
+            self.c1.eq(self.s1)
+        )
+        f.add_domains(sync)
+        f.add_driver(self.c1, "sync")
+
+        f._propagate_ports(ports=())
+        self.assertEqual(f.ports, ValueDict([
+            (self.s1,  "i"),
+            (sync.clk, "i"),
+            (sync.rst, "i"),
+        ]))
+
+    def test_input_cd_reset_less(self):
+        sync = ClockDomain(reset_less=True)
+        f = Fragment()
+        f.add_statements(
+            self.c1.eq(self.s1)
+        )
+        f.add_domains(sync)
+        f.add_driver(self.c1, "sync")
+
+        f._propagate_ports(ports=())
+        self.assertEqual(f.ports, ValueDict([
+            (self.s1,  "i"),
+            (sync.clk, "i"),
+        ]))
+
+
+class FragmentDomainsTestCase(FHDLTestCase):
+    def test_iter_signals(self):
+        cd1 = ClockDomain()
+        cd2 = ClockDomain(reset_less=True)
+        s1 = Signal()
+        s2 = Signal()
+
+        f = Fragment()
+        f.add_domains(cd1, cd2)
+        f.add_driver(s1, "cd1")
+        self.assertEqual(ValueSet((cd1.clk, cd1.rst, s1)), f.iter_signals())
+        f.add_driver(s2, "cd2")
+        self.assertEqual(ValueSet((cd1.clk, cd1.rst, cd2.clk, s1, s2)), f.iter_signals())
+
+    def test_propagate_up(self):
+        cd = ClockDomain()
+
+        f1 = Fragment()
+        f2 = Fragment()
+        f1.add_subfragment(f2)
+        f2.add_domains(cd)
+
+        f1._propagate_domains_up()
+        self.assertEqual(f1.domains, {"cd": cd})
+
+    def test_domain_conflict(self):
+        cda = ClockDomain("sync")
+        cdb = ClockDomain("sync")
+
+        fa = Fragment()
+        fa.add_domains(cda)
+        fb = Fragment()
+        fb.add_domains(cdb)
+        f = Fragment()
+        f.add_subfragment(fa, "a")
+        f.add_subfragment(fb, "b")
+
+        f._propagate_domains_up()
+        self.assertEqual(f.domains, {"a_sync": cda, "b_sync": cdb})
+        (fa, _), (fb, _) = f.subfragments
+        self.assertEqual(fa.domains, {"a_sync": cda})
+        self.assertEqual(fb.domains, {"b_sync": cdb})
+
+    def test_domain_conflict_anon(self):
+        cda = ClockDomain("sync")
+        cdb = ClockDomain("sync")
+
+        fa = Fragment()
+        fa.add_domains(cda)
+        fb = Fragment()
+        fb.add_domains(cdb)
+        f = Fragment()
+        f.add_subfragment(fa, "a")
+        f.add_subfragment(fb)
+
+        with self.assertRaises(DomainError,
+                msg="Domain 'sync' is defined by subfragments 'a', <unnamed #1> of fragment "
+                    "'top'; it is necessary to either rename subfragment domains explicitly, "
+                    "or give names to subfragments"):
+            f._propagate_domains_up()
+
+    def test_domain_conflict_name(self):
+        cda = ClockDomain("sync")
+        cdb = ClockDomain("sync")
+
+        fa = Fragment()
+        fa.add_domains(cda)
+        fb = Fragment()
+        fb.add_domains(cdb)
+        f = Fragment()
+        f.add_subfragment(fa, "x")
+        f.add_subfragment(fb, "x")
+
+        with self.assertRaises(DomainError,
+                msg="Domain 'sync' is defined by subfragments #0, #1 of fragment 'top', some "
+                    "of which have identical names; it is necessary to either rename subfragment "
+                    "domains explicitly, or give distinct names to subfragments"):
+            f._propagate_domains_up()
+
+    def test_propagate_down(self):
+        cd = ClockDomain()
+
+        f1 = Fragment()
+        f2 = Fragment()
+        f1.add_domains(cd)
+        f1.add_subfragment(f2)
+
+        f1._propagate_domains_down()
+        self.assertEqual(f2.domains, {"cd": cd})
+
+    def test_propagate_down_idempotent(self):
+        cd = ClockDomain()
+
+        f1 = Fragment()
+        f1.add_domains(cd)
+        f2 = Fragment()
+        f2.add_domains(cd)
+        f1.add_subfragment(f2)
+
+        f1._propagate_domains_down()
+        self.assertEqual(f1.domains, {"cd": cd})
+        self.assertEqual(f2.domains, {"cd": cd})
+
+    def test_propagate(self):
+        cd = ClockDomain()
+
+        f1 = Fragment()
+        f2 = Fragment()
+        f1.add_domains(cd)
+        f1.add_subfragment(f2)
+
+        f1._propagate_domains(ensure_sync_exists=False)
+        self.assertEqual(f1.domains, {"cd": cd})
+        self.assertEqual(f2.domains, {"cd": cd})
+
+    def test_propagate_ensure_sync(self):
+        f1 = Fragment()
+        f2 = Fragment()
+        f1.add_subfragment(f2)
+
+        f1._propagate_domains(ensure_sync_exists=True)
+        self.assertEqual(f1.domains.keys(), {"sync"})
+        self.assertEqual(f2.domains.keys(), {"sync"})
+        self.assertEqual(f1.domains["sync"], f2.domains["sync"])
+
+
+class FragmentDriverConflictTestCase(FHDLTestCase):
+    def setUp_self_sub(self):
+        self.s1 = Signal()
+        self.c1 = Signal()
+        self.c2 = Signal()
+
+        self.f1 = Fragment()
+        self.f1.add_statements(self.c1.eq(0))
+        self.f1.add_driver(self.s1)
+        self.f1.add_driver(self.c1, "sync")
+
+        self.f1a = Fragment()
+        self.f1.add_subfragment(self.f1a, "f1a")
+
+        self.f2 = Fragment()
+        self.f2.add_statements(self.c2.eq(1))
+        self.f2.add_driver(self.s1)
+        self.f2.add_driver(self.c2, "sync")
+        self.f1.add_subfragment(self.f2)
+
+        self.f1b = Fragment()
+        self.f1.add_subfragment(self.f1b, "f1b")
+
+        self.f2a = Fragment()
+        self.f2.add_subfragment(self.f2a, "f2a")
+
+    def test_conflict_self_sub(self):
+        self.setUp_self_sub()
+
+        self.f1._resolve_driver_conflicts(mode="silent")
+        self.assertEqual(self.f1.subfragments, [
+            (self.f1a, "f1a"),
+            (self.f1b, "f1b"),
+            (self.f2a, "f2a"),
+        ])
+        self.assertRepr(self.f1.statements, """
+        (
+            (eq (sig c1) (const 1'd0))
+            (eq (sig c2) (const 1'd1))
+        )
+        """)
+        self.assertEqual(self.f1.drivers, {
+            None:   ValueSet((self.s1,)),
+            "sync": ValueSet((self.c1, self.c2)),
+        })
+
+    def test_conflict_self_sub_error(self):
+        self.setUp_self_sub()
+
+        with self.assertRaises(DriverConflict,
+                msg="Signal '(sig s1)' is driven from multiple fragments: top, top.<unnamed #1>"):
+            self.f1._resolve_driver_conflicts(mode="error")
+
+    def test_conflict_self_sub_warning(self):
+        self.setUp_self_sub()
+
+        with self.assertWarns(DriverConflict,
+                msg="Signal '(sig s1)' is driven from multiple fragments: top, top.<unnamed #1>; "
+                    "hierarchy will be flattened"):
+            self.f1._resolve_driver_conflicts(mode="warn")
+
+    def setUp_sub_sub(self):
+        self.s1 = Signal()
+        self.c1 = Signal()
+        self.c2 = Signal()
+
+        self.f1 = Fragment()
+
+        self.f2 = Fragment()
+        self.f2.add_driver(self.s1)
+        self.f2.add_statements(self.c1.eq(0))
+        self.f1.add_subfragment(self.f2)
+
+        self.f3 = Fragment()
+        self.f3.add_driver(self.s1)
+        self.f3.add_statements(self.c2.eq(1))
+        self.f1.add_subfragment(self.f3)
+
+    def test_conflict_sub_sub(self):
+        self.setUp_sub_sub()
+
+        self.f1._resolve_driver_conflicts(mode="silent")
+        self.assertEqual(self.f1.subfragments, [])
+        self.assertRepr(self.f1.statements, """
+        (
+            (eq (sig c1) (const 1'd0))
+            (eq (sig c2) (const 1'd1))
+        )
+        """)
+
+    def setUp_self_subsub(self):
+        self.s1 = Signal()
+        self.c1 = Signal()
+        self.c2 = Signal()
+
+        self.f1 = Fragment()
+        self.f1.add_driver(self.s1)
+
+        self.f2 = Fragment()
+        self.f2.add_statements(self.c1.eq(0))
+        self.f1.add_subfragment(self.f2)
+
+        self.f3 = Fragment()
+        self.f3.add_driver(self.s1)
+        self.f3.add_statements(self.c2.eq(1))
+        self.f2.add_subfragment(self.f3)
+
+    def test_conflict_self_subsub(self):
+        self.setUp_self_subsub()
+
+        self.f1._resolve_driver_conflicts(mode="silent")
+        self.assertEqual(self.f1.subfragments, [])
+        self.assertRepr(self.f1.statements, """
+        (
+            (eq (sig c1) (const 1'd0))
+            (eq (sig c2) (const 1'd1))
+        )
+        """)
diff --git a/nmigen/test/test_hdl_xfrm.py b/nmigen/test/test_hdl_xfrm.py
new file mode 100644 (file)
index 0000000..428bad7
--- /dev/null
@@ -0,0 +1,316 @@
+from ..hdl.ast import *
+from ..hdl.cd import *
+from ..hdl.ir import *
+from ..hdl.xfrm import *
+from .tools import *
+
+
+class DomainRenamerTestCase(FHDLTestCase):
+    def setUp(self):
+        self.s1 = Signal()
+        self.s2 = Signal()
+        self.s3 = Signal()
+        self.s4 = Signal()
+        self.s5 = Signal()
+        self.c1 = Signal()
+
+    def test_rename_signals(self):
+        f = Fragment()
+        f.add_statements(
+            self.s1.eq(ClockSignal()),
+            ResetSignal().eq(self.s2),
+            self.s3.eq(0),
+            self.s4.eq(ClockSignal("other")),
+            self.s5.eq(ResetSignal("other")),
+        )
+        f.add_driver(self.s1, None)
+        f.add_driver(self.s2, None)
+        f.add_driver(self.s3, "sync")
+
+        f = DomainRenamer("pix")(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s1) (clk pix))
+            (eq (rst pix) (sig s2))
+            (eq (sig s3) (const 1'd0))
+            (eq (sig s4) (clk other))
+            (eq (sig s5) (rst other))
+        )
+        """)
+        self.assertEqual(f.drivers, {
+            None: ValueSet((self.s1, self.s2)),
+            "pix": ValueSet((self.s3,)),
+        })
+
+    def test_rename_multi(self):
+        f = Fragment()
+        f.add_statements(
+            self.s1.eq(ClockSignal()),
+            self.s2.eq(ResetSignal("other")),
+        )
+
+        f = DomainRenamer({"sync": "pix", "other": "pix2"})(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s1) (clk pix))
+            (eq (sig s2) (rst pix2))
+        )
+        """)
+
+    def test_rename_cd(self):
+        cd_sync = ClockDomain()
+        cd_pix  = ClockDomain()
+
+        f = Fragment()
+        f.add_domains(cd_sync, cd_pix)
+
+        f = DomainRenamer("ext")(f)
+        self.assertEqual(cd_sync.name, "ext")
+        self.assertEqual(f.domains, {
+            "ext": cd_sync,
+            "pix": cd_pix,
+        })
+
+    def test_rename_cd_subfragment(self):
+        cd_sync = ClockDomain()
+        cd_pix  = ClockDomain()
+
+        f1 = Fragment()
+        f1.add_domains(cd_sync, cd_pix)
+        f2 = Fragment()
+        f2.add_domains(cd_sync)
+        f1.add_subfragment(f2)
+
+        f1 = DomainRenamer("ext")(f1)
+        self.assertEqual(cd_sync.name, "ext")
+        self.assertEqual(f1.domains, {
+            "ext": cd_sync,
+            "pix": cd_pix,
+        })
+
+
+class DomainLowererTestCase(FHDLTestCase):
+    def setUp(self):
+        self.s = Signal()
+
+    def test_lower_clk(self):
+        sync = ClockDomain()
+        f = Fragment()
+        f.add_statements(
+            self.s.eq(ClockSignal("sync"))
+        )
+
+        f = DomainLowerer({"sync": sync})(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s) (sig clk))
+        )
+        """)
+
+    def test_lower_rst(self):
+        sync = ClockDomain()
+        f = Fragment()
+        f.add_statements(
+            self.s.eq(ResetSignal("sync"))
+        )
+
+        f = DomainLowerer({"sync": sync})(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s) (sig rst))
+        )
+        """)
+
+    def test_lower_rst_reset_less(self):
+        sync = ClockDomain(reset_less=True)
+        f = Fragment()
+        f.add_statements(
+            self.s.eq(ResetSignal("sync", allow_reset_less=True))
+        )
+
+        f = DomainLowerer({"sync": sync})(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s) (const 1'd0))
+        )
+        """)
+
+    def test_lower_wrong_domain(self):
+        sync = ClockDomain()
+        f = Fragment()
+        f.add_statements(
+            self.s.eq(ClockSignal("xxx"))
+        )
+
+        with self.assertRaises(DomainError,
+                msg="Signal (clk xxx) refers to nonexistent domain 'xxx'"):
+            DomainLowerer({"sync": sync})(f)
+
+    def test_lower_wrong_reset_less_domain(self):
+        sync = ClockDomain(reset_less=True)
+        f = Fragment()
+        f.add_statements(
+            self.s.eq(ResetSignal("sync"))
+        )
+
+        with self.assertRaises(DomainError,
+                msg="Signal (rst sync) refers to reset of reset-less domain 'sync'"):
+            DomainLowerer({"sync": sync})(f)
+
+
+class ResetInserterTestCase(FHDLTestCase):
+    def setUp(self):
+        self.s1 = Signal()
+        self.s2 = Signal(reset=1)
+        self.s3 = Signal(reset=1, reset_less=True)
+        self.c1 = Signal()
+
+    def test_reset_default(self):
+        f = Fragment()
+        f.add_statements(
+            self.s1.eq(1)
+        )
+        f.add_driver(self.s1, "sync")
+
+        f = ResetInserter(self.c1)(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s1) (const 1'd1))
+            (switch (sig c1)
+                (case 1 (eq (sig s1) (const 1'd0)))
+            )
+        )
+        """)
+
+    def test_reset_cd(self):
+        f = Fragment()
+        f.add_statements(
+            self.s1.eq(1),
+            self.s2.eq(0),
+        )
+        f.add_domains(ClockDomain("sync"))
+        f.add_driver(self.s1, "sync")
+        f.add_driver(self.s2, "pix")
+
+        f = ResetInserter({"pix": self.c1})(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s1) (const 1'd1))
+            (eq (sig s2) (const 1'd0))
+            (switch (sig c1)
+                (case 1 (eq (sig s2) (const 1'd1)))
+            )
+        )
+        """)
+
+    def test_reset_value(self):
+        f = Fragment()
+        f.add_statements(
+            self.s2.eq(0)
+        )
+        f.add_driver(self.s2, "sync")
+
+        f = ResetInserter(self.c1)(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s2) (const 1'd0))
+            (switch (sig c1)
+                (case 1 (eq (sig s2) (const 1'd1)))
+            )
+        )
+        """)
+
+    def test_reset_less(self):
+        f = Fragment()
+        f.add_statements(
+            self.s3.eq(0)
+        )
+        f.add_driver(self.s3, "sync")
+
+        f = ResetInserter(self.c1)(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s3) (const 1'd0))
+            (switch (sig c1)
+                (case 1 )
+            )
+        )
+        """)
+
+
+class CEInserterTestCase(FHDLTestCase):
+    def setUp(self):
+        self.s1 = Signal()
+        self.s2 = Signal()
+        self.s3 = Signal()
+        self.c1 = Signal()
+
+    def test_ce_default(self):
+        f = Fragment()
+        f.add_statements(
+            self.s1.eq(1)
+        )
+        f.add_driver(self.s1, "sync")
+
+        f = CEInserter(self.c1)(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s1) (const 1'd1))
+            (switch (sig c1)
+                (case 0 (eq (sig s1) (sig s1)))
+            )
+        )
+        """)
+
+    def test_ce_cd(self):
+        f = Fragment()
+        f.add_statements(
+            self.s1.eq(1),
+            self.s2.eq(0),
+        )
+        f.add_driver(self.s1, "sync")
+        f.add_driver(self.s2, "pix")
+
+        f = CEInserter({"pix": self.c1})(f)
+        self.assertRepr(f.statements, """
+        (
+            (eq (sig s1) (const 1'd1))
+            (eq (sig s2) (const 1'd0))
+            (switch (sig c1)
+                (case 0 (eq (sig s2) (sig s2)))
+            )
+        )
+        """)
+
+    def test_ce_subfragment(self):
+        f1 = Fragment()
+        f1.add_statements(
+            self.s1.eq(1)
+        )
+        f1.add_driver(self.s1, "sync")
+
+        f2 = Fragment()
+        f2.add_statements(
+            self.s2.eq(1)
+        )
+        f2.add_driver(self.s2, "sync")
+        f1.add_subfragment(f2)
+
+        f1 = CEInserter(self.c1)(f1)
+        (f2, _), = f1.subfragments
+        self.assertRepr(f1.statements, """
+        (
+            (eq (sig s1) (const 1'd1))
+            (switch (sig c1)
+                (case 0 (eq (sig s1) (sig s1)))
+            )
+        )
+        """)
+        self.assertRepr(f2.statements, """
+        (
+            (eq (sig s2) (const 1'd1))
+            (switch (sig c1)
+                (case 0 (eq (sig s2) (sig s2)))
+            )
+        )
+        """)