soc.cpu: generate BIOS configuration from SoC constants.
[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, sim=sim, name_force=name_force)
495 if not do_build:
496 return plan
497
498 products = plan.execute_local(f"{build_dir}/{__package__}")
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 "cpu": "None",
588 {% if top.config.phy_name == "A7DDRPHY" %}
589 "speedgrade": {{top.config.speedgrade}},
590 {% endif %}
591 "memtype": "{{top.config.memtype}}",
592
593 # PHY ----------------------------------------------------------------------
594 {% if top.config.phy_name == "A7DDRPHY" %}
595 "cmd_latency": {{top.config.cmd_latency}},
596 {% endif %}
597 "sdram_module": "{{top.config.module_name}}",
598 "sdram_module_nb": {{top.config.module_bytes}},
599 "sdram_rank_nb": {{top.config.module_ranks}},
600 "sdram_phy": "{{top.config.phy_name}}",
601
602 # Electrical ---------------------------------------------------------------
603 {% if top.config.phy_name == "A7DDRPHY" %}
604 "rtt_nom": "{{top.config.rtt_nom}}ohm",
605 "rtt_wr": "{{top.config.rtt_wr}}ohm",
606 "ron": "{{top.config.ron}}ohm",
607 {% endif %}
608
609 # Frequency ----------------------------------------------------------------
610 "input_clk_freq": {{top.config.input_clk_freq}},
611 "sys_clk_freq": {{top.config.user_clk_freq}},
612 {% if top.config.phy_name == "ECP5DDRPHY" %}
613 "init_clk_freq": {{top.config.init_clk_freq}},
614 {% elif top.config.phy_name == "A7DDRPHY" %}
615 "iodelay_clk_freq": {{top.config.iodelay_clk_freq}},
616 {% endif %}
617
618 # Core ---------------------------------------------------------------------
619 "cmd_buffer_depth": {{top.config.cmd_buffer_depth}},
620 "csr_data_width": {{top.config.csr_data_width}},
621
622 # User Ports ---------------------------------------------------------------
623 "user_ports": {
624 "0": {
625 "type": "native",
626 "data_width": {{top.config.user_data_width}},
627 },
628 },
629 }
630 """,
631 }
632 command_templates = [
633 r"""
634 python -m litedram.gen
635 --name {{top.name}}
636 --output-dir {{top.name}}
637 --gateware-dir {{top.name}}
638 --csr-csv {{top.name}}_csr.csv
639 {% if sim %}
640 --sim
641 {% endif %}
642 {{top.name}}_config.yml
643 """,
644 ]
645
646 """LiteDRAM builder.
647
648 Build products
649 --------------
650
651 Any platform:
652 * ``{{top.name}}_csr.csv`` : CSR listing.
653 * ``{{top.name}}/build_{{top.name}}.sh``: LiteDRAM build script.
654 * ``{{top.name}}/{{top.name}}.v`` : LiteDRAM core.
655 * ``{{top.name}}/software/include/generated/csr.h`` : CSR accessors.
656 * ``{{top.name}}/software/include/generated/git.h`` : Git version.
657 * ``{{top.name}}/software/include/generated/mem.h`` : Memory regions.
658 * ``{{top.name}}/software/include/generated/sdram_phy.h`` : SDRAM initialization sequence.
659 * ``{{top.name}}/software/include/generated/soc.h`` : SoC constants.
660
661 Lattice ECP5 platform:
662 * ``{{top.name}}/{{top.name}}.lpf`` : Constraints file.
663 * ``{{top.name}}/{{top.name}}.ys`` : Yosys script.
664
665 Xilinx Artix7 platform:
666 * ``{{top.name}}/{{top.name}}.xdc`` : Constraints file
667 * ``{{top.name}}/{{top.name}}.tcl`` : Vivado script.
668
669 Name conflict avoidance
670 -----------------------
671
672 Every time :meth:`litedram.Builder.prepare` is called, the name of the :class:`litedram.Core`
673 instance is added to ``namespace`. This allows the detection of name conflicts, which are
674 problematic for the following reasons:
675 * if two build plans are executed locally within the same root directory, the latter could
676 overwrite the products of the former.
677 * the LiteDRAM instance name becomes the name of its top-level Verilog module; importing
678 two modules with the same name will cause a toolchain error.
679
680 Attributes
681 ----------
682 namespace : set(str)
683 Builder namespace.
684 """
685 def __init__(self):
686 self.namespace = set()
687
688 def prepare(self, core, *, sim=False, name_force=False):
689 """Prepare a build plan.
690
691 Arguments
692 ---------
693 core : :class:`litedram.Core`
694 The LiteDRAM instance to be built.
695 sim : bool
696 Do the build in simulation mode (i.e. by replacing the PHY with a model).
697 name_force : bool
698 Force name. If ``True``, no exception will be raised in case of a name conflict with a
699 previous LiteDRAM instance.
700
701 Return value
702 ------------
703 A :class:`nmigen.build.run.BuildPlan` for this LiteDRAM instance.
704
705 Exceptions
706 ----------
707 Raises a :exn:`ValueError` if ``core.name`` conflicts with a previous build plan and
708 ``name_force`` is ``False``.
709 """
710 if not isinstance(core, Core):
711 raise TypeError("LiteDRAM core must be an instance of litedram.Core, not {!r}"
712 .format(core))
713
714 if core.name in self.namespace and not name_force:
715 raise ValueError(
716 "LiteDRAM core name '{}' has already been used for a previous build. Building "
717 "this instance may overwrite previous build products. Passing `name_force=True` "
718 "will disable this check".format(core.name)
719 )
720 self.namespace.add(core.name)
721
722 autogenerated = f"Automatically generated by LambdaSoC {__version__}. Do not edit."
723
724 def emit_commands():
725 commands = []
726 for index, command_tpl in enumerate(self.command_templates):
727 command = render(command_tpl, origin="<command#{}>".format(index + 1))
728 command = re.sub(r"\s+", " ", command)
729 commands.append(command)
730 return "\n".join(commands)
731
732 def render(source, origin):
733 try:
734 source = textwrap.dedent(source).strip()
735 compiled = jinja2.Template(source, trim_blocks=True, lstrip_blocks=True)
736 except jinja2.TemplateSyntaxError as e:
737 e.args = ("{} (at {}:{})".format(e.message, origin, e.lineno),)
738 raise
739 return compiled.render({
740 "autogenerated": autogenerated,
741 "emit_commands": emit_commands,
742 "sim": sim,
743 "top": core,
744 })
745
746 plan = BuildPlan(script=f"build_{core.name}")
747 for filename_tpl, content_tpl in self.file_templates.items():
748 plan.add_file(render(filename_tpl, origin=filename_tpl),
749 render(content_tpl, origin=content_tpl))
750 return plan