7f42319f0e8b7a310d690804da8cc27956358b4d
[nmigen-soc.git] / nmigen_soc / test / test_memory.py
1 import unittest
2
3 from ..memory import _RangeMap, MemoryMap
4
5
6 class RangeMapTestCase(unittest.TestCase):
7 def test_insert(self):
8 range_map = _RangeMap()
9 range_map.insert(range(0, 10), "a")
10 range_map.insert(range(20, 21), "c")
11 range_map.insert(range(15, 16), "b")
12 range_map.insert(range(16, 20), "q")
13 self.assertEqual(range_map._keys, [
14 range(0, 10), range(15, 16), range(16, 20), range(20, 21)
15 ])
16
17 def test_overlaps(self):
18 range_map = _RangeMap()
19 range_map.insert(range(10, 20), "a")
20 self.assertEqual(range_map.overlaps(range(5, 15)), ["a"])
21 self.assertEqual(range_map.overlaps(range(15, 25)), ["a"])
22 self.assertEqual(range_map.overlaps(range(5, 25)), ["a"])
23 self.assertEqual(range_map.overlaps(range(0, 3)), [])
24 self.assertEqual(range_map.overlaps(range(0, 5)), [])
25 self.assertEqual(range_map.overlaps(range(25, 30)), [])
26
27 def test_insert_wrong_overlap(self):
28 range_map = _RangeMap()
29 range_map.insert(range(0, 10), "a")
30 with self.assertRaises(AssertionError):
31 range_map.insert(range(5, 15), "b")
32
33 def test_get(self):
34 range_map = _RangeMap()
35 range_map.insert(range(5, 15), "a")
36 self.assertEqual(range_map.get(0), None)
37 self.assertEqual(range_map.get(5), "a")
38 self.assertEqual(range_map.get(10), "a")
39 self.assertEqual(range_map.get(14), "a")
40 self.assertEqual(range_map.get(15), None)
41
42
43 class MemoryMapTestCase(unittest.TestCase):
44 def test_wrong_addr_width(self):
45 with self.assertRaisesRegex(ValueError,
46 r"Address width must be a positive integer, not -1"):
47 MemoryMap(addr_width=-1, data_width=8)
48
49 def test_wrong_data_width(self):
50 with self.assertRaisesRegex(ValueError,
51 r"Data width must be a positive integer, not -1"):
52 MemoryMap(addr_width=16, data_width=-1)
53
54 def test_wrong_alignment(self):
55 with self.assertRaisesRegex(ValueError,
56 r"Alignment must be a non-negative integer, not -1"):
57 MemoryMap(addr_width=16, data_width=8, alignment=-1)
58
59 def test_add_resource(self):
60 memory_map = MemoryMap(addr_width=16, data_width=8)
61 self.assertEqual(memory_map.add_resource("a", size=1), (0, 1))
62 self.assertEqual(memory_map.add_resource("b", size=2), (1, 3))
63
64 def test_add_resource_map_aligned(self):
65 memory_map = MemoryMap(addr_width=16, data_width=8, alignment=1)
66 self.assertEqual(memory_map.add_resource("a", size=1), (0, 2))
67 self.assertEqual(memory_map.add_resource("b", size=2), (2, 4))
68
69 def test_add_resource_explicit_aligned(self):
70 memory_map = MemoryMap(addr_width=16, data_width=8)
71 self.assertEqual(memory_map.add_resource("a", size=1), (0, 1))
72 self.assertEqual(
73 memory_map.add_resource(
74 "b", size=1, alignment=1), (2, 4))
75 self.assertEqual(memory_map.add_resource("c", size=2), (4, 6))
76
77 def test_add_resource_addr(self):
78 memory_map = MemoryMap(addr_width=16, data_width=8)
79 self.assertEqual(
80 memory_map.add_resource(
81 "a", size=1, addr=10), (10, 11))
82 self.assertEqual(memory_map.add_resource("b", size=2), (11, 13))
83
84 def test_add_resource_wrong_address(self):
85 memory_map = MemoryMap(addr_width=16, data_width=8)
86 with self.assertRaisesRegex(ValueError,
87 r"Address must be a non-negative integer, not -1"):
88 memory_map.add_resource("a", size=1, addr=-1)
89
90 def test_add_resource_wrong_address_unaligned(self):
91 memory_map = MemoryMap(addr_width=16, data_width=8, alignment=1)
92 with self.assertRaisesRegex(ValueError,
93 r"Explicitly specified address 0x1 must be "
94 r"a multiple of 0x2 bytes"):
95 memory_map.add_resource("a", size=1, addr=1)
96
97 def test_add_resource_wrong_size(self):
98 memory_map = MemoryMap(addr_width=16, data_width=8)
99 with self.assertRaisesRegex(ValueError,
100 r"Size must be a non-negative integer, not -1"):
101 memory_map.add_resource("a", size=-1)
102
103 def test_add_resource_wrong_alignment(self):
104 memory_map = MemoryMap(addr_width=16, data_width=8)
105 with self.assertRaisesRegex(ValueError,
106 r"Alignment must be a non-negative integer, not -1"):
107 memory_map.add_resource("a", size=1, alignment=-1)
108
109 def test_add_resource_wrong_out_of_bounds(self):
110 memory_map = MemoryMap(addr_width=16, data_width=8)
111 with self.assertRaisesRegex(ValueError,
112 r"Address range 0x10000\.\.0x10001 out of bounds for memory "
113 r"map spanning range 0x0\.\.0x10000 \(16 address bits\)"):
114 memory_map.add_resource("a", addr=0x10000, size=1)
115 with self.assertRaisesRegex(ValueError,
116 r"Address range 0x0\.\.0x10001 out of bounds for memory map "
117 r"spanning range 0x0\.\.0x10000 \(16 address bits\)"):
118 memory_map.add_resource("a", size=0x10001)
119
120 def test_add_resource_wrong_overlap(self):
121 memory_map = MemoryMap(addr_width=16, data_width=8)
122 memory_map.add_resource("a", size=16)
123 with self.assertRaisesRegex(ValueError,
124 r"Address range 0xa\.\.0xb overlaps with resource "
125 r"'a' at 0x0\.\.0x10"):
126 memory_map.add_resource("b", size=1, addr=10)
127
128 def test_add_resource_wrong_twice(self):
129 memory_map = MemoryMap(addr_width=16, data_width=8)
130 memory_map.add_resource("a", size=16)
131 with self.assertRaisesRegex(ValueError,
132 r"Resource 'a' is already added at address range 0x0..0x10"):
133 memory_map.add_resource("a", size=16)
134
135 def test_iter_resources(self):
136 memory_map = MemoryMap(addr_width=16, data_width=8)
137 memory_map.add_resource("a", size=1)
138 memory_map.add_resource("b", size=2)
139 self.assertEqual(list(memory_map.resources()), [
140 ("a", (0, 1)),
141 ("b", (1, 3)),
142 ])
143
144 def test_add_window(self):
145 memory_map = MemoryMap(addr_width=16, data_width=8)
146 self.assertEqual(memory_map.add_resource("a", size=1), (0, 1))
147 self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10,
148 data_width=8)),
149 (0x400, 0x800, 1))
150 self.assertEqual(memory_map.add_resource("b", size=1), (0x800, 0x801))
151
152 def test_add_window_sparse(self):
153 memory_map = MemoryMap(addr_width=16, data_width=32)
154 self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10,
155 data_width=8),
156 sparse=True),
157 (0, 0x400, 1))
158
159 def test_add_window_dense(self):
160 memory_map = MemoryMap(addr_width=16, data_width=32)
161 self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10,
162 data_width=8),
163 sparse=False),
164 (0, 0x100, 4))
165
166 def test_add_window_wrong_window(self):
167 memory_map = MemoryMap(addr_width=16, data_width=8)
168 with self.assertRaisesRegex(TypeError,
169 r"Window must be a MemoryMap, not 'a'"):
170 memory_map.add_window("a")
171
172 def test_add_window_wrong_wider(self):
173 memory_map = MemoryMap(addr_width=16, data_width=8)
174 with self.assertRaisesRegex(ValueError,
175 r"Window has data width 16, and cannot be added to a memory "
176 r"map with data width 8"):
177 memory_map.add_window(MemoryMap(addr_width=10, data_width=16))
178
179 def test_add_window_wrong_no_mode(self):
180 memory_map = MemoryMap(addr_width=16, data_width=16)
181 with self.assertRaisesRegex(ValueError,
182 r"Address translation mode must be explicitly specified "
183 r"when adding a window with data width 8 to a memory map "
184 r"with data width 16"):
185 memory_map.add_window(MemoryMap(addr_width=10, data_width=8))
186
187 def test_add_window_wrong_ratio(self):
188 memory_map = MemoryMap(addr_width=16, data_width=16)
189 with self.assertRaisesRegex(ValueError,
190 r"Dense addressing cannot be used because the memory map "
191 r"data width 16 is not an integer multiple of window data "
192 r"width 7"):
193 memory_map.add_window(MemoryMap(addr_width=10, data_width=7),
194 sparse=False)
195
196 def test_add_window_wrong_overlap(self):
197 memory_map = MemoryMap(addr_width=16, data_width=8)
198 memory_map.add_window(MemoryMap(addr_width=10, data_width=8))
199 with self.assertRaisesRegex(ValueError,
200 r"Address range 0x200\.\.0x600 overlaps with window "
201 r"<nmigen_soc\.memory\.MemoryMap object at .+?> "
202 r"at 0x0\.\.0x400"):
203 memory_map.add_window(MemoryMap(addr_width=10, data_width=8),
204 addr=0x200)
205
206 def test_add_window_wrong_twice(self):
207 memory_map = MemoryMap(addr_width=16, data_width=8)
208 window = MemoryMap(addr_width=10, data_width=8)
209 memory_map.add_window(window)
210 with self.assertRaisesRegex(ValueError,
211 r"Window <nmigen_soc\.memory\.MemoryMap object at .+?> is "
212 r"already added at address range 0x0\.\.0x400"):
213 memory_map.add_window(window)
214
215 def test_iter_windows(self):
216 memory_map = MemoryMap(addr_width=16, data_width=16)
217 window_1 = MemoryMap(addr_width=10, data_width=8)
218 memory_map.add_window(window_1, sparse=False)
219 window_2 = MemoryMap(addr_width=12, data_width=16)
220 memory_map.add_window(window_2)
221 self.assertEqual(list(memory_map.windows()), [
222 (window_1, (0, 0x200, 2)),
223 (window_2, (0x1000, 0x2000, 1)),
224 ])
225
226 def test_iter_window_patterns(self):
227 memory_map = MemoryMap(addr_width=16, data_width=16)
228 window_1 = MemoryMap(addr_width=10, data_width=8)
229 memory_map.add_window(window_1, sparse=False)
230 window_2 = MemoryMap(addr_width=12, data_width=16)
231 memory_map.add_window(window_2)
232 self.assertEqual(list(memory_map.window_patterns()), [
233 (window_1, ("000000----------", 2)),
234 (window_2, ("0001------------", 1)),
235 ])
236
237 def test_align_to(self):
238 memory_map = MemoryMap(addr_width=16, data_width=8)
239 self.assertEqual(memory_map.add_resource("a", size=1), (0, 1))
240 self.assertEqual(memory_map.align_to(10), 0x400)
241 self.assertEqual(memory_map.add_resource("b", size=16), (0x400, 0x410))
242
243 def test_align_to_wrong(self):
244 memory_map = MemoryMap(addr_width=16, data_width=8)
245 with self.assertRaisesRegex(ValueError,
246 r"Alignment must be a non-negative integer, not -1"):
247 memory_map.align_to(-1)
248
249
250 class MemoryMapDiscoveryTestCase(unittest.TestCase):
251 def setUp(self):
252 self.root = MemoryMap(addr_width=32, data_width=32)
253 self.res1 = "res1"
254 self.root.add_resource(self.res1, size=16)
255 self.win1 = MemoryMap(addr_width=16, data_width=32)
256 self.root.add_window(self.win1)
257 self.res2 = "res2"
258 self.win1.add_resource(self.res2, size=32)
259 self.res3 = "res3"
260 self.win1.add_resource(self.res3, size=32)
261 self.res4 = "res4"
262 self.root.add_resource(self.res4, size=1)
263 self.win2 = MemoryMap(addr_width=16, data_width=8)
264 self.root.add_window(self.win2, sparse=True)
265 self.res5 = "res5"
266 self.win2.add_resource(self.res5, size=16)
267 self.win3 = MemoryMap(addr_width=16, data_width=8)
268 self.root.add_window(self.win3, sparse=False)
269 self.res6 = "res6"
270 self.win3.add_resource(self.res6, size=16)
271
272 def test_iter_all_resources(self):
273 self.assertEqual(list(self.root.all_resources()), [
274 (self.res1, (0x00000000, 0x00000010, 32)),
275 (self.res2, (0x00010000, 0x00010020, 32)),
276 (self.res3, (0x00010020, 0x00010040, 32)),
277 (self.res4, (0x00020000, 0x00020001, 32)),
278 (self.res5, (0x00030000, 0x00030010, 8)),
279 (self.res6, (0x00040000, 0x00040004, 32)),
280 ])
281
282 def test_find_resource(self):
283 for res, loc in self.root.all_resources():
284 self.assertEqual(self.root.find_resource(res), loc)
285
286 def test_find_resource_wrong(self):
287 with self.assertRaises(KeyError) as error:
288 self.root.find_resource("resNA")
289 self.assertEqual(error.exception.args, ("resNA",))
290
291 def test_decode_address(self):
292 for res, (start, end, width) in self.root.all_resources():
293 self.assertEqual(self.root.decode_address(start), res)
294 self.assertEqual(self.root.decode_address(end - 1), res)
295
296 def test_decode_address_missing(self):
297 self.assertIsNone(self.root.decode_address(0x00000100))