c5a7c601b2290c6806c1e9860aebf84678b3b3e4
[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
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("Core.build(do_build=True) must be called before accessing "
461 "Core.ctrl_bus")
462 return self._ctrl_bus
463
464 def build(self, builder, *, do_build=True, build_dir="build/litedram", sim=False,
465 name_force=False):
466 """Build the LiteDRAM core.
467
468 Arguments
469 ---------
470 builder: :class:`litedram.Builder`
471 Builder instance.
472 do_build : bool
473 Execute the build locally. Defaults to ``True``.
474 build_dir : str
475 Root build directory. Defaults to ``"build/litedram"``.
476 sim : bool
477 Do the build in simulation mode (i.e. by replacing the PHY with a model). Defaults to
478 ``False``.
479 name_force : bool
480 Ignore builder name conflicts. Defaults to ``False``.
481
482 Return value
483 ------------
484 An instance of :class:`nmigen.build.run.LocalBuildProducts` if ``do_build`` is ``True``.
485 Otherwise, an instance of :class:``nmigen.build.run.BuildPlan``.
486 """
487 if not isinstance(builder, Builder):
488 raise TypeError("Builder must be an instance of litedram.Builder, not {!r}"
489 .format(builder))
490
491 plan = builder.prepare(self, sim=sim, name_force=name_force)
492 if not do_build:
493 return plan
494
495 products = plan.execute_local(build_dir)
496
497 # LiteDRAM's Wishbone to CSR bridge uses an 8-bit granularity.
498 ctrl_map = MemoryMap(addr_width=1, data_width=8)
499
500 with products.extract(f"{self.name}_csr.csv") as csr_csv_filename:
501 with open(csr_csv_filename, "r") as csr_csv:
502 for row in csv.reader(csr_csv, delimiter=","):
503 if row[0][0] == "#": continue
504 res_type, res_name, addr, size, attrs = row
505 if res_type == "csr_register":
506 ctrl_map.add_resource(
507 res_name,
508 addr = int(addr, 16),
509 size = int(size, 10) * self.config.csr_data_width
510 // ctrl_map.data_width,
511 extend = True,
512 )
513
514 self._ctrl_bus = wishbone.Interface(
515 addr_width = ctrl_map.addr_width
516 - log2_int(self.config.csr_data_width // ctrl_map.data_width),
517 data_width = self.config.csr_data_width,
518 granularity = ctrl_map.data_width,
519 )
520 self._ctrl_bus.memory_map = ctrl_map
521
522 return products
523
524 def elaborate(self, platform):
525 core_kwargs = {
526 "i_clk" : ClockSignal(self.config.input_domain),
527 "i_rst" : ResetSignal(self.config.input_domain),
528 "o_user_clk" : ClockSignal(self.config.user_domain),
529 "o_user_rst" : ResetSignal(self.config.user_domain),
530
531 "i_wb_ctrl_adr" : self.ctrl_bus.adr,
532 "i_wb_ctrl_dat_w" : self.ctrl_bus.dat_w,
533 "o_wb_ctrl_dat_r" : self.ctrl_bus.dat_r,
534 "i_wb_ctrl_sel" : self.ctrl_bus.sel,
535 "i_wb_ctrl_cyc" : self.ctrl_bus.cyc,
536 "i_wb_ctrl_stb" : self.ctrl_bus.stb,
537 "o_wb_ctrl_ack" : self.ctrl_bus.ack,
538 "i_wb_ctrl_we" : self.ctrl_bus.we,
539
540 "i_user_port_0_cmd_valid" : self.user_port.cmd.valid,
541 "o_user_port_0_cmd_ready" : self.user_port.cmd.ready,
542 "i_user_port_0_cmd_we" : self.user_port.cmd.we,
543 "i_user_port_0_cmd_addr" : self.user_port.cmd.addr,
544 "i_user_port_0_wdata_valid" : self.user_port.w.valid,
545 "o_user_port_0_wdata_ready" : self.user_port.w.ready,
546 "i_user_port_0_wdata_we" : self.user_port.w.we,
547 "i_user_port_0_wdata_data" : self.user_port.w.data,
548 "o_user_port_0_rdata_valid" : self.user_port.r.valid,
549 "i_user_port_0_rdata_ready" : self.user_port.r.ready,
550 "o_user_port_0_rdata_data" : self.user_port.r.data,
551 }
552
553 if self._pins is not None:
554 core_kwargs.update({
555 "o_ddram_a" : self._pins.a,
556 "o_ddram_ba" : self._pins.ba,
557 "o_ddram_ras_n" : self._pins.ras,
558 "o_ddram_cas_n" : self._pins.cas,
559 "o_ddram_we_n" : self._pins.we,
560 "o_ddram_dm" : self._pins.dm,
561 "o_ddram_clk_p" : self._pins.clk.p,
562 "o_ddram_cke" : self._pins.clk_en,
563 "o_ddram_odt" : self._pins.odt,
564 })
565
566 if hasattr(self._pins, "cs"):
567 core_kwargs.update({
568 "o_ddram_cs_n" : self._pins.cs,
569 })
570
571 if hasattr(self._pins, "rst"):
572 core_kwargs.update({
573 "o_ddram_reset_n" : self._pins.rst,
574 })
575
576 if isinstance(self.config, ECP5Config):
577 core_kwargs.update({
578 "i_ddram_dq" : self._pins.dq,
579 "i_ddram_dqs_p" : self._pins.dqs.p,
580 })
581 elif isinstance(self.config, Artix7Config):
582 core_kwargs.update({
583 "io_ddram_dq" : self._pins.dq,
584 "io_ddram_dqs_p" : self._pins.dqs.p,
585 "io_ddram_dqs_n" : self._pins.dqs.n,
586 "o_ddram_clk_n" : self._pins.clk.n,
587 })
588 else:
589 assert False
590
591 return Instance(f"{self.name}", **core_kwargs)
592
593
594 class Builder:
595 file_templates = {
596 "build_{{top.name}}.sh": r"""
597 # {{autogenerated}}
598 set -e
599 {{emit_commands()}}
600 """,
601 "{{top.name}}_config.yml": r"""
602 # {{autogenerated}}
603 {
604 # General ------------------------------------------------------------------
605 "cpu": "None",
606 {% if top.config.phy_name == "A7DDRPHY" %}
607 "speedgrade": {{top.config.speedgrade}},
608 {% endif %}
609 "memtype": "{{top.config.memtype}}",
610
611 # PHY ----------------------------------------------------------------------
612 {% if top.config.phy_name == "A7DDRPHY" %}
613 "cmd_latency": {{top.config.cmd_latency}},
614 {% endif %}
615 "sdram_module": "{{top.config.module_name}}",
616 "sdram_module_nb": {{top.config.module_bytes}},
617 "sdram_rank_nb": {{top.config.module_ranks}},
618 "sdram_phy": "{{top.config.phy_name}}",
619
620 # Electrical ---------------------------------------------------------------
621 {% if top.config.phy_name == "A7DDRPHY" %}
622 "rtt_nom": "{{top.config.rtt_nom}}ohm",
623 "rtt_wr": "{{top.config.rtt_wr}}ohm",
624 "ron": "{{top.config.ron}}ohm",
625 {% endif %}
626
627 # Frequency ----------------------------------------------------------------
628 "input_clk_freq": {{top.config.input_clk_freq}},
629 "sys_clk_freq": {{top.config.user_clk_freq}},
630 {% if top.config.phy_name == "ECP5DDRPHY" %}
631 "init_clk_freq": {{top.config.init_clk_freq}},
632 {% elif top.config.phy_name == "A7DDRPHY" %}
633 "iodelay_clk_freq": {{top.config.iodelay_clk_freq}},
634 {% endif %}
635
636 # Core ---------------------------------------------------------------------
637 "cmd_buffer_depth": {{top.config.cmd_buffer_depth}},
638 "csr_data_width": {{top.config.csr_data_width}},
639
640 # User Ports ---------------------------------------------------------------
641 "user_ports": {
642 "0": {
643 "type": "native",
644 "data_width": {{top.config.user_data_width}},
645 },
646 },
647 }
648 """,
649 }
650 command_templates = [
651 r"""
652 python -m litedram.gen
653 --name {{top.name}}
654 --output-dir {{top.name}}
655 --gateware-dir {{top.name}}
656 --csr-csv {{top.name}}_csr.csv
657 {% if sim %}
658 --sim
659 {% endif %}
660 {{top.name}}_config.yml
661 """,
662 ]
663
664 """LiteDRAM builder.
665
666 Build products
667 --------------
668
669 Any platform:
670 * ``{{top.name}}_csr.csv`` : CSR listing.
671 * ``{{top.name}}/build_{{top.name}}.sh``: LiteDRAM build script.
672 * ``{{top.name}}/{{top.name}}.v`` : LiteDRAM core.
673 * ``{{top.name}}/software/include/generated/csr.h`` : CSR accessors.
674 * ``{{top.name}}/software/include/generated/git.h`` : Git version.
675 * ``{{top.name}}/software/include/generated/mem.h`` : Memory regions.
676 * ``{{top.name}}/software/include/generated/sdram_phy.h`` : SDRAM initialization sequence.
677 * ``{{top.name}}/software/include/generated/soc.h`` : SoC constants.
678
679 Lattice ECP5 platform:
680 * ``{{top.name}}/{{top.name}}.lpf`` : Constraints file.
681 * ``{{top.name}}/{{top.name}}.ys`` : Yosys script.
682
683 Xilinx Artix7 platform:
684 * ``{{top.name}}/{{top.name}}.xdc`` : Constraints file
685 * ``{{top.name}}/{{top.name}}.tcl`` : Vivado script.
686
687 Name conflict avoidance
688 -----------------------
689
690 Every time :meth:`litedram.Builder.prepare` is called, the name of the :class:`litedram.Core`
691 instance is added to ``namespace`. This allows the detection of name conflicts, which are
692 problematic for the following reasons:
693 * if two build plans are executed locally within the same root directory, the latter could
694 overwrite the products of the former.
695 * the LiteDRAM instance name becomes the name of its top-level Verilog module; importing
696 two modules with the same name will cause a toolchain error.
697
698 Attributes
699 ----------
700 namespace : set(str)
701 Builder namespace.
702 """
703 def __init__(self):
704 self.namespace = set()
705
706 def prepare(self, core, *, sim=False, name_force=False):
707 """Prepare a build plan.
708
709 Arguments
710 ---------
711 core : :class:`litedram.Core`
712 The LiteDRAM instance to be built.
713 sim : bool
714 Do the build in simulation mode (i.e. by replacing the PHY with a model).
715 name_force : bool
716 Force name. If ``True``, no exception will be raised in case of a name conflict with a
717 previous LiteDRAM instance.
718
719 Return value
720 ------------
721 A :class:`nmigen.build.run.BuildPlan` for this LiteDRAM instance.
722
723 Exceptions
724 ----------
725 Raises a :exn:`ValueError` if ``core.name`` conflicts with a previous build plan and
726 ``name_force`` is ``False``.
727 """
728 if not isinstance(core, Core):
729 raise TypeError("LiteDRAM core must be an instance of litedram.Core, not {!r}"
730 .format(core))
731
732 if core.name in self.namespace and not name_force:
733 raise ValueError(
734 "LiteDRAM core name '{}' has already been used for a previous build. Building "
735 "this instance may overwrite previous build products. Passing `name_force=True` "
736 "will disable this check".format(core.name)
737 )
738 self.namespace.add(core.name)
739
740 autogenerated = f"Automatically generated by LambdaSoC {__version__}. Do not edit."
741
742 def emit_commands():
743 commands = []
744 for index, command_tpl in enumerate(self.command_templates):
745 command = render(command_tpl, origin="<command#{}>".format(index + 1))
746 command = re.sub(r"\s+", " ", command)
747 commands.append(command)
748 return "\n".join(commands)
749
750 def render(source, origin):
751 try:
752 source = textwrap.dedent(source).strip()
753 compiled = jinja2.Template(source, trim_blocks=True, lstrip_blocks=True)
754 except jinja2.TemplateSyntaxError as e:
755 e.args = ("{} (at {}:{})".format(e.message, origin, e.lineno),)
756 raise
757 return compiled.render({
758 "autogenerated": autogenerated,
759 "emit_commands": emit_commands,
760 "sim": sim,
761 "top": core,
762 })
763
764 plan = BuildPlan(script=f"build_{core.name}")
765 for filename_tpl, content_tpl in self.file_templates.items():
766 plan.add_file(render(filename_tpl, origin=filename_tpl),
767 render(content_tpl, origin=content_tpl))
768 return plan