lib.cdc: add tests for MultiReg.
[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 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_If(self):
99 m = Module()
100 with m.If(self.s1):
101 m.d.comb += self.c1.eq(1)
102 m._flush()
103 self.assertRepr(m._statements, """
104 (
105 (switch (cat (sig s1))
106 (case 1 (eq (sig c1) (const 1'd1)))
107 )
108 )
109 """)
110
111 def test_If_Elif(self):
112 m = Module()
113 with m.If(self.s1):
114 m.d.comb += self.c1.eq(1)
115 with m.Elif(self.s2):
116 m.d.sync += self.c2.eq(0)
117 m._flush()
118 self.assertRepr(m._statements, """
119 (
120 (switch (cat (sig s1) (sig s2))
121 (case -1 (eq (sig c1) (const 1'd1)))
122 (case 1- (eq (sig c2) (const 1'd0)))
123 )
124 )
125 """)
126
127 def test_If_Elif_Else(self):
128 m = Module()
129 with m.If(self.s1):
130 m.d.comb += self.c1.eq(1)
131 with m.Elif(self.s2):
132 m.d.sync += self.c2.eq(0)
133 with m.Else():
134 m.d.comb += self.c3.eq(1)
135 m._flush()
136 self.assertRepr(m._statements, """
137 (
138 (switch (cat (sig s1) (sig s2))
139 (case -1 (eq (sig c1) (const 1'd1)))
140 (case 1- (eq (sig c2) (const 1'd0)))
141 (case -- (eq (sig c3) (const 1'd1)))
142 )
143 )
144 """)
145
146 def test_If_If(self):
147 m = Module()
148 with m.If(self.s1):
149 m.d.comb += self.c1.eq(1)
150 with m.If(self.s2):
151 m.d.comb += self.c2.eq(1)
152 m._flush()
153 self.assertRepr(m._statements, """
154 (
155 (switch (cat (sig s1))
156 (case 1 (eq (sig c1) (const 1'd1)))
157 )
158 (switch (cat (sig s2))
159 (case 1 (eq (sig c2) (const 1'd1)))
160 )
161 )
162 """)
163
164 def test_If_nested_If(self):
165 m = Module()
166 with m.If(self.s1):
167 m.d.comb += self.c1.eq(1)
168 with m.If(self.s2):
169 m.d.comb += self.c2.eq(1)
170 m._flush()
171 self.assertRepr(m._statements, """
172 (
173 (switch (cat (sig s1))
174 (case 1 (eq (sig c1) (const 1'd1))
175 (switch (cat (sig s2))
176 (case 1 (eq (sig c2) (const 1'd1)))
177 )
178 )
179 )
180 )
181 """)
182
183 def test_If_dangling_Else(self):
184 m = Module()
185 with m.If(self.s1):
186 m.d.comb += self.c1.eq(1)
187 with m.If(self.s2):
188 m.d.comb += self.c2.eq(1)
189 with m.Else():
190 m.d.comb += self.c3.eq(1)
191 m._flush()
192 self.assertRepr(m._statements, """
193 (
194 (switch (cat (sig s1))
195 (case 1
196 (eq (sig c1) (const 1'd1))
197 (switch (cat (sig s2))
198 (case 1 (eq (sig c2) (const 1'd1)))
199 )
200 )
201 (case -
202 (eq (sig c3) (const 1'd1))
203 )
204 )
205 )
206 """)
207
208 def test_Elif_wrong(self):
209 m = Module()
210 with self.assertRaises(SyntaxError,
211 msg="Elif without preceding If"):
212 with m.Elif(self.s2):
213 pass
214
215 def test_Else_wrong(self):
216 m = Module()
217 with self.assertRaises(SyntaxError,
218 msg="Else without preceding If/Elif"):
219 with m.Else():
220 pass
221
222 def test_If_wide(self):
223 m = Module()
224 with m.If(self.w1):
225 m.d.comb += self.c1.eq(1)
226 m._flush()
227 self.assertRepr(m._statements, """
228 (
229 (switch (cat (b (sig w1)))
230 (case 1 (eq (sig c1) (const 1'd1)))
231 )
232 )
233 """)
234
235 def test_Switch(self):
236 m = Module()
237 with m.Switch(self.w1):
238 with m.Case(3):
239 m.d.comb += self.c1.eq(1)
240 with m.Case("11--"):
241 m.d.comb += self.c2.eq(1)
242 m._flush()
243 self.assertRepr(m._statements, """
244 (
245 (switch (sig w1)
246 (case 0011 (eq (sig c1) (const 1'd1)))
247 (case 11-- (eq (sig c2) (const 1'd1)))
248 )
249 )
250 """)
251
252 def test_Switch_default(self):
253 m = Module()
254 with m.Switch(self.w1):
255 with m.Case(3):
256 m.d.comb += self.c1.eq(1)
257 with m.Case():
258 m.d.comb += self.c2.eq(1)
259 m._flush()
260 self.assertRepr(m._statements, """
261 (
262 (switch (sig w1)
263 (case 0011 (eq (sig c1) (const 1'd1)))
264 (case ---- (eq (sig c2) (const 1'd1)))
265 )
266 )
267 """)
268
269 def test_Switch_const_test(self):
270 m = Module()
271 with m.Switch(1):
272 with m.Case(1):
273 m.d.comb += self.c1.eq(1)
274 m._flush()
275 self.assertRepr(m._statements, """
276 (
277 (switch (const 1'd1)
278 (case 1 (eq (sig c1) (const 1'd1)))
279 )
280 )
281 """)
282
283 def test_Case_width_wrong(self):
284 m = Module()
285 with m.Switch(self.w1):
286 with self.assertRaises(SyntaxError,
287 msg="Case value '--' must have the same width as test (which is 4)"):
288 with m.Case("--"):
289 pass
290
291 def test_Case_outside_Switch_wrong(self):
292 m = Module()
293 with self.assertRaises(SyntaxError,
294 msg="Case is not permitted outside of Switch"):
295 with m.Case():
296 pass
297
298 def test_If_inside_Switch_wrong(self):
299 m = Module()
300 with m.Switch(self.s1):
301 with self.assertRaises(SyntaxError,
302 msg="If is not permitted inside of Switch"):
303 with m.If(self.s2):
304 pass
305
306 def test_FSM_basic(self):
307 a = Signal()
308 b = Signal()
309 c = Signal()
310 m = Module()
311 with m.FSM():
312 with m.State("FIRST"):
313 m.d.comb += a.eq(1)
314 m.next = "SECOND"
315 with m.State("SECOND"):
316 m.d.sync += b.eq(~b)
317 with m.If(c):
318 m.next = "FIRST"
319 m._flush()
320 self.assertRepr(m._statements, """
321 (
322 (switch (sig fsm_state)
323 (case 0
324 (eq (sig a) (const 1'd1))
325 (eq (sig fsm_state) (const 1'd1))
326 )
327 (case 1
328 (eq (sig b) (~ (sig b)))
329 (switch (cat (sig c))
330 (case 1
331 (eq (sig fsm_state) (const 1'd0)))
332 )
333 )
334 )
335 )
336 """)
337 self.assertEqual({repr(k): v for k, v in m._driving.items()}, {
338 "(sig a)": None,
339 "(sig fsm_state)": "sync",
340 "(sig b)": "sync",
341 })
342
343 frag = m.lower(platform=None)
344 fsm = frag.find_generated("fsm")
345 self.assertIsInstance(fsm.state, Signal)
346 self.assertEqual(fsm.encoding, OrderedDict({
347 "FIRST": 0,
348 "SECOND": 1,
349 }))
350 self.assertEqual(fsm.decoding, OrderedDict({
351 0: "FIRST",
352 1: "SECOND"
353 }))
354
355 def test_FSM_reset(self):
356 a = Signal()
357 m = Module()
358 with m.FSM(reset="SECOND"):
359 with m.State("FIRST"):
360 m.d.comb += a.eq(0)
361 m.next = "SECOND"
362 with m.State("SECOND"):
363 m.next = "FIRST"
364 m._flush()
365 self.assertRepr(m._statements, """
366 (
367 (switch (sig fsm_state)
368 (case 1
369 (eq (sig a) (const 1'd0))
370 (eq (sig fsm_state) (const 1'd0))
371 )
372 (case 0
373 (eq (sig fsm_state) (const 1'd1))
374 )
375 )
376 )
377 """)
378
379 def test_FSM_wrong_redefined(self):
380 m = Module()
381 with m.FSM():
382 with m.State("FOO"):
383 pass
384 with self.assertRaises(SyntaxError,
385 msg="FSM state 'FOO' is already defined"):
386 with m.State("FOO"):
387 pass
388
389 def test_FSM_wrong_next(self):
390 m = Module()
391 with self.assertRaises(SyntaxError,
392 msg="Only assignment to `m.next` is permitted"):
393 m.next
394 with self.assertRaises(SyntaxError,
395 msg="`m.next = <...>` is only permitted inside an FSM state"):
396 m.next = "FOO"
397 with self.assertRaises(SyntaxError,
398 msg="`m.next = <...>` is only permitted inside an FSM state"):
399 with m.FSM():
400 m.next = "FOO"
401
402 def test_auto_pop_ctrl(self):
403 m = Module()
404 with m.If(self.w1):
405 m.d.comb += self.c1.eq(1)
406 m.d.comb += self.c2.eq(1)
407 self.assertRepr(m._statements, """
408 (
409 (switch (cat (b (sig w1)))
410 (case 1 (eq (sig c1) (const 1'd1)))
411 )
412 (eq (sig c2) (const 1'd1))
413 )
414 """)
415
416 def test_submodule_anon(self):
417 m1 = Module()
418 m2 = Module()
419 m1.submodules += m2
420 self.assertEqual(m1._submodules, [(m2, None)])
421
422 def test_submodule_anon_multi(self):
423 m1 = Module()
424 m2 = Module()
425 m3 = Module()
426 m1.submodules += m2, m3
427 self.assertEqual(m1._submodules, [(m2, None), (m3, None)])
428
429 def test_submodule_named(self):
430 m1 = Module()
431 m2 = Module()
432 m1.submodules.foo = m2
433 self.assertEqual(m1._submodules, [(m2, "foo")])
434
435 def test_submodule_wrong(self):
436 m = Module()
437 with self.assertRaises(TypeError,
438 msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
439 m.submodules.foo = 1
440 with self.assertRaises(TypeError,
441 msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
442 m.submodules += 1
443
444 def test_domain_named_implicit(self):
445 m = Module()
446 m.domains += ClockDomain("sync")
447 self.assertEqual(len(m._domains), 1)
448
449 def test_domain_named_explicit(self):
450 m = Module()
451 m.domains.foo = ClockDomain()
452 self.assertEqual(len(m._domains), 1)
453 self.assertEqual(m._domains[0].name, "foo")
454
455 def test_lower(self):
456 m1 = Module()
457 m1.d.comb += self.c1.eq(self.s1)
458 m2 = Module()
459 m2.d.comb += self.c2.eq(self.s2)
460 m2.d.sync += self.c3.eq(self.s3)
461 m1.submodules.foo = m2
462
463 f1 = m1.lower(platform=None)
464 self.assertRepr(f1.statements, """
465 (
466 (eq (sig c1) (sig s1))
467 )
468 """)
469 self.assertEqual(f1.drivers, {
470 None: SignalSet((self.c1,))
471 })
472 self.assertEqual(len(f1.subfragments), 1)
473 (f2, f2_name), = f1.subfragments
474 self.assertEqual(f2_name, "foo")
475 self.assertRepr(f2.statements, """
476 (
477 (eq (sig c2) (sig s2))
478 (eq (sig c3) (sig s3))
479 )
480 """)
481 self.assertEqual(f2.drivers, {
482 None: SignalSet((self.c2,)),
483 "sync": SignalSet((self.c3,))
484 })
485 self.assertEqual(len(f2.subfragments), 0)