clash-lib-1.8.0: src/Clash/Primitives/Intel/ClockGen.hs
{-|
Copyright : (C) 2018 , Google Inc.,
2021-2023, QBayLogic B.V.,
2022 , Google Inc.
License : BSD2 (see the file LICENSE)
Maintainer : QBayLogic B.V. <devops@qbaylogic.com>
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|
<parameter name="PORT_clk#{n}" value="PORT_USED" />
<parameter name="CLK#{n}_MULTIPLY_BY" value="#{clkMult}" />
<parameter name="CLK#{n}_DIVIDE_BY" value="#{clkDiv}" />
<parameter name="CLK#{n}_DUTY_CYCLE" value="50" />
<parameter name="CLK#{n}_PHASE_SHIFT" value="0" />
|]
| (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|
<?xml version="1.0" encoding="UTF-8"?>
<system name="$${FILENAME}">
<module
name="altpll0"
kind="altpll"
enabled="1"
autoexport="1">
<parameter name="AVALON_USE_SEPARATE_SYSCLK" value="NO" />
<parameter name="BANDWIDTH" value="" />
<parameter name="BANDWIDTH_TYPE" value="AUTO" />
#{qsysParams}
<parameter name="COMPENSATE_CLOCK" value="CLK0" />
<parameter name="INCLK0_INPUT_FREQUENCY" value="#{clkInPeriod}" />
<parameter name="OPERATION_MODE" value="NORMAL" />
<parameter name="PLL_TYPE" value="AUTO" />
<parameter name="PORT_ARESET" value="PORT_USED" />
<parameter name="PORT_INCLK0" value="PORT_USED" />
<parameter name="PORT_LOCKED" value="PORT_USED" />
<parameter name="HIDDEN_IS_FIRST_EDIT" value="0" />
<parameter name="HIDDEN_CONSTANTS">
#{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</parameter>
<parameter name="HIDDEN_IF_PORTS">
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}</parameter>
<parameter name="HIDDEN_MF_PORTS">
MF\#areset 1
MF\#clk 1
MF\#locked 1
MF\#inclk 1</parameter>
<parameter name="HIDDEN_PRIVATES">
#{qsysPrivs}</parameter>
</module>
</system>
|]
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| <parameter name="gui_output_clock_frequency#{n}" value="#{f}"/>|]
| (n,f) <- zip [(0 :: Word)..] (map clkFreq kdOuts)
]
-- See Note [QSys file templates] on how this qsys template was derived.
bbText = [I.__di|
<?xml version="1.0" encoding="UTF-8"?>
<system name="$${FILENAME}">
<module
name="pll_0"
kind="altera_pll"
enabled="1"
autoexport="1">
<parameter name="gui_feedback_clock" value="Global Clock" />
<parameter name="gui_number_of_clocks" value="#{length kdOuts}" />
<parameter name="gui_operation_mode" value="direct" />
#{clkOuts}
<parameter name="gui_pll_mode" value="Integer-N PLL" />
<parameter name="gui_reference_clock_frequency" value="#{clkFreq kdIn}" />
<parameter name="gui_use_locked" value="true" />
</module>
</system>
|]
in
pure bbText
| otherwise
= error $ ppShow bbCtx