From e9a4f8b13e75ae57429dc97f929b69e550b07c8e Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Thu, 24 Feb 2022 17:55:05 +0000 Subject: [PATCH] replace the simulation Clock-Reset-Generator with one that is more general. the icarus verilog simulation now passes where previously it did not --- gram/simulation/crg.py | 180 +++++++++++++++++++++++++++++++++-------- 1 file changed, 146 insertions(+), 34 deletions(-) diff --git a/gram/simulation/crg.py b/gram/simulation/crg.py index deab928..aa44c37 100644 --- a/gram/simulation/crg.py +++ b/gram/simulation/crg.py @@ -1,34 +1,135 @@ -# This file is Copyright (c) 2020 LambdaConcept +# Copyright (c) 2020 LambdaConcept +# Copyright (c) 2021 Luke Kenneth Casson Leighton +# Copyright (c) 2018-2020 Florent Kermarrec +# Copyright (c) 2019 Michael Betz +# +# Based on code from LambaConcept, from the gram example which is BSD-2-License +# https://github.com/jeanthom/gram/tree/master/examples +# +# Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER +# under EU Grants 871528 and 957073, under the LGPLv3+ License -from nmigen import * + +from nmigen import (Elaboratable, Module, Signal, ClockDomain, Instance, + ClockSignal, ResetSignal) __ALL__ = ["ECPIX5CRG"] class PLL(Elaboratable): - def __init__(self, clkin, clksel=Signal(shape=2, reset=2), clkout1=Signal(), clkout2=Signal(), clkout3=Signal(), clkout4=Signal(), lock=Signal(), CLKI_DIV=1, CLKFB_DIV=2, CLK1_DIV=3, CLK2_DIV=24): + nclkouts_max = 3 + clki_div_range = (1, 128+1) + clkfb_div_range = (1, 128+1) + clko_div_range = (1, 128+1) + clki_freq_range = ( 8e6, 400e6) + clko_freq_range = (3.125e6, 400e6) + vco_freq_range = ( 400e6, 800e6) + + def __init__(self, clkin, + clksel=Signal(shape=2, reset=2), + reset=Signal(reset_less=True), + locked=Signal()): self.clkin = clkin - self.clkout1 = clkout1 - self.clkout2 = clkout2 - self.clkout3 = clkout3 - self.clkout4 = clkout4 + self.clkin_freq = None self.clksel = clksel - self.lock = lock - self.CLKI_DIV = CLKI_DIV - self.CLKFB_DIV = CLKFB_DIV - self.CLKOP_DIV = CLK1_DIV - self.CLKOS_DIV = CLK2_DIV - self.ports = [ + self.locked = locked + self.reset = reset + self.nclkouts = 0 + self.clkouts = {} + self.config = {} + self.params = {} + + def ports(self): + return [ self.clkin, - self.clkout1, - self.clkout2, - self.clkout3, - self.clkout4, self.clksel, self.lock, - ] + ] + list(self.clkouts.values()) + + def set_clkin_freq(self, freq): + (clki_freq_min, clki_freq_max) = self.clki_freq_range + assert freq >= clki_freq_min + assert freq <= clki_freq_max + self.clkin_freq = freq + + def create_clkout(self, cd, freq, phase=0, margin=1e-2): + (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) + #create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) + print("clock domain", cd.domain, freq, margin, self.nclkouts) + self.nclkouts += 1 + + def compute_config(self): + config = {} + for clki_div in range(*self.clki_div_range): + config["clki_div"] = clki_div + for clkfb_div in range(*self.clkfb_div_range): + all_valid = True + 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()): + valid = False + for d in range(*self.clko_div_range): + clk_freq = vco_freq/d + if abs(clk_freq - f) <= f*m: + config["clko{}_freq".format(n)] = clk_freq + config["clko{}_div".format(n)] = d + config["clko{}_phase".format(n)] = p + valid = True + break + if not valid: + all_valid = False + else: + all_valid = False + if all_valid: + config["vco"] = vco_freq + config["clkfb_div"] = clkfb_div + #compute_config_log(self.logger, config) + print ("PLL config", config) + return config + raise ValueError("No PLL config found") def elaborate(self, platform): + config = self.compute_config() clkfb = Signal() + self.params.update( + # attributes + a_FREQUENCY_PIN_CLKI = str(self.clkin_freq/1e6), + a_ICP_CURRENT = "6", + a_LPF_RESISTOR = "16", + a_MFG_ENABLE_FILTEROPAMP = "1", + a_MFG_GMCREF_SEL = "2", + # parameters + p_FEEDBK_PATH = "INT_OS3", # CLKOS3 rsvd for feedback with div=1. + p_CLKOS3_ENABLE = "ENABLED", + p_CLKOS3_DIV = 1, + p_CLKFB_DIV = config["clkfb_div"], + p_CLKI_DIV = config["clki_div"], + # reset, input clock, lock-achieved output + i_RST = self.reset, + i_CLKI = self.clkin, + o_LOCK = self.locked, + ) + # for each clock-out, set additional parameters + for n, (clk, f, p, m) 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) + self.params["p_CLKO{}_ENABLE".format(n_to_l[n])] = "ENABLED" + self.params["p_CLKO{}_DIV".format(n_to_l[n])] = div + self.params["p_CLKO{}_FPHASE".format(n_to_l[n])] = 0 + self.params["p_CLKO{}_CPHASE".format(n_to_l[n])] = cphase + self.params["o_CLKO{}".format(n_to_l[n])] = clk + + m = Module() + 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', @@ -64,14 +165,11 @@ class PLL(Elaboratable): o_CLKOS3=self.clkout4, o_LOCK=self.lock, ) - m = Module() - m.submodules += pll - return m class ECPIX5CRG(Elaboratable): - def __init__(self): - ... + def __init__(self, sys_clk_freq=100e6): + self.sys_clk_freq = sys_clk_freq def elaborate(self, platform): m = Module() @@ -88,13 +186,20 @@ class ECPIX5CRG(Elaboratable): gsr1 = Signal() m.submodules += [ - Instance("FD1S3AX", p_GSR="DISABLED", i_CK=ClockSignal("rawclk"), i_D=~reset, o_Q=gsr0), - Instance("FD1S3AX", p_GSR="DISABLED", i_CK=ClockSignal("rawclk"), i_D=gsr0, o_Q=gsr1), - Instance("SGSR", i_CLK=ClockSignal("rawclk"), i_GSR=gsr1), + Instance("FD1S3AX", p_GSR="DISABLED", + i_CK=ClockSignal("rawclk"), + i_D=reset, + o_Q=gsr0), + Instance("FD1S3AX", p_GSR="DISABLED", + i_CK=ClockSignal("rawclk"), + i_D=gsr0, + o_Q=gsr1), + Instance("SGSR", i_CLK=ClockSignal("rawclk"), + i_GSR=gsr1), ] # Power-on delay (655us) - podcnt = Signal(3, reset=2**3-1) + podcnt = Signal(3, reset=-1) pod_done = Signal() with m.If(podcnt != 0): m.d.rawclk += podcnt.eq(podcnt-1) @@ -102,12 +207,15 @@ class ECPIX5CRG(Elaboratable): # Generating sync2x (200Mhz) and init (25Mhz) from clk100 cd_sync2x = ClockDomain("sync2x", local=False) - cd_sync2x_unbuf = ClockDomain("sync2x_unbuf", local=False, reset_less=True) + 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) - m.submodules.pll = pll = PLL(ClockSignal("rawclk"), CLKI_DIV=1, CLKFB_DIV=2, CLK1_DIV=1, CLK2_DIV=4, - clkout1=ClockSignal("sync2x_unbuf"), clkout2=ClockSignal("init")) + m.submodules.pll = pll = PLL(ClockSignal("rawclk"), reset=~reset) + pll.set_clkin_freq(100e6) + pll.create_clkout(ClockSignal("sync2x_unbuf"), 2*self.sys_clk_freq) + pll.create_clkout(ClockSignal("init"), 25e6) m.submodules += Instance("ECLKSYNCB", i_ECLKI = ClockSignal("sync2x_unbuf"), i_STOP = 0, @@ -117,18 +225,22 @@ class ECPIX5CRG(Elaboratable): m.domains += cd_init m.domains += cd_sync m.domains += cd_dramsync - m.d.comb += ResetSignal("init").eq(~pll.lock|~pod_done) - m.d.comb += ResetSignal("sync").eq(~pll.lock|~pod_done) - m.d.comb += ResetSignal("dramsync").eq(~pll.lock|~pod_done) + 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")) + + # temporarily set dram sync clock exactly equal to main sync m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync")) return m -- 2.30.2