hdl.ir: warn if .elaborate() returns None.
[nmigen.git] / nmigen / test / test_hdl_ir.py
1 from collections import OrderedDict
2
3 from ..hdl.ast import *
4 from ..hdl.cd import *
5 from ..hdl.ir import *
6 from ..hdl.mem import *
7 from .tools import *
8
9
10 class BadElaboratable(Elaboratable):
11 def elaborate(self, platform):
12 return
13
14
15 class FragmentGetTestCase(FHDLTestCase):
16 def test_get_wrong(self):
17 with self.assertRaises(AttributeError,
18 msg="Object 'None' cannot be elaborated"):
19 Fragment.get(None, platform=None)
20
21 with self.assertRaises(AttributeError,
22 msg="Object 'None' cannot be elaborated"):
23 Fragment.get(BadElaboratable(), platform=None)
24
25
26 class FragmentGeneratedTestCase(FHDLTestCase):
27 def test_find_subfragment(self):
28 f1 = Fragment()
29 f2 = Fragment()
30 f1.add_subfragment(f2, "f2")
31
32 self.assertEqual(f1.find_subfragment(0), f2)
33 self.assertEqual(f1.find_subfragment("f2"), f2)
34
35 def test_find_subfragment_wrong(self):
36 f1 = Fragment()
37 f2 = Fragment()
38 f1.add_subfragment(f2, "f2")
39
40 with self.assertRaises(NameError,
41 msg="No subfragment at index #1"):
42 f1.find_subfragment(1)
43 with self.assertRaises(NameError,
44 msg="No subfragment with name 'fx'"):
45 f1.find_subfragment("fx")
46
47 def test_find_generated(self):
48 f1 = Fragment()
49 f2 = Fragment()
50 f2.generated["sig"] = sig = Signal()
51 f1.add_subfragment(f2, "f2")
52
53 self.assertEqual(SignalKey(f1.find_generated("f2", "sig")),
54 SignalKey(sig))
55
56
57 class FragmentDriversTestCase(FHDLTestCase):
58 def test_empty(self):
59 f = Fragment()
60 self.assertEqual(list(f.iter_comb()), [])
61 self.assertEqual(list(f.iter_sync()), [])
62
63
64 class FragmentPortsTestCase(FHDLTestCase):
65 def setUp(self):
66 self.s1 = Signal()
67 self.s2 = Signal()
68 self.s3 = Signal()
69 self.c1 = Signal()
70 self.c2 = Signal()
71 self.c3 = Signal()
72
73 def test_empty(self):
74 f = Fragment()
75 self.assertEqual(list(f.iter_ports()), [])
76
77 f._propagate_ports(ports=(), all_undef_as_ports=True)
78 self.assertEqual(f.ports, SignalDict([]))
79
80 def test_iter_signals(self):
81 f = Fragment()
82 f.add_ports(self.s1, self.s2, dir="io")
83 self.assertEqual(SignalSet((self.s1, self.s2)), f.iter_signals())
84
85 def test_self_contained(self):
86 f = Fragment()
87 f.add_statements(
88 self.c1.eq(self.s1),
89 self.s1.eq(self.c1)
90 )
91
92 f._propagate_ports(ports=(), all_undef_as_ports=True)
93 self.assertEqual(f.ports, SignalDict([]))
94
95 def test_infer_input(self):
96 f = Fragment()
97 f.add_statements(
98 self.c1.eq(self.s1)
99 )
100
101 f._propagate_ports(ports=(), all_undef_as_ports=True)
102 self.assertEqual(f.ports, SignalDict([
103 (self.s1, "i")
104 ]))
105
106 def test_request_output(self):
107 f = Fragment()
108 f.add_statements(
109 self.c1.eq(self.s1)
110 )
111
112 f._propagate_ports(ports=(self.c1,), all_undef_as_ports=True)
113 self.assertEqual(f.ports, SignalDict([
114 (self.s1, "i"),
115 (self.c1, "o")
116 ]))
117
118 def test_input_in_subfragment(self):
119 f1 = Fragment()
120 f1.add_statements(
121 self.c1.eq(self.s1)
122 )
123 f2 = Fragment()
124 f2.add_statements(
125 self.s1.eq(0)
126 )
127 f1.add_subfragment(f2)
128 f1._propagate_ports(ports=(), all_undef_as_ports=True)
129 self.assertEqual(f1.ports, SignalDict())
130 self.assertEqual(f2.ports, SignalDict([
131 (self.s1, "o"),
132 ]))
133
134 def test_input_only_in_subfragment(self):
135 f1 = Fragment()
136 f2 = Fragment()
137 f2.add_statements(
138 self.c1.eq(self.s1)
139 )
140 f1.add_subfragment(f2)
141 f1._propagate_ports(ports=(), all_undef_as_ports=True)
142 self.assertEqual(f1.ports, SignalDict([
143 (self.s1, "i"),
144 ]))
145 self.assertEqual(f2.ports, SignalDict([
146 (self.s1, "i"),
147 ]))
148
149 def test_output_from_subfragment(self):
150 f1 = Fragment()
151 f1.add_statements(
152 self.c1.eq(0)
153 )
154 f2 = Fragment()
155 f2.add_statements(
156 self.c2.eq(1)
157 )
158 f1.add_subfragment(f2)
159
160 f1._propagate_ports(ports=(self.c2,), all_undef_as_ports=True)
161 self.assertEqual(f1.ports, SignalDict([
162 (self.c2, "o"),
163 ]))
164 self.assertEqual(f2.ports, SignalDict([
165 (self.c2, "o"),
166 ]))
167
168 def test_output_from_subfragment_2(self):
169 f1 = Fragment()
170 f1.add_statements(
171 self.c1.eq(self.s1)
172 )
173 f2 = Fragment()
174 f2.add_statements(
175 self.c2.eq(self.s1)
176 )
177 f1.add_subfragment(f2)
178 f3 = Fragment()
179 f3.add_statements(
180 self.s1.eq(0)
181 )
182 f2.add_subfragment(f3)
183
184 f1._propagate_ports(ports=(), all_undef_as_ports=True)
185 self.assertEqual(f2.ports, SignalDict([
186 (self.s1, "o"),
187 ]))
188
189 def test_input_output_sibling(self):
190 f1 = Fragment()
191 f2 = Fragment()
192 f2.add_statements(
193 self.c1.eq(self.c2)
194 )
195 f1.add_subfragment(f2)
196 f3 = Fragment()
197 f3.add_statements(
198 self.c2.eq(0)
199 )
200 f3.add_driver(self.c2)
201 f1.add_subfragment(f3)
202
203 f1._propagate_ports(ports=(), all_undef_as_ports=True)
204 self.assertEqual(f1.ports, SignalDict())
205
206 def test_output_input_sibling(self):
207 f1 = Fragment()
208 f2 = Fragment()
209 f2.add_statements(
210 self.c2.eq(0)
211 )
212 f2.add_driver(self.c2)
213 f1.add_subfragment(f2)
214 f3 = Fragment()
215 f3.add_statements(
216 self.c1.eq(self.c2)
217 )
218 f1.add_subfragment(f3)
219
220 f1._propagate_ports(ports=(), all_undef_as_ports=True)
221 self.assertEqual(f1.ports, SignalDict())
222
223 def test_input_cd(self):
224 sync = ClockDomain()
225 f = Fragment()
226 f.add_statements(
227 self.c1.eq(self.s1)
228 )
229 f.add_domains(sync)
230 f.add_driver(self.c1, "sync")
231
232 f._propagate_ports(ports=(), all_undef_as_ports=True)
233 self.assertEqual(f.ports, SignalDict([
234 (self.s1, "i"),
235 (sync.clk, "i"),
236 (sync.rst, "i"),
237 ]))
238
239 def test_input_cd_reset_less(self):
240 sync = ClockDomain(reset_less=True)
241 f = Fragment()
242 f.add_statements(
243 self.c1.eq(self.s1)
244 )
245 f.add_domains(sync)
246 f.add_driver(self.c1, "sync")
247
248 f._propagate_ports(ports=(), all_undef_as_ports=True)
249 self.assertEqual(f.ports, SignalDict([
250 (self.s1, "i"),
251 (sync.clk, "i"),
252 ]))
253
254 def test_inout(self):
255 s = Signal()
256 f1 = Fragment()
257 f2 = Instance("foo", io_x=s)
258 f1.add_subfragment(f2)
259
260 f1._propagate_ports(ports=(), all_undef_as_ports=True)
261 self.assertEqual(f1.ports, SignalDict([
262 (s, "io")
263 ]))
264
265
266 class FragmentDomainsTestCase(FHDLTestCase):
267 def test_iter_signals(self):
268 cd1 = ClockDomain()
269 cd2 = ClockDomain(reset_less=True)
270 s1 = Signal()
271 s2 = Signal()
272
273 f = Fragment()
274 f.add_domains(cd1, cd2)
275 f.add_driver(s1, "cd1")
276 self.assertEqual(SignalSet((cd1.clk, cd1.rst, s1)), f.iter_signals())
277 f.add_driver(s2, "cd2")
278 self.assertEqual(SignalSet((cd1.clk, cd1.rst, cd2.clk, s1, s2)), f.iter_signals())
279
280 def test_propagate_up(self):
281 cd = ClockDomain()
282
283 f1 = Fragment()
284 f2 = Fragment()
285 f1.add_subfragment(f2)
286 f2.add_domains(cd)
287
288 f1._propagate_domains_up()
289 self.assertEqual(f1.domains, {"cd": cd})
290
291 def test_domain_conflict(self):
292 cda = ClockDomain("sync")
293 cdb = ClockDomain("sync")
294
295 fa = Fragment()
296 fa.add_domains(cda)
297 fb = Fragment()
298 fb.add_domains(cdb)
299 f = Fragment()
300 f.add_subfragment(fa, "a")
301 f.add_subfragment(fb, "b")
302
303 f._propagate_domains_up()
304 self.assertEqual(f.domains, {"a_sync": cda, "b_sync": cdb})
305 (fa, _), (fb, _) = f.subfragments
306 self.assertEqual(fa.domains, {"a_sync": cda})
307 self.assertEqual(fb.domains, {"b_sync": cdb})
308
309 def test_domain_conflict_anon(self):
310 cda = ClockDomain("sync")
311 cdb = ClockDomain("sync")
312
313 fa = Fragment()
314 fa.add_domains(cda)
315 fb = Fragment()
316 fb.add_domains(cdb)
317 f = Fragment()
318 f.add_subfragment(fa, "a")
319 f.add_subfragment(fb)
320
321 with self.assertRaises(DomainError,
322 msg="Domain 'sync' is defined by subfragments 'a', <unnamed #1> of fragment "
323 "'top'; it is necessary to either rename subfragment domains explicitly, "
324 "or give names to subfragments"):
325 f._propagate_domains_up()
326
327 def test_domain_conflict_name(self):
328 cda = ClockDomain("sync")
329 cdb = ClockDomain("sync")
330
331 fa = Fragment()
332 fa.add_domains(cda)
333 fb = Fragment()
334 fb.add_domains(cdb)
335 f = Fragment()
336 f.add_subfragment(fa, "x")
337 f.add_subfragment(fb, "x")
338
339 with self.assertRaises(DomainError,
340 msg="Domain 'sync' is defined by subfragments #0, #1 of fragment 'top', some "
341 "of which have identical names; it is necessary to either rename subfragment "
342 "domains explicitly, or give distinct names to subfragments"):
343 f._propagate_domains_up()
344
345 def test_propagate_down(self):
346 cd = ClockDomain()
347
348 f1 = Fragment()
349 f2 = Fragment()
350 f1.add_domains(cd)
351 f1.add_subfragment(f2)
352
353 f1._propagate_domains_down()
354 self.assertEqual(f2.domains, {"cd": cd})
355
356 def test_propagate_down_idempotent(self):
357 cd = ClockDomain()
358
359 f1 = Fragment()
360 f1.add_domains(cd)
361 f2 = Fragment()
362 f2.add_domains(cd)
363 f1.add_subfragment(f2)
364
365 f1._propagate_domains_down()
366 self.assertEqual(f1.domains, {"cd": cd})
367 self.assertEqual(f2.domains, {"cd": cd})
368
369 def test_propagate(self):
370 cd = ClockDomain()
371
372 f1 = Fragment()
373 f2 = Fragment()
374 f1.add_domains(cd)
375 f1.add_subfragment(f2)
376
377 f1._propagate_domains(ensure_sync_exists=False)
378 self.assertEqual(f1.domains, {"cd": cd})
379 self.assertEqual(f2.domains, {"cd": cd})
380
381 def test_propagate_ensure_sync(self):
382 f1 = Fragment()
383 f2 = Fragment()
384 f1.add_subfragment(f2)
385
386 f1._propagate_domains(ensure_sync_exists=True)
387 self.assertEqual(f1.domains.keys(), {"sync"})
388 self.assertEqual(f2.domains.keys(), {"sync"})
389 self.assertEqual(f1.domains["sync"], f2.domains["sync"])
390
391
392 class FragmentHierarchyConflictTestCase(FHDLTestCase):
393 def setUp_self_sub(self):
394 self.s1 = Signal()
395 self.c1 = Signal()
396 self.c2 = Signal()
397
398 self.f1 = Fragment()
399 self.f1.add_statements(self.c1.eq(0))
400 self.f1.add_driver(self.s1)
401 self.f1.add_driver(self.c1, "sync")
402
403 self.f1a = Fragment()
404 self.f1.add_subfragment(self.f1a, "f1a")
405
406 self.f2 = Fragment()
407 self.f2.add_statements(self.c2.eq(1))
408 self.f2.add_driver(self.s1)
409 self.f2.add_driver(self.c2, "sync")
410 self.f1.add_subfragment(self.f2)
411
412 self.f1b = Fragment()
413 self.f1.add_subfragment(self.f1b, "f1b")
414
415 self.f2a = Fragment()
416 self.f2.add_subfragment(self.f2a, "f2a")
417
418 def test_conflict_self_sub(self):
419 self.setUp_self_sub()
420
421 self.f1._resolve_hierarchy_conflicts(mode="silent")
422 self.assertEqual(self.f1.subfragments, [
423 (self.f1a, "f1a"),
424 (self.f1b, "f1b"),
425 (self.f2a, "f2a"),
426 ])
427 self.assertRepr(self.f1.statements, """
428 (
429 (eq (sig c1) (const 1'd0))
430 (eq (sig c2) (const 1'd1))
431 )
432 """)
433 self.assertEqual(self.f1.drivers, {
434 None: SignalSet((self.s1,)),
435 "sync": SignalSet((self.c1, self.c2)),
436 })
437
438 def test_conflict_self_sub_error(self):
439 self.setUp_self_sub()
440
441 with self.assertRaises(DriverConflict,
442 msg="Signal '(sig s1)' is driven from multiple fragments: top, top.<unnamed #1>"):
443 self.f1._resolve_hierarchy_conflicts(mode="error")
444
445 def test_conflict_self_sub_warning(self):
446 self.setUp_self_sub()
447
448 with self.assertWarns(DriverConflict,
449 msg="Signal '(sig s1)' is driven from multiple fragments: top, top.<unnamed #1>; "
450 "hierarchy will be flattened"):
451 self.f1._resolve_hierarchy_conflicts(mode="warn")
452
453 def setUp_sub_sub(self):
454 self.s1 = Signal()
455 self.c1 = Signal()
456 self.c2 = Signal()
457
458 self.f1 = Fragment()
459
460 self.f2 = Fragment()
461 self.f2.add_driver(self.s1)
462 self.f2.add_statements(self.c1.eq(0))
463 self.f1.add_subfragment(self.f2)
464
465 self.f3 = Fragment()
466 self.f3.add_driver(self.s1)
467 self.f3.add_statements(self.c2.eq(1))
468 self.f1.add_subfragment(self.f3)
469
470 def test_conflict_sub_sub(self):
471 self.setUp_sub_sub()
472
473 self.f1._resolve_hierarchy_conflicts(mode="silent")
474 self.assertEqual(self.f1.subfragments, [])
475 self.assertRepr(self.f1.statements, """
476 (
477 (eq (sig c1) (const 1'd0))
478 (eq (sig c2) (const 1'd1))
479 )
480 """)
481
482 def setUp_self_subsub(self):
483 self.s1 = Signal()
484 self.c1 = Signal()
485 self.c2 = Signal()
486
487 self.f1 = Fragment()
488 self.f1.add_driver(self.s1)
489
490 self.f2 = Fragment()
491 self.f2.add_statements(self.c1.eq(0))
492 self.f1.add_subfragment(self.f2)
493
494 self.f3 = Fragment()
495 self.f3.add_driver(self.s1)
496 self.f3.add_statements(self.c2.eq(1))
497 self.f2.add_subfragment(self.f3)
498
499 def test_conflict_self_subsub(self):
500 self.setUp_self_subsub()
501
502 self.f1._resolve_hierarchy_conflicts(mode="silent")
503 self.assertEqual(self.f1.subfragments, [])
504 self.assertRepr(self.f1.statements, """
505 (
506 (eq (sig c1) (const 1'd0))
507 (eq (sig c2) (const 1'd1))
508 )
509 """)
510
511 def setUp_memory(self):
512 self.m = Memory(width=8, depth=4)
513 self.fr = self.m.read_port().elaborate(platform=None)
514 self.fw = self.m.write_port().elaborate(platform=None)
515 self.f1 = Fragment()
516 self.f2 = Fragment()
517 self.f2.add_subfragment(self.fr)
518 self.f1.add_subfragment(self.f2)
519 self.f3 = Fragment()
520 self.f3.add_subfragment(self.fw)
521 self.f1.add_subfragment(self.f3)
522
523 def test_conflict_memory(self):
524 self.setUp_memory()
525
526 self.f1._resolve_hierarchy_conflicts(mode="silent")
527 self.assertEqual(self.f1.subfragments, [
528 (self.fr, None),
529 (self.fw, None),
530 ])
531
532 def test_conflict_memory_error(self):
533 self.setUp_memory()
534
535 with self.assertRaises(DriverConflict,
536 msg="Memory 'm' is accessed from multiple fragments: top.<unnamed #0>, "
537 "top.<unnamed #1>"):
538 self.f1._resolve_hierarchy_conflicts(mode="error")
539
540 def test_conflict_memory_warning(self):
541 self.setUp_memory()
542
543 with self.assertWarns(DriverConflict,
544 msg="Memory 'm' is accessed from multiple fragments: top.<unnamed #0>, "
545 "top.<unnamed #1>; hierarchy will be flattened"):
546 self.f1._resolve_hierarchy_conflicts(mode="warn")
547
548 def test_explicit_flatten(self):
549 self.f1 = Fragment()
550 self.f2 = Fragment()
551 self.f2.flatten = True
552 self.f1.add_subfragment(self.f2)
553
554 self.f1._resolve_hierarchy_conflicts(mode="silent")
555 self.assertEqual(self.f1.subfragments, [])
556
557
558 class InstanceTestCase(FHDLTestCase):
559 def test_construct(self):
560 s1 = Signal()
561 s2 = Signal()
562 s3 = Signal()
563 s4 = Signal()
564 s5 = Signal()
565 s6 = Signal()
566 inst = Instance("foo",
567 ("a", "ATTR1", 1),
568 ("p", "PARAM1", 0x1234),
569 ("i", "s1", s1),
570 ("o", "s2", s2),
571 ("io", "s3", s3),
572 a_ATTR2=2,
573 p_PARAM2=0x5678,
574 i_s4=s4,
575 o_s5=s5,
576 io_s6=s6,
577 )
578 self.assertEqual(inst.attrs, OrderedDict([
579 ("ATTR1", 1),
580 ("ATTR2", 2),
581 ]))
582 self.assertEqual(inst.parameters, OrderedDict([
583 ("PARAM1", 0x1234),
584 ("PARAM2", 0x5678),
585 ]))
586 self.assertEqual(inst.named_ports, OrderedDict([
587 ("s1", (s1, "i")),
588 ("s2", (s2, "o")),
589 ("s3", (s3, "io")),
590 ("s4", (s4, "i")),
591 ("s5", (s5, "o")),
592 ("s6", (s6, "io")),
593 ]))
594
595 def test_wrong_construct_arg(self):
596 s = Signal()
597 with self.assertRaises(NameError,
598 msg="Instance argument ('', 's1', (sig s)) should be a tuple "
599 "(kind, name, value) where kind is one of \"p\", \"i\", \"o\", or \"io\""):
600 Instance("foo", ("", "s1", s))
601
602 def test_wrong_construct_kwarg(self):
603 s = Signal()
604 with self.assertRaises(NameError,
605 msg="Instance keyword argument x_s1=(sig s) does not start with one of "
606 "\"p_\", \"i_\", \"o_\", or \"io_\""):
607 Instance("foo", x_s1=s)
608
609 def setUp_cpu(self):
610 self.rst = Signal()
611 self.stb = Signal()
612 self.pins = Signal(8)
613 self.datal = Signal(4)
614 self.datah = Signal(4)
615 self.inst = Instance("cpu",
616 p_RESET=0x1234,
617 i_clk=ClockSignal(),
618 i_rst=self.rst,
619 o_stb=self.stb,
620 o_data=Cat(self.datal, self.datah),
621 io_pins=self.pins[:]
622 )
623 self.wrap = Fragment()
624 self.wrap.add_subfragment(self.inst)
625
626 def test_init(self):
627 self.setUp_cpu()
628 f = self.inst
629 self.assertEqual(f.type, "cpu")
630 self.assertEqual(f.parameters, OrderedDict([("RESET", 0x1234)]))
631 self.assertEqual(list(f.named_ports.keys()), ["clk", "rst", "stb", "data", "pins"])
632 self.assertEqual(f.ports, SignalDict([]))
633
634 def test_prepare(self):
635 self.setUp_cpu()
636 f = self.wrap.prepare()
637 sync_clk = f.domains["sync"].clk
638 self.assertEqual(f.ports, SignalDict([
639 (sync_clk, "i"),
640 (self.rst, "i"),
641 (self.pins, "io"),
642 ]))
643
644 def test_prepare_explicit_ports(self):
645 self.setUp_cpu()
646 f = self.wrap.prepare(ports=[self.rst, self.stb])
647 sync_clk = f.domains["sync"].clk
648 sync_rst = f.domains["sync"].rst
649 self.assertEqual(f.ports, SignalDict([
650 (sync_clk, "i"),
651 (sync_rst, "i"),
652 (self.rst, "i"),
653 (self.stb, "o"),
654 (self.pins, "io"),
655 ]))
656
657 def test_prepare_slice_in_port(self):
658 s = Signal(2)
659 f = Fragment()
660 f.add_subfragment(Instance("foo", o_O=s[0]))
661 f.add_subfragment(Instance("foo", o_O=s[1]))
662 fp = f.prepare(ports=[s], ensure_sync_exists=False)
663 self.assertEqual(fp.ports, SignalDict([
664 (s, "o"),
665 ]))
666
667 def test_prepare_attrs(self):
668 self.setUp_cpu()
669 self.inst.attrs["ATTR"] = 1
670 f = self.inst.prepare()
671 self.assertEqual(f.attrs, OrderedDict([
672 ("ATTR", 1),
673 ]))