diff --git a/examples/litedram_gen.py b/examples/litedram_gen.py index 392a530..eec2f13 100755 --- a/examples/litedram_gen.py +++ b/examples/litedram_gen.py @@ -72,7 +72,8 @@ def get_dram_ios(core_config): Subsignal("clk_p", Pins("X")), Subsignal("clk_n", Pins("X")), Subsignal("cke", Pins("X")), - Subsignal("odt", Pins("X")), + Subsignal("odt", Pins( + "X "*core_config["sdram_rank_nb"])), Subsignal("reset_n", Pins("X")) ), ] diff --git a/litedram/common.py b/litedram/common.py index 016a122..a6214a1 100644 --- a/litedram/common.py +++ b/litedram/common.py @@ -6,12 +6,13 @@ bankbits_max = 3 class PhySettings: - def __init__(self, memtype, dfi_databits, + def __init__(self, memtype, nranks, dfi_databits, nphases, rdphase, wrphase, rdcmdphase, wrcmdphase, cl, read_latency, write_latency, cwl=None): self.memtype = memtype + self.nranks = nranks self.dfi_databits = dfi_databits self.nphases = nphases @@ -79,9 +80,11 @@ def data_layout(data_width): class LiteDRAMInterface(Record): def __init__(self, address_align, settings): - self.address_width = settings.geom.rowbits + settings.geom.colbits - address_align + rankbits = log2_int(settings.phy.nranks) + self.address_width = settings.geom.rowbits + settings.geom.colbits + rankbits - address_align self.data_width = settings.phy.dfi_databits*settings.phy.nphases - self.nbanks = 2**settings.geom.bankbits + self.nbanks = settings.phy.nranks*(2**settings.geom.bankbits) + self.nranks = settings.phy.nranks self.settings = settings layout = [("bank"+str(i), cmd_layout(self.address_width)) for i in range(self.nbanks)] diff --git a/litedram/core/bankmachine.py b/litedram/core/bankmachine.py index 66a2fef..1df3aa6 100644 --- a/litedram/core/bankmachine.py +++ b/litedram/core/bankmachine.py @@ -27,14 +27,14 @@ class _AddressSlicer: class BankMachine(Module): - def __init__(self, n, aw, address_align, settings): + def __init__(self, n, aw, address_align, nranks, settings): self.req = req = Record(cmd_layout(aw)) self.refresh_req = Signal() self.refresh_gnt = Signal() self.ras_allowed = ras_allowed = Signal() self.cas_allowed = cas_allowed = Signal() a = settings.geom.addressbits - ba = settings.geom.bankbits + ba = settings.geom.bankbits + log2_int(nranks) self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a, ba)) # # # diff --git a/litedram/core/controller.py b/litedram/core/controller.py index 0bd8581..fe2087d 100644 --- a/litedram/core/controller.py +++ b/litedram/core/controller.py @@ -40,8 +40,10 @@ class LiteDRAMController(Module): } address_align = log2_int(burst_lengths[phy_settings.memtype]) - self.dfi = dfi.Interface(geom_settings.addressbits, + self.dfi = dfi.Interface( + geom_settings.addressbits, geom_settings.bankbits, + phy_settings.nranks, phy_settings.dfi_databits, phy_settings.nphases) @@ -54,10 +56,11 @@ class LiteDRAMController(Module): self.submodules.refresher = Refresher(self.settings) bank_machines = [] - for i in range(2**geom_settings.bankbits): + for i in range(phy_settings.nranks*(2**geom_settings.bankbits)): bank_machine = BankMachine(i, self.interface.address_width, address_align, + phy_settings.nranks, settings) bank_machines.append(bank_machine) self.submodules += bank_machine diff --git a/litedram/core/multiplexer.py b/litedram/core/multiplexer.py index 3a59395..a731518 100644 --- a/litedram/core/multiplexer.py +++ b/litedram/core/multiplexer.py @@ -3,6 +3,7 @@ from operator import add, or_, and_ from migen import * from migen.genlib.roundrobin import * +from migen.genlib.coding import Decoder from litex.soc.interconnect import stream from litex.soc.interconnect.csr import AutoCSR @@ -93,21 +94,31 @@ class _Steerer(Module): return cmd.valid & getattr(cmd, attr) for phase, sel in zip(dfi.phases, self.sel): - self.comb += [ - phase.cke.eq(1), - phase.cs_n.eq(0) - ] - if hasattr(phase, "odt"): - self.comb += phase.odt.eq(1) + self.comb += phase.cke.eq(1) if hasattr(phase, "reset_n"): self.comb += phase.reset_n.eq(1) + + nranks = len(phase.cs_n) + rankbits = log2_int(nranks) + if hasattr(phase, "odt"): + self.comb += phase.odt.eq(2**rankbits - 1) # FIXME: dynamic drive for multi-rank + if rankbits: + rank_decoder = Decoder(rankbits) + self.submodules += rank_decoder + self.comb += rank_decoder.i.eq((Array(cmd.ba[-rankbits:] for cmd in commands)[sel])) + self.sync += phase.cs_n.eq(~rank_decoder.o) + self.sync += phase.bank.eq(Array(cmd.ba[:-rankbits] for cmd in commands)[sel]) + else: + self.sync += phase.cs_n.eq(0) + self.sync += phase.bank.eq(Array(cmd.ba[:] for cmd in commands)[sel]) + self.sync += [ phase.address.eq(Array(cmd.a for cmd in commands)[sel]), - phase.bank.eq(Array(cmd.ba for cmd in commands)[sel]), phase.cas_n.eq(~Array(cmd.cas for cmd in commands)[sel]), phase.ras_n.eq(~Array(cmd.ras for cmd in commands)[sel]), phase.we_n.eq(~Array(cmd.we for cmd in commands)[sel]) ] + rddata_ens = Array(valid_and(cmd, "is_read") for cmd in commands) wrdata_ens = Array(valid_and(cmd, "is_write") for cmd in commands) self.sync += [ @@ -190,7 +201,7 @@ class Multiplexer(Module, AutoCSR): # Command steering nop = Record(cmd_request_layout(settings.geom.addressbits, - settings.geom.bankbits)) + log2_int(len(bank_machines)))) # nop must be 1st commands = [nop, choose_cmd.cmd, choose_req.cmd, refresher.cmd] (STEER_NOP, STEER_CMD, STEER_REQ, STEER_REFRESH) = range(4) diff --git a/litedram/core/refresher.py b/litedram/core/refresher.py index 904d671..4a18950 100644 --- a/litedram/core/refresher.py +++ b/litedram/core/refresher.py @@ -9,8 +9,8 @@ from litedram.core.multiplexer import * class Refresher(Module): def __init__(self, settings): # 1st command 1 cycle after assertion of ready - self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(settings.geom.addressbits, - settings.geom.bankbits)) + self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout( + settings.geom.addressbits, settings.geom.bankbits + log2_int(settings.phy.nranks))) # # # diff --git a/litedram/dfii.py b/litedram/dfii.py index 8ffd94d..1e0e5c0 100644 --- a/litedram/dfii.py +++ b/litedram/dfii.py @@ -17,12 +17,12 @@ class PhaseInjector(Module, AutoCSR): self.comb += [ If(self._command_issue.re, - phase.cs_n.eq(~self._command.storage[0]), + phase.cs_n.eq(Replicate(~self._command.storage[0], len(phase.cs_n))), phase.we_n.eq(~self._command.storage[1]), phase.cas_n.eq(~self._command.storage[2]), phase.ras_n.eq(~self._command.storage[3]) ).Else( - phase.cs_n.eq(1), + phase.cs_n.eq(Replicate(1, len(phase.cs_n))), phase.we_n.eq(1), phase.cas_n.eq(1), phase.ras_n.eq(1) @@ -38,10 +38,10 @@ class PhaseInjector(Module, AutoCSR): class DFIInjector(Module, AutoCSR): - def __init__(self, addressbits, bankbits, databits, nphases=1): - inti = dfi.Interface(addressbits, bankbits, databits, nphases) - self.slave = dfi.Interface(addressbits, bankbits, databits, nphases) - self.master = dfi.Interface(addressbits, bankbits, databits, nphases) + def __init__(self, addressbits, bankbits, nranks, databits, nphases=1): + inti = dfi.Interface(addressbits, bankbits, nranks, databits, nphases) + self.slave = dfi.Interface(addressbits, bankbits, nranks, databits, nphases) + self.master = dfi.Interface(addressbits, bankbits, nranks, databits, nphases) self._control = CSRStorage(4) # sel, cke, odt, reset_n diff --git a/litedram/frontend/crossbar.py b/litedram/frontend/crossbar.py index ce61082..04b1090 100644 --- a/litedram/frontend/crossbar.py +++ b/litedram/frontend/crossbar.py @@ -17,11 +17,13 @@ class LiteDRAMCrossbar(Module): self.rca_bits = controller.address_width self.nbanks = controller.nbanks + self.nranks = controller.nranks self.cmd_buffer_depth = controller.settings.cmd_buffer_depth self.read_latency = controller.settings.phy.read_latency + 1 self.write_latency = controller.settings.phy.write_latency + 1 self.bank_bits = log2_int(self.nbanks, False) + self.rank_bits = log2_int(self.nranks, False) self.masters = [] @@ -42,7 +44,7 @@ class LiteDRAMCrossbar(Module): data_width = self.controller.data_width # crossbar port - port = LiteDRAMNativePort(mode, self.rca_bits + self.bank_bits, self.controller.data_width, "sys", len(self.masters), with_reordering) + port = LiteDRAMNativePort(mode, self.rca_bits + self.bank_bits - self.rank_bits, self.controller.data_width, "sys", len(self.masters), with_reordering) self.masters.append(port) # clock domain crossing @@ -177,8 +179,8 @@ class LiteDRAMCrossbar(Module): self.comb += master.wdata.bank.eq(wbank) def split_master_addresses(self, bank_bits, rca_bits, cba_shift): - m_ba = [] # bank address - m_rca = [] # row and column address + m_ba = [] # bank address + m_rca = [] # row and column address for master in self.masters: cba = Signal(self.bank_bits) rca = Signal(self.rca_bits) diff --git a/litedram/phy/dfi.py b/litedram/phy/dfi.py index aae56b0..8eef84a 100644 --- a/litedram/phy/dfi.py +++ b/litedram/phy/dfi.py @@ -2,12 +2,12 @@ from migen import * from migen.genlib.record import * -def phase_cmd_description(addressbits, bankbits): +def phase_cmd_description(addressbits, bankbits, nranks): return [ ("address", addressbits, DIR_M_TO_S), ("bank", bankbits, DIR_M_TO_S), ("cas_n", 1, DIR_M_TO_S), - ("cs_n", 1, DIR_M_TO_S), + ("cs_n", nranks, DIR_M_TO_S), ("ras_n", 1, DIR_M_TO_S), ("we_n", 1, DIR_M_TO_S), ("cke", 1, DIR_M_TO_S), @@ -32,21 +32,21 @@ def phase_rddata_description(databits): ] -def phase_description(addressbits, bankbits, databits): - r = phase_cmd_description(addressbits, bankbits) +def phase_description(addressbits, bankbits, nranks, databits): + r = phase_cmd_description(addressbits, bankbits, nranks) r += phase_wrdata_description(databits) r += phase_rddata_description(databits) return r class Interface(Record): - def __init__(self, addressbits, bankbits, databits, nphases=1): - layout = [("p"+str(i), phase_description(addressbits, bankbits, databits)) for i in range(nphases)] + def __init__(self, addressbits, bankbits, nranks, databits, nphases=1): + layout = [("p"+str(i), phase_description(addressbits, bankbits, nranks, databits)) for i in range(nphases)] Record.__init__(self, layout) self.phases = [getattr(self, "p"+str(i)) for i in range(nphases)] for p in self.phases: p.cas_n.reset = 1 - p.cs_n.reset = 1 + p.cs_n.reset = (2**nranks-1) p.ras_n.reset = 1 p.we_n.reset = 1 diff --git a/litedram/phy/s7ddrphy.py b/litedram/phy/s7ddrphy.py index bc6625b..269bb2b 100644 --- a/litedram/phy/s7ddrphy.py +++ b/litedram/phy/s7ddrphy.py @@ -47,9 +47,12 @@ class S7DDRPHY(Module, AutoCSR): tck = 2/(2*nphases*sys_clk_freq) addressbits = len(pads.a) bankbits = len(pads.ba) + if hasattr(pads, "cs_n"): + nranks = len(pads.cs_n) databits = len(pads.dq) nphases = nphases + iodelay_tap_average = { 200e6: 78e-12, 300e6: 52e-12, @@ -86,6 +89,7 @@ class S7DDRPHY(Module, AutoCSR): self.settings = PhySettings( memtype=memtype, dfi_databits=2*databits, + nranks=nranks, nphases=nphases, rdphase=rdphase, wrphase=wrphase, @@ -97,7 +101,7 @@ class S7DDRPHY(Module, AutoCSR): write_latency=cwl_sys_latency ) - self.dfi = Interface(addressbits, bankbits, 2*databits, 4) + self.dfi = Interface(addressbits, bankbits, nranks, 2*databits, 4) # # # @@ -158,11 +162,26 @@ class S7DDRPHY(Module, AutoCSR): i_D5=self.dfi.phases[2].bank[i], i_D6=self.dfi.phases[2].bank[i], i_D7=self.dfi.phases[3].bank[i], i_D8=self.dfi.phases[3].bank[i] ) + if hasattr(pads, "cs_n"): + for i in range(nranks): + self.specials += \ + Instance("OSERDESE2", + p_DATA_WIDTH=2*nphases, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OQ=pads.cs_n[i], + i_OCE=1, + i_RST=ResetSignal(), + i_CLK=ClockSignal(ddr_clk), i_CLKDIV=ClockSignal(), + i_D1=self.dfi.phases[0].cs_n[i], i_D2=self.dfi.phases[0].cs_n[i], + i_D3=self.dfi.phases[1].cs_n[i], i_D4=self.dfi.phases[1].cs_n[i], + i_D5=self.dfi.phases[2].cs_n[i], i_D6=self.dfi.phases[2].cs_n[i], + i_D7=self.dfi.phases[3].cs_n[i], i_D8=self.dfi.phases[3].cs_n[i] + ) controls = ["ras_n", "cas_n", "we_n", "cke", "odt"] if hasattr(pads, "reset_n"): controls.append("reset_n") - if hasattr(pads, "cs_n"): - controls.append("cs_n") for name in controls: self.specials += \ Instance("OSERDESE2",