From 70054bacdbb4e46c434b698738f3c1b575c746fd Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 29 Apr 2020 12:29:31 +0100 Subject: [PATCH] Add support for DDR4 RDIMMs Signed-off-by: David Shah --- litedram/common.py | 6 +++ litedram/init.py | 93 +++++++++++++++++++++++++++++++++++----- litedram/phy/usddrphy.py | 13 +++++- 3 files changed, 101 insertions(+), 11 deletions(-) diff --git a/litedram/common.py b/litedram/common.py index b15ac63..10d3790 100644 --- a/litedram/common.py +++ b/litedram/common.py @@ -173,6 +173,7 @@ class PhySettings(Settings): cl, read_latency, write_latency, nranks=1, cwl=None): self.set_attributes(locals()) self.cwl = cl if cwl is None else cwl + self.is_rdimm = False # Optional DDR3/DDR4 electrical settings: # rtt_nom: Non-Writes on-die termination impedance @@ -182,6 +183,11 @@ class PhySettings(Settings): assert self.memtype in ["DDR3", "DDR4"] self.set_attributes(locals()) + # Optional RDIMM configuration + def set_rdimm(self, tck, rcd_pll_bypass, rcd_ca_cs_drive, rcd_odt_cke_drive, rcd_clk_drive): + assert self.memtype == "DDR4" + self.is_rdimm = True + self.set_attributes(locals()) class GeomSettings(Settings): def __init__(self, bankbits, rowbits, colbits): diff --git a/litedram/init.py b/litedram/init.py index bb76d35..8a0f90b 100644 --- a/litedram/init.py +++ b/litedram/init.py @@ -364,9 +364,62 @@ def get_ddr4_phy_init_sequence(phy_settings, timing_settings): mr5 = 0 mr6 = format_mr6(4) # FIXME: tCCD + rdimm_init = [] + if phy_settings.is_rdimm: + def get_coarse_speed(tck, pll_bypass): + # JESD82-31A page 78 + f_to_coarse_speed = { + 1600e6: 0, + 1866e6: 1, + 2133e6: 2, + 2400e6: 3, + 2666e6: 4, + 2933e6: 5, + 3200e6: 6, + } + if pll_bypass: + return 7 + else: + for f, speed in f_to_coarse_speed.items(): + if tck >= 2/f: + return speed + raise ValueError + def get_fine_speed(tck): + # JESD82-31A page 83 + freq = 2/tck + fine_speed = (freq - 1240e6) // 20e6 + fine_speed = max(fine_speed, 0) + fine_speed = min(fine_speed, 0b1100001) + return fine_speed + + coarse_speed = get_coarse_speed(phy_settings.tck, phy_settings.rcd_pll_bypass) + fine_speed = get_fine_speed(phy_settings.tck) + + rcd_reset = 0x060 | 0x0 # F0RC06: command space control; 0: reset RCD + + f0rc0f = 0x0F0 | 0x4 # F0RC05: 0 nCK latency adder + + f0rc03 = 0x030 | phy_settings.rcd_ca_cs_drive # F0RC03: CA/CS drive strength + f0rc04 = 0x040 | phy_settings.rcd_odt_cke_drive # F0RC04: ODT/CKE drive strength + f0rc05 = 0x050 | phy_settings.rcd_clk_drive # F0RC04: ODT/CKE drive strength + + f0rc0a = 0x0A0 | coarse_speed # F0RC0A: coarse speed selection and PLL bypass + f0rc3x = 0x300 | fine_speed # F0RC3x: fine speed selection + + rdimm_init = [ + ("Reset RCD", rcd_reset, 7, cmds["MODE_REGISTER"], 50000), + ("Load RCD F0RC0F", f0rc0f, 7, cmds["MODE_REGISTER"], 100), + ("Load RCD F0RC03", f0rc03, 7, cmds["MODE_REGISTER"], 100), + ("Load RCD F0RC04", f0rc04, 7, cmds["MODE_REGISTER"], 100), + ("Load RCD F0RC05", f0rc05, 7, cmds["MODE_REGISTER"], 100), + ("Load RCD F0RC0A", f0rc0a, 7, cmds["MODE_REGISTER"], 100), + ("Load RCD F0RC3X", f0rc3x, 7, cmds["MODE_REGISTER"], 100), + ] + init_sequence = [ ("Release reset", 0x0000, 0, cmds["UNRESET"], 50000), ("Bring CKE high", 0x0000, 0, cmds["CKE"], 10000), + ] + rdimm_init + [ ("Load Mode Register 3", mr3, 3, cmds["MODE_REGISTER"], 0), ("Load Mode Register 6", mr6, 6, cmds["MODE_REGISTER"], 0), ("Load Mode Register 5", mr5, 5, cmds["MODE_REGISTER"], 0), @@ -426,6 +479,10 @@ def get_sdram_phy_c_header(phy_settings, timing_settings): r += "#define SDRAM_PHY_DELAYS 8\n" r += "#define SDRAM_PHY_BITSLIPS 4\n" + if phy_settings.is_rdimm: + assert phy_settings.memtype == "DDR4" + r += "#define SDRAM_PHY_DDR4_RDIMM\n" + r += "\n" r += "static void cdelay(int i);\n" @@ -482,16 +539,32 @@ const unsigned long sdram_dfii_pix_rddata_addr[SDRAM_PHY_PHASES] = {{ r += "static void init_sequence(void)\n{\n" for comment, a, ba, cmd, delay in init_sequence: - r += "\t/* {0} */\n".format(comment) - r += "\tsdram_dfii_pi0_address_write({0:#x});\n".format(a) - r += "\tsdram_dfii_pi0_baddress_write({0:d});\n".format(ba) - if cmd[:12] == "DFII_CONTROL": - r += "\tsdram_dfii_control_write({0});\n".format(cmd) - else: - r += "\tcommand_p0({0});\n".format(cmd) - if delay: - r += "\tcdelay({0:d});\n".format(delay) - r += "\n" + invert_masks = [(0, 0), ] + if phy_settings.is_rdimm: + assert phy_settings.memtype == "DDR4" + # JESD82-31A page 38 + # + # B-side chips have certain usually-inconsequential address and BA + # bits inverted by the RCD to reduce SSO current. For mode register + # writes, however, we must compensate for this. BG[1] also directs + # writes either to the A side (BG[1]=0) or B side (BG[1]=1) + # + # The 'ba != 7' is because we don't do this to writes to the RCD + # itself. + if ba != 7: + invert_masks.append((0b10101111111000, 0b1111)) + + for a_inv, ba_inv in invert_masks: + r += "\t/* {0} */\n".format(comment) + r += "\tsdram_dfii_pi0_address_write({0:#x});\n".format(a ^ a_inv) + r += "\tsdram_dfii_pi0_baddress_write({0:d});\n".format(ba ^ ba_inv) + if cmd[:12] == "DFII_CONTROL": + r += "\tsdram_dfii_control_write({0});\n".format(cmd) + else: + r += "\tcommand_p0({0});\n".format(cmd) + if delay: + r += "\tcdelay({0:d});\n".format(delay) + r += "\n" r += "}\n" r += "#endif\n" diff --git a/litedram/phy/usddrphy.py b/litedram/phy/usddrphy.py index 556f1f4..f87e83d 100644 --- a/litedram/phy/usddrphy.py +++ b/litedram/phy/usddrphy.py @@ -22,7 +22,8 @@ class USDDRPHY(Module, AutoCSR): memtype = "DDR3", sys_clk_freq = 100e6, iodelay_clk_freq = 200e6, - cmd_latency = 0): + cmd_latency = 0, + is_rdimm = False): phytype = self.__class__.__name__ device = {"USDDRPHY": "ULTRASCALE", "USPDDRPHY": "ULTRASCALE_PLUS"}[phytype] pads = PHYPadsCombiner(pads) @@ -90,6 +91,16 @@ class USDDRPHY(Module, AutoCSR): write_latency = cwl_sys_latency ) + if is_rdimm: + # All drive settings for an 8-chip load + self.settings.set_rdimm( + tck = tck, + rcd_pll_bypass = False, + rcd_ca_cs_drive = 0x5, + rcd_odt_cke_drive = 0x5, + rcd_clk_drive = 0x5 + ) + # DFI Interface ---------------------------------------------------------------------------- self.dfi = dfi = Interface(addressbits, bankbits, nranks, 2*databits, nphases) if memtype == "DDR4":