establish power-on reset stabilisation for Arty A7 and ECP5
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 24 Mar 2022 13:28:12 +0000 (13:28 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 24 Mar 2022 13:28:12 +0000 (13:28 +0000)
src/arty_crg.py
src/ls2.py

index ef57b0b47711f1c11d63fa093523b6dd874e32d7..04b22b4e74695b08f62edac315b517f4b59013cd 100644 (file)
@@ -38,8 +38,9 @@ class XilinxClocking(Elaboratable):
     def __init__(self, clkin, vco_margin=0):
         self.clkin      = clkin
         self.vco_margin = vco_margin
-        self.reset      = Signal()
-        self.locked     = Signal()
+        self.reset      = Signal(reset_less=True)
+        self.locked     = Signal(reset_less=True)
+        self.pod_done   = Signal(reset_less=True) # provide this @ rawclk
         self.clkin_freq = None
         self.vcxo_freq  = None
         self.nclkouts   = 0
@@ -66,7 +67,7 @@ class XilinxClocking(Elaboratable):
             if with_reset:
                 arst = Signal()
                 m.submodules += ResetSynchronizer(arst, domain=cd.name)
-                comb += arst.eq(~self.locked | self.reset)
+                comb += arst.eq(~self.locked | self.reset | ~self.pod_done)
             if buf is None:
                 comb += cd.clk.eq(clkout)
             else:
@@ -193,11 +194,12 @@ class ECP5PLL(Elaboratable):
     def __init__(self, clkin,
                        clksel=Signal(shape=2, reset=2),
                        reset=Signal(reset_less=True),
-                       locked=Signal()):
+                       locked=Signal(reset_less=True)):
         self.clkin = clkin
         self.clkin_freq = None
         self.clksel = clksel
         self.locked = locked
+        self.pod_done = Signal(reset_less=True) # must be set in rawclk
         self.reset  = reset
         self.nclkouts   = 0
         self.clkouts    = {}
@@ -217,14 +219,14 @@ class ECP5PLL(Elaboratable):
         assert freq <= clki_freq_max
         self.clkin_freq = freq
 
-    def create_clkout(self, cd, freq, phase=0, margin=1e-2):
+    def create_clkout(self, cd, freq, phase=0, margin=1e-2, reset=True):
         (clko_freq_min, clko_freq_max) = self.clko_freq_range
         assert freq >= clko_freq_min
         assert freq <= clko_freq_max
         assert self.nclkouts < self.nclkouts_max
-        self.clkouts[self.nclkouts] = (cd, freq, phase, margin)
+        self.clkouts[self.nclkouts] = (cd, freq, phase, margin, reset)
         #create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts)
-        print("clock domain", cd.domain, freq, margin, self.nclkouts)
+        print("clock domain", cd.domain, freq, margin, self.nclkouts, reset)
         self.nclkouts += 1
 
     def compute_config(self):
@@ -236,7 +238,7 @@ class ECP5PLL(Elaboratable):
                 vco_freq = self.clkin_freq/clki_div*clkfb_div*1 # clkos3_div=1
                 (vco_freq_min, vco_freq_max) = self.vco_freq_range
                 if vco_freq >= vco_freq_min and vco_freq <= vco_freq_max:
-                    for n, (clk, f, p, m) in sorted(self.clkouts.items()):
+                    for n, (clk, f, p, m, rst) in sorted(self.clkouts.items()):
                         valid = False
                         for d in range(*self.clko_div_range):
                             clk_freq = vco_freq/d
@@ -259,6 +261,8 @@ class ECP5PLL(Elaboratable):
         raise ValueError("No PLL config found")
 
     def elaborate(self, platform):
+        m = Module()
+
         config = self.compute_config()
         clkfb = Signal()
         self.params.update(
@@ -280,7 +284,7 @@ class ECP5PLL(Elaboratable):
             o_LOCK          = self.locked,
         )
         # for each clock-out, set additional parameters
