1 from abc
import ABCMeta
, abstractmethod
9 from nmigen
import tracer
10 from nmigen
.build
.run
import BuildPlan
11 from nmigen
.utils
import log2_int
13 from nmigen_soc
import wishbone
14 from nmigen_soc
.memory
import MemoryMap
16 from .. import __version__
20 "Config", "ECP5Config", "Artix7Config",
26 class Config(metaclass
=ABCMeta
):
33 DRAM type (e.g. `"DDR3"`).
37 Number of byte groups of the DRAM interface.
39 Number of ranks. A rank is a set of DRAM chips that are connected to the same CS pin.
41 Frequency of the input clock, which drives the internal PLL.
43 Frequency of the user clock, which is generated by the internal PLL.
45 Input clock domain. Defaults to `"litedram_input"`.
47 User clock domain. Defaults to `"litedram_user"`.
49 User port data width. Defaults to 128.
50 cmd_buffer_depth : int
51 Command buffer depth. Defaults to 16.
53 CSR bus data width. Defaults to 32.
57 __doc__
= _doc_template
.format(
59 LiteDRAM base configuration.
70 input_domain
= "litedram_input",
71 user_domain
= "litedram_user",
72 user_data_width
= 128,
73 cmd_buffer_depth
= 16,
78 elif memtype
in {"DDR3", "DDR4"}:
81 raise ValueError("Unsupported DRAM type, must be one of \"DDR2\", \"DDR3\" or "
85 if not isinstance(module_name
, str):
86 raise ValueError("Module name must be a string, not {!r}"
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, "
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
))
117 self
.memtype
= memtype
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
133 """LiteDRAM PHY name.
135 raise NotImplementedError
137 def get_module(self
):
138 """Get DRAM module description.
142 An instance of :class:`litedram.modules.SDRAMModule`, describing its geometry and timings.
144 import litedram
.modules
145 module_class
= getattr(litedram
.modules
, self
.module_name
)
146 module
= module_class(
147 clk_freq
= self
.user_clk_freq
,
150 assert module
.memtype
== self
.memtype
153 def request_pins(self
, platform
, name
, number
):
154 """Request DRAM pins.
156 This helper requests the DRAM pins with `dir="-"` and `xdr=0`, because LiteDRAM already
157 provides its own I/O buffers.
161 platform : :class:`nmigen.build.Platform`
166 DRAM resource number.
170 A :class:`Record` providing raw access to DRAM pins.
172 res
= platform
.lookup(name
, number
)
173 return platform
.request(
175 dir={io
.name
: "-" for io
in res
.ios
},
176 xdr
={io
.name
: 0 for io
in res
.ios
},
180 class ECP5Config(Config
):
181 phy_name
= "ECP5DDRPHY"
183 __doc__
= Config
._doc
_template
.format(
185 LiteDRAM configuration for ECP5 FPGAs.
189 Frequency of the PHY initialization clock, which is generated by the internal PLL.
192 def __init__(self
, *, init_clk_freq
, **kwargs
):
193 super().__init
__(**kwargs
)
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
201 class Artix7Config(Config
):
202 phy_name
= "A7DDRPHY"
204 __doc__
= Config
._doc
_template
.format(
206 LiteDRAM configuration for Artix 7 FPGAs.
210 FPGA speed grade (e.g. "-1").
212 Command additional latency.
214 Nominal termination impedance.
216 Write termination impedance.
218 Output driver impedance.
219 iodelay_clk_freq : int
220 IODELAY reference clock frequency.
223 def __init__(self
, *,
231 super().__init
__(**kwargs
)
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, "
244 if not isinstance(rtt_wr
, int) or rtt_wr
< 0:
245 raise ValueError("Write termination impedance must be a non-negative integer, "
248 if not isinstance(ron
, int) or ron
< 0:
249 raise ValueError("Output driver impedance must be a non-negative integer, "
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
))
256 self
.speedgrade
= speedgrade
257 self
.cmd_latency
= cmd_latency
258 self
.rtt_nom
= rtt_nom
261 self
.iodelay_clk_freq
= iodelay_clk_freq
264 class NativePort(Record
):
265 """LiteDRAM native port interface.
267 In the "Attributes" section, port directions are given from the point of view of user logic.
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
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
291 w.valid : Signal(), in
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
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
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
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}"
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}"
314 self
.addr_width
= addr_width
315 self
.data_width
= data_width
325 ("addr", addr_width
),
330 ("data", data_width
),
331 ("we", data_width
// self
.granularity
),
336 ("data", data_width
),
338 ], name
=name
, src_loc_at
=1 + src_loc_at
)
341 def memory_map(self
):
342 """Map of the native port.
346 An instance of :class:`nmigen_soc.memory.MemoryMap`.
350 Raises an :exn:`AttributeError` if the port does not have a memory map.
352 if self
._map
is None:
353 raise AttributeError("Native port {!r} does not have a memory map"
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}"
362 if memory_map
.data_width
!= 8:
363 raise ValueError("Memory map has data width {}, which is not the same as native port "
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
))
373 self
._map
= memory_map
376 class Core(Elaboratable
):
380 def clear_namespace(cls
):
381 """Clear private namespace.
383 Every time an instance of :class:`litedram.Core` is created, its name is stored in a
384 private namespace. This allows us to detect name collisions, which are problematic for at
386 * by default, a sub-directory named after the instance is created at build-time in
387 order to isolate it from other LiteDRAM builds. A collision would overwrite previous
389 * the instance name becomes the name of its top-level Verilog module. Importing two
390 modules with the same name will cause a toolchain error.
392 :meth:`litedram.Core.clear_namespace` resets this namespace. It is intended for cases where
393 stateless class instantiations are desirable, such as unit testing.
395 cls
._namespace
.clear()
397 """An nMigen wrapper for a standalone LiteDRAM core.
401 config : :class:`Config`
402 LiteDRAM configuration.
403 pins : :class:`nmigen.lib.io.Pin`
404 Optional. DRAM pins. See :class:`nmigen_boards.resources.DDR3Resource` for layout.
406 Optional. Name of the LiteDRAM core. If ``None`` (default) the name is inferred from the
407 name of the variable this instance is assigned to.
409 Force name. If ``True``, no exception will be raised in case of a name collision with a
410 previous LiteDRAM instance. Defaults to ``False``.
415 Name of the LiteDRAM core.
418 user_port : :class:`NativePort`
419 User port. Provides access to the DRAM storage.
423 Raises a :exn:`ValueError` if ``name`` collides with the name given to a previous LiteDRAM
424 instance and ``name_force`` is ``False``.
426 def __init__(self
, config
, *, pins
=None, name
=None, name_force
=False, src_loc_at
=0):
427 if not isinstance(config
, Config
):
428 raise TypeError("Config must be an instance of litedram.Config, "
433 if name
is not None and not isinstance(name
, str):
434 raise TypeError("Name must be a string, not {!r}".format(name
))
435 name
= name
or tracer
.get_var_name(depth
=2 + src_loc_at
)
437 if not name_force
and name
in Core
._namespace
:
439 "Name '{}' has already been used for a previous litedram.Core instance. Building "
440 "this instance may overwrite previous build products. Passing `name_force=True` "
441 "will disable this check.".format(name
)
443 Core
._namespace
.add(name
)
446 module
= config
.get_module()
447 size
= config
.module_bytes \
448 * 2**( module
.geom_settings
.bankbits
449 + module
.geom_settings
.rowbits
450 + module
.geom_settings
.colbits
)
454 user_addr_width
= module
.geom_settings
.rowbits \
455 + module
.geom_settings
.colbits \
456 + log2_int(module
.nbanks
) \
457 + max(log2_int(config
.module_ranks
), 1)
459 self
.user_port
= NativePort(
460 addr_width
= user_addr_width
- log2_int(config
.user_data_width
// 8),
461 data_width
= config
.user_data_width
,
463 user_map
= MemoryMap(addr_width
=user_addr_width
, data_width
=8)
464 user_map
.add_resource("user_port_0", size
=size
)
465 self
.user_port
.memory_map
= user_map
467 self
._ctrl
_bus
= None
472 """Control bus interface.
474 *Please note that accesses to the CSRs exposed by this interface are not atomic.*
476 The memory map of this interface is populated by reading the ``{{self.name}}_csr.csv``
477 file from the build products.
481 An instance of :class:`nmigen_soc.wishbone.Interface`.
485 Raises an :exn:`AttributeError` if this getter is called before LiteDRAM is built (i.e.
486 before :meth:`Core.build` is called with `do_build=True`).
488 if self
._ctrl
_bus
is None:
489 raise AttributeError("Core.build(do_build=True) must be called before accessing "
491 return self
._ctrl
_bus
493 def build(self
, do_build
=True, build_dir
="build/litedram", sim
=False):
494 """Build the LiteDRAM core.
499 Build the LiteDRAM core. Defaults to `True`.
503 Do the build in simulation mode (i.e. with a PHY model). Defaults to `False`.
507 An instance of :class:`nmigen.build.run.LocalBuildProducts` if ``do_build`` is ``True``.
508 Otherwise, an instance of :class:``nmigen.build.run.BuildPlan``.
510 plan
= Builder().prepare(self
, build_dir
, sim
)
514 products
= plan
.execute_local(build_dir
)
516 # LiteDRAM's Wishbone to CSR bridge uses an 8-bit granularity.
517 ctrl_map
= MemoryMap(addr_width
=1, data_width
=8)
519 with products
.extract(f
"{self.name}_csr.csv") as csr_csv_filename
:
520 with
open(csr_csv_filename
, "r") as csr_csv
:
521 for row
in csv
.reader(csr_csv
, delimiter
=","):
522 if row
[0][0] == "#": continue
523 res_type
, res_name
, addr
, size
, attrs
= row
524 if res_type
== "csr_register":
525 ctrl_map
.add_resource(
527 addr
= int(addr
, 16),
528 size
= int(size
, 10) * self
.config
.csr_data_width
529 // ctrl_map
.data_width
,
533 self
._ctrl
_bus
= wishbone
.Interface(
534 addr_width
= ctrl_map
.addr_width
535 - log2_int(self
.config
.csr_data_width
// ctrl_map
.data_width
),
536 data_width
= self
.config
.csr_data_width
,
537 granularity
= ctrl_map
.data_width
,
539 self
._ctrl
_bus
.memory_map
= ctrl_map
543 def elaborate(self
, platform
):
545 "i_clk" : ClockSignal(self
.config
.input_domain
),
546 "i_rst" : ResetSignal(self
.config
.input_domain
),
547 "o_user_clk" : ClockSignal(self
.config
.user_domain
),
548 "o_user_rst" : ResetSignal(self
.config
.user_domain
),
550 "i_wb_ctrl_adr" : self
.ctrl_bus
.adr
,
551 "i_wb_ctrl_dat_w" : self
.ctrl_bus
.dat_w
,
552 "o_wb_ctrl_dat_r" : self
.ctrl_bus
.dat_r
,
553 "i_wb_ctrl_sel" : self
.ctrl_bus
.sel
,
554 "i_wb_ctrl_cyc" : self
.ctrl_bus
.cyc
,
555 "i_wb_ctrl_stb" : self
.ctrl_bus
.stb
,
556 "o_wb_ctrl_ack" : self
.ctrl_bus
.ack
,
557 "i_wb_ctrl_we" : self
.ctrl_bus
.we
,
559 "i_user_port_0_cmd_valid" : self
.user_port
.cmd
.valid
,
560 "o_user_port_0_cmd_ready" : self
.user_port
.cmd
.ready
,
561 "i_user_port_0_cmd_we" : self
.user_port
.cmd
.we
,
562 "i_user_port_0_cmd_addr" : self
.user_port
.cmd
.addr
,
563 "i_user_port_0_wdata_valid" : self
.user_port
.w
.valid
,
564 "o_user_port_0_wdata_ready" : self
.user_port
.w
.ready
,
565 "i_user_port_0_wdata_we" : self
.user_port
.w
.we
,
566 "i_user_port_0_wdata_data" : self
.user_port
.w
.data
,
567 "o_user_port_0_rdata_valid" : self
.user_port
.r
.valid
,
568 "i_user_port_0_rdata_ready" : self
.user_port
.r
.ready
,
569 "o_user_port_0_rdata_data" : self
.user_port
.r
.data
,
572 if self
._pins
is not None:
574 "o_ddram_a" : self
._pins
.a
,
575 "o_ddram_ba" : self
._pins
.ba
,
576 "o_ddram_ras_n" : self
._pins
.ras
,
577 "o_ddram_cas_n" : self
._pins
.cas
,
578 "o_ddram_we_n" : self
._pins
.we
,
579 "o_ddram_dm" : self
._pins
.dm
,
580 "o_ddram_clk_p" : self
._pins
.clk
.p
,
581 "o_ddram_cke" : self
._pins
.clk_en
,
582 "o_ddram_odt" : self
._pins
.odt
,
585 if hasattr(self
._pins
, "cs"):
587 "o_ddram_cs_n" : self
._pins
.cs
,
590 if hasattr(self
._pins
, "rst"):
592 "o_ddram_reset_n" : self
._pins
.rst
,
595 if isinstance(self
.config
, ECP5Config
):
597 "i_ddram_dq" : self
._pins
.dq
,
598 "i_ddram_dqs_p" : self
._pins
.dqs
.p
,
600 elif isinstance(self
.config
, Artix7Config
):
602 "io_ddram_dq" : self
._pins
.dq
,
603 "io_ddram_dqs_p" : self
._pins
.dqs
.p
,
604 "io_ddram_dqs_n" : self
._pins
.dqs
.n
,
605 "o_ddram_clk_n" : self
._pins
.clk
.n
,
610 return Instance(f
"{self.name}", **core_kwargs
)
618 Build products (any):
619 * ``{{top.name}}_csr.csv`` : CSR listing.
620 * ``{{top.name}}/build_{{top.name}}.sh``: LiteDRAM build script.
621 * ``{{top.name}}/{{top.name}}.v`` : LiteDRAM core.
622 * ``{{top.name}}/software/include/generated/csr.h`` : CSR accessors.
623 * ``{{top.name}}/software/include/generated/git.h`` : Git version.
624 * ``{{top.name}}/software/include/generated/mem.h`` : Memory regions.
625 * ``{{top.name}}/software/include/generated/sdram_phy.h`` : SDRAM initialization sequence.
626 * ``{{top.name}}/software/include/generated/soc.h`` : SoC constants.
628 Build products (ECP5):
629 * ``{{top.name}}/{{top.name}}.lpf`` : Constraints file.
630 * ``{{top.name}}/{{top.name}}.ys`` : Yosys script.
632 Build products (Artix 7):
633 * ``{{top.name}}/{{top.name}}.xdc`` : Constraints file
634 * ``{{top.name}}/{{top.name}}.tcl`` : Vivado script.
638 "build_{{top.name}}.sh": r
"""
643 "{{top.name}}_config.yml": r
"""
646 # General ------------------------------------------------------------------
648 {% if top.config.phy_name == "A7DDRPHY" %}
649 "speedgrade": {{top.config.speedgrade}},
651 "memtype": "{{top.config.memtype}}",
653 # PHY ----------------------------------------------------------------------
654 {% if top.config.phy_name == "A7DDRPHY" %}
655 "cmd_latency": {{top.config.cmd_latency}},
657 "sdram_module": "{{top.config.module_name}}",
658 "sdram_module_nb": {{top.config.module_bytes}},
659 "sdram_rank_nb": {{top.config.module_ranks}},
660 "sdram_phy": "{{top.config.phy_name}}",
662 # Electrical ---------------------------------------------------------------
663 {% if top.config.phy_name == "A7DDRPHY" %}
664 "rtt_nom": "{{top.config.rtt_nom}}ohm",
665 "rtt_wr": "{{top.config.rtt_wr}}ohm",
666 "ron": "{{top.config.ron}}ohm",
669 # Frequency ----------------------------------------------------------------
670 "input_clk_freq": {{top.config.input_clk_freq}},
671 "sys_clk_freq": {{top.config.user_clk_freq}},
672 {% if top.config.phy_name == "ECP5DDRPHY" %}
673 "init_clk_freq": {{top.config.init_clk_freq}},
674 {% elif top.config.phy_name == "A7DDRPHY" %}
675 "iodelay_clk_freq": {{top.config.iodelay_clk_freq}},
678 # Core ---------------------------------------------------------------------
679 "cmd_buffer_depth": {{top.config.cmd_buffer_depth}},
680 "csr_data_width": {{top.config.csr_data_width}},
682 # User Ports ---------------------------------------------------------------
686 "data_width": {{top.config.user_data_width}},
692 command_templates
= [
694 python -m litedram.gen
696 --output-dir {{top.name}}
697 --gateware-dir {{top.name}}
698 --csr-csv {{top.name}}_csr.csv
702 {{top.name}}_config.yml
706 def prepare(self
, top
, build_dir
, sim
):
707 if not isinstance(top
, Core
):
708 raise TypeError("Top module must be an instance of litedram.Core, not {!r}"
711 autogenerated
= f
"Automatically generated by LambdaSoC {__version__}. Do not edit."
715 for index
, command_tpl
in enumerate(self
.command_templates
):
716 command
= render(command_tpl
, origin
="<command#{}>".format(index
+ 1))
717 command
= re
.sub(r
"\s+", " ", command
)
718 commands
.append(command
)
719 return "\n".join(commands
)
721 def render(source
, origin
):
723 source
= textwrap
.dedent(source
).strip()
724 compiled
= jinja2
.Template(source
, trim_blocks
=True, lstrip_blocks
=True)
725 except jinja2
.TemplateSyntaxError
as e
:
726 e
.args
= ("{} (at {}:{})".format(e
.message
, origin
, e
.lineno
),)
728 return compiled
.render({
729 "autogenerated": autogenerated
,
730 "build_dir": os
.path
.abspath(build_dir
),
731 "emit_commands": emit_commands
,
736 plan
= BuildPlan(script
=f
"build_{top.name}")
737 for filename_tpl
, content_tpl
in self
.file_templates
.items():
738 plan
.add_file(render(filename_tpl
, origin
=filename_tpl
),
739 render(content_tpl
, origin
=content_tpl
))