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