phy/s7ddrphy: add compatibility with DFIRateConverter

This commit is contained in:
Jędrzej Boczar 2021-08-04 12:00:08 +02:00
parent 1d880c4db9
commit f4be065c09
1 changed files with 68 additions and 23 deletions

View File

@ -3,6 +3,7 @@
# #
# Copyright (c) 2015-2020 Florent Kermarrec <florent@enjoy-digital.fr> # Copyright (c) 2015-2020 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk> # Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
# Copyright (c) 2021 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: BSD-2-Clause # SPDX-License-Identifier: BSD-2-Clause
# 1:4, 1:2 frequency-ratio DDR2/DDR3 PHY for Xilinx's Series7 # 1:4, 1:2 frequency-ratio DDR2/DDR3 PHY for Xilinx's Series7
@ -15,6 +16,7 @@ from operator import or_
import math import math
from migen import * from migen import *
from migen.genlib.cdc import PulseSynchronizer
from litex.soc.interconnect.csr import * from litex.soc.interconnect.csr import *
@ -32,7 +34,9 @@ class S7DDRPHY(Module, AutoCSR):
cl = None, cl = None,
cwl = None, cwl = None,
cmd_latency = 0, cmd_latency = 0,
cmd_delay = None): cmd_delay = None,
ddr_clk = None,
csr_cdc = None):
assert not (memtype == "DDR3" and nphases == 2) assert not (memtype == "DDR3" and nphases == 2)
phytype = self.__class__.__name__ phytype = self.__class__.__name__
pads = PHYPadsCombiner(pads) pads = PHYPadsCombiner(pads)
@ -91,6 +95,27 @@ class S7DDRPHY(Module, AutoCSR):
self._rdphase = CSRStorage(int(math.log2(nphases)), reset=rdphase) self._rdphase = CSRStorage(int(math.log2(nphases)), reset=rdphase)
self._wrphase = CSRStorage(int(math.log2(nphases)), reset=wrphase) self._wrphase = CSRStorage(int(math.log2(nphases)), reset=wrphase)
def cdc(i):
if csr_cdc is None:
return i
return csr_cdc(i)
rdly_dq_rst = cdc(self._rdly_dq_rst.re)
rdly_dq_inc = cdc(self._rdly_dq_inc.re)
rdly_dq_bitslip_rst = cdc(self._rdly_dq_bitslip_rst.re)
rdly_dq_bitslip = cdc(self._rdly_dq_bitslip.re)
wlevel_strobe = cdc(self._wlevel_strobe.re)
if with_odelay:
cdly_rst = cdc(self._cdly_rst.re) | self._rst.storage
cdly_inc = cdc(self._cdly_inc.re)
wdly_dq_rst = cdc(self._wdly_dq_rst.re)
wdly_dq_inc = cdc(self._wdly_dq_inc.re)
wdly_dqs_rst = cdc(self._wdly_dqs_rst.re)
wdly_dqs_inc = cdc(self._wdly_dqs_inc.re)
wdly_dq_bitslip_rst = cdc(self._wdly_dq_bitslip_rst.re)
wdly_dq_bitslip = cdc(self._wdly_dq_bitslip.re)
# PHY settings ----------------------------------------------------------------------------- # PHY settings -----------------------------------------------------------------------------
self.settings = PhySettings( self.settings = PhySettings(
phytype = phytype, phytype = phytype,
@ -125,7 +150,7 @@ class S7DDRPHY(Module, AutoCSR):
pads.sel_group(pads_group) pads.sel_group(pads_group)
# Clock -------------------------------------------------------------------------------- # Clock --------------------------------------------------------------------------------
ddr_clk = "sys2x" if nphases == 2 else "sys4x" ddr_clk = ddr_clk or ("sys2x" if nphases == 2 else "sys4x")
for i in range(len(pads.clk_p)): for i in range(len(pads.clk_p)):
sd_clk_se_nodelay = Signal() sd_clk_se_nodelay = Signal()
sd_clk_se_delayed = Signal() sd_clk_se_delayed = Signal()
@ -153,9 +178,9 @@ class S7DDRPHY(Module, AutoCSR):
p_ODELAY_TYPE = "VARIABLE", p_ODELAY_TYPE = "VARIABLE",
p_ODELAY_VALUE = 0, p_ODELAY_VALUE = 0,
i_C = ClockSignal("sys"), i_C = ClockSignal("sys"),
i_LD = self._cdly_rst.re | self._rst.storage, i_LD = cdly_rst | self._rst.storage,
i_LDPIPEEN = 0, i_LDPIPEEN = 0,
i_CE = self._cdly_inc.re, i_CE = cdly_inc,
i_INC = 1, i_INC = 1,
o_ODATAIN = sd_clk_se_nodelay, o_ODATAIN = sd_clk_se_nodelay,
o_DATAOUT = sd_clk_se_delayed, o_DATAOUT = sd_clk_se_delayed,
@ -211,9 +236,9 @@ class S7DDRPHY(Module, AutoCSR):
p_ODELAY_TYPE = "VARIABLE", p_ODELAY_TYPE = "VARIABLE",
p_ODELAY_VALUE = 0, p_ODELAY_VALUE = 0,
i_C = ClockSignal("sys"), i_C = ClockSignal("sys"),
i_LD = self._cdly_rst.re | self._rst.storage, i_LD = cdly_rst | self._rst.storage,
i_LDPIPEEN = 0, i_LDPIPEEN = 0,
i_CE = self._cdly_inc.re, i_CE = cdly_inc,
i_INC = 1, i_INC = 1,
o_ODATAIN = oq, o_ODATAIN = oq,
o_DATAOUT = pad[i], o_DATAOUT = pad[i],
@ -228,7 +253,7 @@ class S7DDRPHY(Module, AutoCSR):
#preamble = dqs_preamble, # FIXME #preamble = dqs_preamble, # FIXME
#postamble = dqs_postamble, # FIXME #postamble = dqs_postamble, # FIXME
wlevel_en = self._wlevel_en.storage, wlevel_en = self._wlevel_en.storage,
wlevel_strobe = self._wlevel_strobe.re, wlevel_strobe = wlevel_strobe,
register = not with_odelay) register = not with_odelay)
self.submodules += dqs_oe_delay, dqs_pattern self.submodules += dqs_oe_delay, dqs_pattern
self.comb += dqs_oe_delay.input.eq(dqs_preamble | dqs_oe | dqs_postamble) self.comb += dqs_oe_delay.input.eq(dqs_preamble | dqs_oe | dqs_postamble)
@ -238,8 +263,8 @@ class S7DDRPHY(Module, AutoCSR):
dqs_t = Signal() dqs_t = Signal()
dqs_bitslip = BitSlip(8, dqs_bitslip = BitSlip(8,
i = dqs_pattern.o, i = dqs_pattern.o,
rst = (self._dly_sel.storage[i] & self._wdly_dq_bitslip_rst.re) | self._rst.storage, rst = (self._dly_sel.storage[i] & wdly_dq_bitslip_rst) | self._rst.storage,
slp = self._dly_sel.storage[i] & self._wdly_dq_bitslip.re, slp = self._dly_sel.storage[i] & wdly_dq_bitslip,
cycles = 1) cycles = 1)
self.submodules += dqs_bitslip self.submodules += dqs_bitslip
self.specials += Instance("OSERDESE2", self.specials += Instance("OSERDESE2",
@ -270,8 +295,8 @@ class S7DDRPHY(Module, AutoCSR):
p_ODELAY_TYPE = "VARIABLE", p_ODELAY_TYPE = "VARIABLE",
p_ODELAY_VALUE = half_sys8x_taps, p_ODELAY_VALUE = half_sys8x_taps,
i_C = ClockSignal("sys"), i_C = ClockSignal("sys"),
i_LD = (self._dly_sel.storage[i] & self._wdly_dqs_rst.re) | self._rst.storage, i_LD = (self._dly_sel.storage[i] & wdly_dqs_rst) | self._rst.storage,
i_CE = self._dly_sel.storage[i] & self._wdly_dqs_inc.re, i_CE = self._dly_sel.storage[i] & wdly_dqs_inc,
i_LDPIPEEN = 0, i_LDPIPEEN = 0,
i_INC = 1, i_INC = 1,
o_ODATAIN = dqs_o_no_delay, o_ODATAIN = dqs_o_no_delay,
@ -290,8 +315,8 @@ class S7DDRPHY(Module, AutoCSR):
dm_o_nodelay = Signal() dm_o_nodelay = Signal()
dm_o_bitslip = BitSlip(8, dm_o_bitslip = BitSlip(8,
i = Cat(*[dfi.phases[n//2].wrdata_mask[n%2*databits//8+i] for n in range(8)]), i = Cat(*[dfi.phases[n//2].wrdata_mask[n%2*databits//8+i] for n in range(8)]),
rst = (self._dly_sel.storage[i] & self._wdly_dq_bitslip_rst.re) | self._rst.storage, rst = (self._dly_sel.storage[i] & wdly_dq_bitslip_rst) | self._rst.storage,
slp = self._dly_sel.storage[i] & self._wdly_dq_bitslip.re, slp = self._dly_sel.storage[i] & wdly_dq_bitslip,
cycles = 1) cycles = 1)
self.submodules += dm_o_bitslip self.submodules += dm_o_bitslip
self.specials += Instance("OSERDESE2", self.specials += Instance("OSERDESE2",
@ -318,9 +343,9 @@ class S7DDRPHY(Module, AutoCSR):
p_ODELAY_TYPE = "VARIABLE", p_ODELAY_TYPE = "VARIABLE",
p_ODELAY_VALUE = 0, p_ODELAY_VALUE = 0,
i_C = ClockSignal("sys"), i_C = ClockSignal("sys"),
i_LD = (self._dly_sel.storage[i] & self._wdly_dq_rst.re) | self._rst.storage, i_LD = (self._dly_sel.storage[i] & wdly_dq_rst) | self._rst.storage,
i_LDPIPEEN = 0, i_LDPIPEEN = 0,
i_CE = self._dly_sel.storage[i] & self._wdly_dq_inc.re, i_CE = self._dly_sel.storage[i] & wdly_dq_inc,
i_INC = 1, i_INC = 1,
o_ODATAIN = dm_o_nodelay, o_ODATAIN = dm_o_nodelay,
o_DATAOUT = pads.dm[i], o_DATAOUT = pads.dm[i],
@ -340,8 +365,8 @@ class S7DDRPHY(Module, AutoCSR):
dq_i_data = Signal(8) dq_i_data = Signal(8)
dq_o_bitslip = BitSlip(8, dq_o_bitslip = BitSlip(8,
i = Cat(*[dfi.phases[n//2].wrdata[n%2*databits+i] for n in range(8)]), i = Cat(*[dfi.phases[n//2].wrdata[n%2*databits+i] for n in range(8)]),
rst = (self._dly_sel.storage[i//8] & self._wdly_dq_bitslip_rst.re) | self._rst.storage, rst = (self._dly_sel.storage[i//8] & wdly_dq_bitslip_rst) | self._rst.storage,
slp = self._dly_sel.storage[i//8] & self._wdly_dq_bitslip.re, slp = self._dly_sel.storage[i//8] & wdly_dq_bitslip,
cycles = 1) cycles = 1)
self.submodules += dq_o_bitslip self.submodules += dq_o_bitslip
self.specials += Instance("OSERDESE2", self.specials += Instance("OSERDESE2",
@ -361,8 +386,8 @@ class S7DDRPHY(Module, AutoCSR):
o_OQ = dq_o_nodelay, o_OQ = dq_o_nodelay,
) )
dq_i_bitslip = BitSlip(8, dq_i_bitslip = BitSlip(8,
rst = (self._dly_sel.storage[i//8] & self._rdly_dq_bitslip_rst.re) | self._rst.storage, rst = (self._dly_sel.storage[i//8] & rdly_dq_bitslip_rst) | self._rst.storage,
slp = self._dly_sel.storage[i//8] & self._rdly_dq_bitslip.re, slp = self._dly_sel.storage[i//8] & rdly_dq_bitslip,
cycles = 1) cycles = 1)
self.submodules += dq_i_bitslip self.submodules += dq_i_bitslip
self.specials += Instance("ISERDESE2", self.specials += Instance("ISERDESE2",
@ -394,9 +419,9 @@ class S7DDRPHY(Module, AutoCSR):
p_ODELAY_TYPE = "VARIABLE", p_ODELAY_TYPE = "VARIABLE",
p_ODELAY_VALUE = 0, p_ODELAY_VALUE = 0,
i_C = ClockSignal("sys"), i_C = ClockSignal("sys"),
i_LD = (self._dly_sel.storage[i//8] & self._wdly_dq_rst.re)| self._rst.storage, i_LD = (self._dly_sel.storage[i//8] & wdly_dq_rst)| self._rst.storage,
i_LDPIPEEN = 0, i_LDPIPEEN = 0,
i_CE = self._dly_sel.storage[i//8] & self._wdly_dq_inc.re, i_CE = self._dly_sel.storage[i//8] & wdly_dq_inc,
i_INC = 1, i_INC = 1,
o_ODATAIN = dq_o_nodelay, o_ODATAIN = dq_o_nodelay,
o_DATAOUT = dq_o_delayed, o_DATAOUT = dq_o_delayed,
@ -411,9 +436,9 @@ class S7DDRPHY(Module, AutoCSR):
p_IDELAY_TYPE = "VARIABLE", p_IDELAY_TYPE = "VARIABLE",
p_IDELAY_VALUE = 0, p_IDELAY_VALUE = 0,
i_C = ClockSignal("sys"), i_C = ClockSignal("sys"),
i_LD = (self._dly_sel.storage[i//8] & self._rdly_dq_rst.re) | self._rst.storage, i_LD = (self._dly_sel.storage[i//8] & rdly_dq_rst) | self._rst.storage,
i_LDPIPEEN = 0, i_LDPIPEEN = 0,
i_CE = self._dly_sel.storage[i//8] & self._rdly_dq_inc.re, i_CE = self._dly_sel.storage[i//8] & rdly_dq_inc,
i_INC = 1, i_INC = 1,
i_IDATAIN = dq_i_nodelay, i_IDATAIN = dq_i_nodelay,
o_DATAOUT = dq_i_delayed o_DATAOUT = dq_i_delayed
@ -460,6 +485,7 @@ class S7DDRPHY(Module, AutoCSR):
self.comb += dqs_preamble.eq( wrdata_en.taps[wrtap - 1] & ~wrdata_en.taps[wrtap + 0]) self.comb += dqs_preamble.eq( wrdata_en.taps[wrtap - 1] & ~wrdata_en.taps[wrtap + 0])
self.comb += dqs_postamble.eq(wrdata_en.taps[wrtap + 1] & ~wrdata_en.taps[wrtap + 0]) self.comb += dqs_postamble.eq(wrdata_en.taps[wrtap + 1] & ~wrdata_en.taps[wrtap + 0])
# Xilinx Virtex7 (S7DDRPHY with odelay) ------------------------------------------------------------ # Xilinx Virtex7 (S7DDRPHY with odelay) ------------------------------------------------------------
class V7DDRPHY(S7DDRPHY): class V7DDRPHY(S7DDRPHY):
@ -477,3 +503,22 @@ class K7DDRPHY(S7DDRPHY):
class A7DDRPHY(S7DDRPHY): class A7DDRPHY(S7DDRPHY):
def __init__(self, pads, **kwargs): def __init__(self, pads, **kwargs):
S7DDRPHY.__init__(self, pads, with_odelay=False, **kwargs) S7DDRPHY.__init__(self, pads, with_odelay=False, **kwargs)
def s7ddrphy_with_ratio(ratio, phy_cls=A7DDRPHY, ddr_clk=None, serdes_reset_cnt=0):
"""Generate PHY class that uses DFIRateConverter to increase MC:PHY frequecy ratio"""
ddr_clk = ddr_clk or f"sys{4*ratio}x"
# Generate new class that wraps the original PHY
wrapper_cls = DFIRateConverter.phy_wrapper(
phy_cls = phy_cls,
ratio = ratio,
serdes_reset_cnt = serdes_reset_cnt,
)
# Create a wrapper that will ensure that correct ddr_clk kwarg is passed to the PHY
def wrapper(*args, **kwargs):
sys_clk_freq = kwargs.pop("sys_clk_freq", 100e6)
return wrapper_cls(*args, ddr_clk=ddr_clk, sys_clk_freq=ratio * sys_clk_freq, **kwargs)
return wrapper