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