hdl.dsl: error on Elif immediately nested in an If.
[nmigen.git] / tests / test_hdl_dsl.py
1 # nmigen: UnusedElaboratable=no
2
3 from collections import OrderedDict
4 from enum import Enum
5
6 from nmigen.hdl.ast import *
7 from nmigen.hdl.cd import *
8 from nmigen.hdl.dsl import *
9
10 from .utils import *
11
12
13 class DSLTestCase(FHDLTestCase):
14 def setUp(self):
15 self.s1 = Signal()
16 self.s2 = Signal()
17 self.s3 = Signal()
18 self.c1 = Signal()
19 self.c2 = Signal()
20 self.c3 = Signal()
21 self.w1 = Signal(4)
22
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$")):
27 class ORGate(Module):
28 pass
29
30 def test_d_comb(self):
31 m = Module()
32 m.d.comb += self.c1.eq(1)
33 m._flush()
34 self.assertEqual(m._driving[self.c1], None)
35 self.assertRepr(m._statements, """(
36 (eq (sig c1) (const 1'd1))
37 )""")
38
39 def test_d_sync(self):
40 m = Module()
41 m.d.sync += self.c1.eq(1)
42 m._flush()
43 self.assertEqual(m._driving[self.c1], "sync")
44 self.assertRepr(m._statements, """(
45 (eq (sig c1) (const 1'd1))
46 )""")
47
48 def test_d_pix(self):
49 m = Module()
50 m.d.pix += self.c1.eq(1)
51 m._flush()
52 self.assertEqual(m._driving[self.c1], "pix")
53 self.assertRepr(m._statements, """(
54 (eq (sig c1) (const 1'd1))
55 )""")
56
57 def test_d_index(self):
58 m = Module()
59 m.d["pix"] += self.c1.eq(1)
60 m._flush()
61 self.assertEqual(m._driving[self.c1], "pix")
62 self.assertRepr(m._statements, """(
63 (eq (sig c1) (const 1'd1))
64 )""")
65
66 def test_d_no_conflict(self):
67 m = Module()
68 m.d.comb += self.w1[0].eq(1)
69 m.d.comb += self.w1[1].eq(1)
70
71 def test_d_conflict(self):
72 m = Module()
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)
78
79 def test_d_wrong(self):
80 m = Module()
81 with self.assertRaisesRegex(AttributeError,
82 r"^Cannot assign 'd\.pix' attribute; did you mean 'd.pix \+='\?$"):
83 m.d.pix = None
84
85 def test_d_asgn_wrong(self):
86 m = Module()
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, {})
90
91 def test_comb_wrong(self):
92 m = Module()
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)
96
97 def test_sync_wrong(self):
98 m = Module()
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)
102
103 def test_attr_wrong(self):
104 m = Module()
105 with self.assertRaisesRegex(AttributeError,
106 r"^'Module' object has no attribute 'nonexistentattr'$"):
107 m.nonexistentattr
108
109 def test_d_suspicious(self):
110 m = Module()
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\?$")):
114 m.d.submodules += []
115
116 def test_clock_signal(self):
117 m = Module()
118 m.d.comb += ClockSignal("pix").eq(ClockSignal())
119 self.assertRepr(m._statements, """
120 (
121 (eq (clk pix) (clk sync))
122 )
123 """)
124
125 def test_reset_signal(self):
126 m = Module()
127 m.d.comb += ResetSignal("pix").eq(1)
128 self.assertRepr(m._statements, """
129 (
130 (eq (rst pix) (const 1'd1))
131 )
132 """)
133
134 def test_sample_domain(self):
135 m = Module()
136 i = Signal()
137 o1 = Signal()
138 o2 = Signal()
139 o3 = Signal()
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, """
145 (
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]))
149 )
150 """)
151
152 def test_If(self):
153 m = Module()
154 with m.If(self.s1):
155 m.d.comb += self.c1.eq(1)
156 m._flush()
157 self.assertRepr(m._statements, """
158 (
159 (switch (cat (sig s1))
160 (case 1 (eq (sig c1) (const 1'd1)))
161 )
162 )
163 """)
164
165 def test_If_Elif(self):
166 m = Module()
167 with m.If(self.s1):
168 m.d.comb += self.c1.eq(1)
169 with m.Elif(self.s2):
170 m.d.sync += self.c2.eq(0)
171 m._flush()
172 self.assertRepr(m._statements, """
173 (
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)))
177 )
178 )
179 """)
180
181 def test_If_Elif_Else(self):
182 m = Module()
183 with m.If(self.s1):
184 m.d.comb += self.c1.eq(1)
185 with m.Elif(self.s2):
186 m.d.sync += self.c2.eq(0)
187 with m.Else():
188 m.d.comb += self.c3.eq(1)
189 m._flush()
190 self.assertRepr(m._statements, """
191 (
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)))
196 )
197 )
198 """)
199
200 def test_If_If(self):
201 m = Module()
202 with m.If(self.s1):
203 m.d.comb += self.c1.eq(1)
204 with m.If(self.s2):
205 m.d.comb += self.c2.eq(1)
206 m._flush()
207 self.assertRepr(m._statements, """
208 (
209 (switch (cat (sig s1))
210 (case 1 (eq (sig c1) (const 1'd1)))
211 )
212 (switch (cat (sig s2))
213 (case 1 (eq (sig c2) (const 1'd1)))
214 )
215 )
216 """)
217
218 def test_If_nested_If(self):
219 m = Module()
220 with m.If(self.s1):
221 m.d.comb += self.c1.eq(1)
222 with m.If(self.s2):
223 m.d.comb += self.c2.eq(1)
224 m._flush()
225 self.assertRepr(m._statements, """
226 (
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)))
231 )
232 )
233 )
234 )
235 """)
236
237 def test_If_dangling_Else(self):
238 m = Module()
239 with m.If(self.s1):
240 m.d.comb += self.c1.eq(1)
241 with m.If(self.s2):
242 m.d.comb += self.c2.eq(1)
243 with m.Else():
244 m.d.comb += self.c3.eq(1)
245 m._flush()
246 self.assertRepr(m._statements, """
247 (
248 (switch (cat (sig s1))
249 (case 1
250 (eq (sig c1) (const 1'd1))
251 (switch (cat (sig s2))
252 (case 1 (eq (sig c2) (const 1'd1)))
253 )
254 )
255 (default
256 (eq (sig c3) (const 1'd1))
257 )
258 )
259 )
260 """)
261
262 def test_Elif_wrong(self):
263 m = Module()
264 with self.assertRaisesRegex(SyntaxError,
265 r"^Elif without preceding If$"):
266 with m.Elif(self.s2):
267 pass
268
269 def test_Elif_wrong_nested(self):
270 m = Module()
271 with m.If(self.s1):
272 with self.assertRaisesRegex(SyntaxError,
273 r"^Elif without preceding If$"):
274 with m.Elif(self.s2):
275 pass
276
277 def test_Else_wrong(self):
278 m = Module()
279 with self.assertRaisesRegex(SyntaxError,
280 r"^Else without preceding If\/Elif$"):
281 with m.Else():
282 pass
283
284 def test_If_wide(self):
285 m = Module()
286 with m.If(self.w1):
287 m.d.comb += self.c1.eq(1)
288 m._flush()
289 self.assertRepr(m._statements, """
290 (
291 (switch (cat (b (sig w1)))
292 (case 1 (eq (sig c1) (const 1'd1)))
293 )
294 )
295 """)
296
297 def test_If_signed_suspicious(self):
298 m = Module()
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\(\)\)`\.\)$")):
304 with m.If(~True):
305 pass
306
307 def test_Elif_signed_suspicious(self):
308 m = Module()
309 with m.If(0):
310 pass
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\(\)\)`\.\)$")):
316 with m.Elif(~True):
317 pass
318
319 def test_if_If_Elif_Else(self):
320 m = Module()
321 with self.assertRaisesRegex(SyntaxError,
322 r"^`if m\.If\(\.\.\.\):` does not work; use `with m\.If\(\.\.\.\)`$"):
323 if m.If(0):
324 pass
325 with m.If(0):
326 pass
327 with self.assertRaisesRegex(SyntaxError,
328 r"^`if m\.Elif\(\.\.\.\):` does not work; use `with m\.Elif\(\.\.\.\)`$"):
329 if m.Elif(0):
330 pass
331 with self.assertRaisesRegex(SyntaxError,
332 r"^`if m\.Else\(\.\.\.\):` does not work; use `with m\.Else\(\.\.\.\)`$"):
333 if m.Else():
334 pass
335
336 def test_Switch(self):
337 m = Module()
338 with m.Switch(self.w1):
339 with m.Case(3):
340 m.d.comb += self.c1.eq(1)
341 with m.Case("11--"):
342 m.d.comb += self.c2.eq(1)
343 with m.Case("1 0--"):
344 m.d.comb += self.c2.eq(1)
345 m._flush()
346 self.assertRepr(m._statements, """
347 (
348 (switch (sig w1)
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)))
352 )
353 )
354 """)
355
356 def test_Switch_default_Case(self):
357 m = Module()
358 with m.Switch(self.w1):
359 with m.Case(3):
360 m.d.comb += self.c1.eq(1)
361 with m.Case():
362 m.d.comb += self.c2.eq(1)
363 m._flush()
364 self.assertRepr(m._statements, """
365 (
366 (switch (sig w1)
367 (case 0011 (eq (sig c1) (const 1'd1)))
368 (default (eq (sig c2) (const 1'd1)))
369 )
370 )
371 """)
372
373 def test_Switch_default_Default(self):
374 m = Module()
375 with m.Switch(self.w1):
376 with m.Case(3):
377 m.d.comb += self.c1.eq(1)
378 with m.Default():
379 m.d.comb += self.c2.eq(1)
380 m._flush()
381 self.assertRepr(m._statements, """
382 (
383 (switch (sig w1)
384 (case 0011 (eq (sig c1) (const 1'd1)))
385 (default (eq (sig c2) (const 1'd1)))
386 )
387 )
388 """)
389
390 def test_Switch_const_test(self):
391 m = Module()
392 with m.Switch(1):
393 with m.Case(1):
394 m.d.comb += self.c1.eq(1)
395 m._flush()
396 self.assertRepr(m._statements, """
397 (
398 (switch (const 1'd1)
399 (case 1 (eq (sig c1) (const 1'd1)))
400 )
401 )
402 """)
403
404 def test_Switch_enum(self):
405 class Color(Enum):
406 RED = 1
407 BLUE = 2
408 m = Module()
409 se = Signal(Color)
410 with m.Switch(se):
411 with m.Case(Color.RED):
412 m.d.comb += self.c1.eq(1)
413 self.assertRepr(m._statements, """
414 (
415 (switch (sig se)
416 (case 01 (eq (sig c1) (const 1'd1)))
417 )
418 )
419 """)
420
421 def test_Case_width_wrong(self):
422 class Color(Enum):
423 RED = 0b10101010
424 m = Module()
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\)$"):
428 with m.Case("--"):
429 pass
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):
434 pass
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):
439 pass
440 self.assertRepr(m._statements, """
441 (
442 (switch (sig w1) )
443 )
444 """)
445
446 def test_Case_bits_wrong(self):
447 m = Module()
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$")):
452 with m.Case("abc"):
453 pass
454
455 def test_Case_pattern_wrong(self):
456 m = Module()
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$"):
460 with m.Case(1.0):
461 pass
462
463 def test_Case_outside_Switch_wrong(self):
464 m = Module()
465 with self.assertRaisesRegex(SyntaxError,
466 r"^Case is not permitted outside of Switch$"):
467 with m.Case():
468 pass
469
470 def test_If_inside_Switch_wrong(self):
471 m = Module()
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$")):
476 with m.If(self.s2):
477 pass
478
479 def test_FSM_basic(self):
480 a = Signal()
481 b = Signal()
482 c = Signal()
483 m = Module()
484 with m.FSM():
485 with m.State("FIRST"):
486 m.d.comb += a.eq(1)
487 m.next = "SECOND"
488 with m.State("SECOND"):
489 m.d.sync += b.eq(~b)
490 with m.If(c):
491 m.next = "FIRST"
492 m._flush()
493 self.assertRepr(m._statements, """
494 (
495 (switch (sig fsm_state)
496 (case 0
497 (eq (sig a) (const 1'd1))
498 (eq (sig fsm_state) (const 1'd1))
499 )
500 (case 1
501 (eq (sig b) (~ (sig b)))
502 (switch (cat (sig c))
503 (case 1
504 (eq (sig fsm_state) (const 1'd0)))
505 )
506 )
507 )
508 )
509 """)
510 self.assertEqual({repr(k): v for k, v in m._driving.items()}, {
511 "(sig a)": None,
512 "(sig fsm_state)": "sync",
513 "(sig b)": "sync",
514 })
515
516 frag = m.elaborate(platform=None)
517 fsm = frag.find_generated("fsm")
518 self.assertIsInstance(fsm.state, Signal)
519 self.assertEqual(fsm.encoding, OrderedDict({
520 "FIRST": 0,
521 "SECOND": 1,
522 }))
523 self.assertEqual(fsm.decoding, OrderedDict({
524 0: "FIRST",
525 1: "SECOND"
526 }))
527
528 def test_FSM_reset(self):
529 a = Signal()
530 m = Module()
531 with m.FSM(reset="SECOND"):
532 with m.State("FIRST"):
533 m.d.comb += a.eq(0)
534 m.next = "SECOND"
535 with m.State("SECOND"):
536 m.next = "FIRST"
537 m._flush()
538 self.assertRepr(m._statements, """
539 (
540 (switch (sig fsm_state)
541 (case 0
542 (eq (sig a) (const 1'd0))
543 (eq (sig fsm_state) (const 1'd1))
544 )
545 (case 1
546 (eq (sig fsm_state) (const 1'd0))
547 )
548 )
549 )
550 """)
551
552 def test_FSM_ongoing(self):
553 a = Signal()
554 b = Signal()
555 m = Module()
556 with m.FSM() as fsm:
557 m.d.comb += b.eq(fsm.ongoing("SECOND"))
558 with m.State("FIRST"):
559 pass
560 m.d.comb += a.eq(fsm.ongoing("FIRST"))
561 with m.State("SECOND"):
562 pass
563 m._flush()
564 self.assertEqual(m._generated["fsm"].state.reset, 1)
565 self.maxDiff = 10000
566 self.assertRepr(m._statements, """
567 (
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)
571 (case 1
572 )
573 (case 0
574 )
575 )
576 )
577 """)
578
579 def test_FSM_empty(self):
580 m = Module()
581 with m.FSM():
582 pass
583 self.assertRepr(m._statements, """
584 ()
585 """)
586
587 def test_FSM_wrong_domain(self):
588 m = Module()
589 with self.assertRaisesRegex(ValueError,
590 r"^FSM may not be driven by the 'comb' domain$"):
591 with m.FSM(domain="comb"):
592 pass
593
594 def test_FSM_wrong_undefined(self):
595 m = Module()
596 with self.assertRaisesRegex(NameError,
597 r"^FSM state 'FOO' is referenced but not defined$"):
598 with m.FSM() as fsm:
599 fsm.ongoing("FOO")
600
601 def test_FSM_wrong_redefined(self):
602 m = Module()
603 with m.FSM():
604 with m.State("FOO"):
605 pass
606 with self.assertRaisesRegex(NameError,
607 r"^FSM state 'FOO' is already defined$"):
608 with m.State("FOO"):
609 pass
610
611 def test_FSM_wrong_next(self):
612 m = Module()
613 with self.assertRaisesRegex(SyntaxError,
614 r"^Only assignment to `m\.next` is permitted$"):
615 m.next
616 with self.assertRaisesRegex(SyntaxError,
617 r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
618 m.next = "FOO"
619 with self.assertRaisesRegex(SyntaxError,
620 r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
621 with m.FSM():
622 m.next = "FOO"
623
624 def test_If_inside_FSM_wrong(self):
625 m = Module()
626 with m.FSM():
627 with m.State("FOO"):
628 pass
629 with self.assertRaisesRegex(SyntaxError,
630 (r"^If is not permitted directly inside of FSM; "
631 r"it is permitted inside of FSM State$")):
632 with m.If(self.s2):
633 pass
634
635 def test_auto_pop_ctrl(self):
636 m = Module()
637 with m.If(self.w1):
638 m.d.comb += self.c1.eq(1)
639 m.d.comb += self.c2.eq(1)
640 self.assertRepr(m._statements, """
641 (
642 (switch (cat (b (sig w1)))
643 (case 1 (eq (sig c1) (const 1'd1)))
644 )
645 (eq (sig c2) (const 1'd1))
646 )
647 """)
648
649 def test_submodule_anon(self):
650 m1 = Module()
651 m2 = Module()
652 m1.submodules += m2
653 self.assertEqual(m1._anon_submodules, [m2])
654 self.assertEqual(m1._named_submodules, {})
655
656 def test_submodule_anon_multi(self):
657 m1 = Module()
658 m2 = Module()
659 m3 = Module()
660 m1.submodules += m2, m3
661 self.assertEqual(m1._anon_submodules, [m2, m3])
662 self.assertEqual(m1._named_submodules, {})
663
664 def test_submodule_named(self):
665 m1 = Module()
666 m2 = Module()
667 m1.submodules.foo = m2
668 self.assertEqual(m1._anon_submodules, [])
669 self.assertEqual(m1._named_submodules, {"foo": m2})
670
671 def test_submodule_named_index(self):
672 m1 = Module()
673 m2 = Module()
674 m1.submodules["foo"] = m2
675 self.assertEqual(m1._anon_submodules, [])
676 self.assertEqual(m1._named_submodules, {"foo": m2})
677
678 def test_submodule_wrong(self):
679 m = Module()
680 with self.assertRaisesRegex(TypeError,
681 r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
682 m.submodules.foo = 1
683 with self.assertRaisesRegex(TypeError,
684 r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
685 m.submodules += 1
686
687 def test_submodule_named_conflict(self):
688 m1 = Module()
689 m2 = Module()
690 m1.submodules.foo = m2
691 with self.assertRaisesRegex(NameError, r"^Submodule named 'foo' already exists$"):
692 m1.submodules.foo = m2
693
694 def test_submodule_get(self):
695 m1 = Module()
696 m2 = Module()
697 m1.submodules.foo = m2
698 m3 = m1.submodules.foo
699 self.assertEqual(m2, m3)
700
701 def test_submodule_get_index(self):
702 m1 = Module()
703 m2 = Module()
704 m1.submodules["foo"] = m2
705 m3 = m1.submodules["foo"]
706 self.assertEqual(m2, m3)
707
708 def test_submodule_get_unset(self):
709 m1 = Module()
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"]
714
715 def test_domain_named_implicit(self):
716 m = Module()
717 m.domains += ClockDomain("sync")
718 self.assertEqual(len(m._domains), 1)
719
720 def test_domain_named_explicit(self):
721 m = Module()
722 m.domains.foo = ClockDomain()
723 self.assertEqual(len(m._domains), 1)
724 self.assertEqual(m._domains["foo"].name, "foo")
725
726 def test_domain_add_wrong(self):
727 m = Module()
728 with self.assertRaisesRegex(TypeError,
729 r"^Only clock domains may be added to `m\.domains`, not 1$"):
730 m.domains.foo = 1
731 with self.assertRaisesRegex(TypeError,
732 r"^Only clock domains may be added to `m\.domains`, not 1$"):
733 m.domains += 1
734
735 def test_domain_add_wrong_name(self):
736 m = Module()
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")
740
741 def test_domain_add_wrong_duplicate(self):
742 m = Module()
743 m.domains += ClockDomain("foo")
744 with self.assertRaisesRegex(NameError,
745 r"^Clock domain named 'foo' already exists$"):
746 m.domains += ClockDomain("foo")
747
748 def test_lower(self):
749 m1 = Module()
750 m1.d.comb += self.c1.eq(self.s1)
751 m2 = Module()
752 m2.d.comb += self.c2.eq(self.s2)
753 m2.d.sync += self.c3.eq(self.s3)
754 m1.submodules.foo = m2
755
756 f1 = m1.elaborate(platform=None)
757 self.assertRepr(f1.statements, """
758 (
759 (eq (sig c1) (sig s1))
760 )
761 """)
762 self.assertEqual(f1.drivers, {
763 None: SignalSet((self.c1,))
764 })
765 self.assertEqual(len(f1.subfragments), 1)
766 (f2, f2_name), = f1.subfragments
767 self.assertEqual(f2_name, "foo")
768 self.assertRepr(f2.statements, """
769 (
770 (eq (sig c2) (sig s2))
771 (eq (sig c3) (sig s3))
772 )
773 """)
774 self.assertEqual(f2.drivers, {
775 None: SignalSet((self.c2,)),
776 "sync": SignalSet((self.c3,))
777 })
778 self.assertEqual(len(f2.subfragments), 0)