cores.litedram: move name conflict detection to the builder.
[lambdasoc.git] / lambdasoc / test / test_cores_litedram.py
1 # nmigen: UnusedElaboratable=no
2
3 import unittest
4
5 from nmigen_soc.memory import MemoryMap
6
7 from litedram.modules import SDRAMModule
8
9 from ..cores import litedram
10
11
12 class DummyConfig(litedram.Config):
13 phy_name = "dummy"
14
15
16 class ConfigTestCase(unittest.TestCase):
17 def test_simple(self):
18 cfg = DummyConfig(
19 memtype = "DDR3",
20 module_name = "MT41K256M16",
21 module_bytes = 2,
22 module_ranks = 1,
23 input_clk_freq = int(100e6),
24 user_clk_freq = int(70e6),
25 input_domain = "input",
26 user_domain = "user",
27 user_data_width = 32,
28 cmd_buffer_depth = 8,
29 csr_data_width = 32,
30 )
31 self.assertEqual(cfg.memtype, "DDR3")
32 self.assertEqual(cfg.module_name, "MT41K256M16")
33 self.assertEqual(cfg.module_bytes, 2)
34 self.assertEqual(cfg.module_ranks, 1)
35 self.assertEqual(cfg.phy_name, "dummy")
36 self.assertEqual(cfg.input_clk_freq, int(100e6))
37 self.assertEqual(cfg.user_clk_freq, int(70e6))
38 self.assertEqual(cfg.input_domain, "input")
39 self.assertEqual(cfg.user_domain, "user")
40 self.assertEqual(cfg.user_data_width, 32)
41 self.assertEqual(cfg.cmd_buffer_depth, 8)
42 self.assertEqual(cfg.csr_data_width, 32)
43
44 def test_get_module(self):
45 cfg = DummyConfig(
46 memtype = "DDR3",
47 module_name = "MT41K256M16",
48 module_bytes = 2,
49 module_ranks = 1,
50 input_clk_freq = int(100e6),
51 user_clk_freq = int(70e6),
52 )
53 module = cfg.get_module()
54 self.assertIsInstance(module, SDRAMModule)
55
56 def test_wrong_memtype(self):
57 with self.assertRaisesRegex(ValueError,
58 r"Unsupported DRAM type, must be one of \"DDR2\", \"DDR3\" or \"DDR4\", "
59 r"not 'foo'"):
60 cfg = DummyConfig(
61 memtype = "foo",
62 module_name = "MT41K256M16",
63 module_bytes = 2,
64 module_ranks = 1,
65 input_clk_freq = int(100e6),
66 user_clk_freq = int(70e6),
67 )
68
69 def test_wrong_module_name(self):
70 with self.assertRaisesRegex(ValueError,
71 r"Module name must be a string, not 42"):
72 cfg = DummyConfig(
73 memtype = "DDR3",
74 module_name = 42,
75 module_bytes = 2,
76 module_ranks = 1,
77 input_clk_freq = int(100e6),
78 user_clk_freq = int(70e6),
79 )
80
81 def test_wrong_module_bytes(self):
82 with self.assertRaisesRegex(ValueError,
83 r"Number of byte groups must be a positive integer, not 'foo'"):
84 cfg = DummyConfig(
85 memtype = "DDR3",
86 module_name = "MT41K256M16",
87 module_bytes = "foo",
88 module_ranks = 1,
89 input_clk_freq = int(100e6),
90 user_clk_freq = int(70e6),
91 )
92
93 def test_wrong_module_ranks(self):
94 with self.assertRaisesRegex(ValueError,
95 r"Number of ranks must be a positive integer, not 'foo'"):
96 cfg = DummyConfig(
97 memtype = "DDR3",
98 module_name = "MT41K256M16",
99 module_bytes = 2,
100 module_ranks = "foo",
101 input_clk_freq = int(100e6),
102 user_clk_freq = int(70e6),
103 )
104
105 def test_wrong_input_clk_freq(self):
106 with self.assertRaisesRegex(ValueError,
107 r"Input clock frequency must be a positive integer, not -1"):
108 cfg = DummyConfig(
109 memtype = "DDR3",
110 module_name = "MT41K256M16",
111 module_bytes = 2,
112 module_ranks = 1,
113 input_clk_freq = -1,
114 user_clk_freq = int(70e6),
115 )
116
117 def test_wrong_user_clk_freq(self):
118 with self.assertRaisesRegex(ValueError,
119 r"User clock frequency must be a positive integer, not -1"):
120 cfg = DummyConfig(
121 memtype = "DDR3",
122 module_name = "MT41K256M16",
123 module_bytes = 2,
124 module_ranks = 1,
125 input_clk_freq = int(100e6),
126 user_clk_freq = -1,
127 )
128
129 def test_wrong_input_domain(self):
130 with self.assertRaisesRegex(ValueError,
131 r"Input domain name must be a string, not 42"):
132 cfg = DummyConfig(
133 memtype = "DDR3",
134 module_name = "MT41K256M16",
135 module_bytes = 2,
136 module_ranks = 1,
137 input_clk_freq = int(100e6),
138 user_clk_freq = int(70e6),
139 input_domain = 42,
140 )
141
142 def test_wrong_user_domain(self):
143 with self.assertRaisesRegex(ValueError,
144 r"User domain name must be a string, not 42"):
145 cfg = DummyConfig(
146 memtype = "DDR3",
147 module_name = "MT41K256M16",
148 module_bytes = 2,
149 module_ranks = 1,
150 input_clk_freq = int(100e6),
151 user_clk_freq = int(70e6),
152 user_domain = 42,
153 )
154
155 def test_wrong_user_data_width(self):
156 with self.assertRaisesRegex(ValueError,
157 r"User port data width must be one of 8, 16, 32, 64 or 128, not 42"):
158 cfg = DummyConfig(
159 memtype = "DDR3",
160 module_name = "MT41K256M16",
161 module_bytes = 2,
162 module_ranks = 1,
163 input_clk_freq = int(100e6),
164 user_clk_freq = int(70e6),
165 user_data_width = 42,
166 )
167
168 def test_wrong_cmd_buffer_depth(self):
169 with self.assertRaisesRegex(ValueError,
170 r"Command buffer depth must be a positive integer, not 'foo'"):
171 cfg = DummyConfig(
172 memtype = "DDR3",
173 module_name = "MT41K256M16",
174 module_bytes = 2,
175 module_ranks = 1,
176 input_clk_freq = int(100e6),
177 user_clk_freq = int(70e6),
178 cmd_buffer_depth = "foo",
179 )
180
181 def test_wrong_csr_data_width(self):
182 with self.assertRaisesRegex(ValueError,
183 r"CSR data width must be one of 8, 16, 32, or 64, not 42"):
184 cfg = DummyConfig(
185 memtype = "DDR3",
186 module_name = "MT41K256M16",
187 module_bytes = 2,
188 module_ranks = 1,
189 input_clk_freq = int(100e6),
190 user_clk_freq = int(70e6),
191 csr_data_width = 42,
192 )
193
194
195 class ECP5ConfigTestCase(unittest.TestCase):
196 def test_simple(self):
197 cfg = litedram.ECP5Config(
198 memtype = "DDR3",
199 module_name = "MT41K256M16",
200 module_bytes = 2,
201 module_ranks = 1,
202 input_clk_freq = int(100e6),
203 user_clk_freq = int(70e6),
204 init_clk_freq = int(25e6),
205 )
206 self.assertEqual(cfg.init_clk_freq, int(25e6))
207 self.assertEqual(cfg.phy_name, "ECP5DDRPHY")
208
209 def test_wrong_init_clk_freq(self):
210 with self.assertRaisesRegex(ValueError,
211 r"Init clock frequency must be a positive integer, not -1"):
212 cfg = litedram.ECP5Config(
213 memtype = "DDR3",
214 module_name = "MT41K256M16",
215 module_bytes = 2,
216 module_ranks = 1,
217 input_clk_freq = int(100e6),
218 user_clk_freq = int(70e6),
219 init_clk_freq = -1,
220 )
221
222
223 class Artix7ConfigTestCase(unittest.TestCase):
224 def test_simple(self):
225 cfg = litedram.Artix7Config(
226 memtype = "DDR3",
227 speedgrade = "-1",
228 cmd_latency = 0,
229 module_name = "MT41K128M16",
230 module_bytes = 2,
231 module_ranks = 1,
232 rtt_nom = 60,
233 rtt_wr = 60,
234 ron = 34,
235 input_clk_freq = int(100e6),
236 user_clk_freq = int(100e6),
237 iodelay_clk_freq = int(200e6),
238 )
239 self.assertEqual(cfg.speedgrade, "-1")
240 self.assertEqual(cfg.cmd_latency, 0)
241 self.assertEqual(cfg.rtt_nom, 60)
242 self.assertEqual(cfg.rtt_wr, 60)
243 self.assertEqual(cfg.ron, 34)
244 self.assertEqual(cfg.iodelay_clk_freq, int(200e6))
245 self.assertEqual(cfg.phy_name, "A7DDRPHY")
246
247 def test_wrong_speedgrade(self):
248 with self.assertRaisesRegex(ValueError,
249 r"Speed grade must be one of '-1', '-2', '-2L', '-2G', '-3', "
250 r"not '-42'"):
251 cfg = litedram.Artix7Config(
252 memtype = "DDR3",
253 speedgrade = "-42",
254 cmd_latency = 0,
255 module_name = "MT41K128M16",
256 module_bytes = 2,
257 module_ranks = 1,
258 rtt_nom = 60,
259 rtt_wr = 60,
260 ron = 34,
261 input_clk_freq = int(100e6),
262 user_clk_freq = int(100e6),
263 iodelay_clk_freq = int(200e6),
264 )
265
266 def test_wrong_cmd_latency(self):
267 with self.assertRaisesRegex(ValueError,
268 r"Command latency must be a non-negative integer, not -42"):
269 cfg = litedram.Artix7Config(
270 memtype = "DDR3",
271 speedgrade = "-1",
272 cmd_latency = -42,
273 module_name = "MT41K128M16",
274 module_bytes = 2,
275 module_ranks = 1,
276 rtt_nom = 60,
277 rtt_wr = 60,
278 ron = 34,
279 input_clk_freq = int(100e6),
280 user_clk_freq = int(100e6),
281 iodelay_clk_freq = int(200e6),
282 )
283
284 def test_wrong_rtt_nom(self):
285 with self.assertRaisesRegex(ValueError,
286 r"Nominal termination impedance must be a non-negative integer, not -42"):
287 cfg = litedram.Artix7Config(
288 memtype = "DDR3",
289 speedgrade = "-1",
290 cmd_latency = 0,
291 module_name = "MT41K128M16",
292 module_bytes = 2,
293 module_ranks = 1,
294 rtt_nom = -42,
295 rtt_wr = 60,
296 ron = 34,
297 input_clk_freq = int(100e6),
298 user_clk_freq = int(100e6),
299 iodelay_clk_freq = int(200e6),
300 )
301
302 def test_wrong_rtt_wr(self):
303 with self.assertRaisesRegex(ValueError,
304 r"Write termination impedance must be a non-negative integer, not -42"):
305 cfg = litedram.Artix7Config(
306 memtype = "DDR3",
307 speedgrade = "-1",
308 cmd_latency = 0,
309 module_name = "MT41K128M16",
310 module_bytes = 2,
311 module_ranks = 1,
312 rtt_nom = 60,
313 rtt_wr = -42,
314 ron = 34,
315 input_clk_freq = int(100e6),
316 user_clk_freq = int(100e6),
317 iodelay_clk_freq = int(200e6),
318 )
319
320 def test_wrong_ron(self):
321 with self.assertRaisesRegex(ValueError,
322 r"Output driver impedance must be a non-negative integer, not -42"):
323 cfg = litedram.Artix7Config(
324 memtype = "DDR3",
325 speedgrade = "-1",
326 cmd_latency = 0,
327 module_name = "MT41K128M16",
328 module_bytes = 2,
329 module_ranks = 1,
330 rtt_nom = 60,
331 rtt_wr = 60,
332 ron = -42,
333 input_clk_freq = int(100e6),
334 user_clk_freq = int(100e6),
335 iodelay_clk_freq = int(200e6),
336 )
337
338 def test_wrong_iodelay_clk_freq(self):
339 with self.assertRaisesRegex(ValueError,
340 r"IODELAY clock frequency must be a positive integer, not -1"):
341 cfg = litedram.Artix7Config(
342 memtype = "DDR3",
343 speedgrade = "-1",
344 cmd_latency = 0,
345 module_name = "MT41K128M16",
346 module_bytes = 2,
347 module_ranks = 1,
348 rtt_nom = 60,
349 rtt_wr = 60,
350 ron = 34,
351 input_clk_freq = int(100e6),
352 user_clk_freq = int(100e6),
353 iodelay_clk_freq = -1,
354 )
355
356
357 class NativePortTestCase(unittest.TestCase):
358 def test_simple(self):
359 port = litedram.NativePort(addr_width=10, data_width=32)
360 self.assertEqual(port.addr_width, 10)
361 self.assertEqual(port.data_width, 32)
362 self.assertEqual(port.granularity, 8)
363 self.assertEqual(len(port.cmd.addr), 10)
364 self.assertEqual(len(port.w.data), 32)
365 self.assertEqual(len(port.w.we), 4)
366 self.assertEqual(len(port.r.data), 32)
367 self.assertEqual(
368 repr(port),
369 "(rec port "
370 "(rec port__cmd valid ready last we addr) "
371 "(rec port__w valid ready data we) "
372 "(rec port__r valid ready data))"
373 )
374
375 def test_memory_map(self):
376 port = litedram.NativePort(addr_width=10, data_width=32)
377 port_map = MemoryMap(addr_width=12, data_width=8)
378 port.memory_map = port_map
379 self.assertIs(port.memory_map, port_map)
380
381 def test_wrong_memory_map(self):
382 port = litedram.NativePort(addr_width=10, data_width=32)
383 with self.assertRaisesRegex(TypeError,
384 r"Memory map must be an instance of MemoryMap, not 'foo'"):
385 port.memory_map = "foo"
386
387 def test_wrong_memory_map_data_width(self):
388 port = litedram.NativePort(addr_width=10, data_width=32)
389 port_map = MemoryMap(addr_width=11, data_width=16)
390 with self.assertRaisesRegex(ValueError,
391 r"Memory map has data width 16, which is not the same as native port granularity "
392 r"8"):
393 port.memory_map = port_map
394
395 def test_wrong_memory_map_addr_width(self):
396 port = litedram.NativePort(addr_width=10, data_width=32)
397 port_map = MemoryMap(addr_width=11, data_width=8)
398 with self.assertRaisesRegex(ValueError,
399 r"Memory map has address width 11, which is not the same as native port address "
400 r"width 12 \(10 address bits \+ 2 granularity bits\)"):
401 port.memory_map = port_map
402
403
404 class CoreTestCase(unittest.TestCase):
405 def __init__(self, *args, **kwargs):
406 super().__init__(*args, **kwargs)
407 self._cfg = litedram.ECP5Config(
408 memtype = "DDR3",
409 module_name = "MT41K256M16",
410 module_bytes = 2,
411 module_ranks = 1,
412 input_clk_freq = int(100e6),
413 user_clk_freq = int(70e6),
414 init_clk_freq = int(25e6),
415 )
416
417 def test_simple(self):
418 core = litedram.Core(self._cfg)
419 self.assertIs(core.config, self._cfg)
420 self.assertEqual(core.name, "core")
421 self.assertEqual(core.size, 512 * 1024 * 1024)
422 self.assertEqual(core.user_port.addr_width, 25)
423 self.assertEqual(core.user_port.data_width, 128)
424 self.assertEqual(core.user_port.memory_map.addr_width, 29)
425 self.assertEqual(core.user_port.memory_map.data_width, 8)
426
427 def test_name_force(self):
428 core_1 = litedram.Core(self._cfg, name="core")
429 core_2 = litedram.Core(self._cfg, name="core", name_force=True)
430 self.assertEqual(core_1.name, "core")
431 self.assertEqual(core_2.name, "core")
432
433 def test_ctrl_bus_not_ready(self):
434 core = litedram.Core(self._cfg)
435 with self.assertRaisesRegex(AttributeError,
436 r"Core.build\(do_build=True\) must be called before accessing Core\.ctrl_bus"):
437 core.ctrl_bus
438
439 def test_wrong_config(self):
440 with self.assertRaisesRegex(TypeError,
441 r"Config must be an instance of litedram\.Config, not 'foo'"):
442 core = litedram.Core("foo")
443
444 def test_wrong_name(self):
445 with self.assertRaisesRegex(TypeError,
446 r"Name must be a string, not 42"):
447 core = litedram.Core(self._cfg, name=42)
448
449
450 class BuilderTestCase(unittest.TestCase):
451 def __init__(self, *args, **kwargs):
452 super().__init__(*args, **kwargs)
453 self._cfg = litedram.ECP5Config(
454 memtype = "DDR3",
455 module_name = "MT41K256M16",
456 module_bytes = 2,
457 module_ranks = 1,
458 input_clk_freq = int(100e6),
459 user_clk_freq = int(70e6),
460 init_clk_freq = int(25e6),
461 )
462
463 def test_prepare(self):
464 core = litedram.Core(self._cfg)
465 builder = litedram.Builder()
466 builder.prepare(core)
467 self.assertEqual(list(builder.namespace), ["core"])
468
469 def test_prepare_name_conflict(self):
470 core = litedram.Core(self._cfg)
471 builder = litedram.Builder()
472 builder.prepare(core)
473 with self.assertRaisesRegex(ValueError,
474 r"LiteDRAM core name 'core' has already been used for a previous build\. Building "
475 r"this instance may overwrite previous build products\. Passing `name_force=True` "
476 r"will disable this check"):
477 builder.prepare(core)
478
479 def test_prepare_name_force(self):
480 core = litedram.Core(self._cfg)
481 builder = litedram.Builder()
482 builder.prepare(core)
483 builder.prepare(core, name_force=True)
484
485 def test_prepare_wrong_core(self):
486 builder = litedram.Builder()
487 with self.assertRaisesRegex(TypeError,
488 r"LiteDRAM core must be an instance of litedram.Core, not 'foo'"):
489 builder.prepare("foo")