Remove everything deprecated in nmigen 0.1.
[nmigen.git] / nmigen / test / test_hdl_rec.py
1 from enum import Enum
2
3 from ..hdl.ast import *
4 from ..hdl.rec import *
5 from .utils import *
6
7
8 class UnsignedEnum(Enum):
9 FOO = 1
10 BAR = 2
11 BAZ = 3
12
13
14 class LayoutTestCase(FHDLTestCase):
15 def test_fields(self):
16 layout = Layout.cast([
17 ("cyc", 1),
18 ("data", signed(32)),
19 ("stb", 1, DIR_FANOUT),
20 ("ack", 1, DIR_FANIN),
21 ("info", [
22 ("a", 1),
23 ("b", 1),
24 ])
25 ])
26
27 self.assertEqual(layout["cyc"], ((1, False), DIR_NONE))
28 self.assertEqual(layout["data"], ((32, True), DIR_NONE))
29 self.assertEqual(layout["stb"], ((1, False), DIR_FANOUT))
30 self.assertEqual(layout["ack"], ((1, False), DIR_FANIN))
31 sublayout = layout["info"][0]
32 self.assertEqual(layout["info"][1], DIR_NONE)
33 self.assertEqual(sublayout["a"], ((1, False), DIR_NONE))
34 self.assertEqual(sublayout["b"], ((1, False), DIR_NONE))
35
36 def test_enum_field(self):
37 layout = Layout.cast([
38 ("enum", UnsignedEnum),
39 ("enum_dir", UnsignedEnum, DIR_FANOUT),
40 ])
41 self.assertEqual(layout["enum"], ((2, False), DIR_NONE))
42 self.assertEqual(layout["enum_dir"], ((2, False), DIR_FANOUT))
43
44 def test_range_field(self):
45 layout = Layout.cast([
46 ("range", range(0, 7)),
47 ])
48 self.assertEqual(layout["range"], ((3, False), DIR_NONE))
49
50 def test_slice_tuple(self):
51 layout = Layout.cast([
52 ("a", 1),
53 ("b", 2),
54 ("c", 3)
55 ])
56 expect = Layout.cast([
57 ("a", 1),
58 ("c", 3)
59 ])
60 self.assertEqual(layout["a", "c"], expect)
61
62 def test_wrong_field(self):
63 with self.assertRaises(TypeError,
64 msg="Field (1,) has invalid layout: should be either (name, shape) or "
65 "(name, shape, direction)"):
66 Layout.cast([(1,)])
67
68 def test_wrong_name(self):
69 with self.assertRaises(TypeError,
70 msg="Field (1, 1) has invalid name: should be a string"):
71 Layout.cast([(1, 1)])
72
73 def test_wrong_name_duplicate(self):
74 with self.assertRaises(NameError,
75 msg="Field ('a', 2) has a name that is already present in the layout"):
76 Layout.cast([("a", 1), ("a", 2)])
77
78 def test_wrong_direction(self):
79 with self.assertRaises(TypeError,
80 msg="Field ('a', 1, 0) has invalid direction: should be a Direction "
81 "instance like DIR_FANIN"):
82 Layout.cast([("a", 1, 0)])
83
84 def test_wrong_shape(self):
85 with self.assertRaises(TypeError,
86 msg="Field ('a', 'x') has invalid shape: should be castable to Shape or "
87 "a list of fields of a nested record"):
88 Layout.cast([("a", "x")])
89
90
91 class RecordTestCase(FHDLTestCase):
92 def test_basic(self):
93 r = Record([
94 ("stb", 1),
95 ("data", 32),
96 ("info", [
97 ("a", 1),
98 ("b", 1),
99 ])
100 ])
101
102 self.assertEqual(repr(r), "(rec r stb data (rec r__info a b))")
103 self.assertEqual(len(r), 35)
104 self.assertIsInstance(r.stb, Signal)
105 self.assertEqual(r.stb.name, "r__stb")
106 self.assertEqual(r["stb"].name, "r__stb")
107
108 self.assertTrue(hasattr(r, "stb"))
109 self.assertFalse(hasattr(r, "xxx"))
110
111 def test_unnamed(self):
112 r = [Record([
113 ("stb", 1)
114 ])][0]
115
116 self.assertEqual(repr(r), "(rec <unnamed> stb)")
117 self.assertEqual(r.stb.name, "stb")
118
119 def test_iter(self):
120 r = Record([
121 ("data", 4),
122 ("stb", 1),
123 ])
124
125 self.assertEqual(repr(r[0]), "(slice (rec r data stb) 0:1)")
126 self.assertEqual(repr(r[0:3]), "(slice (rec r data stb) 0:3)")
127
128 def test_wrong_field(self):
129 r = Record([
130 ("stb", 1),
131 ("ack", 1),
132 ])
133 with self.assertRaises(AttributeError,
134 msg="Record 'r' does not have a field 'en'. Did you mean one of: stb, ack?"):
135 r["en"]
136 with self.assertRaises(AttributeError,
137 msg="Record 'r' does not have a field 'en'. Did you mean one of: stb, ack?"):
138 r.en
139
140 def test_wrong_field_unnamed(self):
141 r = [Record([
142 ("stb", 1),
143 ("ack", 1),
144 ])][0]
145 with self.assertRaises(AttributeError,
146 msg="Unnamed record does not have a field 'en'. Did you mean one of: stb, ack?"):
147 r.en
148
149 def test_construct_with_fields(self):
150 ns = Signal(1)
151 nr = Record([
152 ("burst", 1)
153 ])
154 r = Record([
155 ("stb", 1),
156 ("info", [
157 ("burst", 1)
158 ])
159 ], fields={
160 "stb": ns,
161 "info": nr
162 })
163 self.assertIs(r.stb, ns)
164 self.assertIs(r.info, nr)
165
166 def test_like(self):
167 r1 = Record([("a", 1), ("b", 2)])
168 r2 = Record.like(r1)
169 self.assertEqual(r1.layout, r2.layout)
170 self.assertEqual(r2.name, "r2")
171 r3 = Record.like(r1, name="foo")
172 self.assertEqual(r3.name, "foo")
173 r4 = Record.like(r1, name_suffix="foo")
174 self.assertEqual(r4.name, "r1foo")
175
176 def test_like_modifications(self):
177 r1 = Record([("a", 1), ("b", [("s", 1)])])
178 self.assertEqual(r1.a.name, "r1__a")
179 self.assertEqual(r1.b.name, "r1__b")
180 self.assertEqual(r1.b.s.name, "r1__b__s")
181 r1.a.reset = 1
182 r1.b.s.reset = 1
183 r2 = Record.like(r1)
184 self.assertEqual(r2.a.reset, 1)
185 self.assertEqual(r2.b.s.reset, 1)
186 self.assertEqual(r2.a.name, "r2__a")
187 self.assertEqual(r2.b.name, "r2__b")
188 self.assertEqual(r2.b.s.name, "r2__b__s")
189
190 def test_slice_tuple(self):
191 r1 = Record([("a", 1), ("b", 2), ("c", 3)])
192 r2 = r1["a", "c"]
193 self.assertEqual(r2.layout, Layout([("a", 1), ("c", 3)]))
194 self.assertIs(r2.a, r1.a)
195 self.assertIs(r2.c, r1.c)
196
197
198 class ConnectTestCase(FHDLTestCase):
199 def setUp_flat(self):
200 self.core_layout = [
201 ("addr", 32, DIR_FANOUT),
202 ("data_r", 32, DIR_FANIN),
203 ("data_w", 32, DIR_FANIN),
204 ]
205 self.periph_layout = [
206 ("addr", 32, DIR_FANOUT),
207 ("data_r", 32, DIR_FANIN),
208 ("data_w", 32, DIR_FANIN),
209 ]
210
211 def setUp_nested(self):
212 self.core_layout = [
213 ("addr", 32, DIR_FANOUT),
214 ("data", [
215 ("r", 32, DIR_FANIN),
216 ("w", 32, DIR_FANIN),
217 ]),
218 ]
219 self.periph_layout = [
220 ("addr", 32, DIR_FANOUT),
221 ("data", [
222 ("r", 32, DIR_FANIN),
223 ("w", 32, DIR_FANIN),
224 ]),
225 ]
226
227 def test_flat(self):
228 self.setUp_flat()
229
230 core = Record(self.core_layout)
231 periph1 = Record(self.periph_layout)
232 periph2 = Record(self.periph_layout)
233
234 stmts = core.connect(periph1, periph2)
235 self.assertRepr(stmts, """(
236 (eq (sig periph1__addr) (sig core__addr))
237 (eq (sig periph2__addr) (sig core__addr))
238 (eq (sig core__data_r) (| (sig periph1__data_r) (sig periph2__data_r)))
239 (eq (sig core__data_w) (| (sig periph1__data_w) (sig periph2__data_w)))
240 )""")
241
242 def test_flat_include(self):
243 self.setUp_flat()
244
245 core = Record(self.core_layout)
246 periph1 = Record(self.periph_layout)
247 periph2 = Record(self.periph_layout)
248
249 stmts = core.connect(periph1, periph2, include={"addr": True})
250 self.assertRepr(stmts, """(
251 (eq (sig periph1__addr) (sig core__addr))
252 (eq (sig periph2__addr) (sig core__addr))
253 )""")
254
255 def test_flat_exclude(self):
256 self.setUp_flat()
257
258 core = Record(self.core_layout)
259 periph1 = Record(self.periph_layout)
260 periph2 = Record(self.periph_layout)
261
262 stmts = core.connect(periph1, periph2, exclude={"addr": True})
263 self.assertRepr(stmts, """(
264 (eq (sig core__data_r) (| (sig periph1__data_r) (sig periph2__data_r)))
265 (eq (sig core__data_w) (| (sig periph1__data_w) (sig periph2__data_w)))
266 )""")
267
268 def test_nested(self):
269 self.setUp_nested()
270
271 core = Record(self.core_layout)
272 periph1 = Record(self.periph_layout)
273 periph2 = Record(self.periph_layout)
274
275 stmts = core.connect(periph1, periph2)
276 self.maxDiff = None
277 self.assertRepr(stmts, """(
278 (eq (sig periph1__addr) (sig core__addr))
279 (eq (sig periph2__addr) (sig core__addr))
280 (eq (sig core__data__r) (| (sig periph1__data__r) (sig periph2__data__r)))
281 (eq (sig core__data__w) (| (sig periph1__data__w) (sig periph2__data__w)))
282 )""")
283
284 def test_wrong_include_exclude(self):
285 self.setUp_flat()
286
287 core = Record(self.core_layout)
288 periph = Record(self.periph_layout)
289
290 with self.assertRaises(AttributeError,
291 msg="Cannot include field 'foo' because it is not present in record 'core'"):
292 core.connect(periph, include={"foo": True})
293
294 with self.assertRaises(AttributeError,
295 msg="Cannot exclude field 'foo' because it is not present in record 'core'"):
296 core.connect(periph, exclude={"foo": True})
297
298 def test_wrong_direction(self):
299 recs = [Record([("x", 1)]) for _ in range(2)]
300
301 with self.assertRaises(TypeError,
302 msg="Cannot connect field 'x' of unnamed record because it does not have "
303 "a direction"):
304 recs[0].connect(recs[1])
305
306 def test_wrong_missing_field(self):
307 core = Record([("addr", 32, DIR_FANOUT)])
308 periph = Record([])
309
310 with self.assertRaises(AttributeError,
311 msg="Cannot connect field 'addr' of record 'core' to subordinate record 'periph' "
312 "because the subordinate record does not have this field"):
313 core.connect(periph)