Add support for clam shell topology (#332)
Add clam shell topology support.
This commit is contained in:
parent
d8c327b2b1
commit
83a29b190d
|
@ -229,6 +229,7 @@ class PhySettings(Settings):
|
|||
write_dq_dqs_training: bool = False,
|
||||
write_latency_calibration: bool = False,
|
||||
read_leveling: bool = False,
|
||||
is_clam_shell: bool = False,
|
||||
):
|
||||
if strobes is None:
|
||||
strobes = databits // 8
|
||||
|
|
|
@ -17,11 +17,12 @@ from litedram.core.crossbar import LiteDRAMCrossbar
|
|||
class LiteDRAMCore(Module, AutoCSR):
|
||||
def __init__(self, phy, geom_settings, timing_settings, clk_freq, **kwargs):
|
||||
self.submodules.dfii = DFIInjector(
|
||||
addressbits = max(geom_settings.addressbits, getattr(phy, "addressbits", 0)),
|
||||
bankbits = max(geom_settings.bankbits, getattr(phy, "bankbits", 0)),
|
||||
nranks = phy.settings.nranks,
|
||||
databits = phy.settings.dfi_databits,
|
||||
nphases = phy.settings.nphases)
|
||||
addressbits = max(geom_settings.addressbits, getattr(phy, "addressbits", 0)),
|
||||
bankbits = max(geom_settings.bankbits, getattr(phy, "bankbits", 0)),
|
||||
nranks = phy.settings.nranks,
|
||||
databits = phy.settings.dfi_databits,
|
||||
nphases = phy.settings.nphases,
|
||||
is_clam_shell = phy.settings.is_clam_shell)
|
||||
self.comb += self.dfii.master.connect(phy.dfi)
|
||||
|
||||
self.submodules.controller = controller = LiteDRAMController(
|
||||
|
|
|
@ -21,6 +21,9 @@ class PhaseInjector(Module, AutoCSR):
|
|||
CSRField("ras", size=1, description="DFI row address strobe bus"),
|
||||
CSRField("wren", size=1, description="DFI write data enable bus"),
|
||||
CSRField("rden", size=1, description="DFI read data enable bus"),
|
||||
# Separate cs for clam shell topology
|
||||
CSRField("cs_top", size=1, description="DFI chip select bus for top half only"),
|
||||
CSRField("cs_bottom", size=1, description="DFI chip select bus for bottom half only"),
|
||||
], description="Control DFI signals on a single phase")
|
||||
|
||||
self._command_issue = CSR() # description="The command gets commited on a write to this register"
|
||||
|
@ -33,7 +36,15 @@ class PhaseInjector(Module, AutoCSR):
|
|||
|
||||
self.comb += [
|
||||
If(self._command_issue.re,
|
||||
phase.cs_n.eq(Replicate(~self._command.fields.cs, len(phase.cs_n))),
|
||||
If(self._command.fields.cs_top,
|
||||
phase.cs_n.eq(2), # cs_n=0b10
|
||||
).Else(
|
||||
If(self._command.fields.cs_bottom,
|
||||
phase.cs_n.eq(1), # cs_n=0b01
|
||||
).Else(
|
||||
phase.cs_n.eq(Replicate(~self._command.fields.cs, len(phase.cs_n))),
|
||||
),
|
||||
),
|
||||
phase.we_n.eq(~self._command.fields.we),
|
||||
phase.cas_n.eq(~self._command.fields.cas),
|
||||
phase.ras_n.eq(~self._command.fields.ras)
|
||||
|
@ -55,10 +66,10 @@ class PhaseInjector(Module, AutoCSR):
|
|||
# DFIInjector --------------------------------------------------------------------------------------
|
||||
|
||||
class DFIInjector(Module, AutoCSR):
|
||||
def __init__(self, addressbits, bankbits, nranks, databits, nphases=1):
|
||||
def __init__(self, addressbits, bankbits, nranks, databits, nphases=1, is_clam_shell=False):
|
||||
self.slave = dfi.Interface(addressbits, bankbits, nranks, databits, nphases)
|
||||
self.master = dfi.Interface(addressbits, bankbits, nranks, databits, nphases)
|
||||
csr_dfi = dfi.Interface(addressbits, bankbits, nranks, databits, nphases)
|
||||
self.master = dfi.Interface(addressbits, bankbits, nranks*2 if is_clam_shell else nranks, databits, nphases)
|
||||
csr_dfi = dfi.Interface(addressbits, bankbits, nranks*2 if is_clam_shell else nranks, databits, nphases)
|
||||
|
||||
self.ext_dfi = dfi.Interface(addressbits, bankbits, nranks, databits, nphases)
|
||||
self.ext_dfi_sel = Signal()
|
||||
|
@ -87,7 +98,11 @@ class DFIInjector(Module, AutoCSR):
|
|||
self.ext_dfi.connect(self.master)
|
||||
# Through LiteDRAM controller.
|
||||
).Else(
|
||||
self.slave.connect(self.master)
|
||||
self.slave.connect(self.master),
|
||||
# Broadcast cs_n for clam shell topology
|
||||
If(is_clam_shell,
|
||||
[self.master.phases[i].cs_n.eq(Replicate(self.slave.phases[i].cs_n, 2)) for i in range(nphases)],
|
||||
)
|
||||
)
|
||||
# Software Control (through CSRs).
|
||||
# --------------------------------
|
||||
|
|
|
@ -23,6 +23,14 @@ cmds = {
|
|||
"CKE": "DFII_CONTROL_CKE|DFII_CONTROL_ODT|DFII_CONTROL_RESET_N"
|
||||
}
|
||||
|
||||
# Swap two bits in num
|
||||
# https://www.techiedelight.com/swap-two-bits-given-position-integer/
|
||||
def swap_bit(num, a, b):
|
||||
if ((num >> a) & 1) != ((num >> b) & 1):
|
||||
num = num ^ (1 << a)
|
||||
num = num ^ (1 << b)
|
||||
return num
|
||||
|
||||
def reg(fields):
|
||||
# takes a list of tuples: [(bit_offset, bit_width, value), ...]
|
||||
regval = 0
|
||||
|
@ -894,6 +902,9 @@ def get_sdram_phy_c_header(phy_settings, timing_settings, geom_settings):
|
|||
r.define("DFII_COMMAND_RAS", "0x08")
|
||||
r.define("DFII_COMMAND_WRDATA", "0x10")
|
||||
r.define("DFII_COMMAND_RDDATA", "0x20")
|
||||
if phy_settings.is_clam_shell:
|
||||
r.define("DFII_COMMAND_CS_TOP", "0x40")
|
||||
r.define("DFII_COMMAND_CS_BOTTOM", "0x80")
|
||||
r.newline()
|
||||
|
||||
phytype = phy_settings.phytype.upper()
|
||||
|
@ -941,6 +952,9 @@ def get_sdram_phy_c_header(phy_settings, timing_settings, geom_settings):
|
|||
if phy_settings.is_rdimm:
|
||||
assert phy_settings.memtype == "DDR4"
|
||||
r.define("SDRAM_PHY_DDR4_RDIMM")
|
||||
if phy_settings.is_clam_shell:
|
||||
assert phy_settings.memtype == "DDR4"
|
||||
r.define("SDRAM_PHY_CLAM_SHELL")
|
||||
|
||||
# litedram doesn't support multiple ranks
|
||||
supported_memory = 2 ** (geom_settings.bankbits +
|
||||
|
@ -1010,6 +1024,33 @@ def get_sdram_phy_c_header(phy_settings, timing_settings, geom_settings):
|
|||
invert_masks.append((0b10101111111000, 0b1111))
|
||||
|
||||
for a_inv, ba_inv in invert_masks:
|
||||
# handle clam shell topology
|
||||
if cmd == cmds["MODE_REGISTER"] and phy_settings.is_clam_shell:
|
||||
b += f"/* {comment} for top */"
|
||||
b += f"sdram_dfii_pi0_address_write({a ^ a_inv:#x});"
|
||||
b += f"sdram_dfii_pi0_baddress_write({ba ^ ba_inv:d});"
|
||||
b += f"command_p0({cmd}|DFII_COMMAND_CS_TOP);"
|
||||
if delay:
|
||||
b += f"cdelay({delay});\n"
|
||||
b.newline()
|
||||
|
||||
# swap addr and pass to bottom
|
||||
b += f"/* {comment} for bottom */"
|
||||
addr = a ^ a_inv
|
||||
addr = swap_bit(addr, 3, 4)
|
||||
addr = swap_bit(addr, 5, 6)
|
||||
addr = swap_bit(addr, 7, 8)
|
||||
addr = swap_bit(addr, 11, 13)
|
||||
b += f"sdram_dfii_pi0_address_write({addr:#x});"
|
||||
baddr = ba ^ ba_inv
|
||||
baddr = swap_bit(baddr, 0, 1)
|
||||
b += f"sdram_dfii_pi0_baddress_write({baddr:d});"
|
||||
b += f"command_p0({cmd}|DFII_COMMAND_CS_BOTTOM);"
|
||||
if delay:
|
||||
b += f"cdelay({delay});\n"
|
||||
b.newline()
|
||||
continue
|
||||
|
||||
b += f"/* {comment} */"
|
||||
b += f"sdram_dfii_pi0_address_write({a ^ a_inv:#x});"
|
||||
b += f"sdram_dfii_pi0_baddress_write({ba ^ ba_inv:d});"
|
||||
|
|
|
@ -32,7 +32,8 @@ class USDDRPHY(Module, AutoCSR):
|
|||
cwl = None,
|
||||
cmd_latency = 0,
|
||||
cmd_delay = None,
|
||||
is_rdimm = False):
|
||||
is_rdimm = False,
|
||||
is_clam_shell = False):
|
||||
phytype = self.__class__.__name__
|
||||
device = {"USDDRPHY": "ULTRASCALE", "USPDDRPHY": "ULTRASCALE_PLUS"}[phytype]
|
||||
pads = PHYPadsCombiner(pads)
|
||||
|
@ -97,7 +98,7 @@ class USDDRPHY(Module, AutoCSR):
|
|||
memtype = memtype,
|
||||
databits = databits,
|
||||
dfi_databits = 2*databits,
|
||||
nranks = nranks,
|
||||
nranks = nranks//2 if is_clam_shell else nranks,
|
||||
nphases = nphases,
|
||||
rdphase = self._rdphase.storage,
|
||||
wrphase = self._wrphase.storage,
|
||||
|
@ -112,6 +113,7 @@ class USDDRPHY(Module, AutoCSR):
|
|||
read_leveling = True,
|
||||
delays = 512,
|
||||
bitslips = 8,
|
||||
is_clam_shell = is_clam_shell,
|
||||
)
|
||||
|
||||
if is_rdimm:
|
||||
|
|
Loading…
Reference in New Issue