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