From 438544a84835ca22df8f79538540832028bf0ec4 Mon Sep 17 00:00:00 2001 From: Jean THOMAS Date: Thu, 4 Jun 2020 17:06:22 +0200 Subject: [PATCH] Correct nMigen transition bugs --- gram/common.py | 31 ++++++++++-------- gram/compat.py | 65 +++++++++++++++++-------------------- gram/core/__init__.py | 8 ++--- gram/core/bankmachine.py | 35 ++++++++++---------- gram/core/controller.py | 25 +++++++------- gram/core/crossbar.py | 32 ++++++++++-------- gram/core/multiplexer.py | 49 ++++++++++++++++++---------- gram/core/refresher.py | 63 +++++++++++++++++++----------------- gram/dfii.py | 70 ++++++++++++++++++++++------------------ gram/frontend/axi.py | 4 +-- gram/phy/ecp5ddrphy.py | 4 +-- gram/phy/model.py | 2 +- gram/stream.py | 33 +++++++++++++++++-- 13 files changed, 241 insertions(+), 180 deletions(-) diff --git a/gram/common.py b/gram/common.py index 8b7f0a6..b733f35 100644 --- a/gram/common.py +++ b/gram/common.py @@ -9,6 +9,9 @@ from operator import add from collections import OrderedDict from nmigen import * +from nmigen.hdl.rec import * +from nmigen.utils import log2_int + import gram.stream as stream # Helpers ------------------------------------------------------------------------------------------ @@ -117,7 +120,7 @@ class BitSlip(Elaboratable): def elaborate(self, platform): m = Module() - value = Signal(max=self._cycles*dw) + value = Signal(range(self._cycles*dw)) with m.If(self.slp): m.d.sync += value.eq(value+1) with m.Elif(self.rst): @@ -260,7 +263,7 @@ def cmd_request_rw_layout(a, ba): ] -class LiteDRAMInterface(Record): +class gramInterface(Record): def __init__(self, address_align, settings): rankbits = log2_int(settings.phy.nranks) self.address_align = address_align @@ -276,7 +279,7 @@ class LiteDRAMInterface(Record): # Ports -------------------------------------------------------------------------------------------- -class LiteDRAMNativePort(Settings): +class gramNativePort(Settings): def __init__(self, mode, address_width, data_width, clock_domain="sys", id=0): self.set_attributes(locals()) @@ -308,12 +311,12 @@ class LiteDRAMNativePort(Settings): return self.cmd.addr[:cba_shift] -class LiteDRAMNativeWritePort(LiteDRAMNativePort): +class gramNativeWritePort(gramNativePort): def __init__(self, *args, **kwargs): LiteDRAMNativePort.__init__(self, "write", *args, **kwargs) -class LiteDRAMNativeReadPort(LiteDRAMNativePort): +class gramNativeReadPort(gramNativePort): def __init__(self, *args, **kwargs): LiteDRAMNativePort.__init__(self, "read", *args, **kwargs) @@ -325,16 +328,17 @@ class tXXDController(Elaboratable): self.valid = Signal() self.ready = ready = Signal(reset=txxd is None) #ready.attr.add("no_retiming") TODO + self._txxd = txxd def elaborate(self, platform): m = Module() - if txxd is not None: - count = Signal(range(max(txxd, 2))) + if self._txxd is not None: + count = Signal(range(max(self._txxd, 2))) with m.If(self.valid): m.d.sync += [ - count.eq(txxd-1), - self.ready.eq((txxd - 1) == 0), + count.eq(self._txxd-1), + self.ready.eq((self._txxd - 1) == 0), ] with m.Else(): m.d.sync += count.eq(count-1) @@ -348,15 +352,16 @@ class tFAWController(Elaboratable): self.valid = Signal() self.ready = Signal(reset=1) #ready.attr.add("no_retiming") TODO + self._tfaw = tfaw def elaborate(self, platform): m = Module() - if tfaw is not None: - count = Signal(range(max(tfaw, 2))) - window = Signal(tfaw) + if self._tfaw is not None: + count = Signal(range(max(self._tfaw, 2))) + window = Signal(self._tfaw) m.d.sync += window.eq(Cat(self.valid, window)) - m.d.comb += count.eq(reduce(add, [window[i] for i in range(tfaw)])) + m.d.comb += count.eq(reduce(add, [window[i] for i in range(self._tfaw)])) with m.If(count < 4): with m.If(count == 3): m.d.sync += self.ready.eq(~self.valid) diff --git a/gram/compat.py b/gram/compat.py index 938ce67..90ebf3c 100644 --- a/gram/compat.py +++ b/gram/compat.py @@ -1,4 +1,5 @@ from nmigen import * +from nmigen.compat import Case __ALL__ = ["delayed_enter", "RoundRobin", "Timeline"] @@ -19,47 +20,41 @@ def delayed_enter(m, src, dst, delay): with m.State(statename): m.next = deststate -(SP_WITHDRAW, SP_CE) = range(2) - +# Original nMigen implementation by HarryHo90sHK class RoundRobin(Elaboratable): - def __init__(self, n, switch_policy=SP_WITHDRAW): + """A round-robin scheduler. + Parameters + ---------- + n : int + Maximum number of requests to handle. + Attributes + ---------- + request : Signal(n) + Signal where a '1' on the i-th bit represents an incoming request from the i-th device. + grant : Signal(range(n)) + Signal that equals to the index of the device which is currently granted access. + stb : Signal() + Strobe signal to enable granting access to the next device requesting. Externally driven. + """ + def __init__(self, n): + self.n = n self.request = Signal(n) - self.grant = Signal(max=max(2, n)) - self.switch_policy = switch_policy - if self.switch_policy == SP_CE: - self.ce = Signal() + self.grant = Signal(range(n)) + self.stb = Signal() def elaborate(self, platform): m = Module() - # TODO: fix - - if n > 1: - cases = {} - for i in range(n): - switch = [] - for j in reversed(range(i+1, i+n)): - t = j % n - switch = [ - If(self.request[t], - self.grant.eq(t) - ).Else( - *switch - ) - ] - if self.switch_policy == SP_WITHDRAW: - case = [If(~self.request[i], *switch)] - else: - case = switch - cases[i] = case - statement = Case(self.grant, cases) - if self.switch_policy == SP_CE: - with m.If(self.ce): - m.d.sync += statement - else: - m.d.sync += statement - else: - m.d.comb += self.grant.eq(0) + with m.If(self.stb): + with m.Switch(self.grant): + for i in range(self.n): + with m.Case(i): + for j in reversed(range(i+1, i+self.n)): + # If i+1 <= j < n, then t == j; (after i) + # If n <= j < i+n, then t == j - n (before i) + t = j % self.n + with m.If(self.request[t]): + m.d.sync += self.grant.eq(t) return m diff --git a/gram/core/__init__.py b/gram/core/__init__.py index 7f2254f..a570ad3 100644 --- a/gram/core/__init__.py +++ b/gram/core/__init__.py @@ -19,13 +19,13 @@ class gramCore(Peripheral, Elaboratable): def elaborate(self, platform): m = Module() - m.submodules.dfii = DFIInjector( + m.submodules.dfii = dfii = DFIInjector( addressbits = self._geom_settings.addressbits, bankbits = self._geom_settings.bankbits, nranks = self._phy.settings.nranks, databits = self._phy.settings.dfi_databits, nphases = self._phy.settings.nphases) - m.d.comb += self.dfii.master.connect(self._phy.dfi) + m.d.comb += dfii.master.connect(self._phy.dfi) m.submodules.controller = controller = gramController( phy_settings = self._phy.settings, @@ -33,8 +33,8 @@ class gramCore(Peripheral, Elaboratable): timing_settings = self._timing_settings, clk_freq = self._clk_freq, **self._kwargs) - m.d.comb += controller.dfi.connect(self.dfii.slave) + m.d.comb += controller.dfi.connect(dfii.slave) - m.submodules.crossbar = LiteDRAMCrossbar(controller.interface) + m.submodules.crossbar = gramCrossbar(controller.interface) return m diff --git a/gram/core/bankmachine.py b/gram/core/bankmachine.py index b22a996..d96230f 100644 --- a/gram/core/bankmachine.py +++ b/gram/core/bankmachine.py @@ -32,7 +32,7 @@ class _AddressSlicer: def col(self, address): split = self.colbits - self.address_align - return Cat(Replicate(0, self.address_align), address[:split]) + return Cat(Repl(0, self.address_align), address[:split]) # BankMachine -------------------------------------------------------------------------------------- @@ -85,6 +85,7 @@ class BankMachine(Elaboratable): Stream of commands to the Multiplexer """ def __init__(self, n, address_width, address_align, nranks, settings): + self.settings = settings self.req = req = Record(cmd_layout(address_width)) self.refresh_req = refresh_req = Signal() self.refresh_gnt = refresh_gnt = Signal() @@ -99,23 +100,23 @@ class BankMachine(Elaboratable): auto_precharge = Signal() # Command buffer --------------------------------------------------------------------------- - cmd_buffer_layout = [("we", 1), ("addr", len(req.addr))] + cmd_buffer_layout = [("we", 1), ("addr", len(self.req.addr))] cmd_buffer_lookahead = stream.SyncFIFO( - cmd_buffer_layout, settings.cmd_buffer_depth, - buffered=settings.cmd_buffer_buffered) + cmd_buffer_layout, self.settings.cmd_buffer_depth, + buffered=self.settings.cmd_buffer_buffered) cmd_buffer = stream.Buffer(cmd_buffer_layout) # 1 depth buffer to detect row change m.submodules += cmd_buffer_lookahead, cmd_buffer m.d.comb += [ - req.connect(cmd_buffer_lookahead.sink, keep={"valid", "ready", "we", "addr"}), + self.req.connect(cmd_buffer_lookahead.sink, include={"valid", "ready", "we", "addr"}), cmd_buffer_lookahead.source.connect(cmd_buffer.sink), - cmd_buffer.source.ready.eq(req.wdata_ready | req.rdata_valid), - req.lock.eq(cmd_buffer_lookahead.source.valid | cmd_buffer.source.valid), + cmd_buffer.source.ready.eq(self.req.wdata_ready | self.req.rdata_valid), + self.req.lock.eq(cmd_buffer_lookahead.source.valid | cmd_buffer.source.valid), ] - slicer = _AddressSlicer(settings.geom.colbits, address_align) + slicer = _AddressSlicer(self.settings.geom.colbits, address_align) # Row tracking ----------------------------------------------------------------------------- - row = Signal(settings.geom.rowbits) + row = Signal(self.settings.geom.rowbits) row_opened = Signal() row_hit = Signal() row_open = Signal() @@ -138,17 +139,17 @@ class BankMachine(Elaboratable): m.d.comb += cmd.a.eq((auto_precharge << 10) | slicer.col(cmd_buffer.source.addr)) # tWTP (write-to-precharge) controller ----------------------------------------------------- - write_latency = math.ceil(settings.phy.cwl / settings.phy.nphases) - precharge_time = write_latency + settings.timing.tWR + settings.timing.tCCD # AL=0 + write_latency = math.ceil(self.settings.phy.cwl / self.settings.phy.nphases) + precharge_time = write_latency + self.settings.timing.tWR + self.settings.timing.tCCD # AL=0 m.submodules.twtpcon = twtpcon = tXXDController(precharge_time) m.d.comb += twtpcon.valid.eq(cmd.valid & cmd.ready & cmd.is_write) # tRC (activate-activate) controller ------------------------------------------------------- - m.submodules.trccon = trccon = tXXDController(settings.timing.tRC) + m.submodules.trccon = trccon = tXXDController(self.settings.timing.tRC) m.d.comb += trccon.valid.eq(cmd.valid & cmd.ready & row_open) # tRAS (activate-precharge) controller ----------------------------------------------------- - m.submodules.trascon = trascon = tXXDController(settings.timing.tRAS) + m.submodules.trascon = trascon = tXXDController(self.settings.timing.tRAS) m.d.comb += trascon.valid.eq(cmd.valid & cmd.ready & row_open) # Auto Precharge generation ---------------------------------------------------------------- @@ -173,13 +174,13 @@ class BankMachine(Elaboratable): ] with m.If(cmd_buffer.source.we): m.d.comb += [ - req.wdata_ready.eq(cmd.ready), + self.req.wdata_ready.eq(cmd.ready), cmd.is_write.eq(1), cmd.we.eq(1), ] with m.Else(): m.d.comb += [ - req.rdata_valid.eq(cmd.ready), + self.req.rdata_valid.eq(cmd.ready), cmd.is_read.eq(1), ] with m.If(cmd.ready & auto_precharge): @@ -232,7 +233,7 @@ class BankMachine(Elaboratable): with m.If(~refresh_req): m.next = "Regular" - delayed_enter(m, "TRP", "ACTIVATE", settings.timing.tRP - 1) - delayed_enter(m, "TRCD", "REGULAR", settings.timing.tRCD - 1) + delayed_enter(m, "tRP", "Activate", self.settings.timing.tRP - 1) + delayed_enter(m, "tRCD", "Regular", self.settings.timing.tRCD - 1) return m diff --git a/gram/core/controller.py b/gram/core/controller.py index 5ef23cc..987ea86 100644 --- a/gram/core/controller.py +++ b/gram/core/controller.py @@ -6,6 +6,7 @@ """LiteDRAM Controller.""" from nmigen import * +from nmigen.utils import log2_int from gram.common import * from gram.phy import dfi @@ -46,7 +47,7 @@ class ControllerSettings(Settings): class gramController(Elaboratable): def __init__(self, phy_settings, geom_settings, timing_settings, clk_freq, controller_settings=ControllerSettings()): - address_align = log2_int(burst_lengths[phy_settings.memtype]) + self._address_align = log2_int(burst_lengths[phy_settings.memtype]) # Settings --------------------------------------------------------------------------------- self.settings = controller_settings @@ -54,11 +55,8 @@ class gramController(Elaboratable): self.settings.geom = geom_settings self.settings.timing = timing_settings - nranks = phy_settings.nranks - nbanks = 2**geom_settings.bankbits - # LiteDRAM Interface (User) ---------------------------------------------------------------- - self.interface = interface = LiteDRAMInterface(address_align, self.settings) + self.interface = interface = gramInterface(self._address_align, self.settings) # DFI Interface (Memory) ------------------------------------------------------------------- self.dfi = dfi.Interface( @@ -68,12 +66,17 @@ class gramController(Elaboratable): databits = phy_settings.dfi_databits, nphases = phy_settings.nphases) + self._clk_freq = clk_freq + def elaborate(self, platform): m = Module() + nranks = self.settings.phy.nranks + nbanks = 2**self.settings.geom.bankbits + # Refresher -------------------------------------------------------------------------------- m.submodules.refresher = self.settings.refresh_cls(self.settings, - clk_freq = clk_freq, + clk_freq = self._clk_freq, zqcs_freq = self.settings.refresh_zqcs_freq, postponing = self.settings.refresh_postponing) @@ -81,21 +84,21 @@ class gramController(Elaboratable): bank_machines = [] for n in range(nranks*nbanks): bank_machine = BankMachine(n, - address_width = interface.address_width, - address_align = address_align, + address_width = self.interface.address_width, + address_align = self._address_align, nranks = nranks, settings = self.settings) bank_machines.append(bank_machine) m.submodules += bank_machine - m.d.comb += getattr(interface, "bank"+str(n)).connect(bank_machine.req) + m.d.comb += getattr(self.interface, "bank"+str(n)).connect(bank_machine.req) # Multiplexer ------------------------------------------------------------------------------ m.submodules.multiplexer = Multiplexer( settings = self.settings, bank_machines = bank_machines, - refresher = self.refresher, + refresher = m.submodules.refresher, dfi = self.dfi, - interface = interface) + interface = self.interface) return m diff --git a/gram/core/crossbar.py b/gram/core/crossbar.py index e71650f..5efbd2b 100644 --- a/gram/core/crossbar.py +++ b/gram/core/crossbar.py @@ -19,7 +19,7 @@ import gram.stream as stream # LiteDRAMCrossbar --------------------------------------------------------------------------------- -class gramCrossbar(Module): +class gramCrossbar(Elaboratable): """Multiplexes LiteDRAMController (slave) between ports (masters) To get a port to LiteDRAM, use the `get_port` method. It handles data width @@ -119,7 +119,9 @@ class gramCrossbar(Module): return port - def do_finalize(self): + def elaborate(self, platform): + m = Module() + controller = self.controller nmasters = len(self.masters) @@ -133,8 +135,8 @@ class gramCrossbar(Module): master_wdata_readys = [0]*nmasters master_rdata_valids = [0]*nmasters - arbiters = [roundrobin.RoundRobin(nmasters, roundrobin.SP_CE) for n in range(self.nbanks)] - self.submodules += arbiters + arbiters = [RoundRobin(nmasters) for n in range(self.nbanks)] + m.submodules += arbiters for nb, arbiter in enumerate(arbiters): bank = getattr(controller, "bank"+str(nb)) @@ -152,13 +154,13 @@ class gramCrossbar(Module): # Arbitrate ---------------------------------------------------------------------------- bank_selected = [(ba == nb) & ~locked for ba, locked in zip(m_ba, master_locked)] bank_requested = [bs & master.cmd.valid for bs, master in zip(bank_selected, self.masters)] - self.comb += [ + m.d.comb += [ arbiter.request.eq(Cat(*bank_requested)), - arbiter.ce.eq(~bank.valid & ~bank.lock) + arbiter.stb.eq(~bank.valid & ~bank.lock) ] # Route requests ----------------------------------------------------------------------- - self.comb += [ + m.d.comb += [ bank.addr.eq(Array(m_rca)[arbiter.grant]), bank.we.eq(Array(self.masters)[arbiter.grant].cmd.we), bank.valid.eq(Array(bank_requested)[arbiter.grant]) @@ -174,23 +176,23 @@ class gramCrossbar(Module): for nm, master_wdata_ready in enumerate(master_wdata_readys): for i in range(self.write_latency): new_master_wdata_ready = Signal() - self.sync += new_master_wdata_ready.eq(master_wdata_ready) + m.d.sync += new_master_wdata_ready.eq(master_wdata_ready) master_wdata_ready = new_master_wdata_ready master_wdata_readys[nm] = master_wdata_ready for nm, master_rdata_valid in enumerate(master_rdata_valids): for i in range(self.read_latency): new_master_rdata_valid = Signal() - self.sync += new_master_rdata_valid.eq(master_rdata_valid) + m.d.sync += new_master_rdata_valid.eq(master_rdata_valid) master_rdata_valid = new_master_rdata_valid master_rdata_valids[nm] = master_rdata_valid for master, master_ready in zip(self.masters, master_readys): - self.comb += master.cmd.ready.eq(master_ready) + m.d.comb += master.cmd.ready.eq(master_ready) for master, master_wdata_ready in zip(self.masters, master_wdata_readys): - self.comb += master.wdata.ready.eq(master_wdata_ready) + m.d.comb += master.wdata.ready.eq(master_wdata_ready) for master, master_rdata_valid in zip(self.masters, master_rdata_valids): - self.comb += master.rdata.valid.eq(master_rdata_valid) + m.d.comb += master.rdata.valid.eq(master_rdata_valid) # Route data writes ------------------------------------------------------------------------ wdata_cases = {} @@ -203,8 +205,10 @@ class gramCrossbar(Module): controller.wdata.eq(0), controller.wdata_we.eq(0) ] - self.comb += Case(Cat(*master_wdata_readys), wdata_cases) + m.d.comb += Case(Cat(*master_wdata_readys), wdata_cases) # Route data reads ------------------------------------------------------------------------- for master in self.masters: - self.comb += master.rdata.data.eq(controller.rdata) + m.d.comb += master.rdata.data.eq(controller.rdata) + + return m diff --git a/gram/core/multiplexer.py b/gram/core/multiplexer.py index 3a9203d..433f194 100644 --- a/gram/core/multiplexer.py +++ b/gram/core/multiplexer.py @@ -69,11 +69,11 @@ class _CommandChooser(Elaboratable): command = request.is_cmd & self.want_cmds & (~is_act_cmd | self.want_activates) read = request.is_read == self.want_reads write = request.is_write == self.want_writes - self.comb += valids[i].eq(request.valid & (command | (read & write))) + m.d.comb += valids[i].eq(request.valid & (command | (read & write))) - arbiter = RoundRobin(n, SP_CE) - self.submodules += arbiter + arbiter = RoundRobin(n) + m.submodules += arbiter choices = Array(valids[i] for i in range(n)) m.d.comb += [ arbiter.request.eq(valids), @@ -82,13 +82,13 @@ class _CommandChooser(Elaboratable): for name in ["a", "ba", "is_read", "is_write", "is_cmd"]: choices = Array(getattr(req, name) for req in self._requests) - self.comb += getattr(self.cmd, name).eq(choices[arbiter.grant]) + m.d.comb += getattr(self.cmd, name).eq(choices[arbiter.grant]) for name in ["cas", "ras", "we"]: # we should only assert those signals when valid is 1 choices = Array(getattr(req, name) for req in self._requests) with m.If(self.cmd.valid): - m.d.comb += getattr(cmd, name).eq(choices[arbiter.grant]) + m.d.comb += getattr(self.cmd, name).eq(choices[arbiter.grant]) for i, request in enumerate(self._requests): with m.If(self.cmd.valid & self.cmd.ready & (arbiter.grant == i)): @@ -96,7 +96,7 @@ class _CommandChooser(Elaboratable): # Arbitrate if a command is being accepted or if the command is not valid to ensure a valid # command is selected when cmd.ready goes high. - m.d.comb += arbiter.ce.eq(self.cmd.ready | ~self.cmd.valid) + m.d.comb += arbiter.stb.eq(self.cmd.ready | ~self.cmd.valid) return m @@ -164,14 +164,14 @@ class _Steerer(Elaboratable): nranks = len(phase.cs_n) rankbits = log2_int(nranks) if hasattr(phase, "reset_n"): - self.comb += phase.reset_n.eq(1) - m.d.comb += phase.cke.eq(Replicate(Signal(reset=1), nranks)) + m.d.comb += phase.reset_n.eq(1) + m.d.comb += phase.cke.eq(Repl(Signal(reset=1), nranks)) if hasattr(phase, "odt"): # FIXME: add dynamic drive for multi-rank (will be needed for high frequencies) - m.d.comb += phase.odt.eq(Replicate(Signal(reset=1), nranks)) + m.d.comb += phase.odt.eq(Repl(Signal(reset=1), nranks)) if rankbits: rank_decoder = Decoder(nranks) - self.submodules += rank_decoder + m.submodules += rank_decoder m.d.comb += rank_decoder.i.eq((Array(cmd.ba[-rankbits:] for cmd in commands)[sel])) if i == 0: # Select all ranks on refresh. with m.If(sel == STEER_REFRESH): @@ -233,6 +233,20 @@ class Multiplexer(Peripheral, Elaboratable): dfi, interface): assert(settings.phy.nphases == len(dfi.phases)) + self._settings = settings + self._bank_machines = bank_machines + self._refresher = refresher + self._dfi = dfi + self._interface = interface + + def elaborate(self, platform): + m = Module() + + settings = self._settings + bank_machines = self._bank_machines + refresher = self._refresher + dfi = self._dfi + interface = self._interface ras_allowed = Signal(reset=1) cas_allowed = Signal(reset=1) @@ -300,11 +314,10 @@ class Multiplexer(Peripheral, Elaboratable): t = timeout - 1 time = Signal(range(t+1)) m.d.comb += max_time.eq(time == 0) - m.d.sync += If(~en, - time.eq(t) - ).Elif(~max_time, - time.eq(time - 1) - ) + with m.If(~en): + m.d.sync += time.eq(t) + with m.Elif(~max_time): + m.d.sync += time.eq(time - 1) else: m.d.comb += max_time.eq(0) return en, max_time @@ -409,8 +422,10 @@ class Multiplexer(Peripheral, Elaboratable): m.next = "Read" # TODO: reduce this, actual limit is around (cl+1)/nphases - delayed_enter(m, "RTW", "WRITE", settings.phy.read_latency-1) + delayed_enter(m, "RTW", "Write", settings.phy.read_latency-1) if settings.with_bandwidth: data_width = settings.phy.dfi_databits*settings.phy.nphases - self.submodules.bandwidth = Bandwidth(self.choose_req.cmd, data_width) + m.submodules.bandwidth = Bandwidth(self.choose_req.cmd, data_width) + + return m diff --git a/gram/core/refresher.py b/gram/core/refresher.py index ebaea32..6be5b2a 100644 --- a/gram/core/refresher.py +++ b/gram/core/refresher.py @@ -6,11 +6,11 @@ """LiteDRAM Refresher.""" from nmigen import * - -from litex.soc.interconnect import stream +from nmigen.utils import bits_for, log2_int from gram.core.multiplexer import * from gram.compat import Timeline +import gram.stream as stream # RefreshExecuter ---------------------------------------------------------------------------------- @@ -36,7 +36,7 @@ class RefreshExecuter(Elaboratable): trp = self._trp trfc = self._trfc - self.sync += [ + m.d.sync += [ self._cmd.a.eq( 0), self._cmd.ba.eq( 0), self._cmd.cas.eq(0), @@ -87,20 +87,19 @@ class RefreshSequencer(Elaboratable): def __init__(self, cmd, trp, trfc, postponing=1): self.start = Signal() self.done = Signal() + self._trp = trp self._trfc = trfc self._postponing = postponing + self._cmd = cmd def elaborate(self, platform): m = Module() - trp = self._trp - trfc = self._trfc + executer = RefreshExecuter(self._cmd, self._trp, self._trfc) + m.submodules += executer - executer = RefreshExecuter(cmd, trp, trfc) - self.submodules += executer - - count = Signal(bits_for(postponing), reset=postponing-1) + count = Signal(bits_for(self._postponing), reset=self._postponing-1) with m.If(self.start): m.d.sync += count.eq(count.reset) with m.Elif(executer.done): @@ -161,6 +160,7 @@ class RefreshPostponer(Elaboratable): self._postponing = postponing def elaborate(self, platform): + m = Module() count = Signal(bits_for(self._postponing), reset=self._postponing-1) @@ -257,6 +257,9 @@ class Refresher(Elaboratable): babits = settings.geom.bankbits + log2_int(settings.phy.nranks) self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a=abits, ba=babits)) self._postponing = postponing + self._settings = settings + self._clk_freq = clk_freq + self._zqcs_freq = zqcs_freq def elaborate(self, platform): m = Module() @@ -264,32 +267,34 @@ class Refresher(Elaboratable): wants_refresh = Signal() wants_zqcs = Signal() + settings = self._settings + # Refresh Timer ---------------------------------------------------------------------------- timer = RefreshTimer(settings.timing.tREFI) - self.submodules.timer = timer + m.submodules.timer = timer m.d.comb += timer.wait.eq(~timer.done) # Refresh Postponer ------------------------------------------------------------------------ postponer = RefreshPostponer(self._postponing) - self.submodules.postponer = postponer + m.submodules.postponer = postponer m.d.comb += [ - postponer.req_i.eq(self.timer.done), + postponer.req_i.eq(timer.done), wants_refresh.eq(postponer.req_o), ] # Refresh Sequencer ------------------------------------------------------------------------ - sequencer = RefreshSequencer(cmd, settings.timing.tRP, settings.timing.tRFC, self._postponing) - self.submodules.sequencer = sequencer + sequencer = RefreshSequencer(self.cmd, settings.timing.tRP, settings.timing.tRFC, self._postponing) + m.submodules.sequencer = sequencer if settings.timing.tZQCS is not None: # ZQCS Timer --------------------------------------------------------------------------- - zqcs_timer = RefreshTimer(int(clk_freq/zqcs_freq)) - self.submodules.zqcs_timer = zqcs_timer + zqcs_timer = RefreshTimer(int(self._clk_freq/self._zqcs_freq)) + m.submodules.zqcs_timer = zqcs_timer m.d.comb += wants_zqcs.eq(zqcs_timer.done) # ZQCS Executer ------------------------------------------------------------------------ - zqcs_executer = ZQCSExecuter(cmd, settings.timing.tRP, settings.timing.tZQCS) - self.submodules.zqs_executer = zqcs_executer + zqcs_executer = ZQCSExecuter(self.cmd, settings.timing.tRP, settings.timing.tZQCS) + m.submodules.zqs_executer = zqcs_executer m.d.comb += zqcs_timer.wait.eq(~zqcs_executer.done) # Refresh FSM ------------------------------------------------------------------------------ @@ -299,40 +304,40 @@ class Refresher(Elaboratable): m.next = "Wait-Bank-Machines" with m.State("Wait-Bank-Machines"): - m.d.comb += cmd.valid.eq(1) - with m.If(cmd.ready): + m.d.comb += self.cmd.valid.eq(1) + with m.If(self.cmd.ready): m.d.comb += sequencer.start.eq(1) m.next = "Do-Refresh" if settings.timing.tZQCS is None: with m.State("Do-Refresh"): - m.d.comb += cmd.valid.eq(1) + m.d.comb += self.cmd.valid.eq(1) with m.If(sequencer.done): m.d.comb += [ - cmd.valid.eq(0), - cmd.last.eq(1), + self.cmd.valid.eq(0), + self.cmd.last.eq(1), ] m.next = "Idle" else: with m.State("Do-Refresh"): - m.d.comb += cmd.valid.eq(1) + m.d.comb += self.cmd.valid.eq(1) with m.If(sequencer.done): with m.If(wants_zqcs): m.d.comb += zqcs_executer.start.eq(1) m.next = "Do-Zqcs" with m.Else(): m.d.comb += [ - cmd.valid.eq(0), - cmd.last.eq(1), + self.cmd.valid.eq(0), + self.cmd.last.eq(1), ] m.next = "Idle" with m.State("Do-Zqcs"): - m.d.comb += cmd.valid.eq(1) + m.d.comb += self.cmd.valid.eq(1) with m.If(zqcs_executer.done): m.d.comb += [ - cmd.valid.eq(0), - cmd.last.eq(1), + self.cmd.valid.eq(0), + self.cmd.last.eq(1), ] m.next = "Idle" diff --git a/gram/dfii.py b/gram/dfii.py index 11db5d6..3436e33 100644 --- a/gram/dfii.py +++ b/gram/dfii.py @@ -12,43 +12,47 @@ from lambdasoc.periph import Peripheral class PhaseInjector(Peripheral, Elaboratable): def __init__(self, phase): + super().__init__(name = "phaseinjector") + bank = self.csr_bank() self._command = bank.csr(6, "rw") self._command_issue = bank.csr(1, "rw") - self._address = bank.csr(len(phase.address), "rw", reset_less=True) - self._baddress = bank.csr(len(phase.bank), "rw", reset_less=True) - self._wrdata = bank.csr(len(phase.wrdata), "rw", reset_less=True) - self._rddata = bank.csr(len(phase.rddata)) + self._address = bank.csr(len(phase.address), "rw") + self._baddress = bank.csr(len(phase.bank), "rw") + self._wrdata = bank.csr(len(phase.wrdata), "rw") + self._rddata = bank.csr(len(phase.rddata), "rw") + + self._phase = phase def elaborate(self, platform): m = Module() m.d.comb += [ - phase.address.eq(self._address.storage), - phase.bank.eq(self._baddress.storage), - phase.wrdata_en.eq(self._command_issue.re & self._command.storage[4]), - phase.rddata_en.eq(self._command_issue.re & self._command.storage[5]), - phase.wrdata.eq(self._wrdata.storage), - phase.wrdata_mask.eq(0) + self._phase.address.eq(self._address.r_data), + self._phase.bank.eq(self._baddress.r_data), + self._phase.wrdata_en.eq(self._command_issue.r_stb & self._command.r_data[4]), + self._phase.rddata_en.eq(self._command_issue.r_stb & self._command.r_data[5]), + self._phase.wrdata.eq(self._wrdata.r_data), + self._phase.wrdata_mask.eq(0) ] - with m.If(self._command_issue.re): + with m.If(self._command_issue.r_stb): m.d.comb += [ - 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]), + self._phase.cs_n.eq(Repl(value=~self._command.r_data[0], count=len(self._phase.cs_n))), + self._phase.we_n.eq(~self._command.r_data[1]), + self._phase.cas_n.eq(~self._command.r_data[2]), + self._phase.ras_n.eq(~self._command.r_data[3]), ] with m.Else(): m.d.comb += [ - 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), + self._phase.cs_n.eq(Repl(value=1, count=len(self._phase.cs_n))), + self._phase.we_n.eq(1), + self._phase.cas_n.eq(1), + self._phase.ras_n.eq(1), ] - with m.If(phase.rddata_valid): - m.d.sync += self._rddata.status.eq(phase.rddata) + with m.If(self._phase.rddata_valid): + m.d.sync += self._rddata.w_data.eq(self._phase.rddata) return m @@ -56,29 +60,31 @@ class PhaseInjector(Peripheral, Elaboratable): class DFIInjector(Peripheral, Elaboratable): def __init__(self, addressbits, bankbits, nranks, databits, nphases=1): + super().__init__(name = "dfii") + + self._nranks = nranks + self._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) bank = self.csr_bank() - self._control = bank.csr(4) # sel, cke, odt, reset_n - - #for n, phase in enumerate(inti.phases): - # setattr(self.submodules, "pi" + str(n), PhaseInjector(phase)) TODO - - # # # + self._control = bank.csr(4, "rw") # sel, cke, odt, reset_n def elaborate(self, platform): m = Module() - with m.If(self._control.storage[0]): + for n, phase in enumerate(self._inti.phases): + setattr(m.submodules, "pi" + str(n), PhaseInjector(phase)) + + with m.If(self._control.r_data[0]): m.d.comb += self.slave.connect(self.master) with m.Else(): m.d.comb += self._inti.connect(self.master) - for i in range(nranks): - m.d.comb += [phase.cke[i].eq(self._control.storage[1]) for phase in self._inti.phases] - m.d.comb += [phase.odt[i].eq(self._control.storage[2]) for phase in self._inti.phases if hasattr(phase, "odt")] - m.d.comb += [phase.reset_n.eq(self._control.storage[3]) for phase in self._inti.phases if hasattr(phase, "reset_n")] + for i in range(self._nranks): + m.d.comb += [phase.cke[i].eq(self._control.r_data[1]) for phase in self._inti.phases] + m.d.comb += [phase.odt[i].eq(self._control.r_data[2]) for phase in self._inti.phases if hasattr(phase, "odt")] + m.d.comb += [phase.reset_n.eq(self._control.r_data[3]) for phase in self._inti.phases if hasattr(phase, "reset_n")] return m diff --git a/gram/frontend/axi.py b/gram/frontend/axi.py index d310f65..c2b868b 100644 --- a/gram/frontend/axi.py +++ b/gram/frontend/axi.py @@ -188,9 +188,9 @@ class LiteDRAMAXI2Native(Module): self.submodules.read = LiteDRAMAXI2NativeR(axi, port, r_buffer_depth, base_address) # Write / Read arbitration ----------------------------------------------------------------- - arbiter = RoundRobin(2, SP_CE) + arbiter = RoundRobin(2) self.submodules += arbiter - self.comb += arbiter.ce.eq(~port.cmd.valid | port.cmd.ready) + self.comb += arbiter.stb.eq(~port.cmd.valid | port.cmd.ready) for i, master in enumerate([self.write, self.read]): self.comb += arbiter.request[i].eq(master.cmd_request) self.comb += master.cmd_grant.eq(arbiter.grant == i) diff --git a/gram/phy/ecp5ddrphy.py b/gram/phy/ecp5ddrphy.py index 1070685..b9a9576 100644 --- a/gram/phy/ecp5ddrphy.py +++ b/gram/phy/ecp5ddrphy.py @@ -18,7 +18,7 @@ from lambdasoc.periph import Peripheral import gram.stream as stream from gram.common import * from gram.phy.dfi import * -from gram.timeline import Timeline +from gram.compat import Timeline # Lattice ECP5 DDR PHY Initialization -------------------------------------------------------------- @@ -88,7 +88,7 @@ class ECP5DDRPHYInit(Elaboratable): class ECP5DDRPHY(Peripheral, Elaboratable): def __init__(self, pads, sys_clk_freq=100e6): - super().__init__() # Peripheral init + super().__init__() #self.pads = PHYPadsCombiner(pads) self.pads = pads diff --git a/gram/phy/model.py b/gram/phy/model.py index 33ca2d8..1bfd13c 100644 --- a/gram/phy/model.py +++ b/gram/phy/model.py @@ -71,7 +71,7 @@ class BankModel(Module): write_port.adr.eq(wraddr), write_port.dat_w.eq(self.write_data), If(we_granularity, - write_port.we.eq(Replicate(self.write, data_width//8) & ~self.write_mask), + write_port.we.eq(Repl(self.write, data_width//8) & ~self.write_mask), ).Else( write_port.we.eq(self.write), ), diff --git a/gram/stream.py b/gram/stream.py index bde9c6c..921cb55 100644 --- a/gram/stream.py +++ b/gram/stream.py @@ -3,7 +3,7 @@ from nmigen.hdl.rec import * from nmigen.lib import fifo -__all__ = ["Endpoint", "SyncFIFO", "AsyncFIFO"] +__all__ = ["Endpoint", "SyncFIFO", "AsyncFIFO", "Buffer"] def _make_fanout(layout): @@ -93,9 +93,12 @@ class _FIFOWrapper: class SyncFIFO(Elaboratable, _FIFOWrapper): - def __init__(self, layout, depth, fwft=True): + def __init__(self, layout, depth, fwft=True, buffered=False): super().__init__(layout) - self.fifo = fifo.SyncFIFO(width=len(Record(self.layout)), depth=depth, fwft=fwft) + if buffered: + self.fifo = fifo.SyncFIFOBuffered(width=len(Record(self.layout)), depth=depth, fwft=fwft) + else: + self.fifo = fifo.SyncFIFO(width=len(Record(self.layout)), depth=depth, fwft=fwft) self.depth = self.fifo.depth self.level = self.fifo.level @@ -106,3 +109,27 @@ class AsyncFIFO(Elaboratable, _FIFOWrapper): self.fifo = fifo.AsyncFIFO(width=len(Record(self.layout)), depth=depth, r_domain=r_domain, w_domain=w_domain) self.depth = self.fifo.depth + +class PipeValid(Elaboratable): + """Pipe valid/payload to cut timing path""" + def __init__(self, layout): + self.sink = Endpoint(layout) + self.source = Endpoint(layout) + + def elaborate(self, platform): + m = Module() + + # Pipe when source is not valid or is ready. + with m.If(~self.source.valid | self.source.ready): + m.d.sync += [ + self.source.valid.eq(self.sink.valid), + self.source.first.eq(self.sink.first), + self.source.last.eq(self.sink.last), + self.source.payload.eq(self.sink.payload), + self.source.param.eq(self.sink.param), + ] + m.d.comb += self.sink.ready.eq(~self.source.valid | self.source.ready) + + return m + +class Buffer(PipeValid): pass # FIXME: Replace Buffer with PipeValid in codebase? -- 2.30.2