cores.litedram: move memory map population to _populate_ctrl_map().
[lambdasoc.git] / lambdasoc / cores / litedram.py
1 from abc import ABCMeta, abstractmethod
2 import csv
3 import jinja2
4 import os
5 import re
6 import textwrap
7
8 from nmigen import *
9 from nmigen import tracer
10 from nmigen.build.run import BuildPlan, BuildProducts
11 from nmigen.utils import log2_int
12
13 from nmigen_soc import wishbone
14 from nmigen_soc.memory import MemoryMap
15
16 from .. import __version__
17
18
19 __all__ = [
20 "Config", "ECP5Config", "Artix7Config",
21 "NativePort", "Core",
22 "Builder",
23 ]
24
25
26 class Config(metaclass=ABCMeta):
27 _doc_template = """
28 {description}
29
30 Parameters
31 ----------
32 memtype : str
33 DRAM type (e.g. `"DDR3"`).
34 module_name : str
35 DRAM module name.
36 module_bytes : int
37 Number of byte groups of the DRAM interface.
38 module_ranks : int
39 Number of ranks. A rank is a set of DRAM chips that are connected to the same CS pin.
40 input_clk_freq : int
41 Frequency of the input clock, which drives the internal PLL.
42 user_clk_freq : int
43 Frequency of the user clock, which is generated by the internal PLL.
44 input_domain : str
45 Input clock domain. Defaults to `"litedram_input"`.
46 user_domain : str
47 User clock domain. Defaults to `"litedram_user"`.
48 user_data_width : int
49 User port data width. Defaults to 128.
50 cmd_buffer_depth : int
51 Command buffer depth. Defaults to 16.
52 csr_data_width : int
53 CSR bus data width. Defaults to 32.
54 {parameters}
55 """
56
57 __doc__ = _doc_template.format(
58 description="""
59 LiteDRAM base configuration.
60 """.strip(),
61 parameters="",
62 )
63 def __init__(self, *,
64 memtype,
65 module_name,
66 module_bytes,
67 module_ranks,
68 input_clk_freq,
69 user_clk_freq,
70 input_domain = "litedram_input",
71 user_domain = "litedram_user",
72 user_data_width = 128,
73 cmd_buffer_depth = 16,
74 csr_data_width = 32):
75
76 if memtype == "DDR2":
77 rate = "1:2"
78 elif memtype in {"DDR3", "DDR4"}:
79 rate = "1:4"
80 else:
81 raise ValueError("Unsupported DRAM type, must be one of \"DDR2\", \"DDR3\" or "
82 "\"DDR4\", not {!r}"
83 .format(memtype))
84
85 if not isinstance(module_name, str):
86 raise ValueError("Module name must be a string, not {!r}"
87 .format(module_name))
88 if not isinstance(module_bytes, int) or module_bytes <= 0:
89 raise ValueError("Number of byte groups must be a positive integer, not {!r}"
90 .format(module_bytes))
91 if not isinstance(module_ranks, int) or module_ranks <= 0:
92 raise ValueError("Number of ranks must be a positive integer, not {!r}"
93 .format(module_ranks))
94 if not isinstance(input_clk_freq, int) or input_clk_freq <= 0:
95 raise ValueError("Input clock frequency must be a positive integer, not {!r}"
96 .format(input_clk_freq))
97 if not isinstance(user_clk_freq, int) or user_clk_freq <= 0:
98 raise ValueError("User clock frequency must be a positive integer, not {!r}"
99 .format(user_clk_freq))
100 if not isinstance(input_domain, str):
101 raise ValueError("Input domain name must be a string, not {!r}"
102 .format(input_domain))
103 if not isinstance(user_domain, str):
104 raise ValueError("User domain name must be a string, not {!r}"
105 .format(user_domain))
106 if user_data_width not in {8, 16, 32, 64, 128}:
107 raise ValueError("User port data width must be one of 8, 16, 32, 64 or 128, "
108 "not {!r}"
109 .format(user_data_width))
110 if not isinstance(cmd_buffer_depth, int) or cmd_buffer_depth <= 0:
111 raise ValueError("Command buffer depth must be a positive integer, not {!r}"
112 .format(cmd_buffer_depth))
113 if csr_data_width not in {8, 16, 32, 64}:
114 raise ValueError("CSR data width must be one of 8, 16, 32, or 64, not {!r}"
115 .format(csr_data_width))
116
117 self.memtype = memtype
118 self._rate = rate
119 self.module_name = module_name
120 self.module_bytes = module_bytes
121 self.module_ranks = module_ranks
122 self.input_clk_freq = input_clk_freq
123 self.user_clk_freq = user_clk_freq
124 self.input_domain = input_domain
125 self.user_domain = user_domain
126 self.user_data_width = user_data_width
127 self.cmd_buffer_depth = cmd_buffer_depth
128 self.csr_data_width = csr_data_width
129
130 @property
131 @abstractmethod
132 def phy_name(self):
133 """LiteDRAM PHY name.
134 """
135 raise NotImplementedError
136
137 def get_module(self):
138 """Get DRAM module description.
139
140 Return value
141 ------------
142 An instance of :class:`litedram.modules.SDRAMModule`, describing its geometry and timings.
143 """
144 import litedram.modules
145 module_class = getattr(litedram.modules, self.module_name)
146 module = module_class(
147 clk_freq = self.user_clk_freq,
148 rate = self._rate,
149 )
150 assert module.memtype == self.memtype
151 return module
152
153 def request_pins(self, platform, name, number):
154 """Request DRAM pins.
155
156 This helper requests the DRAM pins with `dir="-"` and `xdr=0`, because LiteDRAM already
157 provides its own I/O buffers.
158
159 Arguments
160 ---------
161 platform : :class:`nmigen.build.Platform`
162 Target platform.
163 name : str
164 DRAM resource name.
165 number : int
166 DRAM resource number.
167
168 Return value
169 ------------
170 A :class:`Record` providing raw access to DRAM pins.
171 """
172 res = platform.lookup(name, number)
173 return platform.request(
174 name, number,
175 dir={io.name: "-" for io in res.ios},
176 xdr={io.name: 0 for io in res.ios},
177 )
178
179
180 class ECP5Config(Config):
181 phy_name = "ECP5DDRPHY"
182
183 __doc__ = Config._doc_template.format(
184 description = """
185 LiteDRAM configuration for ECP5 FPGAs.
186 """.strip(),
187 parameters = r"""
188 init_clk_freq : int
189 Frequency of the PHY initialization clock, which is generated by the internal PLL.
190 """.strip(),
191 )
192 def __init__(self, *, init_clk_freq, **kwargs):
193 super().__init__(**kwargs)
194
195 if not isinstance(init_clk_freq, int) or init_clk_freq <= 0:
196 raise ValueError("Init clock frequency must be a positive integer, not {!r}"
197 .format(init_clk_freq))
198 self.init_clk_freq = init_clk_freq
199
200
201 class Artix7Config(Config):
202 phy_name = "A7DDRPHY"
203
204 __doc__ = Config._doc_template.format(
205 description = """
206 LiteDRAM configuration for Artix 7 FPGAs.
207 """.strip(),
208 parameters = r"""
209 speedgrade : str
210 FPGA speed grade (e.g. "-1").
211 cmd_latency : int
212 Command additional latency.
213 rtt_nom : int
214 Nominal termination impedance.
215 rtt_wr : int
216 Write termination impedance.
217 ron : int
218 Output driver impedance.
219 iodelay_clk_freq : int
220 IODELAY reference clock frequency.
221 """.strip(),
222 )
223 def __init__(self, *,
224 speedgrade,
225 cmd_latency,
226 rtt_nom,
227 rtt_wr,
228 ron,
229 iodelay_clk_freq,
230 **kwargs):
231 super().__init__(**kwargs)
232
233 speedgrades = ("-1", "-2", "-2L", "-2G", "-3")
234 if speedgrade not in speedgrades:
235 raise ValueError("Speed grade must be one of \'{}\', not {!r}"
236 .format("\', \'".join(speedgrades), speedgrade))
237 if not isinstance(cmd_latency, int) or cmd_latency < 0:
238 raise ValueError("Command latency must be a non-negative integer, not {!r}"
239 .format(cmd_latency))
240 if not isinstance(rtt_nom, int) or rtt_nom < 0:
241 raise ValueError("Nominal termination impedance must be a non-negative integer, "
242 "not {!r}"
243 .format(rtt_nom))
244 if not isinstance(rtt_wr, int) or rtt_wr < 0:
245 raise ValueError("Write termination impedance must be a non-negative integer, "
246 "not {!r}"
247 .format(rtt_wr))
248 if not isinstance(ron, int) or ron < 0:
249 raise ValueError("Output driver impedance must be a non-negative integer, "
250 "not {!r}"
251 .format(ron))
252 if not isinstance(iodelay_clk_freq, int) or iodelay_clk_freq <= 0:
253 raise ValueError("IODELAY clock frequency must be a positive integer, not {!r}"
254 .format(iodelay_clk_freq))
255
256 self.speedgrade = speedgrade
257 self.cmd_latency = cmd_latency
258 self.rtt_nom = rtt_nom
259 self.rtt_wr = rtt_wr
260 self.ron = ron
261 self.iodelay_clk_freq = iodelay_clk_freq
262
263
264 class NativePort(Record):
265 """LiteDRAM native port interface.
266
267 In the "Attributes" section, port directions are given from the point of view of user logic.
268
269 Parameters
270 ----------
271 addr_width : int
272 Port address width.
273 data_width : int
274 Port data width.
275
276 Attributes
277 ----------
278 granularity : int
279 Port granularity, i.e. its smallest transferable unit of data. LiteDRAM native ports have a
280 granularity of 8 bits.
281 cmd.valid : Signal(), in
282 Command valid.
283 cmd.ready : Signal(), out
284 Command ready. Commands are accepted when `cmd.valid` and `cmd.ready` are both asserted.
285 cmd.last : Signal(), in
286 Command last. Indicates the last command of a burst.
287 cmd.we : Signal(), in
288 Command write enable. Indicates that this command is a write.
289 cmd.addr : Signal(addr_width), in
290 Command address.
291 w.valid : Signal(), in
292 Write valid.
293 w.ready : Signal(), out
294 Write ready. Write data is accepted when `w.valid` and `w.ready` are both asserted.
295 w.data : Signal(data_width), in
296 Write data.
297 w.we : Signal(data_width // granularity), bitmask, in
298 Write mask. Indicates which bytes in `w.data` are valid.
299 r.valid : Signal(), out
300 Read valid.
301 r.ready : Signal(), in
302 Read ready. Read data is consumed when `r.valid` and `r.ready` are both asserted.
303 r.data : Signal(data_width), out
304 Read data.
305 """
306 def __init__(self, *, addr_width, data_width, name=None, src_loc_at=0):
307 if not isinstance(addr_width, int) or addr_width <= 0:
308 raise ValueError("Address width must be a positive integer, not {!r}"
309 .format(addr_width))
310 if not isinstance(data_width, int) or data_width <= 0 or data_width & data_width - 1:
311 raise ValueError("Data width must be a positive power of two integer, not {!r}"
312 .format(data_width))
313
314 self.addr_width = addr_width
315 self.data_width = data_width
316 self.granularity = 8
317 self._map = None
318
319 super().__init__([
320 ("cmd", [
321 ("valid", 1),
322 ("ready", 1),
323 ("last", 1),
324 ("we", 1),
325 ("addr", addr_width),
326 ]),
327 ("w", [
328 ("valid", 1),
329 ("ready", 1),
330 ("data", data_width),
331 ("we", data_width // self.granularity),
332 ]),
333 ("r", [
334 ("valid", 1),
335 ("ready", 1),
336 ("data", data_width),
337 ]),
338 ], name=name, src_loc_at=1 + src_loc_at)
339
340 @property
341 def memory_map(self):
342 """Map of the native port.
343
344 Return value
345 ------------
346 An instance of :class:`nmigen_soc.memory.MemoryMap`.
347
348 Exceptions
349 ----------
350 Raises an :exn:`AttributeError` if the port does not have a memory map.
351 """
352 if self._map is None:
353 raise AttributeError("Native port {!r} does not have a memory map"
354 .format(self))
355 return self._map
356
357 @memory_map.setter
358 def memory_map(self, memory_map):
359 if not isinstance(memory_map, MemoryMap):
360 raise TypeError("Memory map must be an instance of MemoryMap, not {!r}"
361 .format(memory_map))
362 if memory_map.data_width != 8:
363 raise ValueError("Memory map has data width {}, which is not the same as native port "
364 "granularity {}"
365 .format(memory_map.data_width, 8))
366 granularity_bits = log2_int(self.data_width // 8)
367 if memory_map.addr_width != max(1, self.addr_width + granularity_bits):
368 raise ValueError("Memory map has address width {}, which is not the same as native "
369 "port address width {} ({} address bits + {} granularity bits)"
370 .format(memory_map.addr_width, self.addr_width + granularity_bits,
371 self.addr_width, granularity_bits))
372 memory_map.freeze()
373 self._map = memory_map
374
375
376 class Core(Elaboratable):
377 """An nMigen wrapper for a standalone LiteDRAM core.
378
379 Parameters
380 ----------
381 config : :class:`Config`
382 LiteDRAM configuration.
383 pins : :class:`nmigen.lib.io.Pin`
384 Optional. DRAM pins. See :class:`nmigen_boards.resources.DDR3Resource` for layout.
385 name : str
386 Optional. Name of the LiteDRAM core. If ``None`` (default) the name is inferred from the
387 name of the variable this instance is assigned to.
388 name_force: bool
389 Force name. If ``True``, no exception will be raised in case of a name collision with a
390 previous LiteDRAM instance. Defaults to ``False``.
391
392 Attributes
393 ----------
394 name : str
395 Name of the LiteDRAM core.
396 size : int
397 DRAM size, in bytes.
398 user_port : :class:`NativePort`
399 User port. Provides access to the DRAM storage.
400
401 Exceptions
402 ----------
403 Raises a :exn:`ValueError` if ``name`` collides with the name given to a previous LiteDRAM
404 instance and ``name_force`` is ``False``.
405 """
406 def __init__(self, config, *, pins=None, name=None, name_force=False, src_loc_at=0):
407 if not isinstance(config, Config):
408 raise TypeError("Config must be an instance of litedram.Config, "
409 "not {!r}"
410 .format(config))
411 self.config = config
412
413 if name is not None and not isinstance(name, str):
414 raise TypeError("Name must be a string, not {!r}".format(name))
415 self.name = name or tracer.get_var_name(depth=2 + src_loc_at)
416
417 module = config.get_module()
418 size = config.module_bytes \
419 * 2**( module.geom_settings.bankbits
420 + module.geom_settings.rowbits
421 + module.geom_settings.colbits)
422
423 self.size = size
424
425 user_addr_width = module.geom_settings.rowbits \
426 + module.geom_settings.colbits \
427 + log2_int(module.nbanks) \
428 + max(log2_int(config.module_ranks), 1)
429
430 self.user_port = NativePort(
431 addr_width = user_addr_width - log2_int(config.user_data_width // 8),
432 data_width = config.user_data_width,
433 )
434 user_map = MemoryMap(addr_width=user_addr_width, data_width=8)
435 user_map.add_resource("user_port_0", size=size)
436 self.user_port.memory_map = user_map
437
438 self._ctrl_bus = None
439 self._pins = pins
440
441 @property
442 def ctrl_bus(self):
443 """Control bus interface.
444
445 *Please note that accesses to the CSRs exposed by this interface are not atomic.*
446
447 The memory map of this interface is populated by reading the ``{{self.name}}_csr.csv``
448 file from the build products.
449
450 Return value
451 ------------
452 An instance of :class:`nmigen_soc.wishbone.Interface`.
453
454 Exceptions
455 ----------
456 Raises an :exn:`AttributeError` if this getter is called before LiteDRAM is built (i.e.
457 before :meth:`Core.build` is called with `do_build=True`).
458 """
459 if self._ctrl_bus is None:
460 raise AttributeError("Control bus memory map has not been populated. "
461 "Core.build(do_build=True) must be called before accessing "
462 "Core.ctrl_bus")
463 return self._ctrl_bus
464
465 def _populate_ctrl_map(self, build_products):
466 if not isinstance(build_products, BuildProducts):
467 raise TypeError("Build products must be an instance of BuildProducts, not {!r}"
468 .format(build_products))
469
470 # LiteDRAM's Wishbone to CSR bridge has a granularity of 8 bits.
471 ctrl_map = MemoryMap(addr_width=1, data_width=8)
472
473 csr_csv = build_products.get(f"{self.name}_csr.csv", mode="t")
474 for row in csv.reader(csr_csv.split("\n"), delimiter=","):
475 if not row or row[0][0] == "#": continue
476 res_type, res_name, addr, size, attrs = row
477 if res_type == "csr_register":
478 ctrl_map.add_resource(
479 res_name,
480 addr = int(addr, 16),
481 size = int(size, 10) * self.config.csr_data_width // ctrl_map.data_width,
482 extend = True,
483 )
484
485 self._ctrl_bus = wishbone.Interface(
486 addr_width = ctrl_map.addr_width
487 - log2_int(self.config.csr_data_width // ctrl_map.data_width),
488 data_width = self.config.csr_data_width,
489 granularity = ctrl_map.data_width,
490 )
491 self._ctrl_bus.memory_map = ctrl_map
492
493 def build(self, builder, *, do_build=True, build_dir="build/litedram", sim=False,
494 name_force=False):
495 """Build the LiteDRAM core.
496
497 Arguments
498 ---------
499 builder: :class:`litedram.Builder`
500 Builder instance.
501 do_build : bool
502 Execute the build locally. Defaults to ``True``.
503 build_dir : str
504 Root build directory. Defaults to ``"build/litedram"``.
505 sim : bool
506 Do the build in simulation mode (i.e. by replacing the PHY with a model). Defaults to
507 ``False``.
508 name_force : bool
509 Ignore builder name conflicts. Defaults to ``False``.
510
511 Return value
512 ------------
513 An instance of :class:`nmigen.build.run.LocalBuildProducts` if ``do_build`` is ``True``.
514 Otherwise, an instance of :class:``nmigen.build.run.BuildPlan``.
515 """
516 if not isinstance(builder, Builder):
517 raise TypeError("Builder must be an instance of litedram.Builder, not {!r}"
518 .format(builder))
519
520 plan = builder.prepare(self, sim=sim, name_force=name_force)
521 if not do_build:
522 return plan
523
524 products = plan.execute_local(build_dir)
525 self._populate_ctrl_map(products)
526 return products
527
528 def elaborate(self, platform):
529 core_kwargs = {
530 "i_clk" : ClockSignal(self.config.input_domain),
531 "i_rst" : ResetSignal(self.config.input_domain),
532 "o_user_clk" : ClockSignal(self.config.user_domain),
533 "o_user_rst" : ResetSignal(self.config.user_domain),
534
535 "i_wb_ctrl_adr" : self.ctrl_bus.adr,
536 "i_wb_ctrl_dat_w" : self.ctrl_bus.dat_w,
537 "o_wb_ctrl_dat_r" : self.ctrl_bus.dat_r,
538 "i_wb_ctrl_sel" : self.ctrl_bus.sel,
539 "i_wb_ctrl_cyc" : self.ctrl_bus.cyc,
540 "i_wb_ctrl_stb" : self.ctrl_bus.stb,
541 "o_wb_ctrl_ack" : self.ctrl_bus.ack,
542 "i_wb_ctrl_we" : self.ctrl_bus.we,
543
544 "i_user_port_0_cmd_valid" : self.user_port.cmd.valid,
545 "o_user_port_0_cmd_ready" : self.user_port.cmd.ready,
546 "i_user_port_0_cmd_we" : self.user_port.cmd.we,
547 "i_user_port_0_cmd_addr" : self.user_port.cmd.addr,
548 "i_user_port_0_wdata_valid" : self.user_port.w.valid,
549 "o_user_port_0_wdata_ready" : self.user_port.w.ready,
550 "i_user_port_0_wdata_we" : self.user_port.w.we,
551 "i_user_port_0_wdata_data" : self.user_port.w.data,
552 "o_user_port_0_rdata_valid" : self.user_port.r.valid,
553 "i_user_port_0_rdata_ready" : self.user_port.r.ready,
554 "o_user_port_0_rdata_data" : self.user_port.r.data,
555 }
556
557 if self._pins is not None:
558 core_kwargs.update({
559 "o_ddram_a" : self._pins.a,
560 "o_ddram_ba" : self._pins.ba,
561 "o_ddram_ras_n" : self._pins.ras,
562 "o_ddram_cas_n" : self._pins.cas,
563 "o_ddram_we_n" : self._pins.we,
564 "o_ddram_dm" : self._pins.dm,
565 "o_ddram_clk_p" : self._pins.clk.p,
566 "o_ddram_cke" : self._pins.clk_en,
567 "o_ddram_odt" : self._pins.odt,
568 })
569
570 if hasattr(self._pins, "cs"):
571 core_kwargs.update({
572 "o_ddram_cs_n" : self._pins.cs,
573 })
574
575 if hasattr(self._pins, "rst"):
576 core_kwargs.update({
577 "o_ddram_reset_n" : self._pins.rst,
578 })
579
580 if isinstance(self.config, ECP5Config):
581 core_kwargs.update({
582 "i_ddram_dq" : self._pins.dq,
583 "i_ddram_dqs_p" : self._pins.dqs.p,
584 })
585 elif isinstance(self.config, Artix7Config):
586 core_kwargs.update({
587 "io_ddram_dq" : self._pins.dq,
588 "io_ddram_dqs_p" : self._pins.dqs.p,
589 "io_ddram_dqs_n" : self._pins.dqs.n,
590 "o_ddram_clk_n" : self._pins.clk.n,
591 })
592 else:
593 assert False
594
595 return Instance(f"{self.name}", **core_kwargs)
596
597
598 class Builder:
599 file_templates = {
600 "build_{{top.name}}.sh": r"""
601 # {{autogenerated}}
602 set -e
603 {{emit_commands()}}
604 """,
605 "{{top.name}}_config.yml": r"""
606 # {{autogenerated}}
607 {
608 # General ------------------------------------------------------------------
609 "cpu": "None",
610 {% if top.config.phy_name == "A7DDRPHY" %}
611 "speedgrade": {{top.config.speedgrade}},
612 {% endif %}
613 "memtype": "{{top.config.memtype}}",
614
615 # PHY ----------------------------------------------------------------------
616 {% if top.config.phy_name == "A7DDRPHY" %}
617 "cmd_latency": {{top.config.cmd_latency}},
618 {% endif %}
619 "sdram_module": "{{top.config.module_name}}",
620 "sdram_module_nb": {{top.config.module_bytes}},
621 "sdram_rank_nb": {{top.config.module_ranks}},
622 "sdram_phy": "{{top.config.phy_name}}",
623
624 # Electrical ---------------------------------------------------------------
625 {% if top.config.phy_name == "A7DDRPHY" %}
626 "rtt_nom": "{{top.config.rtt_nom}}ohm",
627 "rtt_wr": "{{top.config.rtt_wr}}ohm",
628 "ron": "{{top.config.ron}}ohm",
629 {% endif %}
630
631 # Frequency ----------------------------------------------------------------
632 "input_clk_freq": {{top.config.input_clk_freq}},
633 "sys_clk_freq": {{top.config.user_clk_freq}},
634 {% if top.config.phy_name == "ECP5DDRPHY" %}
635 "init_clk_freq": {{top.config.init_clk_freq}},
636 {% elif top.config.phy_name == "A7DDRPHY" %}
637 "iodelay_clk_freq": {{top.config.iodelay_clk_freq}},
638 {% endif %}
639
640 # Core ---------------------------------------------------------------------
641 "cmd_buffer_depth": {{top.config.cmd_buffer_depth}},
642 "csr_data_width": {{top.config.csr_data_width}},
643
644 # User Ports ---------------------------------------------------------------
645 "user_ports": {
646 "0": {
647 "type": "native",
648 "data_width": {{top.config.user_data_width}},
649 },
650 },
651 }
652 """,
653 }
654 command_templates = [
655 r"""
656 python -m litedram.gen
657 --name {{top.name}}
658 --output-dir {{top.name}}
659 --gateware-dir {{top.name}}
660 --csr-csv {{top.name}}_csr.csv
661 {% if sim %}
662 --sim
663 {% endif %}
664 {{top.name}}_config.yml
665 """,
666 ]
667
668 """LiteDRAM builder.
669
670 Build products
671 --------------
672
673 Any platform:
674 * ``{{top.name}}_csr.csv`` : CSR listing.
675 * ``{{top.name}}/build_{{top.name}}.sh``: LiteDRAM build script.
676 * ``{{top.name}}/{{top.name}}.v`` : LiteDRAM core.
677 * ``{{top.name}}/software/include/generated/csr.h`` : CSR accessors.
678 * ``{{top.name}}/software/include/generated/git.h`` : Git version.
679 * ``{{top.name}}/software/include/generated/mem.h`` : Memory regions.
680 * ``{{top.name}}/software/include/generated/sdram_phy.h`` : SDRAM initialization sequence.
681 * ``{{top.name}}/software/include/generated/soc.h`` : SoC constants.
682
683 Lattice ECP5 platform:
684 * ``{{top.name}}/{{top.name}}.lpf`` : Constraints file.
685 * ``{{top.name}}/{{top.name}}.ys`` : Yosys script.
686
687 Xilinx Artix7 platform:
688 * ``{{top.name}}/{{top.name}}.xdc`` : Constraints file
689 * ``{{top.name}}/{{top.name}}.tcl`` : Vivado script.
690
691 Name conflict avoidance
692 -----------------------
693
694 Every time :meth:`litedram.Builder.prepare` is called, the name of the :class:`litedram.Core`
695 instance is added to ``namespace`. This allows the detection of name conflicts, which are
696 problematic for the following reasons:
697 * if two build plans are executed locally within the same root directory, the latter could
698 overwrite the products of the former.
699 * the LiteDRAM instance name becomes the name of its top-level Verilog module; importing
700 two modules with the same name will cause a toolchain error.
701
702 Attributes
703 ----------
704 namespace : set(str)
705 Builder namespace.
706 """
707 def __init__(self):
708 self.namespace = set()
709
710 def prepare(self, core, *, sim=False, name_force=False):
711 """Prepare a build plan.
712
713 Arguments
714 ---------
715 core : :class:`litedram.Core`
716 The LiteDRAM instance to be built.
717 sim : bool
718 Do the build in simulation mode (i.e. by replacing the PHY with a model).
719 name_force : bool
720 Force name. If ``True``, no exception will be raised in case of a name conflict with a
721 previous LiteDRAM instance.
722
723 Return value
724 ------------
725 A :class:`nmigen.build.run.BuildPlan` for this LiteDRAM instance.
726
727 Exceptions
728 ----------
729 Raises a :exn:`ValueError` if ``core.name`` conflicts with a previous build plan and
730 ``name_force`` is ``False``.
731 """
732 if not isinstance(core, Core):
733 raise TypeError("LiteDRAM core must be an instance of litedram.Core, not {!r}"
734 .format(core))
735
736 if core.name in self.namespace and not name_force:
737 raise ValueError(
738 "LiteDRAM core name '{}' has already been used for a previous build. Building "
739 "this instance may overwrite previous build products. Passing `name_force=True` "
740 "will disable this check".format(core.name)
741 )
742 self.namespace.add(core.name)
743
744 autogenerated = f"Automatically generated by LambdaSoC {__version__}. Do not edit."
745
746 def emit_commands():
747 commands = []
748 for index, command_tpl in enumerate(self.command_templates):
749 command = render(command_tpl, origin="<command#{}>".format(index + 1))
750 command = re.sub(r"\s+", " ", command)
751 commands.append(command)
752 return "\n".join(commands)
753
754 def render(source, origin):
755 try:
756 source = textwrap.dedent(source).strip()
757 compiled = jinja2.Template(source, trim_blocks=True, lstrip_blocks=True)
758 except jinja2.TemplateSyntaxError as e:
759 e.args = ("{} (at {}:{})".format(e.message, origin, e.lineno),)
760 raise
761 return compiled.render({
762 "autogenerated": autogenerated,
763 "emit_commands": emit_commands,
764 "sim": sim,
765 "top": core,
766 })
767
768 plan = BuildPlan(script=f"build_{core.name}")
769 for filename_tpl, content_tpl in self.file_templates.items():
770 plan.add_file(render(filename_tpl, origin=filename_tpl),
771 render(content_tpl, origin=content_tpl))
772 return plan