clash-lib-0.2: src/CLaSH/Netlist/BlackBox/Util.hs
{-# LANGUAGE TemplateHaskell #-}
-- | Utilties to verify blackbox contexts against templates and rendering
-- filled in templates
module CLaSH.Netlist.BlackBox.Util where
import Control.Lens (at, use, (%=), (+=), _1,
_2)
import Control.Monad.State (State, lift, runState)
import Control.Monad.Writer (runWriterT, tell)
import Data.Foldable (foldrM)
import qualified Data.IntMap as IntMap
import qualified Data.List as List
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as Text
import Text.PrettyPrint.Leijen.Text.Monadic (displayT, renderOneLine)
import CLaSH.Netlist.BlackBox.Types
import CLaSH.Netlist.Types (HWType (..), Identifier,
VHDLState)
import CLaSH.Netlist.VHDL (vhdlType,
vhdlTypeDefault,
vhdlTypeMark)
import CLaSH.Util
-- | Determine if the number of normal/literal/function inputs of a blackbox
-- context at least matches the number of argument that is expected by the
-- template.
verifyBlackBoxContext :: BlackBoxTemplate -- ^ Template to check against
-> BlackBoxContext -- ^ Blackbox to verify
-> Bool
verifyBlackBoxContext tmpl bbCtx =
((length (inputs bbCtx) - 1) >= countArgs tmpl) &&
((length (litInputs bbCtx) - 1) >= countLits tmpl) &&
((length (funInputs bbCtx) - 1) >= countFuns tmpl)
-- | Count the number of argument tags/holes in a blackbox template
countArgs :: BlackBoxTemplate -> Int
countArgs [] = -1
countArgs l = maximum
$ map (\e -> case e of
I n -> n
D (Decl _ l') -> maximum $ map countArgs l'
_ -> -1
) l
-- | Counter the number of literal tags/holes in a blackbox template
countLits :: BlackBoxTemplate -> Int
countLits [] = -1
countLits l = maximum
$ map (\e -> case e of
L n -> n
D (Decl _ l') -> maximum $ map countLits l'
_ -> -1
) l
-- | Count the number of function instantiations in a blackbox template
countFuns :: BlackBoxTemplate -> Int
countFuns [] = -1
countFuns l = maximum $ map (\e -> case e of { D (Decl n _) -> n; _ -> -1 }) l
-- | Update all the symbol references in a template, and increment the symbol
-- counter for every newly encountered symbol.
setSym :: Int -> BlackBoxTemplate -> (BlackBoxTemplate,Int)
setSym i l
= second fst
$ runState (setSym' l) (i,IntMap.empty)
where
setSym' :: BlackBoxTemplate -> State (Int,IntMap.IntMap Int) BlackBoxTemplate
setSym' = mapM (\e -> case e of
Sym i' -> do symM <- use (_2 . at i')
case symM of
Nothing -> do k <- use _1
_1 += 1
_2 %= IntMap.insert i' k
return (Sym k)
Just k -> return (Sym k)
D (Decl n l') -> D <$> (Decl n <$> mapM setSym' l')
_ -> pure e
)
-- | Get the name of the clock of an identifier
clkSyncId :: SyncIdentifier -> Identifier
clkSyncId (Right (_,clk)) = clk
clkSyncId (Left i) = error $ $(curLoc) ++ "No clock for: " ++ show i
-- | Render a blackbox given a certain context. Returns a filled out template
-- and a list of 'hidden' inputs that must be added to the encompassing component.
renderBlackBox :: BlackBoxTemplate -- ^ Blackbox template
-> BlackBoxContext -- ^ Context used to fill in the hole
-> VHDLState
-> ((Text, [(Identifier,HWType)]),VHDLState)
renderBlackBox l bbCtx s
= first (Text.concat *** List.nub)
$ flip runState s
$ runWriterT
$ runBlackBoxM
$ mapM (renderElem bbCtx) l
-- | Render a single template element
renderElem :: BlackBoxContext
-> Element
-> BlackBoxMonad Text
renderElem b (D (Decl n (l:ls))) = do
o <- lineToIdentifier b l
is <- mapM (lineToIdentifier b) ls
let (templ,pCtx) = indexNote ($(curLoc) ++ "No function argument " ++ show n) (funInputs b) n
let b' = pCtx { result = o, inputs = inputs pCtx ++ is }
if verifyBlackBoxContext templ b'
then Text.concat <$> mapM (renderElem b') templ
else error $ $(curLoc) ++ "\nCan't match context:\n" ++ show b' ++ "\nwith template:\n" ++ show templ
renderElem b e = either id fst <$> mkSyncIdentifier b e
-- | Fill out the template corresponding to an output/input assignment of a
-- component instantiation, and turn it into a single identifier so it can
-- be used for a new blackbox context.
lineToIdentifier :: BlackBoxContext
-> BlackBoxTemplate
-> BlackBoxMonad (SyncIdentifier,HWType)
lineToIdentifier b = foldrM (\e (a,_) -> do
e' <- mkSyncIdentifier b e
case (e', a) of
(Left t, Left t') -> return (Left (t `Text.append` t'), ty)
(Left t, Right (t',clk)) -> return (Right (t `Text.append` t',clk), ty)
(Right (t,clk), Left t') -> return (Right (t `Text.append` t',clk), ty)
(Right (t,clk), Right (t',_)) -> return (Right (t `Text.append` t',clk), ty)
) (Left Text.empty,ty)
where
ty = Void
-- | Give a context and a tagged hole (of a template), returns part of the
-- context that matches the tag of the hole.
mkSyncIdentifier :: BlackBoxContext
-> Element
-> BlackBoxMonad SyncIdentifier
mkSyncIdentifier _ (C t) = return $ Left t
mkSyncIdentifier b O = return $ fst $ result b
mkSyncIdentifier b (I n) = return $ fst $ inputs b !! n
mkSyncIdentifier b (L n) = return $ Left $ litInputs b !! n
mkSyncIdentifier _ (Sym n) = return $ Left $ Text.pack ("n_" ++ show n)
mkSyncIdentifier b (Clk Nothing) = let t = clkSyncId $ fst $ result b
in tell [(t,Clock 10)] >> return (Left t)
mkSyncIdentifier b (Clk (Just n)) = let t = clkSyncId $ fst $ inputs b !! n
in tell [(t,Clock 10)] >> return (Left t)
mkSyncIdentifier b (Rst Nothing) = let t = (`Text.append` Text.pack "_rst") . clkSyncId $ fst $ result b
in tell [(t,Reset 10)] >> return (Left t)
mkSyncIdentifier b (Rst (Just n)) = let t = (`Text.append` Text.pack "_rst") . clkSyncId $ fst $ inputs b !! n
in tell [(t,Reset 10)] >> return (Left t)
mkSyncIdentifier b (Typ Nothing) = fmap (Left . displayT . renderOneLine) . B . lift . vhdlType . snd $ result b
mkSyncIdentifier b (Typ (Just n)) = fmap (Left . displayT . renderOneLine) . B . lift . vhdlType . snd $ inputs b !! n
mkSyncIdentifier b (TypM Nothing) = fmap (Left . displayT . renderOneLine) . B . lift . vhdlTypeMark . snd $ result b
mkSyncIdentifier b (TypM (Just n)) = fmap (Left . displayT . renderOneLine) . B . lift . vhdlTypeMark . snd $ inputs b !! n
mkSyncIdentifier b (Def Nothing) = fmap (Left . displayT . renderOneLine) . B . lift . vhdlTypeDefault . snd $ result b
mkSyncIdentifier b (Def (Just n)) = fmap (Left . displayT . renderOneLine) . B . lift . vhdlTypeDefault . snd $ inputs b !! n
mkSyncIdentifier b (D _) = error $ $(curLoc) ++ "Unexpected component declaration"