From 55b5acc9cbe8c67b4b64ab4ba145b93347020e04 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Thu, 14 Apr 2022 10:41:06 +0100 Subject: [PATCH] move 2x-clock-and-dividing into separate function in ECP5 CRG 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 | 86 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 30 deletions(-) diff --git a/src/ecp5_crg.py b/src/ecp5_crg.py index 5c975d6..1740169 100644 --- a/src/ecp5_crg.py +++ b/src/ecp5_crg.py @@ -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 -- 2.30.2