1 # nmigen: UnusedElaboratable=no
3 from collections
import OrderedDict
6 from nmigen
.hdl
.ast
import *
7 from nmigen
.hdl
.cd
import *
8 from nmigen
.hdl
.dsl
import *
13 class DSLTestCase(FHDLTestCase
):
23 def test_cant_inherit(self
):
24 with self
.assertRaisesRegex(SyntaxError,
25 (r
"^Instead of inheriting from `Module`, inherit from `Elaboratable` and "
26 r
"return a `Module` from the `elaborate\(self, platform\)` method$")):
30 def test_d_comb(self
):
32 m
.d
.comb
+= self
.c1
.eq(1)
34 self
.assertEqual(m
._driving
[self
.c1
], None)
35 self
.assertRepr(m
._statements
, """(
36 (eq (sig c1) (const 1'd1))
39 def test_d_sync(self
):
41 m
.d
.sync
+= self
.c1
.eq(1)
43 self
.assertEqual(m
._driving
[self
.c1
], "sync")
44 self
.assertRepr(m
._statements
, """(
45 (eq (sig c1) (const 1'd1))
50 m
.d
.pix
+= self
.c1
.eq(1)
52 self
.assertEqual(m
._driving
[self
.c1
], "pix")
53 self
.assertRepr(m
._statements
, """(
54 (eq (sig c1) (const 1'd1))
57 def test_d_index(self
):
59 m
.d
["pix"] += self
.c1
.eq(1)
61 self
.assertEqual(m
._driving
[self
.c1
], "pix")
62 self
.assertRepr(m
._statements
, """(
63 (eq (sig c1) (const 1'd1))
66 def test_d_no_conflict(self
):
68 m
.d
.comb
+= self
.w1
[0].eq(1)
69 m
.d
.comb
+= self
.w1
[1].eq(1)
71 def test_d_conflict(self
):
73 with self
.assertRaisesRegex(SyntaxError,
74 (r
"^Driver-driver conflict: trying to drive \(sig c1\) from d\.sync, but it "
75 r
"is already driven from d\.comb$")):
76 m
.d
.comb
+= self
.c1
.eq(1)
77 m
.d
.sync
+= self
.c1
.eq(1)
79 def test_d_wrong(self
):
81 with self
.assertRaisesRegex(AttributeError,
82 r
"^Cannot assign 'd\.pix' attribute; did you mean 'd.pix \+='\?$"):
85 def test_d_asgn_wrong(self
):
87 with self
.assertRaisesRegex(SyntaxError,
88 r
"^Only assignments and property checks may be appended to d\.sync$"):
89 m
.d
.sync
+= Switch(self
.s1
, {})
91 def test_comb_wrong(self
):
93 with self
.assertRaisesRegex(AttributeError,
94 r
"^'Module' object has no attribute 'comb'; did you mean 'd\.comb'\?$"):
95 m
.comb
+= self
.c1
.eq(1)
97 def test_sync_wrong(self
):
99 with self
.assertRaisesRegex(AttributeError,
100 r
"^'Module' object has no attribute 'sync'; did you mean 'd\.sync'\?$"):
101 m
.sync
+= self
.c1
.eq(1)
103 def test_attr_wrong(self
):
105 with self
.assertRaisesRegex(AttributeError,
106 r
"^'Module' object has no attribute 'nonexistentattr'$"):
109 def test_d_suspicious(self
):
111 with self
.assertWarnsRegex(SyntaxWarning,
112 (r
"^Using '<module>\.d\.submodules' would add statements to clock domain "
113 r
"'submodules'; did you mean <module>\.submodules instead\?$")):
116 def test_clock_signal(self
):
118 m
.d
.comb
+= ClockSignal("pix").eq(ClockSignal())
119 self
.assertRepr(m
._statements
, """
121 (eq (clk pix) (clk sync))
125 def test_reset_signal(self
):
127 m
.d
.comb
+= ResetSignal("pix").eq(1)
128 self
.assertRepr(m
._statements
, """
130 (eq (rst pix) (const 1'd1))
134 def test_sample_domain(self
):
140 m
.d
.sync
+= o1
.eq(Past(i
))
141 m
.d
.pix
+= o2
.eq(Past(i
))
142 m
.d
.pix
+= o3
.eq(Past(i
, domain
="sync"))
143 f
= m
.elaborate(platform
=None)
144 self
.assertRepr(f
.statements
, """
146 (eq (sig o1) (sample (sig i) @ sync[1]))
147 (eq (sig o2) (sample (sig i) @ pix[1]))
148 (eq (sig o3) (sample (sig i) @ sync[1]))
155 m
.d
.comb
+= self
.c1
.eq(1)
157 self
.assertRepr(m
._statements
, """
159 (switch (cat (sig s1))
160 (case 1 (eq (sig c1) (const 1'd1)))
165 def test_If_Elif(self
):
168 m
.d
.comb
+= self
.c1
.eq(1)
169 with m
.Elif(self
.s2
):
170 m
.d
.sync
+= self
.c2
.eq(0)
172 self
.assertRepr(m
._statements
, """
174 (switch (cat (sig s1) (sig s2))
175 (case -1 (eq (sig c1) (const 1'd1)))
176 (case 1- (eq (sig c2) (const 1'd0)))
181 def test_If_Elif_Else(self
):
184 m
.d
.comb
+= self
.c1
.eq(1)
185 with m
.Elif(self
.s2
):
186 m
.d
.sync
+= self
.c2
.eq(0)
188 m
.d
.comb
+= self
.c3
.eq(1)
190 self
.assertRepr(m
._statements
, """
192 (switch (cat (sig s1) (sig s2))
193 (case -1 (eq (sig c1) (const 1'd1)))
194 (case 1- (eq (sig c2) (const 1'd0)))
195 (default (eq (sig c3) (const 1'd1)))
200 def test_If_If(self
):
203 m
.d
.comb
+= self
.c1
.eq(1)
205 m
.d
.comb
+= self
.c2
.eq(1)
207 self
.assertRepr(m
._statements
, """
209 (switch (cat (sig s1))
210 (case 1 (eq (sig c1) (const 1'd1)))
212 (switch (cat (sig s2))
213 (case 1 (eq (sig c2) (const 1'd1)))
218 def test_If_nested_If(self
):
221 m
.d
.comb
+= self
.c1
.eq(1)
223 m
.d
.comb
+= self
.c2
.eq(1)
225 self
.assertRepr(m
._statements
, """
227 (switch (cat (sig s1))
228 (case 1 (eq (sig c1) (const 1'd1))
229 (switch (cat (sig s2))
230 (case 1 (eq (sig c2) (const 1'd1)))
237 def test_If_dangling_Else(self
):
240 m
.d
.comb
+= self
.c1
.eq(1)
242 m
.d
.comb
+= self
.c2
.eq(1)
244 m
.d
.comb
+= self
.c3
.eq(1)
246 self
.assertRepr(m
._statements
, """
248 (switch (cat (sig s1))
250 (eq (sig c1) (const 1'd1))
251 (switch (cat (sig s2))
252 (case 1 (eq (sig c2) (const 1'd1)))
256 (eq (sig c3) (const 1'd1))
262 def test_Elif_wrong(self
):
264 with self
.assertRaisesRegex(SyntaxError,
265 r
"^Elif without preceding If$"):
266 with m
.Elif(self
.s2
):
269 def test_Elif_wrong_nested(self
):
272 with self
.assertRaisesRegex(SyntaxError,
273 r
"^Elif without preceding If$"):
274 with m
.Elif(self
.s2
):
277 def test_Else_wrong(self
):
279 with self
.assertRaisesRegex(SyntaxError,
280 r
"^Else without preceding If\/Elif$"):
284 def test_If_wide(self
):
287 m
.d
.comb
+= self
.c1
.eq(1)
289 self
.assertRepr(m
._statements
, """
291 (switch (cat (b (sig w1)))
292 (case 1 (eq (sig c1) (const 1'd1)))
297 def test_If_signed_suspicious(self
):
299 with self
.assertWarnsRegex(SyntaxWarning,
300 (r
"^Signed values in If\/Elif conditions usually result from inverting Python "
301 r
"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
302 r
"`not flag`\. \(If this is a false positive, silence this warning with "
303 r
"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")):
307 def test_Elif_signed_suspicious(self
):
311 with self
.assertWarnsRegex(SyntaxWarning,
312 (r
"^Signed values in If\/Elif conditions usually result from inverting Python "
313 r
"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
314 r
"`not flag`\. \(If this is a false positive, silence this warning with "
315 r
"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")):
319 def test_if_If_Elif_Else(self
):
321 with self
.assertRaisesRegex(SyntaxError,
322 r
"^`if m\.If\(\.\.\.\):` does not work; use `with m\.If\(\.\.\.\)`$"):
327 with self
.assertRaisesRegex(SyntaxError,
328 r
"^`if m\.Elif\(\.\.\.\):` does not work; use `with m\.Elif\(\.\.\.\)`$"):
331 with self
.assertRaisesRegex(SyntaxError,
332 r
"^`if m\.Else\(\.\.\.\):` does not work; use `with m\.Else\(\.\.\.\)`$"):
336 def test_Switch(self
):
338 with m
.Switch(self
.w1
):
340 m
.d
.comb
+= self
.c1
.eq(1)
342 m
.d
.comb
+= self
.c2
.eq(1)
343 with m
.Case("1 0--"):
344 m
.d
.comb
+= self
.c2
.eq(1)
346 self
.assertRepr(m
._statements
, """
349 (case 0011 (eq (sig c1) (const 1'd1)))
350 (case 11-- (eq (sig c2) (const 1'd1)))
351 (case 10-- (eq (sig c2) (const 1'd1)))
356 def test_Switch_default_Case(self
):
358 with m
.Switch(self
.w1
):
360 m
.d
.comb
+= self
.c1
.eq(1)
362 m
.d
.comb
+= self
.c2
.eq(1)
364 self
.assertRepr(m
._statements
, """
367 (case 0011 (eq (sig c1) (const 1'd1)))
368 (default (eq (sig c2) (const 1'd1)))
373 def test_Switch_default_Default(self
):
375 with m
.Switch(self
.w1
):
377 m
.d
.comb
+= self
.c1
.eq(1)
379 m
.d
.comb
+= self
.c2
.eq(1)
381 self
.assertRepr(m
._statements
, """
384 (case 0011 (eq (sig c1) (const 1'd1)))
385 (default (eq (sig c2) (const 1'd1)))
390 def test_Switch_const_test(self
):
394 m
.d
.comb
+= self
.c1
.eq(1)
396 self
.assertRepr(m
._statements
, """
399 (case 1 (eq (sig c1) (const 1'd1)))
404 def test_Switch_enum(self
):
411 with m
.Case(Color
.RED
):
412 m
.d
.comb
+= self
.c1
.eq(1)
413 self
.assertRepr(m
._statements
, """
416 (case 01 (eq (sig c1) (const 1'd1)))
421 def test_Case_width_wrong(self
):
425 with m
.Switch(self
.w1
):
426 with self
.assertRaisesRegex(SyntaxError,
427 r
"^Case pattern '--' must have the same width as switch value \(which is 4\)$"):
430 with self
.assertWarnsRegex(SyntaxWarning,
431 (r
"^Case pattern '10110' is wider than switch value \(which has width 4\); "
432 r
"comparison will never be true$")):
433 with m
.Case(0b10110):
435 with self
.assertWarnsRegex(SyntaxWarning,
436 (r
"^Case pattern '10101010' \(Color\.RED\) is wider than switch value "
437 r
"\(which has width 4\); comparison will never be true$")):
438 with m
.Case(Color
.RED
):
440 self
.assertRepr(m
._statements
, """
446 def test_Case_bits_wrong(self
):
448 with m
.Switch(self
.w1
):
449 with self
.assertRaisesRegex(SyntaxError,
450 (r
"^Case pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, "
451 r
"and may include whitespace$")):
455 def test_Case_pattern_wrong(self
):
457 with m
.Switch(self
.w1
):
458 with self
.assertRaisesRegex(SyntaxError,
459 r
"^Case pattern must be an integer, a string, or an enumeration, not 1\.0$"):
463 def test_Case_outside_Switch_wrong(self
):
465 with self
.assertRaisesRegex(SyntaxError,
466 r
"^Case is not permitted outside of Switch$"):
470 def test_If_inside_Switch_wrong(self
):
472 with m
.Switch(self
.s1
):
473 with self
.assertRaisesRegex(SyntaxError,
474 (r
"^If is not permitted directly inside of Switch; "
475 r
"it is permitted inside of Switch Case$")):
479 def test_FSM_basic(self
):
485 with m
.State("FIRST"):
488 with m
.State("SECOND"):
493 self
.assertRepr(m
._statements
, """
495 (switch (sig fsm_state)
497 (eq (sig a) (const 1'd1))
498 (eq (sig fsm_state) (const 1'd1))
501 (eq (sig b) (~ (sig b)))
502 (switch (cat (sig c))
504 (eq (sig fsm_state) (const 1'd0)))
510 self
.assertEqual({repr(k
): v
for k
, v
in m
._driving
.items()}, {
512 "(sig fsm_state)": "sync",
516 frag
= m
.elaborate(platform
=None)
517 fsm
= frag
.find_generated("fsm")
518 self
.assertIsInstance(fsm
.state
, Signal
)
519 self
.assertEqual(fsm
.encoding
, OrderedDict({
523 self
.assertEqual(fsm
.decoding
, OrderedDict({
528 def test_FSM_reset(self
):
531 with m
.FSM(reset
="SECOND"):
532 with m
.State("FIRST"):
535 with m
.State("SECOND"):
538 self
.assertRepr(m
._statements
, """
540 (switch (sig fsm_state)
542 (eq (sig a) (const 1'd0))
543 (eq (sig fsm_state) (const 1'd1))
546 (eq (sig fsm_state) (const 1'd0))
552 def test_FSM_ongoing(self
):
557 m
.d
.comb
+= b
.eq(fsm
.ongoing("SECOND"))
558 with m
.State("FIRST"):
560 m
.d
.comb
+= a
.eq(fsm
.ongoing("FIRST"))
561 with m
.State("SECOND"):
564 self
.assertEqual(m
._generated
["fsm"].state
.reset
, 1)
566 self
.assertRepr(m
._statements
, """
568 (eq (sig b) (== (sig fsm_state) (const 1'd0)))
569 (eq (sig a) (== (sig fsm_state) (const 1'd1)))
570 (switch (sig fsm_state)
579 def test_FSM_empty(self
):
583 self
.assertRepr(m
._statements
, """
587 def test_FSM_wrong_domain(self
):
589 with self
.assertRaisesRegex(ValueError,
590 r
"^FSM may not be driven by the 'comb' domain$"):
591 with m
.FSM(domain
="comb"):
594 def test_FSM_wrong_undefined(self
):
596 with self
.assertRaisesRegex(NameError,
597 r
"^FSM state 'FOO' is referenced but not defined$"):
601 def test_FSM_wrong_redefined(self
):
606 with self
.assertRaisesRegex(NameError,
607 r
"^FSM state 'FOO' is already defined$"):
611 def test_FSM_wrong_next(self
):
613 with self
.assertRaisesRegex(SyntaxError,
614 r
"^Only assignment to `m\.next` is permitted$"):
616 with self
.assertRaisesRegex(SyntaxError,
617 r
"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
619 with self
.assertRaisesRegex(SyntaxError,
620 r
"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
624 def test_If_inside_FSM_wrong(self
):
629 with self
.assertRaisesRegex(SyntaxError,
630 (r
"^If is not permitted directly inside of FSM; "
631 r
"it is permitted inside of FSM State$")):
635 def test_auto_pop_ctrl(self
):
638 m
.d
.comb
+= self
.c1
.eq(1)
639 m
.d
.comb
+= self
.c2
.eq(1)
640 self
.assertRepr(m
._statements
, """
642 (switch (cat (b (sig w1)))
643 (case 1 (eq (sig c1) (const 1'd1)))
645 (eq (sig c2) (const 1'd1))
649 def test_submodule_anon(self
):
653 self
.assertEqual(m1
._anon_submodules
, [m2
])
654 self
.assertEqual(m1
._named_submodules
, {})
656 def test_submodule_anon_multi(self
):
660 m1
.submodules
+= m2
, m3
661 self
.assertEqual(m1
._anon_submodules
, [m2
, m3
])
662 self
.assertEqual(m1
._named_submodules
, {})
664 def test_submodule_named(self
):
667 m1
.submodules
.foo
= m2
668 self
.assertEqual(m1
._anon_submodules
, [])
669 self
.assertEqual(m1
._named_submodules
, {"foo": m2
})
671 def test_submodule_named_index(self
):
674 m1
.submodules
["foo"] = m2
675 self
.assertEqual(m1
._anon_submodules
, [])
676 self
.assertEqual(m1
._named_submodules
, {"foo": m2
})
678 def test_submodule_wrong(self
):
680 with self
.assertRaisesRegex(TypeError,
681 r
"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
683 with self
.assertRaisesRegex(TypeError,
684 r
"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
687 def test_submodule_named_conflict(self
):
690 m1
.submodules
.foo
= m2
691 with self
.assertRaisesRegex(NameError, r
"^Submodule named 'foo' already exists$"):
692 m1
.submodules
.foo
= m2
694 def test_submodule_get(self
):
697 m1
.submodules
.foo
= m2
698 m3
= m1
.submodules
.foo
699 self
.assertEqual(m2
, m3
)
701 def test_submodule_get_index(self
):
704 m1
.submodules
["foo"] = m2
705 m3
= m1
.submodules
["foo"]
706 self
.assertEqual(m2
, m3
)
708 def test_submodule_get_unset(self
):
710 with self
.assertRaisesRegex(AttributeError, r
"^No submodule named 'foo' exists$"):
711 m2
= m1
.submodules
.foo
712 with self
.assertRaisesRegex(AttributeError, r
"^No submodule named 'foo' exists$"):
713 m2
= m1
.submodules
["foo"]
715 def test_domain_named_implicit(self
):
717 m
.domains
+= ClockDomain("sync")
718 self
.assertEqual(len(m
._domains
), 1)
720 def test_domain_named_explicit(self
):
722 m
.domains
.foo
= ClockDomain()
723 self
.assertEqual(len(m
._domains
), 1)
724 self
.assertEqual(m
._domains
["foo"].name
, "foo")
726 def test_domain_add_wrong(self
):
728 with self
.assertRaisesRegex(TypeError,
729 r
"^Only clock domains may be added to `m\.domains`, not 1$"):
731 with self
.assertRaisesRegex(TypeError,
732 r
"^Only clock domains may be added to `m\.domains`, not 1$"):
735 def test_domain_add_wrong_name(self
):
737 with self
.assertRaisesRegex(NameError,
738 r
"^Clock domain name 'bar' must match name in `m\.domains\.foo \+= \.\.\.` syntax$"):
739 m
.domains
.foo
= ClockDomain("bar")
741 def test_domain_add_wrong_duplicate(self
):
743 m
.domains
+= ClockDomain("foo")
744 with self
.assertRaisesRegex(NameError,
745 r
"^Clock domain named 'foo' already exists$"):
746 m
.domains
+= ClockDomain("foo")
748 def test_lower(self
):
750 m1
.d
.comb
+= self
.c1
.eq(self
.s1
)
752 m2
.d
.comb
+= self
.c2
.eq(self
.s2
)
753 m2
.d
.sync
+= self
.c3
.eq(self
.s3
)
754 m1
.submodules
.foo
= m2
756 f1
= m1
.elaborate(platform
=None)
757 self
.assertRepr(f1
.statements
, """
759 (eq (sig c1) (sig s1))
762 self
.assertEqual(f1
.drivers
, {
763 None: SignalSet((self
.c1
,))
765 self
.assertEqual(len(f1
.subfragments
), 1)
766 (f2
, f2_name
), = f1
.subfragments
767 self
.assertEqual(f2_name
, "foo")
768 self
.assertRepr(f2
.statements
, """
770 (eq (sig c2) (sig s2))
771 (eq (sig c3) (sig s3))
774 self
.assertEqual(f2
.drivers
, {
775 None: SignalSet((self
.c2
,)),
776 "sync": SignalSet((self
.c3
,))
778 self
.assertEqual(len(f2
.subfragments
), 0)