3 from nmigen
.hdl
.ast
import *
4 from nmigen
.hdl
.rec
import *
9 class UnsignedEnum(Enum
):
15 class LayoutTestCase(FHDLTestCase
):
16 def assertFieldEqual(self
, field
, expected
):
18 shape
= Shape
.cast(shape
)
19 self
.assertEqual((shape
, dir), expected
)
21 def test_fields(self
):
22 layout
= Layout
.cast([
25 ("stb", 1, DIR_FANOUT
),
26 ("ack", 1, DIR_FANIN
),
33 self
.assertFieldEqual(layout
["cyc"], ((1, False), DIR_NONE
))
34 self
.assertFieldEqual(layout
["data"], ((32, True), DIR_NONE
))
35 self
.assertFieldEqual(layout
["stb"], ((1, False), DIR_FANOUT
))
36 self
.assertFieldEqual(layout
["ack"], ((1, False), DIR_FANIN
))
37 sublayout
= layout
["info"][0]
38 self
.assertEqual(layout
["info"][1], DIR_NONE
)
39 self
.assertFieldEqual(sublayout
["a"], ((1, False), DIR_NONE
))
40 self
.assertFieldEqual(sublayout
["b"], ((1, False), DIR_NONE
))
42 def test_enum_field(self
):
43 layout
= Layout
.cast([
44 ("enum", UnsignedEnum
),
45 ("enum_dir", UnsignedEnum
, DIR_FANOUT
),
47 self
.assertFieldEqual(layout
["enum"], ((2, False), DIR_NONE
))
48 self
.assertFieldEqual(layout
["enum_dir"], ((2, False), DIR_FANOUT
))
50 def test_range_field(self
):
51 layout
= Layout
.cast([
52 ("range", range(0, 7)),
54 self
.assertFieldEqual(layout
["range"], ((3, False), DIR_NONE
))
56 def test_slice_tuple(self
):
57 layout
= Layout
.cast([
62 expect
= Layout
.cast([
66 self
.assertEqual(layout
["a", "c"], expect
)
69 self
.assertEqual(repr(Layout([("a", unsigned(1)), ("b", signed(2))])),
70 "Layout([('a', unsigned(1)), ('b', signed(2))])")
71 self
.assertEqual(repr(Layout([("a", unsigned(1)), ("b", [("c", signed(3))])])),
72 "Layout([('a', unsigned(1)), "
73 "('b', Layout([('c', signed(3))]))])")
75 def test_wrong_field(self
):
76 with self
.assertRaisesRegex(TypeError,
77 (r
"^Field \(1,\) has invalid layout: should be either \(name, shape\) or "
78 r
"\(name, shape, direction\)$")):
81 def test_wrong_name(self
):
82 with self
.assertRaisesRegex(TypeError,
83 r
"^Field \(1, 1\) has invalid name: should be a string$"):
86 def test_wrong_name_duplicate(self
):
87 with self
.assertRaisesRegex(NameError,
88 r
"^Field \('a', 2\) has a name that is already present in the layout$"):
89 Layout
.cast([("a", 1), ("a", 2)])
91 def test_wrong_direction(self
):
92 with self
.assertRaisesRegex(TypeError,
93 (r
"^Field \('a', 1, 0\) has invalid direction: should be a Direction "
94 r
"instance like DIR_FANIN$")):
95 Layout
.cast([("a", 1, 0)])
97 def test_wrong_shape(self
):
98 with self
.assertRaisesRegex(TypeError,
99 (r
"^Field \('a', 'x'\) has invalid shape: should be castable to Shape or "
100 r
"a list of fields of a nested record$")):
101 Layout
.cast([("a", "x")])
104 class RecordTestCase(FHDLTestCase
):
105 def test_basic(self
):
115 self
.assertEqual(repr(r
), "(rec r stb data (rec r__info a b))")
116 self
.assertEqual(len(r
), 35)
117 self
.assertIsInstance(r
.stb
, Signal
)
118 self
.assertEqual(r
.stb
.name
, "r__stb")
119 self
.assertEqual(r
["stb"].name
, "r__stb")
121 self
.assertTrue(hasattr(r
, "stb"))
122 self
.assertFalse(hasattr(r
, "xxx"))
124 def test_unnamed(self
):
129 self
.assertEqual(repr(r
), "(rec <unnamed> stb)")
130 self
.assertEqual(r
.stb
.name
, "stb")
138 self
.assertEqual(repr(r
[0]), "(slice (rec r data stb) 0:1)")
139 self
.assertEqual(repr(r
[0:3]), "(slice (rec r data stb) 0:3)")
141 def test_wrong_field(self
):
146 with self
.assertRaisesRegex(AttributeError,
147 r
"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
149 with self
.assertRaisesRegex(AttributeError,
150 r
"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
153 def test_wrong_field_unnamed(self
):
158 with self
.assertRaisesRegex(AttributeError,
159 r
"^Unnamed record does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
162 def test_construct_with_fields(self
):
176 self
.assertIs(r
.stb
, ns
)
177 self
.assertIs(r
.info
, nr
)
180 r1
= Record([("a", 1), ("b", 2)])
182 self
.assertEqual(r1
.layout
, r2
.layout
)
183 self
.assertEqual(r2
.name
, "r2")
184 r3
= Record
.like(r1
, name
="foo")
185 self
.assertEqual(r3
.name
, "foo")
186 r4
= Record
.like(r1
, name_suffix
="foo")
187 self
.assertEqual(r4
.name
, "r1foo")
189 def test_like_modifications(self
):
190 r1
= Record([("a", 1), ("b", [("s", 1)])])
191 self
.assertEqual(r1
.a
.name
, "r1__a")
192 self
.assertEqual(r1
.b
.name
, "r1__b")
193 self
.assertEqual(r1
.b
.s
.name
, "r1__b__s")
197 self
.assertEqual(r2
.a
.reset
, 1)
198 self
.assertEqual(r2
.b
.s
.reset
, 1)
199 self
.assertEqual(r2
.a
.name
, "r2__a")
200 self
.assertEqual(r2
.b
.name
, "r2__b")
201 self
.assertEqual(r2
.b
.s
.name
, "r2__b__s")
203 def test_slice_tuple(self
):
204 r1
= Record([("a", 1), ("b", 2), ("c", 3)])
206 self
.assertEqual(r2
.layout
, Layout([("a", 1), ("c", 3)]))
207 self
.assertIs(r2
.a
, r1
.a
)
208 self
.assertIs(r2
.c
, r1
.c
)
210 def test_enum_decoder(self
):
211 r1
= Record([("a", UnsignedEnum
)])
212 self
.assertEqual(r1
.a
.decoder(UnsignedEnum
.FOO
), "FOO/1")
215 class ConnectTestCase(FHDLTestCase
):
216 def setUp_flat(self
):
218 ("addr", 32, DIR_FANOUT
),
219 ("data_r", 32, DIR_FANIN
),
220 ("data_w", 32, DIR_FANIN
),
222 self
.periph_layout
= [
223 ("addr", 32, DIR_FANOUT
),
224 ("data_r", 32, DIR_FANIN
),
225 ("data_w", 32, DIR_FANIN
),
228 def setUp_nested(self
):
230 ("addr", 32, DIR_FANOUT
),
232 ("r", 32, DIR_FANIN
),
233 ("w", 32, DIR_FANIN
),
236 self
.periph_layout
= [
237 ("addr", 32, DIR_FANOUT
),
239 ("r", 32, DIR_FANIN
),
240 ("w", 32, DIR_FANIN
),
247 core
= Record(self
.core_layout
)
248 periph1
= Record(self
.periph_layout
)
249 periph2
= Record(self
.periph_layout
)
251 stmts
= core
.connect(periph1
, periph2
)
252 self
.assertRepr(stmts
, """(
253 (eq (sig periph1__addr) (sig core__addr))
254 (eq (sig periph2__addr) (sig core__addr))
255 (eq (sig core__data_r) (| (sig periph1__data_r) (sig periph2__data_r)))
256 (eq (sig core__data_w) (| (sig periph1__data_w) (sig periph2__data_w)))
259 def test_flat_include(self
):
262 core
= Record(self
.core_layout
)
263 periph1
= Record(self
.periph_layout
)
264 periph2
= Record(self
.periph_layout
)
266 stmts
= core
.connect(periph1
, periph2
, include
={"addr": True})
267 self
.assertRepr(stmts
, """(
268 (eq (sig periph1__addr) (sig core__addr))
269 (eq (sig periph2__addr) (sig core__addr))
272 def test_flat_exclude(self
):
275 core
= Record(self
.core_layout
)
276 periph1
= Record(self
.periph_layout
)
277 periph2
= Record(self
.periph_layout
)
279 stmts
= core
.connect(periph1
, periph2
, exclude
={"addr": True})
280 self
.assertRepr(stmts
, """(
281 (eq (sig core__data_r) (| (sig periph1__data_r) (sig periph2__data_r)))
282 (eq (sig core__data_w) (| (sig periph1__data_w) (sig periph2__data_w)))
285 def test_nested(self
):
288 core
= Record(self
.core_layout
)
289 periph1
= Record(self
.periph_layout
)
290 periph2
= Record(self
.periph_layout
)
292 stmts
= core
.connect(periph1
, periph2
)
294 self
.assertRepr(stmts
, """(
295 (eq (sig periph1__addr) (sig core__addr))
296 (eq (sig periph2__addr) (sig core__addr))
297 (eq (sig core__data__r) (| (sig periph1__data__r) (sig periph2__data__r)))
298 (eq (sig core__data__w) (| (sig periph1__data__w) (sig periph2__data__w)))
301 def test_wrong_include_exclude(self
):
304 core
= Record(self
.core_layout
)
305 periph
= Record(self
.periph_layout
)
307 with self
.assertRaisesRegex(AttributeError,
308 r
"^Cannot include field 'foo' because it is not present in record 'core'$"):
309 core
.connect(periph
, include
={"foo": True})
311 with self
.assertRaisesRegex(AttributeError,
312 r
"^Cannot exclude field 'foo' because it is not present in record 'core'$"):
313 core
.connect(periph
, exclude
={"foo": True})
315 def test_wrong_direction(self
):
316 recs
= [Record([("x", 1)]) for _
in range(2)]
318 with self
.assertRaisesRegex(TypeError,
319 (r
"^Cannot connect field 'x' of unnamed record because it does not have "
321 recs
[0].connect(recs
[1])
323 def test_wrong_missing_field(self
):
324 core
= Record([("addr", 32, DIR_FANOUT
)])
327 with self
.assertRaisesRegex(AttributeError,
328 (r
"^Cannot connect field 'addr' of record 'core' to subordinate record 'periph' "
329 r
"because the subordinate record does not have this field$")):