-        for n, (clk, f, p, m) in sorted(self.clkouts.items()):
+        for n, (clk, f, p, m, rst) in sorted(self.clkouts.items()):
             n_to_l = {0: "P", 1: "S", 2: "S2"}
             div    = config["clko{}_div".format(n)]
             cphase = int(p*(div + 1)/360 + div)
@@ -290,57 +294,34 @@ class ECP5PLL(Elaboratable):
             self.params["p_CLKO{}_CPHASE".format(n_to_l[n])] = cphase
             self.params["o_CLKO{}".format(n_to_l[n])]        = clk
 
-        m = Module()
+        # for each clock-out, do a reset sync if required
+        arst = Signal()
+        comb += arst.eq(~self.locked | self.reset | ~self.pod_done)
+        for n, (clk, f, p, m, rst) in sorted(self.clkouts.items()):
+            rsts = ResetSynchroniser(arst, domain=clk)
+            m.submodules['%s_rst' % clk] = rsts
+
+        # actual PLL, finally
         print ("params", self.params)
         pll = Instance("EHXPLLL", **self.params)
         m.submodules.pll = pll
         return m
 
-        pll = Instance("EHXPLLL",
-                       p_OUTDIVIDER_MUXA='DIVA',
-                       p_OUTDIVIDER_MUXB='DIVB',
-                       p_CLKOP_ENABLE='ENABLED',
-                       p_CLKOS_ENABLE='ENABLED',
-                       p_CLKOS2_ENABLE='DISABLED',
-                       p_CLKOS3_ENABLE='DISABLED',
-                       p_CLKOP_DIV=self.CLKOP_DIV,
-                       p_CLKOS_DIV=self.CLKOS_DIV,
-                       p_CLKFB_DIV=self.CLKFB_DIV,
-                       p_CLKI_DIV=self.CLKI_DIV,
-                       p_FEEDBK_PATH='INT_OP',
-                       p_CLKOP_TRIM_POL="FALLING",
-                       p_CLKOP_TRIM_DELAY=0,
-                       p_CLKOS_TRIM_POL="FALLING",
-                       p_CLKOS_TRIM_DELAY=0,
-                       i_CLKI=self.clkin,
-                       i_RST=0,
-                       i_STDBY=0,
-                       i_PHASESEL0=0,
-                       i_PHASESEL1=0,
-                       i_PHASEDIR=0,
-                       i_PHASESTEP=0,
-                       i_PHASELOADREG=0,
-                       i_PLLWAKESYNC=0,
-                       i_ENCLKOP=1,
-                       i_ENCLKOS=1,
-                       i_ENCLKOS2=0,
-                       i_ENCLKOS3=0,
-                       o_CLKOP=self.clkout1,
-                       o_CLKOS=self.clkout2,
-                       o_CLKOS2=self.clkout3,
-                       o_CLKOS3=self.clkout4,
-                       o_LOCK=self.lock,
-                       )
 
 # CRG ----------------------------------------------------------------
 
 class ArtyA7CRG(Elaboratable):
     def __init__(self, sys_clk_freq):
         self.sys_clk_freq = sys_clk_freq
+        self.reset = Signal(reset_less=True)
 
     def elaborate(self, platform):
         m = Module()
 
+        # reset
+        reset = platform.request(platform.default_rst).i
+        m.d.comb += self.reset.eq(reset)
+
         # Get 100Mhz from oscillator
         clk100 = platform.request("clk100")
         cd_rawclk = ClockDomain("rawclk", local=True, reset_less=True)
@@ -363,11 +344,20 @@ class ArtyA7CRG(Elaboratable):
         #m.domains += cd_eth
         m.domains += dramsync
 
+        # PLL module
         m.submodules.pll = pll = S7PLL(clk100, speedgrade=-1)
-        reset = platform.request(platform.default_rst).i
+
+        # Power-on delay (a LOT)
+        podcnt = Signal(18, reset=-1)
+        pod_done = Signal(reset_less=True)
+        with m.If((podcnt != 0) & pll.locked):
+            m.d.rawclk += podcnt.eq(podcnt-1)
+        m.d.rawclk += pod_done.eq(podcnt == 0)
+
+        # PLL setup
         m.d.comb += pll.reset.eq(reset)
