# nmigen: UnusedElaboratable=no from ..hdl.ast import * from ..hdl.cd import * from ..hdl.ir import * from ..hdl.xfrm import * from ..hdl.mem import * from .utils 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: SignalSet((self.s1, self.s2)), "pix": SignalSet((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, }) def test_rename_wrong_to_comb(self): with self.assertRaises(ValueError, msg="Domain 'sync' may not be renamed to 'comb'"): DomainRenamer("comb") def test_rename_wrong_from_comb(self): with self.assertRaises(ValueError, msg="Domain 'comb' may not be renamed"): DomainRenamer({"comb": "sync"}) class DomainLowererTestCase(FHDLTestCase): def setUp(self): self.s = Signal() def test_lower_clk(self): sync = ClockDomain() f = Fragment() f.add_domains(sync) f.add_statements( self.s.eq(ClockSignal("sync")) ) f = DomainLowerer()(f) self.assertRepr(f.statements, """ ( (eq (sig s) (sig clk)) ) """) def test_lower_rst(self): sync = ClockDomain() f = Fragment() f.add_domains(sync) f.add_statements( self.s.eq(ResetSignal("sync")) ) f = DomainLowerer()(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_domains(sync) f.add_statements( self.s.eq(ResetSignal("sync", allow_reset_less=True)) ) f = DomainLowerer()(f) self.assertRepr(f.statements, """ ( (eq (sig s) (const 1'd0)) ) """) def test_lower_drivers(self): sync = ClockDomain() pix = ClockDomain() f = Fragment() f.add_domains(sync, pix) f.add_driver(ClockSignal("pix"), None) f.add_driver(ResetSignal("pix"), "sync") f = DomainLowerer()(f) self.assertEqual(f.drivers, { None: SignalSet((pix.clk,)), "sync": SignalSet((pix.rst,)) }) def test_lower_wrong_domain(self): f = Fragment() f.add_statements( self.s.eq(ClockSignal("xxx")) ) with self.assertRaises(DomainError, msg="Signal (clk xxx) refers to nonexistent domain 'xxx'"): DomainLowerer()(f) def test_lower_wrong_reset_less_domain(self): sync = ClockDomain(reset_less=True) f = Fragment() f.add_domains(sync) 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()(f) class SampleLowererTestCase(FHDLTestCase): def setUp(self): self.i = Signal() self.o1 = Signal() self.o2 = Signal() self.o3 = Signal() def test_lower_signal(self): f = Fragment() f.add_statements( self.o1.eq(Sample(self.i, 2, "sync")), self.o2.eq(Sample(self.i, 1, "sync")), self.o3.eq(Sample(self.i, 1, "pix")), ) f = SampleLowerer()(f) self.assertRepr(f.statements, """ ( (eq (sig o1) (sig $sample$s$i$sync$2)) (eq (sig o2) (sig $sample$s$i$sync$1)) (eq (sig o3) (sig $sample$s$i$pix$1)) (eq (sig $sample$s$i$sync$1) (sig i)) (eq (sig $sample$s$i$sync$2) (sig $sample$s$i$sync$1)) (eq (sig $sample$s$i$pix$1) (sig i)) ) """) self.assertEqual(len(f.drivers["sync"]), 2) self.assertEqual(len(f.drivers["pix"]), 1) def test_lower_const(self): f = Fragment() f.add_statements( self.o1.eq(Sample(1, 2, "sync")), ) f = SampleLowerer()(f) self.assertRepr(f.statements, """ ( (eq (sig o1) (sig $sample$c$1$sync$2)) (eq (sig $sample$c$1$sync$1) (const 1'd1)) (eq (sig $sample$c$1$sync$2) (sig $sample$c$1$sync$1)) ) """) self.assertEqual(len(f.drivers["sync"]), 2) class SwitchCleanerTestCase(FHDLTestCase): def test_clean(self): a = Signal() b = Signal() c = Signal() stmts = [ Switch(a, { 1: a.eq(0), 0: [ b.eq(1), Switch(b, {1: [ Switch(a|b, {}) ]}) ] }) ] self.assertRepr(SwitchCleaner()(stmts), """ ( (switch (sig a) (case 1 (eq (sig a) (const 1'd0))) (case 0 (eq (sig b) (const 1'd1))) ) ) """) class LHSGroupAnalyzerTestCase(FHDLTestCase): def test_no_group_unrelated(self): a = Signal() b = Signal() stmts = [ a.eq(0), b.eq(0), ] groups = LHSGroupAnalyzer()(stmts) self.assertEqual(list(groups.values()), [ SignalSet((a,)), SignalSet((b,)), ]) def test_group_related(self): a = Signal() b = Signal() stmts = [ a.eq(0), Cat(a, b).eq(0), ] groups = LHSGroupAnalyzer()(stmts) self.assertEqual(list(groups.values()), [ SignalSet((a, b)), ]) def test_no_loops(self): a = Signal() b = Signal() stmts = [ a.eq(0), Cat(a, b).eq(0), Cat(a, b).eq(0), ] groups = LHSGroupAnalyzer()(stmts) self.assertEqual(list(groups.values()), [ SignalSet((a, b)), ]) def test_switch(self): a = Signal() b = Signal() stmts = [ a.eq(0), Switch(a, { 1: b.eq(0), }) ] groups = LHSGroupAnalyzer()(stmts) self.assertEqual(list(groups.values()), [ SignalSet((a,)), SignalSet((b,)), ]) def test_lhs_empty(self): stmts = [ Cat().eq(0) ] groups = LHSGroupAnalyzer()(stmts) self.assertEqual(list(groups.values()), [ ]) class LHSGroupFilterTestCase(FHDLTestCase): def test_filter(self): a = Signal() b = Signal() c = Signal() stmts = [ Switch(a, { 1: a.eq(0), 0: [ b.eq(1), Switch(b, {1: []}) ] }) ] self.assertRepr(LHSGroupFilter(SignalSet((a,)))(stmts), """ ( (switch (sig a) (case 1 (eq (sig a) (const 1'd0))) (case 0 ) ) ) """) def test_lhs_empty(self): stmts = [ Cat().eq(0) ] self.assertRepr(LHSGroupFilter(SignalSet())(stmts), "()") 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 EnableInserterTestCase(FHDLTestCase): def setUp(self): self.s1 = Signal() self.s2 = Signal() self.s3 = Signal() self.c1 = Signal() def test_enable_default(self): f = Fragment() f.add_statements( self.s1.eq(1) ) f.add_driver(self.s1, "sync") f = EnableInserter(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_enable_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 = EnableInserter({"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_enable_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 = EnableInserter(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))) ) ) """) def test_enable_read_port(self): mem = Memory(width=8, depth=4) f = EnableInserter(self.c1)(mem.read_port(transparent=False)).elaborate(platform=None) self.assertRepr(f.named_ports["EN"][0], """ (m (sig c1) (sig mem_r_en) (const 1'd0)) """) def test_enable_write_port(self): mem = Memory(width=8, depth=4) f = EnableInserter(self.c1)(mem.write_port()).elaborate(platform=None) self.assertRepr(f.named_ports["EN"][0], """ (m (sig c1) (cat (repl (slice (sig mem_w_en) 0:1) 8)) (const 8'd0)) """) class _MockElaboratable(Elaboratable): def __init__(self): self.s1 = Signal() def elaborate(self, platform): f = Fragment() f.add_statements( self.s1.eq(1) ) f.add_driver(self.s1, "sync") return f class TransformedElaboratableTestCase(FHDLTestCase): def setUp(self): self.c1 = Signal() self.c2 = Signal() def test_getattr(self): e = _MockElaboratable() te = EnableInserter(self.c1)(e) self.assertIs(te.s1, e.s1) def test_composition(self): e = _MockElaboratable() te1 = EnableInserter(self.c1)(e) te2 = ResetInserter(self.c2)(te1) self.assertIsInstance(te1, TransformedElaboratable) self.assertIs(te1, te2) f = Fragment.get(te2, None) self.assertRepr(f.statements, """ ( (eq (sig s1) (const 1'd1)) (switch (sig c1) (case 0 (eq (sig s1) (sig s1))) ) (switch (sig c2) (case 1 (eq (sig s1) (const 1'd0))) ) ) """) class MockUserValue(UserValue): def __init__(self, lowered): super().__init__() self.lowered = lowered def lower(self): return self.lowered class UserValueTestCase(FHDLTestCase): def setUp(self): self.s = Signal() self.c = Signal() self.uv = MockUserValue(self.s) def test_lower(self): sync = ClockDomain() f = Fragment() f.add_domains(sync) f.add_statements( self.uv.eq(1) ) for signal in self.uv._lhs_signals(): f.add_driver(signal, "sync") f = ResetInserter(self.c)(f) f = DomainLowerer()(f) self.assertRepr(f.statements, """ ( (eq (sig s) (const 1'd1)) (switch (sig c) (case 1 (eq (sig s) (const 1'd0))) ) (switch (sig rst) (case 1 (eq (sig s) (const 1'd0))) ) ) """)