wishbone.bus: add Decoder.
[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(memory_map.add_resource("b", size=1, alignment=1), (2, 4))
73 self.assertEqual(memory_map.add_resource("c", size=2), (4, 6))
74
75 def test_add_resource_addr(self):
76 memory_map = MemoryMap(addr_width=16, data_width=8)
77 self.assertEqual(memory_map.add_resource("a", size=1, addr=10), (10, 11))
78 self.assertEqual(memory_map.add_resource("b", size=2), (11, 13))
79
80 def test_add_resource_wrong_address(self):
81 memory_map = MemoryMap(addr_width=16, data_width=8)
82 with self.assertRaisesRegex(ValueError,
83 r"Address must be a non-negative integer, not -1"):
84 memory_map.add_resource("a", size=1, addr=-1)
85
86 def test_add_resource_wrong_address_unaligned(self):
87 memory_map = MemoryMap(addr_width=16, data_width=8, alignment=1)
88 with self.assertRaisesRegex(ValueError,
89 r"Explicitly specified address 0x1 must be a multiple of 0x2 bytes"):
90 memory_map.add_resource("a", size=1, addr=1)
91
92 def test_add_resource_wrong_size(self):
93 memory_map = MemoryMap(addr_width=16, data_width=8)
94 with self.assertRaisesRegex(ValueError,
95 r"Size must be a non-negative integer, not -1"):
96 memory_map.add_resource("a", size=-1)
97
98 def test_add_resource_wrong_alignment(self):
99 memory_map = MemoryMap(addr_width=16, data_width=8)
100 with self.assertRaisesRegex(ValueError,
101 r"Alignment must be a non-negative integer, not -1"):
102 memory_map.add_resource("a", size=1, alignment=-1)
103
104 def test_add_resource_wrong_out_of_bounds(self):
105 memory_map = MemoryMap(addr_width=16, data_width=8)
106 with self.assertRaisesRegex(ValueError,
107 r"Address range 0x10000\.\.0x10001 out of bounds for memory map spanning "
108 r"range 0x0\.\.0x10000 \(16 address bits\)"):
109 memory_map.add_resource("a", addr=0x10000, size=1)
110 with self.assertRaisesRegex(ValueError,
111 r"Address range 0x0\.\.0x10001 out of bounds for memory map spanning "
112 r"range 0x0\.\.0x10000 \(16 address bits\)"):
113 memory_map.add_resource("a", size=0x10001)
114
115 def test_add_resource_wrong_overlap(self):
116 memory_map = MemoryMap(addr_width=16, data_width=8)
117 memory_map.add_resource("a", size=16)
118 with self.assertRaisesRegex(ValueError,
119 r"Address range 0xa\.\.0xb overlaps with resource 'a' at 0x0\.\.0x10"):
120 memory_map.add_resource("b", size=1, addr=10)
121
122 def test_add_resource_wrong_twice(self):
123 memory_map = MemoryMap(addr_width=16, data_width=8)
124 memory_map.add_resource("a", size=16)
125 with self.assertRaisesRegex(ValueError,
126 r"Resource 'a' is already added at address range 0x0..0x10"):
127 memory_map.add_resource("a", size=16)
128
129 def test_iter_resources(self):
130 memory_map = MemoryMap(addr_width=16, data_width=8)
131 memory_map.add_resource("a", size=1)
132 memory_map.add_resource("b", size=2)
133 self.assertEqual(list(memory_map.resources()), [
134 ("a", (0, 1)),
135 ("b", (1, 3)),
136 ])
137
138 def test_add_window(self):
139 memory_map = MemoryMap(addr_width=16, data_width=8)
140 self.assertEqual(memory_map.add_resource("a", size=1), (0, 1))
141 self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10, data_width=8)),
142 (0x400, 0x800, 1))
143 self.assertEqual(memory_map.add_resource("b", size=1), (0x800, 0x801))
144
145 def test_add_window_sparse(self):
146 memory_map = MemoryMap(addr_width=16, data_width=32)
147 self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10, data_width=8),
148 sparse=True),
149 (0, 0x400, 1))
150
151 def test_add_window_dense(self):
152 memory_map = MemoryMap(addr_width=16, data_width=32)
153 self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10, data_width=8),
154 sparse=False),
155 (0, 0x100, 4))
156
157 def test_add_window_wrong_window(self):
158 memory_map = MemoryMap(addr_width=16, data_width=8)
159 with self.assertRaisesRegex(TypeError,
160 r"Window must be a MemoryMap, not 'a'"):
161 memory_map.add_window("a")
162
163 def test_add_window_wrong_wider(self):
164 memory_map = MemoryMap(addr_width=16, data_width=8)
165 with self.assertRaisesRegex(ValueError,
166 r"Window has data width 16, and cannot be added to a memory map "
167 r"with data width 8"):
168 memory_map.add_window(MemoryMap(addr_width=10, data_width=16))
169
170 def test_add_window_wrong_no_mode(self):
171 memory_map = MemoryMap(addr_width=16, data_width=16)
172 with self.assertRaisesRegex(ValueError,
173 r"Address translation mode must be explicitly specified when adding "
174 r"a window with data width 8 to a memory map with data width 16"):
175 memory_map.add_window(MemoryMap(addr_width=10, data_width=8))
176
177 def test_add_window_wrong_ratio(self):
178 memory_map = MemoryMap(addr_width=16, data_width=16)
179 with self.assertRaisesRegex(ValueError,
180 r"Dense addressing cannot be used because the memory map data width "
181 r"16 is not an integer multiple of window data width 7"):
182 memory_map.add_window(MemoryMap(addr_width=10, data_width=7), sparse=False)
183
184 def test_add_window_wrong_overlap(self):
185 memory_map = MemoryMap(addr_width=16, data_width=8)
186 memory_map.add_window(MemoryMap(addr_width=10, data_width=8))
187 with self.assertRaisesRegex(ValueError,
188 r"Address range 0x200\.\.0x600 overlaps with window "
189 r"<nmigen_soc\.memory\.MemoryMap object at .+?> at 0x0\.\.0x400"):
190 memory_map.add_window(MemoryMap(addr_width=10, data_width=8), addr=0x200)
191
192 def test_add_window_wrong_twice(self):
193 memory_map = MemoryMap(addr_width=16, data_width=8)
194 window = MemoryMap(addr_width=10, data_width=8)
195 memory_map.add_window(window)
196 with self.assertRaisesRegex(ValueError,
197 r"Window <nmigen_soc\.memory\.MemoryMap object at .+?> is already added "
198 r"at address range 0x0\.\.0x400"):
199 memory_map.add_window(window)
200
201 def test_iter_windows(self):
202 memory_map = MemoryMap(addr_width=16, data_width=16)
203 window_1 = MemoryMap(addr_width=10, data_width=8)
204 memory_map.add_window(window_1, sparse=False)
205 window_2 = MemoryMap(addr_width=12, data_width=16)
206 memory_map.add_window(window_2)
207 self.assertEqual(list(memory_map.windows()), [
208 (window_1, (0, 0x200, 2)),
209 (window_2, (0x1000, 0x2000, 1)),
210 ])
211
212 def test_iter_window_patterns(self):
213 memory_map = MemoryMap(addr_width=16, data_width=16)
214 window_1 = MemoryMap(addr_width=10, data_width=8)
215 memory_map.add_window(window_1, sparse=False)
216 window_2 = MemoryMap(addr_width=12, data_width=16)
217 memory_map.add_window(window_2)
218 self.assertEqual(list(memory_map.window_patterns()), [
219 (window_1, ("000000----------", 2)),
220 (window_2, ("0001------------", 1)),
221 ])
222
223 def test_align_to(self):
224 memory_map = MemoryMap(addr_width=16, data_width=8)
225 self.assertEqual(memory_map.add_resource("a", size=1), (0, 1))
226 self.assertEqual(memory_map.align_to(10), 0x400)
227 self.assertEqual(memory_map.add_resource("b", size=16), (0x400, 0x410))
228
229 def test_align_to_wrong(self):
230 memory_map = MemoryMap(addr_width=16, data_width=8)
231 with self.assertRaisesRegex(ValueError,
232 r"Alignment must be a non-negative integer, not -1"):
233 memory_map.align_to(-1)
234
235
236 class MemoryMapDiscoveryTestCase(unittest.TestCase):
237 def setUp(self):
238 self.root = MemoryMap(addr_width=32, data_width=32)
239 self.res1 = "res1"
240 self.root.add_resource(self.res1, size=16)
241 self.win1 = MemoryMap(addr_width=16, data_width=32)
242 self.root.add_window(self.win1)
243 self.res2 = "res2"
244 self.win1.add_resource(self.res2, size=32)
245 self.res3 = "res3"
246 self.win1.add_resource(self.res3, size=32)
247 self.res4 = "res4"
248 self.root.add_resource(self.res4, size=1)
249 self.win2 = MemoryMap(addr_width=16, data_width=8)
250 self.root.add_window(self.win2, sparse=True)
251 self.res5 = "res5"
252 self.win2.add_resource(self.res5, size=16)
253 self.win3 = MemoryMap(addr_width=16, data_width=8)
254 self.root.add_window(self.win3, sparse=False)
255 self.res6 = "res6"
256 self.win3.add_resource(self.res6, size=16)
257
258 def test_iter_all_resources(self):
259 self.assertEqual(list(self.root.all_resources()), [
260 (self.res1, (0x00000000, 0x00000010, 32)),
261 (self.res2, (0x00010000, 0x00010020, 32)),
262 (self.res3, (0x00010020, 0x00010040, 32)),
263 (self.res4, (0x00020000, 0x00020001, 32)),
264 (self.res5, (0x00030000, 0x00030010, 8)),
265 (self.res6, (0x00040000, 0x00040004, 32)),
266 ])
267
268 def test_find_resource(self):
269 for res, loc in self.root.all_resources():
270 self.assertEqual(self.root.find_resource(res), loc)
271
272 def test_find_resource_wrong(self):
273 with self.assertRaises(KeyError) as error:
274 self.root.find_resource("resNA")
275 self.assertEqual(error.exception.args, ("resNA",))
276
277 def test_decode_address(self):
278 for res, (start, end, width) in self.root.all_resources():
279 self.assertEqual(self.root.decode_address(start), res)
280 self.assertEqual(self.root.decode_address(end - 1), res)
281
282 def test_decode_address_missing(self):
283 self.assertIsNone(self.root.decode_address(0x00000100))