Add option to switch ECP5 DDR3 clock/addr/cmd signals to ODDRX1F

Tested to work on ECP5 Versa
parent f17037fd
......@@ -5,6 +5,7 @@
#
# Copyright (c) 2018-2020 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2020 Stefan Schrijvers <ximin@ximinity.net>
# Copyright (c) 2021 Raptor Engineering, LLC <sales@raptorengineering.com>
# SPDX-License-Identifier: BSD-2-Clause
"""
......@@ -246,6 +247,7 @@ class LiteDRAMECP5DDRPHYCRG(Module):
self.clock_domains.cd_init = ClockDomain()
self.clock_domains.cd_por = ClockDomain(reset_less=True)
self.clock_domains.cd_sys = ClockDomain()
self.clock_domains.cd_sys_inv = ClockDomain(reset_less=True)
self.clock_domains.cd_sys2x = ClockDomain()
self.clock_domains.cd_sys2x_i = ClockDomain(reset_less=True)
......@@ -254,6 +256,10 @@ class LiteDRAMECP5DDRPHYCRG(Module):
self.stop = Signal()
self.reset = Signal()
self.clk_inv_alignwd = Signal()
self.sys_inv_clk_bridge = Signal()
self.sys_inv_clk_syncb = Signal()
# clk / rst
clk = platform.request("clk")
rst = platform.request("rst")
......@@ -282,10 +288,30 @@ class LiteDRAMECP5DDRPHYCRG(Module):
i_CLKI = self.cd_sys2x.clk,
i_RST = self.reset,
o_CDIVX = self.cd_sys.clk),
Instance("ECLKBRIDGECS",
i_CLK0 = self.cd_sys2x_i.clk,
i_CLK1 = 0,
i_SEL = 0,
o_ECSOUT = self.sys_inv_clk_bridge),
Instance("ECLKSYNCB",
i_ECLKI = self.sys_inv_clk_bridge,
i_STOP = self.stop,
o_ECLKO = self.sys_inv_clk_syncb),
Instance("CLKDIVF",
p_DIV = "2.0",
i_ALIGNWD = self.clk_inv_alignwd,
i_CLKI = self.sys_inv_clk_syncb,
i_RST = self.reset,
o_CDIVX = self.cd_sys_inv.clk),
AsyncResetSynchronizer(self.cd_sys, ~pll.locked | self.reset),
AsyncResetSynchronizer(self.cd_sys2x, ~pll.locked | self.reset),
]
# Generate inverted sys clock using CLKDIVF word align feature
self.sync.sys += [
If(((~self.cd_sys.clk) ^ self.cd_sys_inv.clk), self.clk_inv_alignwd.eq(~self.clk_inv_alignwd)).Else(self.clk_inv_alignwd.eq(0))
]
class LiteDRAMS7DDRPHYCRG(Module):
def __init__(self, platform, core_config):
assert core_config["memtype"] in ["DDR2", "DDR3"]
......
......@@ -3,6 +3,7 @@
#
# Copyright (c) 2019 David Shah <dave@ds0.me>
# Copyright (c) 2019-2020 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2021 Raptor Engineering, LLC <sales@raptorengineering.com>
# SPDX-License-Identifier: BSD-2-Clause
# 1:2 frequency-ratio DDR3 PHY for Lattice's ECP5
......@@ -117,7 +118,9 @@ class ECP5DDRPHY(Module, AutoCSR):
cl = None,
cwl = None,
cmd_delay = 0,
clk_polarity = 0):
clk_polarity = 0,
ddrclk_prim = "X2",
addrctl_prim = "X2"):
assert isinstance(cmd_delay, int) and cmd_delay < 128
pads = PHYPadsCombiner(pads)
memtype = "DDR3"
......@@ -179,58 +182,128 @@ class ECP5DDRPHY(Module, AutoCSR):
# Iterate on pads groups -------------------------------------------------------------------
for pads_group in range(len(pads.groups)):
pads.sel_group(pads_group)
pads.sel_group(pads_group)\
# Clock --------------------------------------------------------------------------------
clk_pattern = {0: 0b1010, 1: 0b0101}[clk_polarity]
for i in range(len(pads.clk_p)):
pad_oddrx2f = Signal()
self.specials += Instance("ODDRX2F",
i_RST = ResetSignal("sys"),
i_SCLK = ClockSignal("sys"),
i_ECLK = ClockSignal("sys2x"),
**{f"i_D{n}": (clk_pattern >> n) & 0b1 for n in range(4)},
o_Q = pad_oddrx2f
)
self.specials += Instance("DELAYG",
p_DEL_VALUE = cmd_delay,
i_A = pad_oddrx2f,
o_Z = pads.clk_p[i]
)
# Commands -----------------------------------------------------------------------------
commands = {
# Pad name: (DFI name, Pad type (required or optional))
"reset_n" : ("reset_n", "optional"),
"cs_n" : ("cs_n", "optional"),
"a" : ("address", "required"),
"ba" : ("bank" , "required"),
"ras_n" : ("ras_n" , "required"),
"cas_n" : ("cas_n" , "required"),
"we_n" : ("we_n" , "required"),
"cke" : ("cke" , "optional"),
"odt" : ("odt" , "optional"),
}
for pad_name, (dfi_name, pad_type) in commands.items():
pad = getattr(pads, pad_name, None)
if (pad is None):
if (pad_type == "required"):
raise ValueError(f"DRAM pad {pad_name} required but not found in pads.")
continue
for i in range(len(pad)):
if ddrclk_prim == "X2":
# DDR3 X2 Clock ------------------------------------------------------------------------
clk_pattern = {0: 0b1010, 1: 0b0101}[clk_polarity]
for i in range(len(pads.clk_p)):
pad_oddrx2f = Signal()
self.specials += Instance("ODDRX2F",
i_RST = ResetSignal("sys"),
i_SCLK = ClockSignal("sys"),
i_ECLK = ClockSignal("sys2x"),
**{f"i_D{n}": getattr(dfi.phases[n//2], dfi_name)[i] for n in range(4)},
**{f"i_D{n}": (clk_pattern >> n) & 0b1 for n in range(4)},
o_Q = pad_oddrx2f
)
self.specials += Instance("DELAYG",
p_DEL_VALUE = cmd_delay,
i_A = pad_oddrx2f,
o_Z = pad[i]
o_Z = pads.clk_p[i]
)
elif ddrclk_prim == "X1":
# DDR3 X1 Clock ------------------------------------------------------------------------
clk_pattern = {0: 0b10, 1: 0b01}[clk_polarity]
for i in range(len(pads.clk_p)):
pad_oddrx1f = Signal()
self.specials += Instance("ODDRX1F",
i_RST = ResetSignal("sys"),
i_SCLK = ClockSignal("sys2x_i"),
**{f"i_D{n}": (clk_pattern >> n) & 0b1 for n in range(2)},
o_Q = pad_oddrx1f
)
self.specials += Instance("DELAYG",
p_DEL_VALUE = cmd_delay,
i_A = pad_oddrx1f,
o_Z = pads.clk_p[i]
)
else:
raise "Invalid clock primitive specified"
if addrctl_prim == "X2":
# DDR3 X2 Commands ----------------------------------------------------------------------
commands = {
# Pad name: (DFI name, Pad type (required or optional))
"reset_n" : ("reset_n", "optional"),
"cs_n" : ("cs_n", "optional"),
"a" : ("address", "required"),
"ba" : ("bank" , "required"),
"ras_n" : ("ras_n" , "required"),
"cas_n" : ("cas_n" , "required"),
"we_n" : ("we_n" , "required"),
"cke" : ("cke" , "optional"),
"odt" : ("odt" , "optional"),
}
for pad_name, (dfi_name, pad_type) in commands.items():
pad = getattr(pads, pad_name, None)
if (pad is None):
if (pad_type == "required"):
raise ValueError(f"DRAM pad {pad_name} required but not found in pads.")
continue
for i in range(len(pad)):
pad_oddrx2f = Signal()
self.specials += Instance("ODDRX2F",
i_RST = ResetSignal("sys"),
i_SCLK = ClockSignal("sys"),
i_ECLK = ClockSignal("sys2x"),
**{f"i_D{n}": getattr(dfi.phases[n//2], dfi_name)[i] for n in range(4)},
o_Q = pad_oddrx2f
)
self.specials += Instance("DELAYG",
p_DEL_VALUE = cmd_delay,
i_A = pad_oddrx2f,
o_Z = pad[i]
)
elif addrctl_prim == "X1":
# DDR3 X1 Commands ----------------------------------------------------------------------
commands = {
# Pad name: (DFI name, Pad type (required or optional))
"reset_n" : ("reset_n", "optional"),
"cs_n" : ("cs_n", "optional"),
"a" : ("address", "required"),
"ba" : ("bank" , "required"),
"ras_n" : ("ras_n" , "required"),
"cas_n" : ("cas_n" , "required"),
"we_n" : ("we_n" , "required"),
"cke" : ("cke" , "optional"),
"odt" : ("odt" , "optional"),
}
for pad_name, (dfi_name, pad_type) in commands.items():
pad = getattr(pads, pad_name, None)
if (pad is None):
if (pad_type == "required"):
raise ValueError(f"DRAM pad {pad_name} required but not found in pads.")
continue
for i in range(len(pad)):
buffered_phase0 = Signal()
buffered_phase1 = Signal()
self.specials += Instance("FD1P3AX",
i_D = getattr(dfi.phases[0], dfi_name)[i],
i_SP = 1,
i_CK = ClockSignal("sys"),
o_Q = buffered_phase0
)
self.specials += Instance("FD1P3AX",
i_D = getattr(dfi.phases[1], dfi_name)[i],
i_SP = 1,
i_CK = ClockSignal("sys"),
o_Q = buffered_phase1
)
pad_oddrx1f = Signal()
self.specials += Instance("ODDRX1F",
i_RST = ResetSignal("sys"),
i_SCLK = ClockSignal("sys_inv"),
i_D0 = buffered_phase0,
i_D1 = buffered_phase1,
o_Q = pad_oddrx1f
)
self.specials += Instance("DELAYG",
p_DEL_VALUE = cmd_delay,
i_A = pad_oddrx1f,
o_Z = pad[i]
)
else:
raise "Invalid address / control primitive specified"
# DQS/DM/DQ --------------------------------------------------------------------------------
dq_oe = Signal()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment