1 # nmigen: UnusedElaboratable=no
3 from ..hdl
.ast
import *
6 from ..hdl
.xfrm
import *
7 from ..hdl
.mem
import *
11 class DomainRenamerTestCase(FHDLTestCase
):
20 def test_rename_signals(self
):
23 self
.s1
.eq(ClockSignal()),
24 ResetSignal().eq(self
.s2
),
26 self
.s4
.eq(ClockSignal("other")),
27 self
.s5
.eq(ResetSignal("other")),
29 f
.add_driver(self
.s1
, None)
30 f
.add_driver(self
.s2
, None)
31 f
.add_driver(self
.s3
, "sync")
33 f
= DomainRenamer("pix")(f
)
34 self
.assertRepr(f
.statements
, """
36 (eq (sig s1) (clk pix))
37 (eq (rst pix) (sig s2))
38 (eq (sig s3) (const 1'd0))
39 (eq (sig s4) (clk other))
40 (eq (sig s5) (rst other))
43 self
.assertEqual(f
.drivers
, {
44 None: SignalSet((self
.s1
, self
.s2
)),
45 "pix": SignalSet((self
.s3
,)),
48 def test_rename_multi(self
):
51 self
.s1
.eq(ClockSignal()),
52 self
.s2
.eq(ResetSignal("other")),
55 f
= DomainRenamer({"sync": "pix", "other": "pix2"})(f
)
56 self
.assertRepr(f
.statements
, """
58 (eq (sig s1) (clk pix))
59 (eq (sig s2) (rst pix2))
63 def test_rename_cd(self
):
64 cd_sync
= ClockDomain()
65 cd_pix
= ClockDomain()
68 f
.add_domains(cd_sync
, cd_pix
)
70 f
= DomainRenamer("ext")(f
)
71 self
.assertEqual(cd_sync
.name
, "ext")
72 self
.assertEqual(f
.domains
, {
77 def test_rename_cd_preserves_allow_reset_less(self
):
78 cd_pix
= ClockDomain(reset_less
=True)
83 self
.s1
.eq(ResetSignal(allow_reset_less
=True)),
86 f
= DomainRenamer("pix")(f
)
87 f
= DomainLowerer()(f
)
88 self
.assertRepr(f
.statements
, """
90 (eq (sig s1) (const 1'd0))
95 def test_rename_cd_subfragment(self
):
96 cd_sync
= ClockDomain()
97 cd_pix
= ClockDomain()
100 f1
.add_domains(cd_sync
, cd_pix
)
102 f2
.add_domains(cd_sync
)
103 f1
.add_subfragment(f2
)
105 f1
= DomainRenamer("ext")(f1
)
106 self
.assertEqual(cd_sync
.name
, "ext")
107 self
.assertEqual(f1
.domains
, {
112 def test_rename_wrong_to_comb(self
):
113 with self
.assertRaisesRegex(ValueError,
114 r
"^Domain 'sync' may not be renamed to 'comb'$"):
115 DomainRenamer("comb")
117 def test_rename_wrong_from_comb(self
):
118 with self
.assertRaisesRegex(ValueError,
119 r
"^Domain 'comb' may not be renamed$"):
120 DomainRenamer({"comb": "sync"})
123 class DomainLowererTestCase(FHDLTestCase
):
127 def test_lower_clk(self
):
132 self
.s
.eq(ClockSignal("sync"))
135 f
= DomainLowerer()(f
)
136 self
.assertRepr(f
.statements
, """
138 (eq (sig s) (sig clk))
142 def test_lower_rst(self
):
147 self
.s
.eq(ResetSignal("sync"))
150 f
= DomainLowerer()(f
)
151 self
.assertRepr(f
.statements
, """
153 (eq (sig s) (sig rst))
157 def test_lower_rst_reset_less(self
):
158 sync
= ClockDomain(reset_less
=True)
162 self
.s
.eq(ResetSignal("sync", allow_reset_less
=True))
165 f
= DomainLowerer()(f
)
166 self
.assertRepr(f
.statements
, """
168 (eq (sig s) (const 1'd0))
172 def test_lower_drivers(self
):
176 f
.add_domains(sync
, pix
)
177 f
.add_driver(ClockSignal("pix"), None)
178 f
.add_driver(ResetSignal("pix"), "sync")
180 f
= DomainLowerer()(f
)
181 self
.assertEqual(f
.drivers
, {
182 None: SignalSet((pix
.clk
,)),
183 "sync": SignalSet((pix
.rst
,))
186 def test_lower_wrong_domain(self
):
189 self
.s
.eq(ClockSignal("xxx"))
192 with self
.assertRaisesRegex(DomainError
,
193 r
"^Signal \(clk xxx\) refers to nonexistent domain 'xxx'$"):
196 def test_lower_wrong_reset_less_domain(self
):
197 sync
= ClockDomain(reset_less
=True)
201 self
.s
.eq(ResetSignal("sync"))
204 with self
.assertRaisesRegex(DomainError
,
205 r
"^Signal \(rst sync\) refers to reset of reset-less domain 'sync'$"):
209 class SampleLowererTestCase(FHDLTestCase
):
216 def test_lower_signal(self
):
219 self
.o1
.eq(Sample(self
.i
, 2, "sync")),
220 self
.o2
.eq(Sample(self
.i
, 1, "sync")),
221 self
.o3
.eq(Sample(self
.i
, 1, "pix")),
224 f
= SampleLowerer()(f
)
225 self
.assertRepr(f
.statements
, """
227 (eq (sig o1) (sig $sample$s$i$sync$2))
228 (eq (sig o2) (sig $sample$s$i$sync$1))
229 (eq (sig o3) (sig $sample$s$i$pix$1))
230 (eq (sig $sample$s$i$sync$1) (sig i))
231 (eq (sig $sample$s$i$sync$2) (sig $sample$s$i$sync$1))
232 (eq (sig $sample$s$i$pix$1) (sig i))
235 self
.assertEqual(len(f
.drivers
["sync"]), 2)
236 self
.assertEqual(len(f
.drivers
["pix"]), 1)
238 def test_lower_const(self
):
241 self
.o1
.eq(Sample(1, 2, "sync")),
244 f
= SampleLowerer()(f
)
245 self
.assertRepr(f
.statements
, """
247 (eq (sig o1) (sig $sample$c$1$sync$2))
248 (eq (sig $sample$c$1$sync$1) (const 1'd1))
249 (eq (sig $sample$c$1$sync$2) (sig $sample$c$1$sync$1))
252 self
.assertEqual(len(f
.drivers
["sync"]), 2)
255 class SwitchCleanerTestCase(FHDLTestCase
):
256 def test_clean(self
):
272 self
.assertRepr(SwitchCleaner()(stmts
), """
276 (eq (sig a) (const 1'd0)))
278 (eq (sig b) (const 1'd1)))
284 class LHSGroupAnalyzerTestCase(FHDLTestCase
):
285 def test_no_group_unrelated(self
):
293 groups
= LHSGroupAnalyzer()(stmts
)
294 self
.assertEqual(list(groups
.values()), [
299 def test_group_related(self
):
307 groups
= LHSGroupAnalyzer()(stmts
)
308 self
.assertEqual(list(groups
.values()), [
312 def test_no_loops(self
):
321 groups
= LHSGroupAnalyzer()(stmts
)
322 self
.assertEqual(list(groups
.values()), [
326 def test_switch(self
):
336 groups
= LHSGroupAnalyzer()(stmts
)
337 self
.assertEqual(list(groups
.values()), [
342 def test_lhs_empty(self
):
347 groups
= LHSGroupAnalyzer()(stmts
)
348 self
.assertEqual(list(groups
.values()), [
352 class LHSGroupFilterTestCase(FHDLTestCase
):
353 def test_filter(self
):
367 self
.assertRepr(LHSGroupFilter(SignalSet((a
,)))(stmts
), """
371 (eq (sig a) (const 1'd0)))
377 def test_lhs_empty(self
):
382 self
.assertRepr(LHSGroupFilter(SignalSet())(stmts
), "()")
385 class ResetInserterTestCase(FHDLTestCase
):
388 self
.s2
= Signal(reset
=1)
389 self
.s3
= Signal(reset
=1, reset_less
=True)
392 def test_reset_default(self
):
397 f
.add_driver(self
.s1
, "sync")
399 f
= ResetInserter(self
.c1
)(f
)
400 self
.assertRepr(f
.statements
, """
402 (eq (sig s1) (const 1'd1))
404 (case 1 (eq (sig s1) (const 1'd0)))
409 def test_reset_cd(self
):
415 f
.add_domains(ClockDomain("sync"))
416 f
.add_driver(self
.s1
, "sync")
417 f
.add_driver(self
.s2
, "pix")
419 f
= ResetInserter({"pix": self
.c1
})(f
)
420 self
.assertRepr(f
.statements
, """
422 (eq (sig s1) (const 1'd1))
423 (eq (sig s2) (const 1'd0))
425 (case 1 (eq (sig s2) (const 1'd1)))
430 def test_reset_value(self
):
435 f
.add_driver(self
.s2
, "sync")
437 f
= ResetInserter(self
.c1
)(f
)
438 self
.assertRepr(f
.statements
, """
440 (eq (sig s2) (const 1'd0))
442 (case 1 (eq (sig s2) (const 1'd1)))
447 def test_reset_less(self
):
452 f
.add_driver(self
.s3
, "sync")
454 f
= ResetInserter(self
.c1
)(f
)
455 self
.assertRepr(f
.statements
, """
457 (eq (sig s3) (const 1'd0))
465 class EnableInserterTestCase(FHDLTestCase
):
472 def test_enable_default(self
):
477 f
.add_driver(self
.s1
, "sync")
479 f
= EnableInserter(self
.c1
)(f
)
480 self
.assertRepr(f
.statements
, """
482 (eq (sig s1) (const 1'd1))
484 (case 0 (eq (sig s1) (sig s1)))
489 def test_enable_cd(self
):
495 f
.add_driver(self
.s1
, "sync")
496 f
.add_driver(self
.s2
, "pix")
498 f
= EnableInserter({"pix": self
.c1
})(f
)
499 self
.assertRepr(f
.statements
, """
501 (eq (sig s1) (const 1'd1))
502 (eq (sig s2) (const 1'd0))
504 (case 0 (eq (sig s2) (sig s2)))
509 def test_enable_subfragment(self
):
514 f1
.add_driver(self
.s1
, "sync")
520 f2
.add_driver(self
.s2
, "sync")
521 f1
.add_subfragment(f2
)
523 f1
= EnableInserter(self
.c1
)(f1
)
524 (f2
, _
), = f1
.subfragments
525 self
.assertRepr(f1
.statements
, """
527 (eq (sig s1) (const 1'd1))
529 (case 0 (eq (sig s1) (sig s1)))
533 self
.assertRepr(f2
.statements
, """
535 (eq (sig s2) (const 1'd1))
537 (case 0 (eq (sig s2) (sig s2)))
542 def test_enable_read_port(self
):
543 mem
= Memory(width
=8, depth
=4)
544 f
= EnableInserter(self
.c1
)(mem
.read_port(transparent
=False)).elaborate(platform
=None)
545 self
.assertRepr(f
.named_ports
["EN"][0], """
546 (m (sig c1) (sig mem_r_en) (const 1'd0))
549 def test_enable_write_port(self
):
550 mem
= Memory(width
=8, depth
=4)
551 f
= EnableInserter(self
.c1
)(mem
.write_port()).elaborate(platform
=None)
552 self
.assertRepr(f
.named_ports
["EN"][0], """
553 (m (sig c1) (cat (repl (slice (sig mem_w_en) 0:1) 8)) (const 8'd0))
557 class _MockElaboratable(Elaboratable
):
561 def elaborate(self
, platform
):
566 f
.add_driver(self
.s1
, "sync")
570 class TransformedElaboratableTestCase(FHDLTestCase
):
575 def test_getattr(self
):
576 e
= _MockElaboratable()
577 te
= EnableInserter(self
.c1
)(e
)
579 self
.assertIs(te
.s1
, e
.s1
)
581 def test_composition(self
):
582 e
= _MockElaboratable()
583 te1
= EnableInserter(self
.c1
)(e
)
584 te2
= ResetInserter(self
.c2
)(te1
)
586 self
.assertIsInstance(te1
, TransformedElaboratable
)
587 self
.assertIs(te1
, te2
)
589 f
= Fragment
.get(te2
, None)
590 self
.assertRepr(f
.statements
, """
592 (eq (sig s1) (const 1'd1))
594 (case 0 (eq (sig s1) (sig s1)))
597 (case 1 (eq (sig s1) (const 1'd0)))
603 class MockUserValue(UserValue
):
604 def __init__(self
, lowered
):
606 self
.lowered
= lowered
612 class UserValueTestCase(FHDLTestCase
):
616 self
.uv
= MockUserValue(self
.s
)
618 def test_lower(self
):
625 for signal
in self
.uv
._lhs
_signals
():
626 f
.add_driver(signal
, "sync")
628 f
= ResetInserter(self
.c
)(f
)
629 f
= DomainLowerer()(f
)
630 self
.assertRepr(f
.statements
, """
632 (eq (sig s) (const 1'd1))
634 (case 1 (eq (sig s) (const 1'd0)))
637 (case 1 (eq (sig s) (const 1'd0)))
643 class UserValueRecursiveTestCase(UserValueTestCase
):
647 self
.uv
= MockUserValue(MockUserValue(self
.s
))
649 # inherit the test_lower method from UserValueTestCase because the checks are the same