From 56cd335c0ad39f1e1a610dc07301402ad7a182be Mon Sep 17 00:00:00 2001 From: Hidde Moll Date: Wed, 22 Feb 2023 09:05:43 +0100 Subject: [PATCH] Add Xilinx clock wizard support through Tcl --- clash-lib/clash-lib.cabal | 1 + .../Clash_Xilinx_ClockGen.primitives.yaml | 54 +++++--- .../Clash_Xilinx_ClockGen.primitives.yaml | 67 ++++++---- clash-lib/src/Clash/Driver.hs | 3 + .../src/Clash/Primitives/Xilinx/ClockGen.hs | 126 ++++++++++++++++++ clash-prelude/src/Clash/Xilinx/ClockGen.hs | 50 ++++--- 6 files changed, 235 insertions(+), 66 deletions(-) create mode 100644 clash-lib/src/Clash/Primitives/Xilinx/ClockGen.hs diff --git a/clash-lib/clash-lib.cabal b/clash-lib/clash-lib.cabal index 2a61125095..e4ecc27793 100644 --- a/clash-lib/clash-lib.cabal +++ b/clash-lib/clash-lib.cabal @@ -278,6 +278,7 @@ Library Clash.Primitives.Sized.Signed Clash.Primitives.Sized.Vector Clash.Primitives.Verification + Clash.Primitives.Xilinx.ClockGen Clash.Rewrite.Combinators Clash.Rewrite.Types diff --git a/clash-lib/prims/commonverilog/Clash_Xilinx_ClockGen.primitives.yaml b/clash-lib/prims/commonverilog/Clash_Xilinx_ClockGen.primitives.yaml index 8b07a868ed..f14b4d06ac 100644 --- a/clash-lib/prims/commonverilog/Clash_Xilinx_ClockGen.primitives.yaml +++ b/clash-lib/prims/commonverilog/Clash_Xilinx_ClockGen.primitives.yaml @@ -4,19 +4,25 @@ type: |- clockWizard :: ( KnownDomain domIn confIn -- ARG[0] - , KnownDomain domOut confOut ) -- ARG[1] - => SSymbol name -- ARG[2] - -> Clock pllIn -- ARG[3] - -> Reset pllIn -- ARG[4] - -> (Clock pllOut, Enable pllOut) + , KnownDomain domOut confOut -- ARG[1] + , KnownDomain domPllLock confPllLock ) -- ARG[2] + => SSymbol name -- ARG[3] + -> Clock pllIn -- ARG[4] + -> Reset pllIn -- ARG[5] + -> (Clock pllOut, Signal domPllLock Bool) template: |- // clockWizard begin - ~NAME[2] ~GENSYM[clockWizard_inst][2] - (.CLK_IN1 (~ARG[3]) - ,.RESET (~IF ~ISACTIVEHIGH[0] ~THEN ~ELSE ! ~FI ~ARG[4]) - ,.CLK_OUT1 (~RESULT[1]) - ,.LOCKED (~RESULT[0])); + ~NAME[3] ~GENSYM[clockWizard_inst][2] + (.clk_in1 (~ARG[4]) + ,.reset (~IF ~ISACTIVEHIGH[0] ~THEN ~ELSE ! ~FI ~ARG[5]) + ,.clk_out1 (~RESULT[1]) + ,.locked (~RESULT[0])); // clockWizard end + includes: + - name: clk_wiz + extension: clash.tcl + format: Haskell + templateFunction: Clash.Primitives.Xilinx.ClockGen.clockWizardTclTF workInfo: Always - BlackBox: name: Clash.Xilinx.ClockGen.clockWizardDifferential @@ -24,19 +30,25 @@ type: |- clockWizardDifferential :: ( KnownDomain domIn confIn -- ARG[0] - , KnownDomain domOut confOut ) -- ARG[1] - :: SSymbol name -- ARG[2] - -> Clock pllIn -- ARG[3] + , KnownDomain domOut confOut -- ARG[1] + , KnownDomain domPllLock confPllLock ) -- ARG[2] + :: SSymbol name -- ARG[3] -> Clock pllIn -- ARG[4] - -> Reset pllIn -- ARG[5] - -> (Clock pllOut, Enable pllOut) + -> Clock pllIn -- ARG[5] + -> Reset pllIn -- ARG[6] + -> (Clock pllOut, Signal domPllLock Bool) template: |- // clockWizardDifferential begin - ~NAME[2] ~GENSYM[clockWizardDifferential_inst][2] - (.CLK_IN1_D_clk_n (~ARG[3]) - ,.CLK_IN1_D_clk_n (~ARG[4]) - ,.RESET (~IF ~ISACTIVEHIGH[0] ~THEN ~ELSE ! ~FI ~ARG[5]) - ,.CLK_OUT1 (~RESULT[1]) - ,.LOCKED (~RESULT[0])); + ~NAME[3] ~GENSYM[clockWizardDifferential_inst][2] + (.clk_in1_n (~ARG[4]) + ,.clk_in1_p (~ARG[5]) + ,.reset (~IF ~ISACTIVEHIGH[0] ~THEN ~ELSE ! ~FI ~ARG[6]) + ,.clk_out1 (~RESULT[1]) + ,.locked (~RESULT[0])); // clockWizardDifferential end + includes: + - name: clk_wiz + extension: clash.tcl + format: Haskell + templateFunction: Clash.Primitives.Xilinx.ClockGen.clockWizardDifferentialTclTF workInfo: Always diff --git a/clash-lib/prims/vhdl/Clash_Xilinx_ClockGen.primitives.yaml b/clash-lib/prims/vhdl/Clash_Xilinx_ClockGen.primitives.yaml index c3181e0759..ee3b6ed9da 100644 --- a/clash-lib/prims/vhdl/Clash_Xilinx_ClockGen.primitives.yaml +++ b/clash-lib/prims/vhdl/Clash_Xilinx_ClockGen.primitives.yaml @@ -4,11 +4,12 @@ type: |- clockWizard :: ( KnownDomain domIn confIn -- ARG[0] - , KnownDomain domOut confOut ) -- ARG[1] - => SSymbol name -- ARG[2] - -> Clock pllIn -- ARG[3] - -> Reset pllIn -- ARG[4] - -> (Clock pllOut, Enable pllOut) + , KnownDomain domOut confOut -- ARG[1] + , KnownDomain domPllLock confPllLock ) -- ARG[2] + => SSymbol name -- ARG[3] + -> Clock pllIn -- ARG[4] + -> Reset pllIn -- ARG[5] + -> (Clock pllOut, Signal domPllLock Bool) template: |- -- clockWizard begin ~GENSYM[clockWizard][0] : block @@ -16,50 +17,60 @@ signal ~GENSYM[locked][2] : std_logic; signal ~GENSYM[pllLock][3] : boolean; - component ~NAME[2] - port (CLK_IN1 : in std_logic; - RESET : in std_logic; - CLK_OUT1 : out std_logic; - LOCKED : out std_logic); + component ~NAME[3] + port (clk_in1 : in std_logic; + reset : in std_logic; + clk_out1 : out std_logic; + locked : out std_logic); end component; begin - ~GENSYM[clockWizard_inst][4] : component ~NAME[2] port map (~ARG[3],~IF ~ISACTIVEHIGH[0] ~THEN ~ARG[4] ~ELSE NOT(~ARG[4]) ~FI,~SYM[1],~SYM[2]); + ~GENSYM[clockWizard_inst][4] : component ~NAME[3] port map (~ARG[4],~IF ~ISACTIVEHIGH[0] ~THEN ~ARG[5] ~ELSE NOT(~ARG[5]) ~FI,~SYM[1],~SYM[2]); ~SYM[3] <= true when ~SYM[2] = '1' else false; ~RESULT <= (~SYM[1],~SYM[3]); end block; -- clockWizard end + includes: + - name: clk_wiz + extension: clash.tcl + format: Haskell + templateFunction: Clash.Primitives.Xilinx.ClockGen.clockWizardTclTF workInfo: Always - BlackBox: name: Clash.Xilinx.ClockGen.clockWizardDifferential kind: Declaration type: |- - clockWizardDifferential - :: ( KnownDomain domIn confIn -- ARG[0] - , KnownDomain domOut confOut ) -- ARG[1] - => SSymbol name -- ARG[2] - -> Clock pllIn -- ARG[3] - -> Clock pllIn -- ARG[4] - -> Reset pllIn -- ARG[5] - -> (Clock pllOut, Enable pllOut) + clockWizard + :: ( KnownDomain domIn confIn -- ARG[0] + , KnownDomain domOut confOut -- ARG[1] + , KnownDomain domPllLock confPllLock ) -- ARG[2] + => SSymbol name -- ARG[3] + -> Clock pllIn -- ARG[4] + -> Clock pllIn -- ARG[5] + -> Reset pllIn -- ARG[6] + -> (Clock pllOut, Signal domPllLock Bool) template: |- -- clockWizardDifferential begin ~GENSYM[clockWizardDifferential][0] : block signal ~GENSYM[pllOut][1] : std_logic; signal ~GENSYM[locked][2] : std_logic; signal ~GENSYM[pllLock][3] : boolean; - - component ~NAME[2] - port (CLK_IN1_D_clk_n : in std_logic; - CLK_IN1_D_clk_p : in std_logic; - RESET : in std_logic; - CLK_OUT1 : out std_logic; - LOCKED : out std_logic); + component ~NAME[3] + port (clk_in1_n : in std_logic; + clk_in1_p : in std_logic; + reset : in std_logic; + clk_out1 : out std_logic; + locked : out std_logic); end component; begin - ~GENSYM[clockWizardDifferential_inst][4] : component ~NAME[2] - port map (~ARG[3],~ARG[4],~IF ~ISACTIVEHIGH[0] ~THEN ~ARG[5] ~ELSE NOT(~ARG[5]) ~FI,~SYM[1],~SYM[2]); + ~GENSYM[clockWizardDifferential_inst][4] : component ~NAME[3] + port map (~ARG[4],~ARG[5],~IF ~ISACTIVEHIGH[0] ~THEN ~ARG[6] ~ELSE NOT(~ARG[6]) ~FI,~SYM[1],~SYM[2]); ~SYM[3] <= true when ~SYM[2] = '1' else false; ~RESULT <= (~SYM[1],~SYM[3]); end block; -- clockWizardDifferential end + includes: + - name: clk_wiz + extension: clash.tcl + format: Haskell + templateFunction: Clash.Primitives.Xilinx.ClockGen.clockWizardDifferentialTclTF workInfo: Always diff --git a/clash-lib/src/Clash/Driver.hs b/clash-lib/src/Clash/Driver.hs index dbc61132e7..c4af596484 100644 --- a/clash-lib/src/Clash/Driver.hs +++ b/clash-lib/src/Clash/Driver.hs @@ -141,6 +141,7 @@ import qualified Clash.Primitives.GHC.Word as P import qualified Clash.Primitives.Intel.ClockGen as P import qualified Clash.Primitives.Prelude as P import qualified Clash.Primitives.Verification as P +import qualified Clash.Primitives.Xilinx.ClockGen as P import Clash.Primitives.Types import Clash.Signal.Internal import Clash.Unique (Unique, getUnique) @@ -602,6 +603,8 @@ knownTemplateFunctions = , ('P.alteraPllTF, P.alteraPllTF) , ('P.altpllTF, P.altpllTF) , ('P.fromIntegerTFvhdl, P.fromIntegerTFvhdl) + , ('P.clockWizardTclTF, P.clockWizardTclTF) + , ('P.clockWizardDifferentialTclTF, P.clockWizardDifferentialTclTF) ] -- | Compiles blackbox functions and parses blackbox templates. diff --git a/clash-lib/src/Clash/Primitives/Xilinx/ClockGen.hs b/clash-lib/src/Clash/Primitives/Xilinx/ClockGen.hs new file mode 100644 index 0000000000..9068473e4e --- /dev/null +++ b/clash-lib/src/Clash/Primitives/Xilinx/ClockGen.hs @@ -0,0 +1,126 @@ +{-| + Copyright : (C) 2023, QBayLogic B.V. + 2023, Google Inc. + License : BSD2 (see the file LICENSE) + Maintainer : QBayLogic B.V. + + Blackbox template functions for + Clash.Xilinx.ClockGen.{clockWizard,clockWizardDifferential} +-} + +{-# LANGUAGE QuasiQuotes #-} + +module Clash.Primitives.Xilinx.ClockGen where + +import Clash.Backend (Backend) +import Clash.Netlist.BlackBox.Util (exprToString) +import Clash.Netlist.Types +import Clash.Netlist.Util (stripVoid) + +import Control.Monad.State (State) +import qualified Data.String.Interpolate as I +import Data.Text.Prettyprint.Doc.Extra (Doc) + + +clockWizardTclTF :: TemplateFunction +clockWizardTclTF = + TemplateFunction used valid clockWizardTclTemplate + where + used = [0..4] + valid bbCtx + | [_,_,_,(nm,_,_),_,_] <- bbInputs bbCtx + , Just _ <- exprToString nm + , [(Identifier _ Nothing,Product {})] <- bbResults bbCtx + = True + valid _ = False + +clockWizardTclTemplate + :: Backend s + => BlackBoxContext + -> State s Doc +clockWizardTclTemplate bbCtx = pure bbText + where + ((_,stripVoid -> kdIn,_):(_,stripVoid -> kdOut,_):_:(nm,_,_):_) = bbInputs bbCtx + Just compName = exprToString nm + KnownDomain _ clkInPeriod _ _ _ _ = kdIn + KnownDomain _ clkOutPeriod _ _ _ _ = kdOut + clkInFreq :: Double + clkInFreq = (1.0 / (fromInteger clkInPeriod * 1.0e-12)) / 1e6 + clkOutFreq :: Double + clkOutFreq = (1.0 / (fromInteger clkOutPeriod * 1.0e-12)) / 1e6 + + bbText = [I.__i| + namespace eval $tclIface { + variable api 1 + variable scriptPurpose createIp + variable ipName {#{compName}} + + proc createIp {ipName0 args} { + create_ip \\ + -name clk_wiz \\ + -vendor xilinx.com \\ + -library ip \\ + -version 6.0 \\ + -module_name $ipName0 \\ + {*}$args + + set_property \\ + -dict [list \\ + CONFIG.PRIM_IN_FREQ #{clkInFreq} \\ + CONFIG.CLKOUT1_REQUESTED_OUT_FREQ #{clkOutFreq} \\ + ] [get_ips $ipName0] + return + } + }|] + +clockWizardDifferentialTclTF :: TemplateFunction +clockWizardDifferentialTclTF = + TemplateFunction used valid clockWizardDifferentialTclTemplate + where + used = [0..5] + valid bbCtx + | [_,_,_,(nm,_,_),_,_,_] <- bbInputs bbCtx + , Just _ <- exprToString nm + , [(Identifier _ Nothing,Product {})] <- bbResults bbCtx + = True + valid _ = False + +clockWizardDifferentialTclTemplate + :: Backend s + => BlackBoxContext + -> State s Doc +clockWizardDifferentialTclTemplate bbCtx = pure bbText + where + ((_,stripVoid -> kdIn,_):(_,stripVoid -> kdOut,_):_:(nm,_,_):_) = bbInputs bbCtx + Just compName = exprToString nm + KnownDomain _ clkInPeriod _ _ _ _ = kdIn + KnownDomain _ clkOutPeriod _ _ _ _ = kdOut + clkInFreq :: Double + clkInFreq = (1.0 / (fromInteger clkInPeriod * 1.0e-12)) / 1e6 + clkOutFreq :: Double + clkOutFreq = (1.0 / (fromInteger clkOutPeriod * 1.0e-12)) / 1e6 + + bbText = [I.__i| + namespace eval $tclIface { + variable api 1 + variable scriptPurpose createIp + variable ipName {#{compName}} + + proc createIp {ipName0 args} { + create_ip \\ + -name clk_wiz \\ + -vendor xilinx.com \\ + -library ip \\ + -version 6.0 \\ + -module_name $ipName0 \\ + {*}$args + + set_property \\ + -dict [list \\ + CONFIG.PRIM_SOURCE Differential_clock_capable_pin \\ + CONFIG.PRIM_IN_FREQ #{clkInFreq} \\ + CONFIG.CLKOUT1_REQUESTED_OUT_FREQ #{clkOutFreq} \\ + ] [get_ips $ipName0] + return + } + }|] diff --git a/clash-prelude/src/Clash/Xilinx/ClockGen.hs b/clash-prelude/src/Clash/Xilinx/ClockGen.hs index 3e4d87a1fa..2ee9e5acb4 100644 --- a/clash-prelude/src/Clash/Xilinx/ClockGen.hs +++ b/clash-prelude/src/Clash/Xilinx/ClockGen.hs @@ -12,9 +12,21 @@ PLL and other clock-related components for Xilinx FPGAs module Clash.Xilinx.ClockGen where import Clash.Annotations.Primitive (hasBlackBox) +import Clash.Clocks (clocks) import Clash.Promoted.Symbol import Clash.Signal.Internal -import Unsafe.Coerce + +-- import qualified Clash.Primitives.Xilinx.ClockGen as P + + +-- -- | List of known TemplateFunctions to prevent Hint from firing. This +-- -- improves Clash startup times. +-- knownTemplateFunctions :: HashMap String TemplateFunction +-- knownTemplateFunctions = +-- HashMap.fromList $ map (first show) $ +-- [ ('P.clockWizardDifferentialTclTF, P.clockWizardDifferentialTclTF) +-- ] + -- | A clock source that corresponds to the Xilinx PLL/MMCM component created -- with the \"Clock Wizard\" with settings to provide a stable 'Clock' from @@ -36,9 +48,10 @@ import Unsafe.Coerce -- clockWizard @@Dom100MHz (SSymbol @@"clkWizard50to100") clk50 rst -- @ clockWizard - :: forall domIn domOut periodIn periodOut edge init polarity name - . ( KnownConfiguration domIn ('DomainConfiguration domIn periodIn edge 'Asynchronous init polarity) - , KnownConfiguration domOut ('DomainConfiguration domOut periodOut edge 'Asynchronous init polarity) ) + :: forall domIn domOut domPllLock name + . ( KnownDomain domIn + , KnownDomain domOut + , KnownDomain domPllLock ) => SSymbol name -- ^ Name of the component, must correspond to the name entered in the -- \"Clock Wizard\" dialog. @@ -50,12 +63,13 @@ clockWizard -- ^ Free running clock (i.e. a clock pin connected to a crystal) -> Reset domIn -- ^ Reset for the PLL - -> (Clock domOut, Enable domOut) + -> (Clock domOut, Signal domPllLock Bool) -- ^ (Stable PLL clock, PLL lock) -clockWizard !_ clk@(Clock _ Nothing) rst = - (unsafeCoerce clk, unsafeCoerce (toEnable (unsafeToHighPolarity rst))) -clockWizard _ _ _ = - error "clockWizard: no support for dynamic clocks" +clockWizard !_ = clocks +-- let clkO = Clock SSymbol Nothing +-- in (clkO, unsafeSynchronizer clk (clockGen @domPllLock) $ unsafeToLowPolarity rst) +-- clockWizard _ _ _ = +-- error "clockWizard: no support for dynamic clocks" {-# NOINLINE clockWizard #-} {-# ANN clockWizard hasBlackBox #-} @@ -79,9 +93,10 @@ clockWizard _ _ _ = -- clockWizardDifferential @@Dom100MHz (SSymbol @@"clkWizardD50to100") clk50N clk50P rst -- @ clockWizardDifferential - :: forall domIn domOut periodIn periodOut edge init polarity name - . ( KnownConfiguration domIn ('DomainConfiguration domIn periodIn edge 'Asynchronous init polarity) - , KnownConfiguration domOut ('DomainConfiguration domOut periodOut edge 'Asynchronous init polarity) ) + :: forall domIn domOut domPllLock name + . ( KnownDomain domIn + , KnownDomain domOut + , KnownDomain domPllLock ) => SSymbol name -- ^ Name of the component, must correspond to the name entered in the -- \"Clock Wizard\" dialog. @@ -95,11 +110,12 @@ clockWizardDifferential -- ^ Free running clock, positive phase -> Reset domIn -- ^ Reset for the PLL - -> (Clock domOut, Enable domOut) + -> (Clock domOut, Signal domPllLock Bool) -- ^ (Stable PLL clock, PLL lock) -clockWizardDifferential !_name (Clock _ Nothing) (Clock _ Nothing) rst = - (Clock SSymbol Nothing, unsafeCoerce (toEnable (unsafeToHighPolarity rst))) -clockWizardDifferential !_name _ _ _ = - error "clockWizardDifferential: no support for dynamic clocks" +clockWizardDifferential !_ !_ = clocks +-- let clkO = Clock SSymbol Nothing +-- in (clkO, unsafeSynchronizer clkp (clockGen @domPllLock) $ unsafeToLowPolarity rst) +-- clockWizardDifferential !_name _ _ _ = +-- error "clockWizardDifferential: no support for dynamic clocks" {-# NOINLINE clockWizardDifferential #-} {-# ANN clockWizardDifferential hasBlackBox #-}