{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
module Clash.Primitives.DSL
(
blackBoxHaskell
, BlockState (..)
, TExpr
, declaration
, declarationReturn
, instDecl
, instHO
, viaAnnotatedSignal
, bvLit
, LitHDL (..)
, pattern High
, pattern Low
, tuple
, vec
, tInputs
, tResult
, getStr
, getBool
, exprToInteger
, tExprToInteger
, untuple
, unvec
, toBV
, fromBV
, boolToBit
, boolFromBit
, boolFromBitVector
, unsignedFromBitVector
, boolFromBits
, andExpr
, notExpr
, pureToBV
, pureToBVResized
, open
, toIdentifier
, tySize
, clog2
) where
import Clash.Util (HasCallStack, clogBase)
import Control.Lens hiding (Indexed, assign)
import Control.Monad.State
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import Data.List (intersperse)
import Data.Maybe (fromMaybe)
import Data.Semigroup hiding (Product)
import Data.Semigroup.Monad
import Data.String
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Lazy as LText
import Data.Text.Prettyprint.Doc.Extra
import TextShow (showt)
import Clash.Annotations.Primitive (HDL (..), Primitive (..))
import Clash.Backend hiding (fromBV, toBV)
import Clash.Backend.VHDL (VHDLState)
import Clash.Core.Var (Attr')
import Clash.Netlist.BlackBox.Util (exprToString, renderElem)
import Clash.Netlist.BlackBox.Types
(BlackBoxTemplate, Element(Component, Text), Decl(..))
import Clash.Netlist.Id
import Clash.Netlist.Types hiding (Component, toBit)
import Clash.Netlist.Util hiding (mkUniqueIdentifier)
import qualified Data.String.Interpolate as I
import Data.String.Interpolate.Util (unindent)
import Language.Haskell.TH (Name)
import Prelude
blackBoxHaskell
:: [Int]
-> HDL
-> Name
-> Name
-> Primitive
blackBoxHaskell (show -> ign) hdl bb tf =
InlinePrimitive [hdl] $ unindent [I.i|
[ { "BlackBoxHaskell" :
{ "name" : "#{bb}"
, "templateFunction" : "#{tf}"
, "ignoredArguments" : #{ign}
}
}
]
|]
data BlockState backend = BlockState
{ _bsDeclarations :: [Declaration]
, _bsHigherOrderCalls :: IntMap Int
, _bsBackend :: backend
}
makeLenses ''BlockState
data TExpr = TExpr
{ ety :: HWType
, eex :: Expr
} deriving Show
makeLenses ''TExpr
declarationReturn
:: Backend backend
=> BlackBoxContext
-> Text.Text
-> State (BlockState backend) TExpr
-> State backend Doc
declarationReturn bbCtx blockName blockBuilder =
declaration blockName $ do
res <- blockBuilder
let (Identifier resultNm Nothing, _) = bbResult bbCtx
addDeclaration (Assignment resultNm (eex res))
pure ()
declaration
:: Backend backend
=> Text.Text
-> State (BlockState backend) ()
-> State backend Doc
declaration blockName s = do
backend0 <- get
let initState = BlockState [] IntMap.empty backend0
BlockState decs _hoCalls backend1 = execState s initState
put backend1
blockNameUnique <- mkUniqueIdentifier Basic blockName
getMon $ blockDecl blockNameUnique (reverse decs)
addDeclaration :: Declaration -> State (BlockState backend) ()
addDeclaration dec = bsDeclarations %= cons dec
newName :: Backend backend => Text -> State (BlockState backend) Identifier
newName nm = zoom bsBackend $ mkUniqueIdentifier Basic nm
declare'
:: Backend backend
=> Identifier
-> WireOrReg
-> HWType
-> State (BlockState backend) Identifier
declare' decName wireOrReg ty = do
uniqueName <- newName decName
addDeclaration (NetDecl' Nothing wireOrReg uniqueName (Right ty) Nothing)
pure uniqueName
declare
:: Backend backend
=> Identifier
-> WireOrReg
-> HWType
-> State (BlockState backend) TExpr
declare decName wireOrReg ty = do
uniqueName <- declare' decName wireOrReg ty
pure (TExpr ty (Identifier uniqueName Nothing))
assign
:: Backend backend
=> Text
-> TExpr
-> State (BlockState backend) TExpr
assign aName (TExpr ty aExpr) = do
texp@(~(TExpr _ (Identifier uniqueName Nothing))) <- declare aName Wire ty
addDeclaration (Assignment uniqueName aExpr)
pure texp
unvec
:: Backend backend
=> Identifier
-> TExpr
-> State (BlockState backend) [TExpr]
unvec vName v@(ety -> Vector vSize eType) = do
~(TExpr _ (Identifier vUniqueName Nothing)) <- toIdentifier vName v
let vIndex i = Identifier vUniqueName (Just (Indexed (ety v, 10, i)))
pure (map (TExpr eType . vIndex) [0..vSize-1])
unvec _ e = error $ "unvec: cannot be called on non-vector: " <> show (ety e)
untuple
:: (HasCallStack, Backend backend)
=> TExpr
-> [Identifier]
-> State (BlockState backend) [TExpr]
untuple (TExpr ty@(Product _ _ tys) (Identifier resName _)) vals = do
newNames <- zipWithM (flip declare Wire) vals tys
addDeclaration $ Assignment resName $ DataCon ty (DC (ty, 0)) (fmap eex newNames)
pure newNames
untuple e i = error $ "untuple: " <> show e <> " " <> show i
pattern High :: TExpr
pattern High <- TExpr Bit (Literal _ (BitLit H))
where High = TExpr Bit (Literal (Just (Bit,1)) (BitLit H))
pattern Low :: TExpr
pattern Low <- TExpr Bit (Literal _ (BitLit L))
where Low = TExpr Bit (Literal (Just (Bit,1)) (BitLit L))
pattern T :: TExpr
pattern T <- TExpr Bool (Literal _ (BoolLit True))
where T = TExpr Bool (Literal (Just (Bool,1)) (BoolLit True))
pattern F :: TExpr
pattern F <- TExpr Bool (Literal _ (BoolLit False))
where F = TExpr Bool (Literal (Just (Bool,1)) (BoolLit False))
bvLit
:: Int
-> Integer
-> TExpr
bvLit sz n =
TExpr
(BitVector sz)
(Literal (Just (BitVector sz, sz)) (BitVecLit 0 n))
boolToBit
:: (HasCallStack, Backend backend)
=> Identifier
-> TExpr
-> State (BlockState backend) TExpr
boolToBit bitName = \case
T -> pure High
F -> pure Low
TExpr Bool boolExpr -> do
texp@(~(TExpr _ (Identifier uniqueBitName Nothing))) <- declare bitName Wire Bit
addDeclaration $
CondAssignment uniqueBitName Bit boolExpr Bool
[ (Just (BoolLit True), Literal Nothing (BitLit H))
, (Nothing , Literal Nothing (BitLit L))
]
pure texp
tExpr -> error $ "boolToBit: Got \"" <> show tExpr <> "\" expected Bool"
boolFromBit
:: Backend backend
=> Identifier
-> TExpr
-> State (BlockState backend) TExpr
boolFromBit = outputCoerce Bit Bool (<> " = '1'")
boolFromBitVector
:: Backend backend
=> Size
-> Identifier
-> TExpr
-> State (BlockState backend) TExpr
boolFromBitVector n =
outputCoerce (BitVector n) Bool (\i -> "unsigned(" <> i <> ") > 0")
unsignedFromBitVector
:: Size
-> Identifier
-> TExpr
-> State (BlockState VHDLState) TExpr
unsignedFromBitVector n =
outputCoerce (BitVector n) (Unsigned n) (\i -> "unsigned(" <> i <> ")")
boolFromBits
:: [Identifier]
-> TExpr
-> State (BlockState VHDLState) [TExpr]
boolFromBits inNames = outputFn (map (const Bit) inNames) Bool
(foldl (<>) "" . intersperse " and " . map (\i -> "(" <> i <> " = '1')")) inNames
outputCoerce
:: (HasCallStack, Backend backend)
=> HWType
-> HWType
-> (Identifier -> Identifier)
-> Identifier
-> TExpr
-> State (BlockState backend) TExpr
outputCoerce fromType toType exprStringFn inName expr_
| TExpr outType (Identifier outName Nothing) <- expr_
, outType == toType = do
inName' <- newName inName
let exprIdent = Identifier (exprStringFn inName') Nothing
addDeclaration (NetDecl Nothing inName' fromType)
addDeclaration (Assignment outName exprIdent)
pure (TExpr fromType (Identifier inName' Nothing))
outputCoerce _ toType _ _ texpr = error $ "outputCoerce: the expression " <> show texpr
<> " must be an Identifier with type " <> show toType
outputFn
:: (HasCallStack, Backend backend)
=> [HWType]
-> HWType
-> ([Identifier] -> Identifier)
-> [Identifier]
-> TExpr
-> State (BlockState backend) [TExpr]
outputFn fromTypes toType exprFn inNames (TExpr outType (Identifier outName Nothing))
| outType == toType = do
inNames' <- mapM newName inNames
let exprIdent = Identifier (exprFn inNames') Nothing
sequenceOf_ each [ addDeclaration (NetDecl Nothing nm t)
| (nm,t) <- zip inNames' fromTypes ]
addDeclaration (Assignment outName exprIdent)
pure [ TExpr t (Identifier nm Nothing)
| (nm,t) <- zip inNames' fromTypes ]
outputFn _ outType _ _ texpr =
error $ "outputFn: the expression " <> show texpr
<> " must be an Identifier with type " <> show outType
vec
:: (HasCallStack, Backend backend)
=> [TExpr]
-> State (BlockState backend) TExpr
vec els@(el:_)
| all (\e -> ety e == ety el) els
= pure (TExpr (Vector (length els) (ety el)) theVec)
| otherwise
= error $ "vec: elements not of same type: " ++ show els
where
theVec = mkVectorChain (length els) (ety el) (map eex els)
vec [] = error "vec: can't be used on empty lists"
tuple :: [TExpr] -> TExpr
tuple [] = error $ "nTuple: Cannot create empty tuple"
tuple [_] =
error $ "nTuple: Cannot create 1-tuple"
tuple els =
TExpr tupTy (DataCon tupTy (DC (tupTy,0)) (map eex els))
where
commas = Text.replicate (length els - 1) ","
tupTy = Product ("GHC.Tuple.(" <> commas <> ")") Nothing (map ety els)
getStr :: TExpr -> Maybe String
getStr (TExpr _ e) = exprToString e
getBool :: TExpr -> Maybe Bool
getBool (TExpr _ (Literal _ (BoolLit b))) = Just b
getBool _ = Nothing
tExprToInteger :: TExpr -> Maybe Integer
tExprToInteger (TExpr _ e) = exprToInteger e
exprToInteger :: Expr -> Maybe Integer
exprToInteger (DataCon _ _ [n]) = exprToInteger n
exprToInteger (Literal _ (NumLit n)) = Just n
exprToInteger _ = Nothing
toBV
:: Backend backend
=> Identifier
-> TExpr
-> State (BlockState backend) TExpr
toBV bvName a = case a of
TExpr BitVector{} _ -> pure a
TExpr aTy aExpr -> assign bvName $
TExpr (BitVector (typeSize aTy)) (ConvBV Nothing aTy True aExpr)
fromBV
:: (HasCallStack, Backend backend)
=> Identifier
-> TExpr
-> State (BlockState backend) TExpr
fromBV _ a@(TExpr BitVector{} _) = pure a
fromBV bvName (TExpr aTy (Identifier aName Nothing)) = do
bvName' <- newName bvName
let bvExpr = ConvBV Nothing aTy False (Identifier bvName' Nothing)
bvTy = BitVector (typeSize aTy)
addDeclaration (NetDecl Nothing bvName' bvTy)
addDeclaration (Assignment aName bvExpr)
pure (TExpr bvTy (Identifier bvName' Nothing))
fromBV _ texpr = error $
"fromBV: the expression " <> show texpr <> "must be an Indentifier"
clog2 :: Num i => Integer -> i
clog2 = fromIntegral . fromMaybe 0 . clogBase 2
tySize :: Num i => HWType -> i
tySize = fromIntegral . typeSize
data LitHDL
= B Bool
| S String
| I Integer
deriving Show
instance Num LitHDL where
(+) = undefined
(*) = undefined
abs = undefined
signum = undefined
negate = undefined
fromInteger = I
instance IsString LitHDL where
fromString = S
instHO
:: Backend backend
=> BlackBoxContext
-> Int
-> (HWType, BlackBoxTemplate)
-> [(TExpr, BlackBoxTemplate)]
-> State (BlockState backend) TExpr
instHO bbCtx fPos (resTy, bbResTy) argsWithTypes = do
let (args0, argTypes) = unzip argsWithTypes
fSubPos <- fromMaybe 0 . IntMap.lookup fPos <$> use bsHigherOrderCalls
bsHigherOrderCalls %= IntMap.insert fPos (succ fSubPos)
let
ctxName = last (Text.split (=='.') (bbName bbCtx))
baseArgName = ctxName <> "_" <> "ho" <> showt fPos <> "_" <> showt fSubPos
argName n = baseArgName <> "_arg" <> showt n
args1 <- zipWithM (\argN -> toIdentifier' (argName argN)) [(0 :: Int)..] args0
let
args2 = map (pure . Text . LText.fromStrict) args1
args3 = zip args2 argTypes
resWireOrReg =
case IntMap.lookup fPos (bbFunctions bbCtx) of
Just ((_,rw,_,_,_,_):_) -> rw
_ -> error "internal error"
resName <- declare' (ctxName <> "_" <> "ho" <> showt fPos <> "_"
<> showt fSubPos <> "_res") resWireOrReg resTy
let res = ([Text (LText.fromStrict resName)], bbResTy)
let component = Component (Decl fPos fSubPos (res:args3))
rendered0 <-
zoom bsBackend (string =<< (renderElem bbCtx component <*> pure 0))
let
layout = LayoutOptions (AvailablePerLine 120 0.4)
rendered1 = renderLazy (layoutPretty layout rendered0)
addDeclaration $
BlackBoxD
("__INST_" <> bbName bbCtx <> "_BB_INTERNAL__") [] [] []
(BBTemplate [Text rendered1])
(emptyBBContext ("__INST_" <> bbName bbCtx <> "_BB_INTERNAL__"))
pure (TExpr resTy (Identifier resName Nothing))
instDecl
:: Backend backend
=> EntityOrComponent
-> Identifier
-> Identifier
-> [(Text, LitHDL)]
-> [(Text, TExpr)]
-> [(Text, TExpr)]
-> State (BlockState backend) ()
instDecl entOrComp compName instLbl attrs inPorts outPorts = do
inPorts' <- mapM (mkPort In) inPorts
outPorts' <- mapM (mkPort Out) outPorts
addDeclaration $ InstDecl entOrComp Nothing compName instLbl (mkAttrs attrs) (inPorts' ++ outPorts')
where
mkPort inOrOut (nm, pExpr) = do
TExpr ty pExpr' <- toIdentifier (nm <> "_port") pExpr
pure (Identifier nm Nothing, inOrOut, ty, pExpr')
mkAttrs :: [(Text.Text, LitHDL)] -> [(Expr, HWType, Expr)]
mkAttrs = map (\(s, ty) -> (Identifier s Nothing, hdlTy ty, litExpr ty))
litExpr :: LitHDL -> Expr
litExpr (B b) = Literal Nothing (BoolLit b)
litExpr (S s) = Literal Nothing (StringLit s)
litExpr (I i) = Literal Nothing (NumLit i)
hdlTy :: LitHDL -> HWType
hdlTy = \case
B{} -> Bool
S{} -> String
I{} -> Integer
viaAnnotatedSignal
:: (HasCallStack, Backend backend)
=> Text
-> TExpr
-> TExpr
-> [Attr']
-> State (BlockState backend) ()
viaAnnotatedSignal sigNm (TExpr fromTy fromExpr) (TExpr toTy (Identifier outNm Nothing)) attrs
| fromTy == toTy = do
addDeclaration (NetDecl Nothing sigNm (Annotated attrs fromTy))
addDeclaration (Assignment sigNm fromExpr)
addDeclaration (Assignment outNm (Identifier sigNm Nothing))
viaAnnotatedSignal _ inTExpr outTExpr@(TExpr _ (Identifier _ _)) _ =
error $ "viaAnnotatedSignal: The in and out expressions \"" <> show inTExpr <>
"\" and \"" <> show outTExpr <> "\" have non-matching types."
viaAnnotatedSignal _ _ outTExpr _ =
error $ "viaAnnotatedSignal: The out expression \"" <> show outTExpr <>
"\" must be an Identifier."
tInputs :: BlackBoxContext -> [(TExpr, HWType)]
tInputs = map (\(x, t, _) -> (TExpr t x, t)) . bbInputs
tResult :: BlackBoxContext -> TExpr
tResult = (\(x,t) -> TExpr t x) . bbResult
toIdentifier'
:: Backend backend
=> Identifier
-> TExpr
-> State (BlockState backend) Identifier
toIdentifier' _ (TExpr _ (Identifier aExpr Nothing)) = pure aExpr
toIdentifier' nm texp = do
~(TExpr _ (Identifier nm' Nothing)) <- assign nm texp
pure nm'
toIdentifier
:: Backend backend
=> Identifier
-> TExpr
-> State (BlockState backend) TExpr
toIdentifier nm texp = do
id' <- toIdentifier' nm texp
pure (TExpr (ety texp) (Identifier id' Nothing))
andExpr
:: Backend backend
=> Identifier
-> TExpr
-> TExpr
-> State (BlockState backend) TExpr
andExpr _ T bExpr = pure bExpr
andExpr _ F _ = pure F
andExpr _ aExpr T = pure aExpr
andExpr _ _ F = pure F
andExpr nm a b = do
aIdent <- toIdentifier' (nm <> "_a") a
bIdent <- toIdentifier' (nm <> "_b") b
andTxt <-
uses bsBackend hdlKind <&> \case
VHDL -> aIdent <> " and " <> bIdent
Verilog -> aIdent <> " && " <> bIdent
SystemVerilog -> aIdent <> " && " <> bIdent
assign nm $ TExpr Bool (Identifier andTxt Nothing)
notExpr
:: Backend backend
=> Identifier
-> TExpr
-> State (BlockState backend) TExpr
notExpr _ T = pure F
notExpr _ F = pure T
notExpr nm aExpr = do
aIdent <- toIdentifier' (nm <> "_a") aExpr
notTxt <- uses bsBackend hdlKind <&> \case
VHDL -> "not " <> aIdent
Verilog -> "! " <> aIdent
SystemVerilog -> "! " <> aIdent
assign nm $ TExpr Bit (Identifier notTxt Nothing)
pureToBV
:: Identifier
-> Int
-> TExpr
-> State (BlockState VHDLState) TExpr
pureToBV nm n arg = do
arg' <- toIdentifier' nm arg
let text = "(0 to " <> showt n <> " => " <> arg' <> ")"
assign nm $ TExpr (BitVector (n+1)) (Identifier text Nothing)
pureToBVResized
:: Identifier
-> Int
-> TExpr
-> State (BlockState VHDLState) TExpr
pureToBVResized nm n arg = do
arg' <- toIdentifier' nm arg
let text = "std_logic_vector(resize(" <> arg' <> ", " <> showt n <> "))"
assign nm $ TExpr (BitVector n) (Identifier text Nothing)
open
:: Backend backend
=> HWType
-> State (BlockState backend) TExpr
open hwType = pure $ TExpr hwType (Identifier "open" Nothing)