3 from ..hdl
.ast
import *
4 from ..hdl
.rec
import *
8 class UnsignedEnum(Enum
):
14 class LayoutTestCase(FHDLTestCase
):
15 def assertFieldEqual(self
, field
, expected
):
17 shape
= Shape
.cast(shape
)
18 self
.assertEqual((shape
, dir), expected
)
20 def test_fields(self
):
21 layout
= Layout
.cast([
24 ("stb", 1, DIR_FANOUT
),
25 ("ack", 1, DIR_FANIN
),
32 self
.assertFieldEqual(layout
["cyc"], ((1, False), DIR_NONE
))
33 self
.assertFieldEqual(layout
["data"], ((32, True), DIR_NONE
))
34 self
.assertFieldEqual(layout
["stb"], ((1, False), DIR_FANOUT
))
35 self
.assertFieldEqual(layout
["ack"], ((1, False), DIR_FANIN
))
36 sublayout
= layout
["info"][0]
37 self
.assertEqual(layout
["info"][1], DIR_NONE
)
38 self
.assertFieldEqual(sublayout
["a"], ((1, False), DIR_NONE
))
39 self
.assertFieldEqual(sublayout
["b"], ((1, False), DIR_NONE
))
41 def test_enum_field(self
):
42 layout
= Layout
.cast([
43 ("enum", UnsignedEnum
),
44 ("enum_dir", UnsignedEnum
, DIR_FANOUT
),
46 self
.assertFieldEqual(layout
["enum"], ((2, False), DIR_NONE
))
47 self
.assertFieldEqual(layout
["enum_dir"], ((2, False), DIR_FANOUT
))
49 def test_range_field(self
):
50 layout
= Layout
.cast([
51 ("range", range(0, 7)),
53 self
.assertFieldEqual(layout
["range"], ((3, False), DIR_NONE
))
55 def test_slice_tuple(self
):
56 layout
= Layout
.cast([
61 expect
= Layout
.cast([
65 self
.assertEqual(layout
["a", "c"], expect
)
68 self
.assertEqual(repr(Layout([("a", unsigned(1)), ("b", signed(2))])),
69 "Layout([('a', unsigned(1)), ('b', signed(2))])")
70 self
.assertEqual(repr(Layout([("a", unsigned(1)), ("b", [("c", signed(3))])])),
71 "Layout([('a', unsigned(1)), "
72 "('b', Layout([('c', signed(3))]))])")
74 def test_wrong_field(self
):
75 with self
.assertRaisesRegex(TypeError,
76 (r
"^Field \(1,\) has invalid layout: should be either \(name, shape\) or "
77 r
"\(name, shape, direction\)$")):
80 def test_wrong_name(self
):
81 with self
.assertRaisesRegex(TypeError,
82 r
"^Field \(1, 1\) has invalid name: should be a string$"):
85 def test_wrong_name_duplicate(self
):
86 with self
.assertRaisesRegex(NameError,
87 r
"^Field \('a', 2\) has a name that is already present in the layout$"):
88 Layout
.cast([("a", 1), ("a", 2)])
90 def test_wrong_direction(self
):
91 with self
.assertRaisesRegex(TypeError,
92 (r
"^Field \('a', 1, 0\) has invalid direction: should be a Direction "
93 r
"instance like DIR_FANIN$")):
94 Layout
.cast([("a", 1, 0)])
96 def test_wrong_shape(self
):
97 with self
.assertRaisesRegex(TypeError,
98 (r
"^Field \('a', 'x'\) has invalid shape: should be castable to Shape or "
99 r
"a list of fields of a nested record$")):
100 Layout
.cast([("a", "x")])
103 class RecordTestCase(FHDLTestCase
):
104 def test_basic(self
):
114 self
.assertEqual(repr(r
), "(rec r stb data (rec r__info a b))")
115 self
.assertEqual(len(r
), 35)
116 self
.assertIsInstance(r
.stb
, Signal
)
117 self
.assertEqual(r
.stb
.name
, "r__stb")
118 self
.assertEqual(r
["stb"].name
, "r__stb")
120 self
.assertTrue(hasattr(r
, "stb"))
121 self
.assertFalse(hasattr(r
, "xxx"))
123 def test_unnamed(self
):
128 self
.assertEqual(repr(r
), "(rec <unnamed> stb)")
129 self
.assertEqual(r
.stb
.name
, "stb")
137 self
.assertEqual(repr(r
[0]), "(slice (rec r data stb) 0:1)")
138 self
.assertEqual(repr(r
[0:3]), "(slice (rec r data stb) 0:3)")
140 def test_wrong_field(self
):
145 with self
.assertRaisesRegex(AttributeError,
146 r
"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
148 with self
.assertRaisesRegex(AttributeError,
149 r
"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
152 def test_wrong_field_unnamed(self
):
157 with self
.assertRaisesRegex(AttributeError,
158 r
"^Unnamed record does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
161 def test_construct_with_fields(self
):
175 self
.assertIs(r
.stb
, ns
)
176 self
.assertIs(r
.info
, nr
)
179 r1
= Record([("a", 1), ("b", 2)])
181 self
.assertEqual(r1
.layout
, r2
.layout
)
182 self
.assertEqual(r2
.name
, "r2")
183 r3
= Record
.like(r1
, name
="foo")
184 self
.assertEqual(r3
.name
, "foo")
185 r4
= Record
.like(r1
, name_suffix
="foo")
186 self
.assertEqual(r4
.name
, "r1foo")
188 def test_like_modifications(self
):
189 r1
= Record([("a", 1), ("b", [("s", 1)])])
190 self
.assertEqual(r1
.a
.name
, "r1__a")
191 self
.assertEqual(r1
.b
.name
, "r1__b")
192 self
.assertEqual(r1
.b
.s
.name
, "r1__b__s")
196 self
.assertEqual(r2
.a
.reset
, 1)
197 self
.assertEqual(r2
.b
.s
.reset
, 1)
198 self
.assertEqual(r2
.a
.name
, "r2__a")
199 self
.assertEqual(r2
.b
.name
, "r2__b")
200 self
.assertEqual(r2
.b
.s
.name
, "r2__b__s")
202 def test_slice_tuple(self
):
203 r1
= Record([("a", 1), ("b", 2), ("c", 3)])
205 self
.assertEqual(r2
.layout
, Layout([("a", 1), ("c", 3)]))
206 self
.assertIs(r2
.a
, r1
.a
)
207 self
.assertIs(r2
.c
, r1
.c
)
209 def test_enum_decoder(self
):
210 r1
= Record([("a", UnsignedEnum
)])
211 self
.assertEqual(r1
.a
.decoder(UnsignedEnum
.FOO
), "FOO/1")
214 class ConnectTestCase(FHDLTestCase
):
215 def setUp_flat(self
):
217 ("addr", 32, DIR_FANOUT
),
218 ("data_r", 32, DIR_FANIN
),
219 ("data_w", 32, DIR_FANIN
),
221 self
.periph_layout
= [
222 ("addr", 32, DIR_FANOUT
),
223 ("data_r", 32, DIR_FANIN
),
224 ("data_w", 32, DIR_FANIN
),
227 def setUp_nested(self
):
229 ("addr", 32, DIR_FANOUT
),
231 ("r", 32, DIR_FANIN
),
232 ("w", 32, DIR_FANIN
),
235 self
.periph_layout
= [
236 ("addr", 32, DIR_FANOUT
),
238 ("r", 32, DIR_FANIN
),
239 ("w", 32, DIR_FANIN
),
246 core
= Record(self
.core_layout
)
247 periph1
= Record(self
.periph_layout
)
248 periph2
= Record(self
.periph_layout
)
250 stmts
= core
.connect(periph1
, periph2
)
251 self
.assertRepr(stmts
, """(
252 (eq (sig periph1__addr) (sig core__addr))
253 (eq (sig periph2__addr) (sig core__addr))
254 (eq (sig core__data_r) (| (sig periph1__data_r) (sig periph2__data_r)))
255 (eq (sig core__data_w) (| (sig periph1__data_w) (sig periph2__data_w)))
258 def test_flat_include(self
):
261 core
= Record(self
.core_layout
)
262 periph1
= Record(self
.periph_layout
)
263 periph2
= Record(self
.periph_layout
)
265 stmts
= core
.connect(periph1
, periph2
, include
={"addr": True})
266 self
.assertRepr(stmts
, """(
267 (eq (sig periph1__addr) (sig core__addr))
268 (eq (sig periph2__addr) (sig core__addr))
271 def test_flat_exclude(self
):
274 core
= Record(self
.core_layout
)
275 periph1
= Record(self
.periph_layout
)
276 periph2
= Record(self
.periph_layout
)
278 stmts
= core
.connect(periph1
, periph2
, exclude
={"addr": True})
279 self
.assertRepr(stmts
, """(
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)))
284 def test_nested(self
):
287 core
= Record(self
.core_layout
)
288 periph1
= Record(self
.periph_layout
)
289 periph2
= Record(self
.periph_layout
)
291 stmts
= core
.connect(periph1
, periph2
)
293 self
.assertRepr(stmts
, """(
294 (eq (sig periph1__addr) (sig core__addr))
295 (eq (sig periph2__addr) (sig core__addr))
296 (eq (sig core__data__r) (| (sig periph1__data__r) (sig periph2__data__r)))
297 (eq (sig core__data__w) (| (sig periph1__data__w) (sig periph2__data__w)))
300 def test_wrong_include_exclude(self
):
303 core
= Record(self
.core_layout
)
304 periph
= Record(self
.periph_layout
)
306 with self
.assertRaisesRegex(AttributeError,
307 r
"^Cannot include field 'foo' because it is not present in record 'core'$"):
308 core
.connect(periph
, include
={"foo": True})
310 with self
.assertRaisesRegex(AttributeError,
311 r
"^Cannot exclude field 'foo' because it is not present in record 'core'$"):
312 core
.connect(periph
, exclude
={"foo": True})
314 def test_wrong_direction(self
):
315 recs
= [Record([("x", 1)]) for _
in range(2)]
317 with self
.assertRaisesRegex(TypeError,
318 (r
"^Cannot connect field 'x' of unnamed record because it does not have "
320 recs
[0].connect(recs
[1])
322 def test_wrong_missing_field(self
):
323 core
= Record([("addr", 32, DIR_FANOUT
)])
326 with self
.assertRaisesRegex(AttributeError,
327 (r
"^Cannot connect field 'addr' of record 'core' to subordinate record 'periph' "
328 r
"because the subordinate record does not have this field$")):