1 # nmigen: UnusedElaboratable=no
3 from nmigen
.hdl
.ast
import *
4 from nmigen
.hdl
.cd
import *
5 from nmigen
.hdl
.ir
import *
6 from nmigen
.hdl
.xfrm
import *
7 from nmigen
.hdl
.mem
import *
12 class DomainRenamerTestCase(FHDLTestCase
):
21 def test_rename_signals(self
):
24 self
.s1
.eq(ClockSignal()),
25 ResetSignal().eq(self
.s2
),
27 self
.s4
.eq(ClockSignal("other")),
28 self
.s5
.eq(ResetSignal("other")),
30 f
.add_driver(self
.s1
, None)
31 f
.add_driver(self
.s2
, None)
32 f
.add_driver(self
.s3
, "sync")
34 f
= DomainRenamer("pix")(f
)
35 self
.assertRepr(f
.statements
, """
37 (eq (sig s1) (clk pix))
38 (eq (rst pix) (sig s2))
39 (eq (sig s3) (const 1'd0))
40 (eq (sig s4) (clk other))
41 (eq (sig s5) (rst other))
44 self
.assertEqual(f
.drivers
, {
45 None: SignalSet((self
.s1
, self
.s2
)),
46 "pix": SignalSet((self
.s3
,)),
49 def test_rename_multi(self
):
52 self
.s1
.eq(ClockSignal()),
53 self
.s2
.eq(ResetSignal("other")),
56 f
= DomainRenamer({"sync": "pix", "other": "pix2"})(f
)
57 self
.assertRepr(f
.statements
, """
59 (eq (sig s1) (clk pix))
60 (eq (sig s2) (rst pix2))
64 def test_rename_cd(self
):
65 cd_sync
= ClockDomain()
66 cd_pix
= ClockDomain()
69 f
.add_domains(cd_sync
, cd_pix
)
71 f
= DomainRenamer("ext")(f
)
72 self
.assertEqual(cd_sync
.name
, "ext")
73 self
.assertEqual(f
.domains
, {
78 def test_rename_cd_preserves_allow_reset_less(self
):
79 cd_pix
= ClockDomain(reset_less
=True)
84 self
.s1
.eq(ResetSignal(allow_reset_less
=True)),
87 f
= DomainRenamer("pix")(f
)
88 f
= DomainLowerer()(f
)
89 self
.assertRepr(f
.statements
, """
91 (eq (sig s1) (const 1'd0))
96 def test_rename_cd_subfragment(self
):
97 cd_sync
= ClockDomain()
98 cd_pix
= ClockDomain()
101 f1
.add_domains(cd_sync
, cd_pix
)
103 f2
.add_domains(cd_sync
)
104 f1
.add_subfragment(f2
)
106 f1
= DomainRenamer("ext")(f1
)
107 self
.assertEqual(cd_sync
.name
, "ext")
108 self
.assertEqual(f1
.domains
, {
113 def test_rename_wrong_to_comb(self
):
114 with self
.assertRaisesRegex(ValueError,
115 r
"^Domain 'sync' may not be renamed to 'comb'$"):
116 DomainRenamer("comb")
118 def test_rename_wrong_from_comb(self
):
119 with self
.assertRaisesRegex(ValueError,
120 r
"^Domain 'comb' may not be renamed$"):
121 DomainRenamer({"comb": "sync"})
124 class DomainLowererTestCase(FHDLTestCase
):
128 def test_lower_clk(self
):
133 self
.s
.eq(ClockSignal("sync"))
136 f
= DomainLowerer()(f
)
137 self
.assertRepr(f
.statements
, """
139 (eq (sig s) (sig clk))
143 def test_lower_rst(self
):
148 self
.s
.eq(ResetSignal("sync"))
151 f
= DomainLowerer()(f
)
152 self
.assertRepr(f
.statements
, """
154 (eq (sig s) (sig rst))
158 def test_lower_rst_reset_less(self
):
159 sync
= ClockDomain(reset_less
=True)
163 self
.s
.eq(ResetSignal("sync", allow_reset_less
=True))
166 f
= DomainLowerer()(f
)
167 self
.assertRepr(f
.statements
, """
169 (eq (sig s) (const 1'd0))
173 def test_lower_drivers(self
):
177 f
.add_domains(sync
, pix
)
178 f
.add_driver(ClockSignal("pix"), None)
179 f
.add_driver(ResetSignal("pix"), "sync")
181 f
= DomainLowerer()(f
)
182 self
.assertEqual(f
.drivers
, {
183 None: SignalSet((pix
.clk
,)),
184 "sync": SignalSet((pix
.rst
,))
187 def test_lower_wrong_domain(self
):
190 self
.s
.eq(ClockSignal("xxx"))
193 with self
.assertRaisesRegex(DomainError
,
194 r
"^Signal \(clk xxx\) refers to nonexistent domain 'xxx'$"):
197 def test_lower_wrong_reset_less_domain(self
):
198 sync
= ClockDomain(reset_less
=True)
202 self
.s
.eq(ResetSignal("sync"))
205 with self
.assertRaisesRegex(DomainError
,
206 r
"^Signal \(rst sync\) refers to reset of reset-less domain 'sync'$"):
210 class SampleLowererTestCase(FHDLTestCase
):
217 def test_lower_signal(self
):
220 self
.o1
.eq(Sample(self
.i
, 2, "sync")),
221 self
.o2
.eq(Sample(self
.i
, 1, "sync")),
222 self
.o3
.eq(Sample(self
.i
, 1, "pix")),
225 f
= SampleLowerer()(f
)
226 self
.assertRepr(f
.statements
, """
228 (eq (sig o1) (sig $sample$s$i$sync$2))
229 (eq (sig o2) (sig $sample$s$i$sync$1))
230 (eq (sig o3) (sig $sample$s$i$pix$1))
231 (eq (sig $sample$s$i$sync$1) (sig i))
232 (eq (sig $sample$s$i$sync$2) (sig $sample$s$i$sync$1))
233 (eq (sig $sample$s$i$pix$1) (sig i))
236 self
.assertEqual(len(f
.drivers
["sync"]), 2)
237 self
.assertEqual(len(f
.drivers
["pix"]), 1)
239 def test_lower_const(self
):
242 self
.o1
.eq(Sample(1, 2, "sync")),
245 f
= SampleLowerer()(f
)
246 self
.assertRepr(f
.statements
, """
248 (eq (sig o1) (sig $sample$c$1$sync$2))
249 (eq (sig $sample$c$1$sync$1) (const 1'd1))
250 (eq (sig $sample$c$1$sync$2) (sig $sample$c$1$sync$1))
253 self
.assertEqual(len(f
.drivers
["sync"]), 2)
256 class SwitchCleanerTestCase(FHDLTestCase
):
257 def test_clean(self
):
273 self
.assertRepr(SwitchCleaner()(stmts
), """
277 (eq (sig a) (const 1'd0)))
279 (eq (sig b) (const 1'd1)))
285 class LHSGroupAnalyzerTestCase(FHDLTestCase
):
286 def test_no_group_unrelated(self
):
294 groups
= LHSGroupAnalyzer()(stmts
)
295 self
.assertEqual(list(groups
.values()), [
300 def test_group_related(self
):
308 groups
= LHSGroupAnalyzer()(stmts
)
309 self
.assertEqual(list(groups
.values()), [
313 def test_no_loops(self
):
322 groups
= LHSGroupAnalyzer()(stmts
)
323 self
.assertEqual(list(groups
.values()), [
327 def test_switch(self
):
337 groups
= LHSGroupAnalyzer()(stmts
)
338 self
.assertEqual(list(groups
.values()), [
343 def test_lhs_empty(self
):
348 groups
= LHSGroupAnalyzer()(stmts
)
349 self
.assertEqual(list(groups
.values()), [
353 class LHSGroupFilterTestCase(FHDLTestCase
):
354 def test_filter(self
):
368 self
.assertRepr(LHSGroupFilter(SignalSet((a
,)))(stmts
), """
372 (eq (sig a) (const 1'd0)))
378 def test_lhs_empty(self
):
383 self
.assertRepr(LHSGroupFilter(SignalSet())(stmts
), "()")
386 class ResetInserterTestCase(FHDLTestCase
):
389 self
.s2
= Signal(reset
=1)
390 self
.s3
= Signal(reset
=1, reset_less
=True)
393 def test_reset_default(self
):
398 f
.add_driver(self
.s1
, "sync")
400 f
= ResetInserter(self
.c1
)(f
)
401 self
.assertRepr(f
.statements
, """
403 (eq (sig s1) (const 1'd1))
405 (case 1 (eq (sig s1) (const 1'd0)))
410 def test_reset_cd(self
):
416 f
.add_domains(ClockDomain("sync"))
417 f
.add_driver(self
.s1
, "sync")
418 f
.add_driver(self
.s2
, "pix")
420 f
= ResetInserter({"pix": self
.c1
})(f
)
421 self
.assertRepr(f
.statements
, """
423 (eq (sig s1) (const 1'd1))
424 (eq (sig s2) (const 1'd0))
426 (case 1 (eq (sig s2) (const 1'd1)))
431 def test_reset_value(self
):
436 f
.add_driver(self
.s2
, "sync")
438 f
= ResetInserter(self
.c1
)(f
)
439 self
.assertRepr(f
.statements
, """
441 (eq (sig s2) (const 1'd0))
443 (case 1 (eq (sig s2) (const 1'd1)))
448 def test_reset_less(self
):
453 f
.add_driver(self
.s3
, "sync")
455 f
= ResetInserter(self
.c1
)(f
)
456 self
.assertRepr(f
.statements
, """
458 (eq (sig s3) (const 1'd0))
466 class EnableInserterTestCase(FHDLTestCase
):
473 def test_enable_default(self
):
478 f
.add_driver(self
.s1
, "sync")
480 f
= EnableInserter(self
.c1
)(f
)
481 self
.assertRepr(f
.statements
, """
483 (eq (sig s1) (const 1'd1))
485 (case 0 (eq (sig s1) (sig s1)))
490 def test_enable_cd(self
):
496 f
.add_driver(self
.s1
, "sync")
497 f
.add_driver(self
.s2
, "pix")
499 f
= EnableInserter({"pix": self
.c1
})(f
)
500 self
.assertRepr(f
.statements
, """
502 (eq (sig s1) (const 1'd1))
503 (eq (sig s2) (const 1'd0))
505 (case 0 (eq (sig s2) (sig s2)))
510 def test_enable_subfragment(self
):
515 f1
.add_driver(self
.s1
, "sync")
521 f2
.add_driver(self
.s2
, "sync")
522 f1
.add_subfragment(f2
)
524 f1
= EnableInserter(self
.c1
)(f1
)
525 (f2
, _
), = f1
.subfragments
526 self
.assertRepr(f1
.statements
, """
528 (eq (sig s1) (const 1'd1))
530 (case 0 (eq (sig s1) (sig s1)))
534 self
.assertRepr(f2
.statements
, """
536 (eq (sig s2) (const 1'd1))
538 (case 0 (eq (sig s2) (sig s2)))
543 def test_enable_read_port(self
):
544 mem
= Memory(width
=8, depth
=4)
545 f
= EnableInserter(self
.c1
)(mem
.read_port(transparent
=False)).elaborate(platform
=None)
546 self
.assertRepr(f
.named_ports
["EN"][0], """
547 (m (sig c1) (sig mem_r_en) (const 1'd0))
550 def test_enable_write_port(self
):
551 mem
= Memory(width
=8, depth
=4)
552 f
= EnableInserter(self
.c1
)(mem
.write_port()).elaborate(platform
=None)
553 self
.assertRepr(f
.named_ports
["EN"][0], """
554 (m (sig c1) (cat (repl (slice (sig mem_w_en) 0:1) 8)) (const 8'd0))
558 class _MockElaboratable(Elaboratable
):
562 def elaborate(self
, platform
):
567 f
.add_driver(self
.s1
, "sync")
571 class TransformedElaboratableTestCase(FHDLTestCase
):
576 def test_getattr(self
):
577 e
= _MockElaboratable()
578 te
= EnableInserter(self
.c1
)(e
)
580 self
.assertIs(te
.s1
, e
.s1
)
582 def test_composition(self
):
583 e
= _MockElaboratable()
584 te1
= EnableInserter(self
.c1
)(e
)
585 te2
= ResetInserter(self
.c2
)(te1
)
587 self
.assertIsInstance(te1
, TransformedElaboratable
)
588 self
.assertIs(te1
, te2
)
590 f
= Fragment
.get(te2
, None)
591 self
.assertRepr(f
.statements
, """
593 (eq (sig s1) (const 1'd1))
595 (case 0 (eq (sig s1) (sig s1)))
598 (case 1 (eq (sig s1) (const 1'd0)))
604 class MockUserValue(UserValue
):
605 def __init__(self
, lowered
):
607 self
.lowered
= lowered
613 class UserValueTestCase(FHDLTestCase
):
617 self
.uv
= MockUserValue(self
.s
)
619 def test_lower(self
):
626 for signal
in self
.uv
._lhs
_signals
():
627 f
.add_driver(signal
, "sync")
629 f
= ResetInserter(self
.c
)(f
)
630 f
= DomainLowerer()(f
)
631 self
.assertRepr(f
.statements
, """
633 (eq (sig s) (const 1'd1))
635 (case 1 (eq (sig s) (const 1'd0)))
638 (case 1 (eq (sig s) (const 1'd0)))
644 class UserValueRecursiveTestCase(UserValueTestCase
):
648 self
.uv
= MockUserValue(MockUserValue(self
.s
))
650 # inherit the test_lower method from UserValueTestCase because the checks are the same