{-| Copyright : (C) 2018 , Google Inc., 2021-2023, QBayLogic B.V., 2022 , Google Inc. License : BSD2 (see the file LICENSE) Maintainer : QBayLogic B.V. Blackbox template functions for Clash.Intel.ClockGen -} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ViewPatterns #-} module Clash.Primitives.Intel.ClockGen where import Control.Monad.State import Data.List (zip4) import Data.List.Infinite (Infinite(..), (...)) import Data.Maybe (fromMaybe) import Data.Text.Prettyprint.Doc.Extra import Text.Show.Pretty (ppShow) import Clash.Backend import qualified Clash.Netlist.Id as Id import Clash.Netlist.Types import Clash.Netlist.Util import qualified Clash.Primitives.DSL as DSL import Clash.Signal (periodToHz) import Data.Text.Extra (showt) import qualified Data.String.Interpolate as I import qualified Data.Text as TextS import qualified Prettyprinter.Interpolate as I data Variant = Altpll | AlteraPll hdlUsed :: [Int] hdlUsed = [ clk, rst ] where _knownDomIn :< _clocksClass :< _clocksCxt :< _numOutClocks :< clk :< rst :< _ = (0...) hdlValid :: BlackBoxContext -> Bool hdlValid bbCtx | [(_,Product {})] <- bbResults bbCtx = True hdlValid _ = False qsysUsed :: [Int] qsysUsed = [ knownDomIn, clocksCxt ] where knownDomIn :< _clocksClass :< clocksCxt :< _ = (0...) altpllTF :: TemplateFunction altpllTF = TemplateFunction hdlUsed hdlValid (hdlTemplate Altpll) altpllQsysTF :: TemplateFunction altpllQsysTF = TemplateFunction qsysUsed valid altpllQsysTemplate where valid = const True alteraPllTF :: TemplateFunction alteraPllTF = TemplateFunction hdlUsed hdlValid (hdlTemplate AlteraPll) alteraPllQsysTF :: TemplateFunction alteraPllQsysTF = TemplateFunction qsysUsed valid alteraPllQsysTemplate where valid = const True hdlTemplate :: forall s . Backend s => Variant -> BlackBoxContext -> State s Doc hdlTemplate variant bbCtx | [ _knownDomIn , _clocksClass , _clocksCxt , _numOutClocks , clk , rst ] <- map fst (DSL.tInputs bbCtx) , [DSL.ety -> resultTy] <- DSL.tResults bbCtx , Product _ _ (init -> pllOutTys) <- resultTy , [compName] <- bbQsysIncName bbCtx = do let stdName Altpll = "altpll" stdName AlteraPll = "altera_pll" pllOutName Altpll = "c" pllOutName AlteraPll = "outclk_" clkInName Altpll = "clk" clkInName AlteraPll = "refclk" rstName Altpll = "areset" rstName AlteraPll = "rst" instName <- Id.makeBasic $ fromMaybe (stdName variant) $ bbCtxName bbCtx -- TODO: unsafeMake is dubious here: I don't think we take names in -- TODO: bbQsysIncName into account when generating fresh ids let compNameId = Id.unsafeMake compName DSL.declarationReturn bbCtx (stdName variant <> "_block") $ do rstHigh <- DSL.unsafeToActiveHigh "reset" rst pllOuts <- DSL.declareN "pllOut" pllOutTys locked <- DSL.declare "locked" Bit pllLock <- DSL.boolFromBit "pllLock" locked let pllOutNames = map (\n -> pllOutName variant <> showt n) [0 .. length pllOutTys - 1] compInps = [ (clkInName variant, DSL.ety clk) , (rstName variant, DSL.ety rstHigh) ] compOuts = zip pllOutNames pllOutTys <> [("locked", Bit)] inps = [ (clkInName variant, clk) , (rstName variant, rstHigh) ] outs = zip pllOutNames pllOuts <> [("locked", locked)] DSL.compInBlock compName compInps compOuts DSL.instDecl Empty compNameId instName [] inps outs pure [DSL.constructProduct resultTy (pllOuts <> [pllLock])] | otherwise = error $ ppShow bbCtx altpllQsysTemplate :: Backend s => BlackBoxContext -> State s Doc altpllQsysTemplate bbCtx | (_,stripVoid -> (KnownDomain _ clkInPeriod _ _ _ _),_) : _clocksClass : (_,stripVoid -> Product _ _ (init -> kdOuts),_) : _ <- bbInputs bbCtx = let clkPeriod (KnownDomain _ p _ _ _ _) = p clkPeriod _ = error $ "Internal error: not a KnownDomain\n" <> ppShow bbCtx clkFreq p = periodToHz (fromInteger p) / 1e6 :: Double clkOutPeriods = map clkPeriod kdOuts clkLcms = map (lcm clkInPeriod) clkOutPeriods clkMults = zipWith quot clkLcms clkOutPeriods clkDivs = map (`quot` clkInPeriod) clkLcms clkOutFreqs = map clkFreq clkOutPeriods qsysParams = TextS.intercalate "\n " [[I.__i| |] | (clkMult, clkDiv, n) <- zip3 clkMults clkDivs [(0 :: Word)..] ] qsysConsts = TextS.intercalate "\n " [[I.__i| CT\#PORT_clk#{n} PORT_USED CT\#CLK#{n}_MULTIPLY_BY #{clkMult} CT\#CLK#{n}_DIVIDE_BY #{clkDiv} CT\#CLK#{n}_DUTY_CYCLE 50 CT\#CLK#{n}_PHASE_SHIFT 0 |] | (clkMult, clkDiv, n) <- zip3 clkMults clkDivs [(0 :: Word)..] ] qsysPorts = TextS.intercalate "\n " [[I.i|IF\#c#{n} {output 0}|] | n <- [0 .. length kdOuts - 1]] qsysPrivs = TextS.intercalate "\n " [[I.__i| PT\#MULT_FACTOR#{n} #{clkMult} PT\#DIV_FACTOR#{n} #{clkDiv} PT\#EFF_OUTPUT_FREQ_VALUE#{n} #{clkOutFreq} PT\#DUTY_CYCLE#{n} 50.00000000 PT\#PHASE_SHIFT0 0.00000000 |] | (clkMult, clkDiv, clkOutFreq, n) <- zip4 clkMults clkDivs clkOutFreqs [(0 :: Word)..] ] -- Note [QSys file templates] -- This QSys file template was derived from a "full" QSys system with a single -- "altpll" IP. Module parameters were then stripped on a trial-and-error -- basis to get a template that has the minimal number of parameters, but -- still has the desired, working, configuration. bbText = [I.__di| #{qsysParams} #{qsysConsts} CT\#WIDTH_CLOCK 5 CT\#LPM_TYPE altpll CT\#PLL_TYPE AUTO CT\#OPERATION_MODE NORMAL CT\#COMPENSATE_CLOCK CLK0 CT\#INCLK0_INPUT_FREQUENCY #{clkInPeriod} CT\#PORT_INCLK0 PORT_USED CT\#PORT_ARESET PORT_USED CT\#BANDWIDTH_TYPE AUTO CT\#PORT_LOCKED PORT_USED IF\#phasecounterselect {input 4} IF\#locked {output 0} IF\#reset {input 0} IF\#clk {input 0} IF\#phaseupdown {input 0} IF\#scandone {output 0} IF\#readdata {output 32} IF\#write {input 0} IF\#scanclk {input 0} IF\#phasedone {output 0} IF\#address {input 2} #{qsysPorts} IF\#writedata {input 32} IF\#read {input 0} IF\#areset {input 0} IF\#scanclkena {input 0} IF\#scandataout {output 0} IF\#configupdate {input 0} IF\#phasestep {input 0} IF\#scandata {input 0} MF\#areset 1 MF\#clk 1 MF\#locked 1 MF\#inclk 1 #{qsysPrivs} |] in pure bbText | otherwise = error $ ppShow bbCtx alteraPllQsysTemplate :: Backend s => BlackBoxContext -> State s Doc alteraPllQsysTemplate bbCtx | (_,stripVoid -> kdIn,_) : _clocksClass : (_,stripVoid -> Product _ _ (init -> kdOuts),_) : _ <- bbInputs bbCtx = let clkFreq (KnownDomain _ p _ _ _ _) = periodToHz (fromIntegral p) / 1e6 :: Double clkFreq _ = error $ "Internal error: not a KnownDomain\n" <> ppShow bbCtx clkOuts = TextS.intercalate "\n" [[I.i| |] | (n,f) <- zip [(0 :: Word)..] (map clkFreq kdOuts) ] -- See Note [QSys file templates] on how this qsys template was derived. bbText = [I.__di| #{clkOuts} |] in pure bbText | otherwise = error $ ppShow bbCtx