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