bc5a1ceefe873231e2834104a124da8da7a37097
[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_align_to(self):
213 memory_map = MemoryMap(addr_width=16, data_width=8)
214 self.assertEqual(memory_map.add_resource("a", size=1), (0, 1))
215 self.assertEqual(memory_map.align_to(10), 0x400)
216 self.assertEqual(memory_map.add_resource("b", size=16), (0x400, 0x410))
217
218 def test_align_to_wrong(self):
219 memory_map = MemoryMap(addr_width=16, data_width=8)
220 with self.assertRaisesRegex(ValueError,
221 r"Alignment must be a non-negative integer, not -1"):
222 memory_map.align_to(-1)
223
224
225 class MemoryMapDiscoveryTestCase(unittest.TestCase):
226 def setUp(self):
227 self.root = MemoryMap(addr_width=32, data_width=32)
228 self.res1 = "res1"
229 self.root.add_resource(self.res1, size=16)
230 self.win1 = MemoryMap(addr_width=16, data_width=32)
231 self.root.add_window(self.win1)
232 self.res2 = "res2"
233 self.win1.add_resource(self.res2, size=32)
234 self.res3 = "res3"
235 self.win1.add_resource(self.res3, size=32)
236 self.res4 = "res4"
237 self.root.add_resource(self.res4, size=1)
238 self.win2 = MemoryMap(addr_width=16, data_width=8)
239 self.root.add_window(self.win2, sparse=True)
240 self.res5 = "res5"
241 self.win2.add_resource(self.res5, size=16)
242 self.win3 = MemoryMap(addr_width=16, data_width=8)
243 self.root.add_window(self.win3, sparse=False)
244 self.res6 = "res6"
245 self.win3.add_resource(self.res6, size=16)
246
247 def test_iter_all_resources(self):
248 self.assertEqual(list(self.root.all_resources()), [
249 (self.res1, (0x00000000, 0x00000010, 32)),
250 (self.res2, (0x00010000, 0x00010020, 32)),
251 (self.res3, (0x00010020, 0x00010040, 32)),
252 (self.res4, (0x00020000, 0x00020001, 32)),
253 (self.res5, (0x00030000, 0x00030010, 8)),
254 (self.res6, (0x00040000, 0x00040004, 32)),
255 ])
256
257 def test_find_resource(self):
258 for res, loc in self.root.all_resources():
259 self.assertEqual(self.root.find_resource(res), loc)
260
261 def test_find_resource_wrong(self):
262 with self.assertRaises(KeyError) as error:
263 self.root.find_resource("resNA")
264 self.assertEqual(error.exception.args, ("resNA",))
265
266 def test_decode_address(self):
267 for res, (start, end, width) in self.root.all_resources():
268 self.assertEqual(self.root.decode_address(start), res)
269 self.assertEqual(self.root.decode_address(end - 1), res)
270
271 def test_decode_address_missing(self):
272 self.assertIsNone(self.root.decode_address(0x00000100))