move 2x-clock-and-dividing into separate function in ECP5 CRG
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 14 Apr 2022 09:41:06 +0000 (10:41 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 14 Apr 2022 09:41:06 +0000 (10:41 +0100)
the reason for this is to make it easy to set up xdr=4x IOpads
which need a *pair* of domains in order to get the 4 phases:
double-freq and freq

src/ecp5_crg.py

index 5c975d66c1dacaed906e13e96cb4781ceb64593a..1740169befd5ffa2d8444a8ef59beaced7679eb6 100644 (file)
@@ -173,6 +173,40 @@ class ECP5CRG(Elaboratable):
         self.sys_clk_freq = sys_clk_freq
         self.pod_bits = pod_bits
 
+    def do_2x_phase_clock_create(self, m, pll, name, freq):
+        """creates a domain that can be used with xdr=4 platform resources.
+
+        this requires creating a domain at *twice* the frequency,
+        then halving it.  the doubled-frequency can then go to the
+        eclk parts of a 4-phase IOPad:
+            pads.a.o_clk.eq(ClockSignal("dramsync")),
+            pads.a.o_fclk.eq(ClockSignal("dramsync2x")),
+        """
+
+        # Generating sync2x from extclk
+        cd_2x = ClockDomain("%s2x" % name, local=False)
+        cd_2x_unbuf = ClockDomain("%s2x_unbuf" % name,
+                                      local=False, reset_less=True)
+        cd_ = ClockDomain("%s" % name, local=False)
+
+        # create PLL clocks
+        pll.create_clkout(ClockSignal("%s2x_unbuf" % name), 2*freq)
+        m.submodules += Instance("ECLKSYNCB",
+                i_ECLKI = ClockSignal("%s2x_unbuf" % name),
+                i_STOP  = 0,
+                o_ECLKO = ClockSignal("%s2x" % name))
+        m.domains += cd_2x_unbuf
+        m.domains += cd_2x
+        m.domains += cd_
+
+        # # Generating sync from sync2x
+        m.submodules += Instance("CLKDIVF",
+            p_DIV="2.0",
+            i_ALIGNWD=0,
+            i_CLKI=ClockSignal("%s2x" % name),
+            i_RST=0,
+            o_CDIVX=ClockSignal("%s" % name))
+
     def elaborate(self, platform):
         m = Module()
 
@@ -214,44 +248,36 @@ class ECP5CRG(Elaboratable):
             m.d.rawclk += podcnt.eq(podcnt-1)
         m.d.rawclk += pod_done.eq(podcnt == 0)
 
-        # Generating sync2x (200Mhz) and init (25Mhz) from extclk
-        cd_sync2x = ClockDomain("sync2x", local=False)
-        cd_sync2x_unbuf = ClockDomain("sync2x_unbuf",
-                                      local=False, reset_less=True)
-        cd_init = ClockDomain("init", local=False)
-        cd_sync = ClockDomain("sync", local=False)
-        cd_dramsync = ClockDomain("dramsync", local=False)
+        # and reset which only drops when the PLL is done and pod completes
+        reset_ok = Signal(reset_less=True)
+        m.d.comb += reset_ok.eq(~pll.locked|~pod_done)
 
-        # create PLL clocks
+        # create PLL input clock from platform default frequency
         pll.set_clkin_freq(platform.default_clk_frequency)
-        pll.create_clkout(ClockSignal("sync2x_unbuf"), 2*self.sys_clk_freq)
+
+        # create 25 mhz "init" clock, straight (no 2x phase stuff)
+        cd_init = ClockDomain("init", local=False)
         pll.create_clkout(ClockSignal("init"), 25e6)
-        m.submodules += Instance("ECLKSYNCB",
-                i_ECLKI = ClockSignal("sync2x_unbuf"),
-                i_STOP  = 0,
-                o_ECLKO = ClockSignal("sync2x"))
-        m.domains += cd_sync2x_unbuf
-        m.domains += cd_sync2x
         m.domains += cd_init
-        m.domains += cd_sync
-        m.domains += cd_dramsync
-        reset_ok = Signal(reset_less=True)
-        m.d.comb += reset_ok.eq(~pll.locked|~pod_done)
         m.d.comb += ResetSignal("init").eq(reset_ok)
-        m.d.comb += ResetSignal("sync").eq(reset_ok)
-        m.d.comb += ResetSignal("dramsync").eq(reset_ok)
 
-        # # Generating sync (100Mhz) from sync2x
-
-        m.submodules += Instance("CLKDIVF",
-            p_DIV="2.0",
-            i_ALIGNWD=0,
-            i_CLKI=ClockSignal("sync2x"),
-            i_RST=0,
-            o_CDIVX=ClockSignal("sync"))
+        # Generating sync2x and sync from extclk, which is *only* how
+        # xdr=4 can be requested on the sync domain
+        self.do_2x_phase_clock_create(m, pll, "sync", self.sys_clk_freq)
+        m.d.comb += ResetSignal("sync").eq(reset_ok)
 
-        # temporarily set dram sync clock exactly equal to main sync
+        # temporarily set dram sync clock exactly equal to main sync,
+        # which turns out to be a dog's dinner botch-job, because
+        # GRAM's ECP5PHY uses dramsync for the pads.o_clk but
+        # sync2x for the pads.o_fclk. sigh.
+        # TODO: this to be replaced with
+        # self.do_2x_phase_clock_create(m, pll, "dramsync", self.dram_clk_freq)
+        # and then a suitable DomainRenamer for *both* dramsync *and*
+        # dramsync2x applied
+        cd_dramsync = ClockDomain("dramsync", local=False)
+        m.domains += cd_dramsync
         m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
+        m.d.comb += ResetSignal("dramsync").eq(reset_ok)
 
         return m