+        m.d.comb += pll.pod_done.eq(pod_done)
         pll.set_clkin_freq(100e6)
-
         pll.create_clkout(sync, self.sys_clk_freq)
 
         #platform.add_period_constraint(clk100_buf, 1e9/100e6)
@@ -376,12 +366,14 @@ class ArtyA7CRG(Elaboratable):
 
         # temporarily set dram sync clock exactly equal to main sync
         m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
+
         return m
 
 
 class ECPIX5CRG(Elaboratable):
     def __init__(self, sys_clk_freq=100e6):
         self.sys_clk_freq = sys_clk_freq
+        self.reset = Signal(reset_less=True)
 
     def elaborate(self, platform):
         m = Module()
@@ -394,6 +386,8 @@ class ECPIX5CRG(Elaboratable):
 
         # Reset
         reset = platform.request(platform.default_rst).i
+        m.d.comb += self.reset.eq(reset)
+
         gsr0 = Signal()
         gsr1 = Signal()
 
@@ -410,10 +404,13 @@ class ECPIX5CRG(Elaboratable):
                              i_GSR=gsr1),
         ]
 
-        # Power-on delay (655us)
-        podcnt = Signal(3, reset=-1)
+        # create PLL module
+        m.submodules.pll = pll = ECP5PLL(ClockSignal("rawclk"), reset=~reset)
+
+        # Power-on delay (lots)
+        podcnt = Signal(8, reset=-1)
         pod_done = Signal()
-        with m.If(podcnt != 0):
+        with m.If((podcnt != 0) & pll.locked):
             m.d.rawclk += podcnt.eq(podcnt-1)
         m.d.rawclk += pod_done.eq(podcnt == 0)
 
@@ -424,7 +421,9 @@ class ECPIX5CRG(Elaboratable):
         cd_init = ClockDomain("init", local=False)
         cd_sync = ClockDomain("sync", local=False)
         cd_dramsync = ClockDomain("dramsync", local=False)
-        m.submodules.pll = pll = ECP5PLL(ClockSignal("rawclk"), reset=~reset)
+
+        # create clocks for the PLL
+        m.d.comb += pll.pod_done.eq(pod_done)
         pll.set_clkin_freq(100e6)
         pll.create_clkout(ClockSignal("sync2x_unbuf"), 2*self.sys_clk_freq)
         pll.create_clkout(ClockSignal("init"), 25e6)
@@ -437,11 +436,6 @@ class ECPIX5CRG(Elaboratable):
         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
 
index 60aafd20ba96fce907afa70bd35d060aab4b57aa..0ecc9cee555de28eb20f79ae09a00ee0e2beada7 100644 (file)
@@ -587,7 +587,7 @@ def build_platform(fpga, firmware):
     if fpga == 'versa_ecp5_85':
         clk_freq = 55e6
     if fpga == 'arty_a7':
-        clk_freq = 12e6
+        clk_freq = 25e6
 
     # select a firmware address
     fw_addr = None
@@ -635,10 +635,9 @@ def build_platform(fpga, firmware):
     elif platform is not None and fpga in ['arty_a7']:
         hyperram_ios = HyperRAMResource(0, cs_n="B11",
                                         dq="D4 D3 F4 F3 G2 H2 D2 E2",
-                                        rwds="U13", rst_n="T13", ck_p="V10"
+                                        rwds="U13", rst_n="T13", ck_p="V10",
                                         # ck_n="D12" - for later (DDR)
-                                        )
-                                        #attrs=Attrs(IO_TYPE="LVCMOS33"))
+                                        attrs=Attrs(IOSTANDARD="LVCMOS33"))
         platform.add_resources(hyperram_ios)
         hyperram_pins = platform.request("hyperram")
         print ("arty a7 hyperram", hyperram_ios)