hdl.{ast,cd,dsl,xfrm}: reject inappropriately used comb domain.
[nmigen.git] / nmigen / test / test_hdl_dsl.py
1 from collections import OrderedDict
2
3 from ..hdl.ast import *
4 from ..hdl.cd import *
5 from ..hdl.dsl import *
6 from .tools import *
7
8
9 class DSLTestCase(FHDLTestCase):
10 def setUp(self):
11 self.s1 = Signal()
12 self.s2 = Signal()
13 self.s3 = Signal()
14 self.c1 = Signal()
15 self.c2 = Signal()
16 self.c3 = Signal()
17 self.w1 = Signal(4)
18
19 def test_d_comb(self):
20 m = Module()
21 m.d.comb += self.c1.eq(1)
22 m._flush()
23 self.assertEqual(m._driving[self.c1], None)
24 self.assertRepr(m._statements, """(
25 (eq (sig c1) (const 1'd1))
26 )""")
27
28 def test_d_sync(self):
29 m = Module()
30 m.d.sync += self.c1.eq(1)
31 m._flush()
32 self.assertEqual(m._driving[self.c1], "sync")
33 self.assertRepr(m._statements, """(
34 (eq (sig c1) (const 1'd1))
35 )""")
36
37 def test_d_pix(self):
38 m = Module()
39 m.d.pix += self.c1.eq(1)
40 m._flush()
41 self.assertEqual(m._driving[self.c1], "pix")
42 self.assertRepr(m._statements, """(
43 (eq (sig c1) (const 1'd1))
44 )""")
45
46 def test_d_index(self):
47 m = Module()
48 m.d["pix"] += self.c1.eq(1)
49 m._flush()
50 self.assertEqual(m._driving[self.c1], "pix")
51 self.assertRepr(m._statements, """(
52 (eq (sig c1) (const 1'd1))
53 )""")
54
55 def test_d_no_conflict(self):
56 m = Module()
57 m.d.comb += self.w1[0].eq(1)
58 m.d.comb += self.w1[1].eq(1)
59
60 def test_d_conflict(self):
61 m = Module()
62 with self.assertRaises(SyntaxError,
63 msg="Driver-driver conflict: trying to drive (sig c1) from d.sync, but it "
64 "is already driven from d.comb"):
65 m.d.comb += self.c1.eq(1)
66 m.d.sync += self.c1.eq(1)
67
68 def test_d_wrong(self):
69 m = Module()
70 with self.assertRaises(AttributeError,
71 msg="Cannot assign 'd.pix' attribute; did you mean 'd.pix +='?"):
72 m.d.pix = None
73
74 def test_d_asgn_wrong(self):
75 m = Module()
76 with self.assertRaises(SyntaxError,
77 msg="Only assignments, asserts, and assumes may be appended to d.sync"):
78 m.d.sync += Switch(self.s1, {})
79
80 def test_comb_wrong(self):
81 m = Module()
82 with self.assertRaises(AttributeError,
83 msg="'Module' object has no attribute 'comb'; did you mean 'd.comb'?"):
84 m.comb += self.c1.eq(1)
85
86 def test_sync_wrong(self):
87 m = Module()
88 with self.assertRaises(AttributeError,
89 msg="'Module' object has no attribute 'sync'; did you mean 'd.sync'?"):
90 m.sync += self.c1.eq(1)
91
92 def test_attr_wrong(self):
93 m = Module()
94 with self.assertRaises(AttributeError,
95 msg="'Module' object has no attribute 'nonexistentattr'"):
96 m.nonexistentattr
97
98 def test_clock_signal(self):
99 m = Module()
100 m.d.comb += ClockSignal("pix").eq(ClockSignal())
101 self.assertRepr(m._statements, """
102 (
103 (eq (clk pix) (clk sync))
104 )
105 """)
106
107 def test_reset_signal(self):
108 m = Module()
109 m.d.comb += ResetSignal("pix").eq(1)
110 self.assertRepr(m._statements, """
111 (
112 (eq (rst pix) (const 1'd1))
113 )
114 """)
115
116 def test_sample_domain(self):
117 m = Module()
118 i = Signal()
119 o1 = Signal()
120 o2 = Signal()
121 o3 = Signal()
122 m.d.sync += o1.eq(Past(i))
123 m.d.pix += o2.eq(Past(i))
124 m.d.pix += o3.eq(Past(i, domain="sync"))
125 f = m.elaborate(platform=None)
126 self.assertRepr(f.statements, """
127 (
128 (eq (sig o1) (sample (sig i) @ sync[1]))
129 (eq (sig o2) (sample (sig i) @ pix[1]))
130 (eq (sig o3) (sample (sig i) @ sync[1]))
131 )
132 """)
133
134 def test_If(self):
135 m = Module()
136 with m.If(self.s1):
137 m.d.comb += self.c1.eq(1)
138 m._flush()
139 self.assertRepr(m._statements, """
140 (
141 (switch (cat (sig s1))
142 (case 1 (eq (sig c1) (const 1'd1)))
143 )
144 )
145 """)
146
147 def test_If_Elif(self):
148 m = Module()
149 with m.If(self.s1):
150 m.d.comb += self.c1.eq(1)
151 with m.Elif(self.s2):
152 m.d.sync += self.c2.eq(0)
153 m._flush()
154 self.assertRepr(m._statements, """
155 (
156 (switch (cat (sig s1) (sig s2))
157 (case -1 (eq (sig c1) (const 1'd1)))
158 (case 1- (eq (sig c2) (const 1'd0)))
159 )
160 )
161 """)
162
163 def test_If_Elif_Else(self):
164 m = Module()
165 with m.If(self.s1):
166 m.d.comb += self.c1.eq(1)
167 with m.Elif(self.s2):
168 m.d.sync += self.c2.eq(0)
169 with m.Else():
170 m.d.comb += self.c3.eq(1)
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 (default (eq (sig c3) (const 1'd1)))
178 )
179 )
180 """)
181
182 def test_If_If(self):
183 m = Module()
184 with m.If(self.s1):
185 m.d.comb += self.c1.eq(1)
186 with m.If(self.s2):
187 m.d.comb += self.c2.eq(1)
188 m._flush()
189 self.assertRepr(m._statements, """
190 (
191 (switch (cat (sig s1))
192 (case 1 (eq (sig c1) (const 1'd1)))
193 )
194 (switch (cat (sig s2))
195 (case 1 (eq (sig c2) (const 1'd1)))
196 )
197 )
198 """)
199
200 def test_If_nested_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 (switch (cat (sig s2))
212 (case 1 (eq (sig c2) (const 1'd1)))
213 )
214 )
215 )
216 )
217 """)
218
219 def test_If_dangling_Else(self):
220 m = Module()
221 with m.If(self.s1):
222 m.d.comb += self.c1.eq(1)
223 with m.If(self.s2):
224 m.d.comb += self.c2.eq(1)
225 with m.Else():
226 m.d.comb += self.c3.eq(1)
227 m._flush()
228 self.assertRepr(m._statements, """
229 (
230 (switch (cat (sig s1))
231 (case 1
232 (eq (sig c1) (const 1'd1))
233 (switch (cat (sig s2))
234 (case 1 (eq (sig c2) (const 1'd1)))
235 )
236 )
237 (default
238 (eq (sig c3) (const 1'd1))
239 )
240 )
241 )
242 """)
243
244 def test_Elif_wrong(self):
245 m = Module()
246 with self.assertRaises(SyntaxError,
247 msg="Elif without preceding If"):
248 with m.Elif(self.s2):
249 pass
250
251 def test_Else_wrong(self):
252 m = Module()
253 with self.assertRaises(SyntaxError,
254 msg="Else without preceding If/Elif"):
255 with m.Else():
256 pass
257
258 def test_If_wide(self):
259 m = Module()
260 with m.If(self.w1):
261 m.d.comb += self.c1.eq(1)
262 m._flush()
263 self.assertRepr(m._statements, """
264 (
265 (switch (cat (b (sig w1)))
266 (case 1 (eq (sig c1) (const 1'd1)))
267 )
268 )
269 """)
270
271 def test_Switch(self):
272 m = Module()
273 with m.Switch(self.w1):
274 with m.Case(3):
275 m.d.comb += self.c1.eq(1)
276 with m.Case("11--"):
277 m.d.comb += self.c2.eq(1)
278 m._flush()
279 self.assertRepr(m._statements, """
280 (
281 (switch (sig w1)
282 (case 0011 (eq (sig c1) (const 1'd1)))
283 (case 11-- (eq (sig c2) (const 1'd1)))
284 )
285 )
286 """)
287
288 def test_Switch_default(self):
289 m = Module()
290 with m.Switch(self.w1):
291 with m.Case(3):
292 m.d.comb += self.c1.eq(1)
293 with m.Case():
294 m.d.comb += self.c2.eq(1)
295 m._flush()
296 self.assertRepr(m._statements, """
297 (
298 (switch (sig w1)
299 (case 0011 (eq (sig c1) (const 1'd1)))
300 (default (eq (sig c2) (const 1'd1)))
301 )
302 )
303 """)
304
305 def test_Switch_const_test(self):
306 m = Module()
307 with m.Switch(1):
308 with m.Case(1):
309 m.d.comb += self.c1.eq(1)
310 m._flush()
311 self.assertRepr(m._statements, """
312 (
313 (switch (const 1'd1)
314 (case 1 (eq (sig c1) (const 1'd1)))
315 )
316 )
317 """)
318
319 def test_Case_width_wrong(self):
320 m = Module()
321 with m.Switch(self.w1):
322 with self.assertRaises(SyntaxError,
323 msg="Case value '--' must have the same width as test (which is 4)"):
324 with m.Case("--"):
325 pass
326 with self.assertWarns(SyntaxWarning,
327 msg="Case value '10110' is wider than test (which has width 4); comparison "
328 "will never be true"):
329 with m.Case(0b10110):
330 pass
331 self.assertRepr(m._statements, """
332 (
333 (switch (sig w1) )
334 )
335 """)
336
337 def test_Case_outside_Switch_wrong(self):
338 m = Module()
339 with self.assertRaises(SyntaxError,
340 msg="Case is not permitted outside of Switch"):
341 with m.Case():
342 pass
343
344 def test_If_inside_Switch_wrong(self):
345 m = Module()
346 with m.Switch(self.s1):
347 with self.assertRaises(SyntaxError,
348 msg="If is not permitted directly inside of Switch; "
349 "it is permitted inside of Switch Case"):
350 with m.If(self.s2):
351 pass
352
353 def test_FSM_basic(self):
354 a = Signal()
355 b = Signal()
356 c = Signal()
357 m = Module()
358 with m.FSM():
359 with m.State("FIRST"):
360 m.d.comb += a.eq(1)
361 m.next = "SECOND"
362 with m.State("SECOND"):
363 m.d.sync += b.eq(~b)
364 with m.If(c):
365 m.next = "FIRST"
366 m._flush()
367 self.assertRepr(m._statements, """
368 (
369 (switch (sig fsm_state)
370 (case 0
371 (eq (sig a) (const 1'd1))
372 (eq (sig fsm_state) (const 1'd1))
373 )
374 (case 1
375 (eq (sig b) (~ (sig b)))
376 (switch (cat (sig c))
377 (case 1
378 (eq (sig fsm_state) (const 1'd0)))
379 )
380 )
381 )
382 )
383 """)
384 self.assertEqual({repr(k): v for k, v in m._driving.items()}, {
385 "(sig a)": None,
386 "(sig fsm_state)": "sync",
387 "(sig b)": "sync",
388 })
389
390 frag = m.elaborate(platform=None)
391 fsm = frag.find_generated("fsm")
392 self.assertIsInstance(fsm.state, Signal)
393 self.assertEqual(fsm.encoding, OrderedDict({
394 "FIRST": 0,
395 "SECOND": 1,
396 }))
397 self.assertEqual(fsm.decoding, OrderedDict({
398 0: "FIRST",
399 1: "SECOND"
400 }))
401
402 def test_FSM_reset(self):
403 a = Signal()
404 m = Module()
405 with m.FSM(reset="SECOND"):
406 with m.State("FIRST"):
407 m.d.comb += a.eq(0)
408 m.next = "SECOND"
409 with m.State("SECOND"):
410 m.next = "FIRST"
411 m._flush()
412 self.assertRepr(m._statements, """
413 (
414 (switch (sig fsm_state)
415 (case 0
416 (eq (sig a) (const 1'd0))
417 (eq (sig fsm_state) (const 1'd1))
418 )
419 (case 1
420 (eq (sig fsm_state) (const 1'd0))
421 )
422 )
423 )
424 """)
425
426 def test_FSM_ongoing(self):
427 a = Signal()
428 b = Signal()
429 m = Module()
430 with m.FSM() as fsm:
431 m.d.comb += b.eq(fsm.ongoing("SECOND"))
432 with m.State("FIRST"):
433 pass
434 m.d.comb += a.eq(fsm.ongoing("FIRST"))
435 with m.State("SECOND"):
436 pass
437 m._flush()
438 self.assertEqual(m._generated["fsm"].state.reset, 1)
439 self.maxDiff = 10000
440 self.assertRepr(m._statements, """
441 (
442 (eq (sig b) (== (sig fsm_state) (const 1'd0)))
443 (eq (sig a) (== (sig fsm_state) (const 1'd1)))
444 (switch (sig fsm_state)
445 (case 1
446 )
447 (case 0
448 )
449 )
450 )
451 """)
452
453 def test_FSM_empty(self):
454 m = Module()
455 with m.FSM():
456 pass
457 self.assertRepr(m._statements, """
458 ()
459 """)
460
461 def test_FSM_wrong_domain(self):
462 m = Module()
463 with self.assertRaises(ValueError,
464 msg="FSM may not be driven by the 'comb' domain"):
465 with m.FSM(domain="comb"):
466 pass
467
468 def test_FSM_wrong_redefined(self):
469 m = Module()
470 with m.FSM():
471 with m.State("FOO"):
472 pass
473 with self.assertRaises(SyntaxError,
474 msg="FSM state 'FOO' is already defined"):
475 with m.State("FOO"):
476 pass
477
478 def test_FSM_wrong_next(self):
479 m = Module()
480 with self.assertRaises(SyntaxError,
481 msg="Only assignment to `m.next` is permitted"):
482 m.next
483 with self.assertRaises(SyntaxError,
484 msg="`m.next = <...>` is only permitted inside an FSM state"):
485 m.next = "FOO"
486 with self.assertRaises(SyntaxError,
487 msg="`m.next = <...>` is only permitted inside an FSM state"):
488 with m.FSM():
489 m.next = "FOO"
490
491 def test_If_inside_FSM_wrong(self):
492 m = Module()
493 with m.FSM():
494 with m.State("FOO"):
495 pass
496 with self.assertRaises(SyntaxError,
497 msg="If is not permitted directly inside of FSM; "
498 "it is permitted inside of FSM State"):
499 with m.If(self.s2):
500 pass
501
502 def test_auto_pop_ctrl(self):
503 m = Module()
504 with m.If(self.w1):
505 m.d.comb += self.c1.eq(1)
506 m.d.comb += self.c2.eq(1)
507 self.assertRepr(m._statements, """
508 (
509 (switch (cat (b (sig w1)))
510 (case 1 (eq (sig c1) (const 1'd1)))
511 )
512 (eq (sig c2) (const 1'd1))
513 )
514 """)
515
516 def test_submodule_anon(self):
517 m1 = Module()
518 m2 = Module()
519 m1.submodules += m2
520 self.assertEqual(m1._submodules, [(m2, None)])
521
522 def test_submodule_anon_multi(self):
523 m1 = Module()
524 m2 = Module()
525 m3 = Module()
526 m1.submodules += m2, m3
527 self.assertEqual(m1._submodules, [(m2, None), (m3, None)])
528
529 def test_submodule_named(self):
530 m1 = Module()
531 m2 = Module()
532 m1.submodules.foo = m2
533 self.assertEqual(m1._submodules, [(m2, "foo")])
534
535 def test_submodule_named_index(self):
536 m1 = Module()
537 m2 = Module()
538 m1.submodules["foo"] = m2
539 self.assertEqual(m1._submodules, [(m2, "foo")])
540
541 def test_submodule_wrong(self):
542 m = Module()
543 with self.assertRaises(TypeError,
544 msg="Trying to add '1', which does not implement .elaborate(), as a submodule"):
545 m.submodules.foo = 1
546 with self.assertRaises(TypeError,
547 msg="Trying to add '1', which does not implement .elaborate(), as a submodule"):
548 m.submodules += 1
549
550 def test_domain_named_implicit(self):
551 m = Module()
552 m.domains += ClockDomain("sync")
553 self.assertEqual(len(m._domains), 1)
554
555 def test_domain_named_explicit(self):
556 m = Module()
557 m.domains.foo = ClockDomain()
558 self.assertEqual(len(m._domains), 1)
559 self.assertEqual(m._domains[0].name, "foo")
560
561 def test_lower(self):
562 m1 = Module()
563 m1.d.comb += self.c1.eq(self.s1)
564 m2 = Module()
565 m2.d.comb += self.c2.eq(self.s2)
566 m2.d.sync += self.c3.eq(self.s3)
567 m1.submodules.foo = m2
568
569 f1 = m1.elaborate(platform=None)
570 self.assertRepr(f1.statements, """
571 (
572 (eq (sig c1) (sig s1))
573 )
574 """)
575 self.assertEqual(f1.drivers, {
576 None: SignalSet((self.c1,))
577 })
578 self.assertEqual(len(f1.subfragments), 1)
579 (f2, f2_name), = f1.subfragments
580 self.assertEqual(f2_name, "foo")
581 self.assertRepr(f2.statements, """
582 (
583 (eq (sig c2) (sig s2))
584 (eq (sig c3) (sig s3))
585 )
586 """)
587 self.assertEqual(f2.drivers, {
588 None: SignalSet((self.c2,)),
589 "sync": SignalSet((self.c3,))
590 })
591 self.assertEqual(len(f2.subfragments), 0)