Rework DFI interface code
authorJean THOMAS <git0@pub.jeanthomas.me>
Mon, 8 Jun 2020 09:17:46 +0000 (11:17 +0200)
committerJean THOMAS <git0@pub.jeanthomas.me>
Mon, 8 Jun 2020 09:18:28 +0000 (11:18 +0200)
gram/phy/dfi.py
gram/phy/ecp5ddrphy.py

index f78bb801126b05e36279491cc51b83dce1374ad9..867c6f13574ca0236aa978ba0a9b12dd8c03d277 100644 (file)
@@ -1,11 +1,14 @@
 # This file is Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
-#              Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
+# This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
 
 from nmigen import *
 from nmigen.hdl.rec import *
 
-def phase_cmd_description(addressbits, bankbits, nranks):
+__ALL__ = ["Interface"]
+
+def phase_description(addressbits, bankbits, nranks, databits):
     return [
+        # cmd description
         ("address", addressbits, DIR_FANOUT),
         ("bank",       bankbits, DIR_FANOUT),
         ("cas_n",             1, DIR_FANOUT),
@@ -15,97 +18,37 @@ def phase_cmd_description(addressbits, bankbits, nranks):
         ("cke",          nranks, DIR_FANOUT),
         ("odt",          nranks, DIR_FANOUT),
         ("reset_n",           1, DIR_FANOUT),
-        ("act_n",             1, DIR_FANOUT)
-    ]
-
-
-def phase_wrdata_description(databits):
-    return [
+        ("act_n",             1, DIR_FANOUT),
+        # wrdata description
         ("wrdata",         databits, DIR_FANOUT),
         ("wrdata_en",             1, DIR_FANOUT),
-        ("wrdata_mask", databits//8, DIR_FANOUT)
-    ]
-
-
-def phase_rddata_description(databits):
-    return [
+        ("wrdata_mask", databits//8, DIR_FANOUT),
+        # rddata description
         ("rddata_en",           1, DIR_FANOUT),
         ("rddata",       databits, DIR_FANIN),
-        ("rddata_valid",        1, DIR_FANIN)
+        ("rddata_valid",        1, DIR_FANIN),
     ]
 
 
-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):
+class Interface:
     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:
+        self.phases = []
+        for p in range(nphases):
+            p = Record(phase_description(addressbits, bankbits, nranks, databits))
+            self.phases += [p]
             p.cas_n.reset = 1
             p.cs_n.reset = (2**nranks-1)
             p.ras_n.reset = 1
             p.we_n.reset = 1
             p.act_n.reset = 1
 
-    # Returns pairs (DFI-mandated signal name, Migen signal object)
-    def get_standard_names(self, m2s=True, s2m=True):
-        r = []
-        add_suffix = len(self.phases) > 1
-        for n, phase in enumerate(self.phases):
-            for field, size, direction in phase.layout:
-                if (m2s and direction == DIR_FANOUT) or (s2m and direction == DIR_FANIN):
-                    if add_suffix:
-                        if direction == DIR_FANOUT:
-                            suffix = "_p" + str(n)
-                        else:
-                            suffix = "_w" + str(n)
-                    else:
-                        suffix = ""
-                    r.append(("dfi_" + field + suffix, getattr(phase, field)))
-        return r
-
-
-class Interconnect(Elaboratable):
-    def __init__(self, master, slave):
-        self._master = master
-        self._slave = slave
-
-    def elaborate(self, platform):
-        m = Module()
-        m.d.comb += self._master.connect(self._slave)
-        return m
-
-
-class DDR4DFIMux(Elaboratable):
-    def __init__(self, dfi_i, dfi_o):
-        self.dfi_i = dfi_i
-        self.dfi_o = dfi_o
-
-    def elaborate(self, platform):
-        m = Module()
-
-        dfi_i = self.dfi_i
-        dfi_o = self.dfi_o
+    def connect(self, target):
+        if not isinstance(target, Interface):
+            raise TypeError("Target must be an instance of Interface, not {!r}"
+                .format(target))
 
-        for i in range(len(dfi_i.phases)):
-            p_i = dfi_i.phases[i]
-            p_o = dfi_o.phases[i]
-            m.d.comb += p_i.connect(p_o)
-            with m.If(~p_i.ras_n & p_i.cas_n & p_i.we_n):
-                m.d.comb += [
-                    p_o.act_n.eq(0),
-                    p_o.we_n.eq(p_i.address[14]),
-                    p_o.cas_n.eq(p_i.address[15]),
-                    p_o.ras_n.eq(p_i.address[16]),
-                ]
-            with m.Else():
-                m.d.comb += p_o.act_n.eq(1)
+        ret = []
+        for i in range(min(len(self.phases), len(target.phases))):
+            ret += [self.phases[i].connect(target.phases[i])]
 
-        return m
+        return ret
index b9a9576beaf8bd5b938ed61e3738f7edfa2b000c..e228f4d69e8b034b87665e69496dae17281d9e77 100644 (file)
@@ -1,5 +1,6 @@
 # This file is Copyright (c) 2019 David Shah <dave@ds0.me>
 # This file is Copyright (c) 2019-2020 Florent Kermarrec <florent@enjoy-digital.fr>
+# This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
 # License: BSD
 
 # 1:2 frequency-ratio DDR3 PHY for Lattice's ECP5
@@ -7,8 +8,6 @@
 
 import math
 
-# from litex.soc.interconnect.csr import *
-
 from nmigen import *
 from nmigen.lib.cdc import FFSynchronizer
 from nmigen.utils import log2_int
@@ -17,7 +16,7 @@ from lambdasoc.periph import Peripheral
 
 import gram.stream as stream
 from gram.common import *
-from gram.phy.dfi import *
+from gram.phy.dfi import Interface
 from gram.compat import Timeline
 
 # Lattice ECP5 DDR PHY Initialization --------------------------------------------------------------
@@ -116,22 +115,27 @@ class ECP5DDRPHY(Peripheral, Elaboratable):
         self.bus = self._bridge.bus
         self.irq = self._bridge.irq
 
+        addressbits = len(self.pads.a.o)
+        bankbits = len(self.pads.ba.o)
+        nranks = 1 if not hasattr(self.pads, "cs_n") else len(self.pads.cs_n)
+        databits = len(self.pads.dq.oe)
+        self.dfi = Interface(addressbits, bankbits, nranks, 4*databits, 4)
+
     def elaborate(self, platform):
         m = Module()
 
-        memtype = "DDR3"
         tck = 2/(2*2*self._sys_clk_freq)
+        nphases = 2
+        databits = len(self.pads.dq.oe)
+        nranks = 1 if not hasattr(self.pads, "cs_n") else len(self.pads.cs_n)
         addressbits = len(self.pads.a.o)
         bankbits = len(self.pads.ba.o)
-        nranks = 1 if not hasattr(self.pads, "cs_n") else len(self.pads.cs_n)
-        databits = len(self.pads.dq.oe)
-        nphases = 2
 
         # Init -------------------------------------------------------------------------------------
         m.submodules.init = DomainRenamer("init")(ECP5DDRPHYInit("sys2x"))
 
         # Parameters -------------------------------------------------------------------------------
-        cl, cwl         = get_cl_cw(memtype, tck)
+        cl, cwl         = get_cl_cw("DDR3", tck)
         cl_sys_latency  = get_sys_latency(nphases, cl)
         cwl_sys_latency = get_sys_latency(nphases, cwl)
 
@@ -143,7 +147,7 @@ class ECP5DDRPHY(Peripheral, Elaboratable):
         wrcmdphase, wrphase = get_sys_phases(nphases, cwl_sys_latency, cwl)
         self.settings = PhySettings(
             phytype       = "ECP5DDRPHY",
-            memtype       = memtype,
+            memtype       = "DDR3",
             databits      = databits,
             dfi_databits  = 4*databits,
             nranks        = nranks,
@@ -159,7 +163,7 @@ class ECP5DDRPHY(Peripheral, Elaboratable):
         )
 
         # DFI Interface ----------------------------------------------------------------------------
-        self.dfi = dfi = Interface(addressbits, bankbits, nranks, 4*databits, 4)
+        dfi = self.dfi
 
         # # #