tests: move out of the main package.
[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_Else_wrong(self):
270 m = Module()
271 with self.assertRaisesRegex(SyntaxError,
272 r"^Else without preceding If\/Elif$"):
273 with m.Else():
274 pass
275
276 def test_If_wide(self):
277 m = Module()
278 with m.If(self.w1):
279 m.d.comb += self.c1.eq(1)
280 m._flush()
281 self.assertRepr(m._statements, """
282 (
283 (switch (cat (b (sig w1)))
284 (case 1 (eq (sig c1) (const 1'd1)))
285 )
286 )
287 """)
288
289 def test_If_signed_suspicious(self):
290 m = Module()
291 with self.assertWarnsRegex(SyntaxWarning,
292 (r"^Signed values in If\/Elif conditions usually result from inverting Python "
293 r"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
294 r"`not flag`\. \(If this is a false positive, silence this warning with "
295 r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")):
296 with m.If(~True):
297 pass
298
299 def test_Elif_signed_suspicious(self):
300 m = Module()
301 with m.If(0):
302 pass
303 with self.assertWarnsRegex(SyntaxWarning,
304 (r"^Signed values in If\/Elif conditions usually result from inverting Python "
305 r"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
306 r"`not flag`\. \(If this is a false positive, silence this warning with "
307 r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")):
308 with m.Elif(~True):
309 pass
310
311 def test_if_If_Elif_Else(self):
312 m = Module()
313 with self.assertRaisesRegex(SyntaxError,
314 r"^`if m\.If\(\.\.\.\):` does not work; use `with m\.If\(\.\.\.\)`$"):
315 if m.If(0):
316 pass
317 with m.If(0):
318 pass
319 with self.assertRaisesRegex(SyntaxError,
320 r"^`if m\.Elif\(\.\.\.\):` does not work; use `with m\.Elif\(\.\.\.\)`$"):
321 if m.Elif(0):
322 pass
323 with self.assertRaisesRegex(SyntaxError,
324 r"^`if m\.Else\(\.\.\.\):` does not work; use `with m\.Else\(\.\.\.\)`$"):
325 if m.Else():
326 pass
327
328 def test_Switch(self):
329 m = Module()
330 with m.Switch(self.w1):
331 with m.Case(3):
332 m.d.comb += self.c1.eq(1)
333 with m.Case("11--"):
334 m.d.comb += self.c2.eq(1)
335 with m.Case("1 0--"):
336 m.d.comb += self.c2.eq(1)
337 m._flush()
338 self.assertRepr(m._statements, """
339 (
340 (switch (sig w1)
341 (case 0011 (eq (sig c1) (const 1'd1)))
342 (case 11-- (eq (sig c2) (const 1'd1)))
343 (case 10-- (eq (sig c2) (const 1'd1)))
344 )
345 )
346 """)
347
348 def test_Switch_default_Case(self):
349 m = Module()
350 with m.Switch(self.w1):
351 with m.Case(3):
352 m.d.comb += self.c1.eq(1)
353 with m.Case():
354 m.d.comb += self.c2.eq(1)
355 m._flush()
356 self.assertRepr(m._statements, """
357 (
358 (switch (sig w1)
359 (case 0011 (eq (sig c1) (const 1'd1)))
360 (default (eq (sig c2) (const 1'd1)))
361 )
362 )
363 """)
364
365 def test_Switch_default_Default(self):
366 m = Module()
367 with m.Switch(self.w1):
368 with m.Case(3):
369 m.d.comb += self.c1.eq(1)
370 with m.Default():
371 m.d.comb += self.c2.eq(1)
372 m._flush()
373 self.assertRepr(m._statements, """
374 (
375 (switch (sig w1)
376 (case 0011 (eq (sig c1) (const 1'd1)))
377 (default (eq (sig c2) (const 1'd1)))
378 )
379 )
380 """)
381
382 def test_Switch_const_test(self):
383 m = Module()
384 with m.Switch(1):
385 with m.Case(1):
386 m.d.comb += self.c1.eq(1)
387 m._flush()
388 self.assertRepr(m._statements, """
389 (
390 (switch (const 1'd1)
391 (case 1 (eq (sig c1) (const 1'd1)))
392 )
393 )
394 """)
395
396 def test_Switch_enum(self):
397 class Color(Enum):
398 RED = 1
399 BLUE = 2
400 m = Module()
401 se = Signal(Color)
402 with m.Switch(se):
403 with m.Case(Color.RED):
404 m.d.comb += self.c1.eq(1)
405 self.assertRepr(m._statements, """
406 (
407 (switch (sig se)
408 (case 01 (eq (sig c1) (const 1'd1)))
409 )
410 )
411 """)
412
413 def test_Case_width_wrong(self):
414 class Color(Enum):
415 RED = 0b10101010
416 m = Module()
417 with m.Switch(self.w1):
418 with self.assertRaisesRegex(SyntaxError,
419 r"^Case pattern '--' must have the same width as switch value \(which is 4\)$"):
420 with m.Case("--"):
421 pass
422 with self.assertWarnsRegex(SyntaxWarning,
423 (r"^Case pattern '10110' is wider than switch value \(which has width 4\); "
424 r"comparison will never be true$")):
425 with m.Case(0b10110):
426 pass
427 with self.assertWarnsRegex(SyntaxWarning,
428 (r"^Case pattern '10101010' \(Color\.RED\) is wider than switch value "
429 r"\(which has width 4\); comparison will never be true$")):
430 with m.Case(Color.RED):
431 pass
432 self.assertRepr(m._statements, """
433 (
434 (switch (sig w1) )
435 )
436 """)
437
438 def test_Case_bits_wrong(self):
439 m = Module()
440 with m.Switch(self.w1):
441 with self.assertRaisesRegex(SyntaxError,
442 (r"^Case pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, "
443 r"and may include whitespace$")):
444 with m.Case("abc"):
445 pass
446
447 def test_Case_pattern_wrong(self):
448 m = Module()
449 with m.Switch(self.w1):
450 with self.assertRaisesRegex(SyntaxError,
451 r"^Case pattern must be an integer, a string, or an enumeration, not 1\.0$"):
452 with m.Case(1.0):
453 pass
454
455 def test_Case_outside_Switch_wrong(self):
456 m = Module()
457 with self.assertRaisesRegex(SyntaxError,
458 r"^Case is not permitted outside of Switch$"):
459 with m.Case():
460 pass
461
462 def test_If_inside_Switch_wrong(self):
463 m = Module()
464 with m.Switch(self.s1):
465 with self.assertRaisesRegex(SyntaxError,
466 (r"^If is not permitted directly inside of Switch; "
467 r"it is permitted inside of Switch Case$")):
468 with m.If(self.s2):
469 pass
470
471 def test_FSM_basic(self):
472 a = Signal()
473 b = Signal()
474 c = Signal()
475 m = Module()
476 with m.FSM():
477 with m.State("FIRST"):
478 m.d.comb += a.eq(1)
479 m.next = "SECOND"
480 with m.State("SECOND"):
481 m.d.sync += b.eq(~b)
482 with m.If(c):
483 m.next = "FIRST"
484 m._flush()
485 self.assertRepr(m._statements, """
486 (
487 (switch (sig fsm_state)
488 (case 0
489 (eq (sig a) (const 1'd1))
490 (eq (sig fsm_state) (const 1'd1))
491 )
492 (case 1
493 (eq (sig b) (~ (sig b)))
494 (switch (cat (sig c))
495 (case 1
496 (eq (sig fsm_state) (const 1'd0)))
497 )
498 )
499 )
500 )
501 """)
502 self.assertEqual({repr(k): v for k, v in m._driving.items()}, {
503 "(sig a)": None,
504 "(sig fsm_state)": "sync",
505 "(sig b)": "sync",
506 })
507
508 frag = m.elaborate(platform=None)
509 fsm = frag.find_generated("fsm")
510 self.assertIsInstance(fsm.state, Signal)
511 self.assertEqual(fsm.encoding, OrderedDict({
512 "FIRST": 0,
513 "SECOND": 1,
514 }))
515 self.assertEqual(fsm.decoding, OrderedDict({
516 0: "FIRST",
517 1: "SECOND"
518 }))
519
520 def test_FSM_reset(self):
521 a = Signal()
522 m = Module()
523 with m.FSM(reset="SECOND"):
524 with m.State("FIRST"):
525 m.d.comb += a.eq(0)
526 m.next = "SECOND"
527 with m.State("SECOND"):
528 m.next = "FIRST"
529 m._flush()
530 self.assertRepr(m._statements, """
531 (
532 (switch (sig fsm_state)
533 (case 0
534 (eq (sig a) (const 1'd0))
535 (eq (sig fsm_state) (const 1'd1))
536 )
537 (case 1
538 (eq (sig fsm_state) (const 1'd0))
539 )
540 )
541 )
542 """)
543
544 def test_FSM_ongoing(self):
545 a = Signal()
546 b = Signal()
547 m = Module()
548 with m.FSM() as fsm:
549 m.d.comb += b.eq(fsm.ongoing("SECOND"))
550 with m.State("FIRST"):
551 pass
552 m.d.comb += a.eq(fsm.ongoing("FIRST"))
553 with m.State("SECOND"):
554 pass
555 m._flush()
556 self.assertEqual(m._generated["fsm"].state.reset, 1)
557 self.maxDiff = 10000
558 self.assertRepr(m._statements, """
559 (
560 (eq (sig b) (== (sig fsm_state) (const 1'd0)))
561 (eq (sig a) (== (sig fsm_state) (const 1'd1)))
562 (switch (sig fsm_state)
563 (case 1
564 )
565 (case 0
566 )
567 )
568 )
569 """)
570
571 def test_FSM_empty(self):
572 m = Module()
573 with m.FSM():
574 pass
575 self.assertRepr(m._statements, """
576 ()
577 """)
578
579 def test_FSM_wrong_domain(self):
580 m = Module()
581 with self.assertRaisesRegex(ValueError,
582 r"^FSM may not be driven by the 'comb' domain$"):
583 with m.FSM(domain="comb"):
584 pass
585
586 def test_FSM_wrong_undefined(self):
587 m = Module()
588 with self.assertRaisesRegex(NameError,
589 r"^FSM state 'FOO' is referenced but not defined$"):
590 with m.FSM() as fsm:
591 fsm.ongoing("FOO")
592
593 def test_FSM_wrong_redefined(self):
594 m = Module()
595 with m.FSM():
596 with m.State("FOO"):
597 pass
598 with self.assertRaisesRegex(NameError,
599 r"^FSM state 'FOO' is already defined$"):
600 with m.State("FOO"):
601 pass
602
603 def test_FSM_wrong_next(self):
604 m = Module()
605 with self.assertRaisesRegex(SyntaxError,
606 r"^Only assignment to `m\.next` is permitted$"):
607 m.next
608 with self.assertRaisesRegex(SyntaxError,
609 r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
610 m.next = "FOO"
611 with self.assertRaisesRegex(SyntaxError,
612 r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
613 with m.FSM():
614 m.next = "FOO"
615
616 def test_If_inside_FSM_wrong(self):
617 m = Module()
618 with m.FSM():
619 with m.State("FOO"):
620 pass
621 with self.assertRaisesRegex(SyntaxError,
622 (r"^If is not permitted directly inside of FSM; "
623 r"it is permitted inside of FSM State$")):
624 with m.If(self.s2):
625 pass
626
627 def test_auto_pop_ctrl(self):
628 m = Module()
629 with m.If(self.w1):
630 m.d.comb += self.c1.eq(1)
631 m.d.comb += self.c2.eq(1)
632 self.assertRepr(m._statements, """
633 (
634 (switch (cat (b (sig w1)))
635 (case 1 (eq (sig c1) (const 1'd1)))
636 )
637 (eq (sig c2) (const 1'd1))
638 )
639 """)
640
641 def test_submodule_anon(self):
642 m1 = Module()
643 m2 = Module()
644 m1.submodules += m2
645 self.assertEqual(m1._anon_submodules, [m2])
646 self.assertEqual(m1._named_submodules, {})
647
648 def test_submodule_anon_multi(self):
649 m1 = Module()
650 m2 = Module()
651 m3 = Module()
652 m1.submodules += m2, m3
653 self.assertEqual(m1._anon_submodules, [m2, m3])
654 self.assertEqual(m1._named_submodules, {})
655
656 def test_submodule_named(self):
657 m1 = Module()
658 m2 = Module()
659 m1.submodules.foo = m2
660 self.assertEqual(m1._anon_submodules, [])
661 self.assertEqual(m1._named_submodules, {"foo": m2})
662
663 def test_submodule_named_index(self):
664 m1 = Module()
665 m2 = Module()
666 m1.submodules["foo"] = m2
667 self.assertEqual(m1._anon_submodules, [])
668 self.assertEqual(m1._named_submodules, {"foo": m2})
669
670 def test_submodule_wrong(self):
671 m = Module()
672 with self.assertRaisesRegex(TypeError,
673 r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
674 m.submodules.foo = 1
675 with self.assertRaisesRegex(TypeError,
676 r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
677 m.submodules += 1
678
679 def test_submodule_named_conflict(self):
680 m1 = Module()
681 m2 = Module()
682 m1.submodules.foo = m2
683 with self.assertRaisesRegex(NameError, r"^Submodule named 'foo' already exists$"):
684 m1.submodules.foo = m2
685
686 def test_submodule_get(self):
687 m1 = Module()
688 m2 = Module()
689 m1.submodules.foo = m2
690 m3 = m1.submodules.foo
691 self.assertEqual(m2, m3)
692
693 def test_submodule_get_index(self):
694 m1 = Module()
695 m2 = Module()
696 m1.submodules["foo"] = m2
697 m3 = m1.submodules["foo"]
698 self.assertEqual(m2, m3)
699
700 def test_submodule_get_unset(self):
701 m1 = Module()
702 with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"):
703 m2 = m1.submodules.foo
704 with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"):
705 m2 = m1.submodules["foo"]
706
707 def test_domain_named_implicit(self):
708 m = Module()
709 m.domains += ClockDomain("sync")
710 self.assertEqual(len(m._domains), 1)
711
712 def test_domain_named_explicit(self):
713 m = Module()
714 m.domains.foo = ClockDomain()
715 self.assertEqual(len(m._domains), 1)
716 self.assertEqual(m._domains["foo"].name, "foo")
717
718 def test_domain_add_wrong(self):
719 m = Module()
720 with self.assertRaisesRegex(TypeError,
721 r"^Only clock domains may be added to `m\.domains`, not 1$"):
722 m.domains.foo = 1
723 with self.assertRaisesRegex(TypeError,
724 r"^Only clock domains may be added to `m\.domains`, not 1$"):
725 m.domains += 1
726
727 def test_domain_add_wrong_name(self):
728 m = Module()
729 with self.assertRaisesRegex(NameError,
730 r"^Clock domain name 'bar' must match name in `m\.domains\.foo \+= \.\.\.` syntax$"):
731 m.domains.foo = ClockDomain("bar")
732
733 def test_domain_add_wrong_duplicate(self):
734 m = Module()
735 m.domains += ClockDomain("foo")
736 with self.assertRaisesRegex(NameError,
737 r"^Clock domain named 'foo' already exists$"):
738 m.domains += ClockDomain("foo")
739
740 def test_lower(self):
741 m1 = Module()
742 m1.d.comb += self.c1.eq(self.s1)
743 m2 = Module()
744 m2.d.comb += self.c2.eq(self.s2)
745 m2.d.sync += self.c3.eq(self.s3)
746 m1.submodules.foo = m2
747
748 f1 = m1.elaborate(platform=None)
749 self.assertRepr(f1.statements, """
750 (
751 (eq (sig c1) (sig s1))
752 )
753 """)
754 self.assertEqual(f1.drivers, {
755 None: SignalSet((self.c1,))
756 })
757 self.assertEqual(len(f1.subfragments), 1)
758 (f2, f2_name), = f1.subfragments
759 self.assertEqual(f2_name, "foo")
760 self.assertRepr(f2.statements, """
761 (
762 (eq (sig c2) (sig s2))
763 (eq (sig c3) (sig s3))
764 )
765 """)
766 self.assertEqual(f2.drivers, {
767 None: SignalSet((self.c2,)),
768 "sync": SignalSet((self.c3,))
769 })
770 self.assertEqual(len(f2.subfragments), 0)