clash (empty) → 0.1
raw patch · 28 files changed
+6838/−0 lines, 28 filesdep +basedep +containersdep +data-accessorsetup-changed
Dependencies added: base, containers, data-accessor, data-accessor-template, data-accessor-transformers, directory, filepath, ghc, haskell98, pretty, prettyclass, syb, template-haskell, tfp, th-lift, time, transformers, vhdl
Files
- CLasH/HardwareTypes.hs +93/−0
- CLasH/Normalize.hs +1043/−0
- CLasH/Normalize/NormalizeTools.hs +245/−0
- CLasH/Normalize/NormalizeTypes.hs +34/−0
- CLasH/Translator.hs +142/−0
- CLasH/Translator/Annotations.hs +24/−0
- CLasH/Translator/TranslatorTypes.hs +131/−0
- CLasH/Utils.hs +69/−0
- CLasH/Utils/Core/BinderTools.hs +95/−0
- CLasH/Utils/Core/CoreShow.hs +80/−0
- CLasH/Utils/Core/CoreTools.hs +463/−0
- CLasH/Utils/GhcTools.hs +249/−0
- CLasH/Utils/HsTools.hs +212/−0
- CLasH/Utils/Pretty.hs +81/−0
- CLasH/VHDL.hs +99/−0
- CLasH/VHDL/Constants.hs +399/−0
- CLasH/VHDL/Generate.hs +1634/−0
- CLasH/VHDL/Testbench.hs +173/−0
- CLasH/VHDL/VHDLTools.hs +704/−0
- CLasH/VHDL/VHDLTypes.hs +24/−0
- Data/Param/Index.hs +104/−0
- Data/Param/Integer.hs +13/−0
- Data/Param/Signed.hs +172/−0
- Data/Param/Unsigned.hs +157/−0
- Data/Param/Vector.hs +316/−0
- LICENSE +25/−0
- Setup.hs +2/−0
- clash.cabal +55/−0
+ CLasH/HardwareTypes.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE TemplateHaskell, DeriveDataTypeable, FlexibleContexts, TypeFamilies, TypeOperators #-}++module CLasH.HardwareTypes+ ( module Types+ , module Data.Param.Vector+ , module Data.Param.Index+ , module Data.Param.Signed+ , module Data.Param.Unsigned+ , module Prelude+ , Bit(..)+ , State(..)+ , resizeInt+ , resizeWord+ , hwand+ , hwor+ , hwxor+ , hwnot+ , RAM+ , MemState+ , blockRAM+ ) where++import qualified Prelude as P+import Prelude hiding (+ null, length, head, tail, last, init, take, drop, (++), map, foldl, foldr,+ zipWith, zip, unzip, concat, reverse, iterate )+import Types+import Data.Param.Vector+import Data.Param.Index+import qualified Data.Param.Signed as Signed+import Data.Param.Signed hiding (resize)+import qualified Data.Param.Unsigned as Unsigned+import Data.Param.Unsigned hiding (resize) ++import Language.Haskell.TH.Lift+import Data.Typeable++newtype State s = State s deriving (P.Show)++resizeInt :: (NaturalT nT, NaturalT nT') => Signed nT -> Signed nT'+resizeInt = Signed.resize++resizeWord :: (NaturalT nT, NaturalT nT') => Unsigned nT -> Unsigned nT'+resizeWord = Unsigned.resize++-- The plain Bit type+data Bit = High | Low+ deriving (P.Show, P.Eq, P.Read, Typeable)++deriveLift ''Bit++hwand :: Bit -> Bit -> Bit+hwor :: Bit -> Bit -> Bit+hwxor :: Bit -> Bit -> Bit+hwnot :: Bit -> Bit++High `hwand` High = High+_ `hwand` _ = Low++High `hwor` _ = High+_ `hwor` High = High+Low `hwor` Low = Low++High `hwxor` Low = High+Low `hwxor` High = High+_ `hwxor` _ = Low++hwnot High = Low+hwnot Low = High++type RAM s a = Vector (s :+: D1) a++type MemState s a = State (RAM s a)++blockRAM :: + (NaturalT s+ ,PositiveT (s :+: D1)+ ,((s :+: D1) :>: s) ~ True ) =>+ (MemState s a) -> + a ->+ Index s ->+ Index s ->+ Bool -> + ((MemState s a), a )+blockRAM (State mem) data_in rdaddr wraddr wrenable = + ((State mem'), data_out)+ where+ data_out = mem!rdaddr+ -- Only write data_in to memory if write is enabled+ mem' = if wrenable then+ replace mem wraddr data_in+ else+ mem
+ CLasH/Normalize.hs view
@@ -0,0 +1,1043 @@+--+-- Functions to bring a Core expression in normal form. This module provides a+-- top level function "normalize", and defines the actual transformation passes that+-- are performed.+--+module CLasH.Normalize (getNormalized, normalizeExpr, splitNormalized) where++-- Standard modules+import Debug.Trace+import qualified Maybe+import qualified List+import qualified Control.Monad.Trans.Class as Trans+import qualified Control.Monad as Monad+import qualified Control.Monad.Trans.Writer as Writer+import qualified Data.Accessor.Monad.Trans.State as MonadState+import qualified Data.Monoid as Monoid+import qualified Data.Map as Map++-- GHC API+import CoreSyn+import qualified CoreUtils+import qualified BasicTypes+import qualified Type+import qualified TysWiredIn+import qualified Id+import qualified Var+import qualified Name+import qualified DataCon+import qualified VarSet+import qualified CoreFVs+import qualified Class+import qualified MkCore+import Outputable ( showSDoc, ppr, nest )++-- Local imports+import CLasH.Normalize.NormalizeTypes+import CLasH.Translator.TranslatorTypes+import CLasH.Normalize.NormalizeTools+import CLasH.VHDL.Constants (builtinIds)+import qualified CLasH.Utils as Utils+import CLasH.Utils.Core.CoreTools+import CLasH.Utils.Core.BinderTools+import CLasH.Utils.Pretty++----------------------------------------------------------------+-- Cleanup transformations+----------------------------------------------------------------++--------------------------------+-- β-reduction+--------------------------------+beta :: Transform+-- Substitute arg for x in expr. For value lambda's, also clone before+-- substitution.+beta c (App (Lam x expr) arg) | CoreSyn.isTyVar x = setChanged >> substitute x arg c expr+ | otherwise = setChanged >> substitute_clone x arg c expr+-- Leave all other expressions unchanged+beta c expr = return expr++--------------------------------+-- Unused let binding removal+--------------------------------+letremoveunused :: Transform+letremoveunused c expr@(Let (NonRec b bound) res) = do+ let used = expr_uses_binders [b] res+ if used+ then return expr+ else change res+letremoveunused c expr@(Let (Rec binds) res) = do+ -- Filter out all unused binds.+ let binds' = filter dobind binds+ -- Only set the changed flag if binds got removed+ changeif (length binds' /= length binds) (Let (Rec binds') res)+ where+ bound_exprs = map snd binds+ -- For each bind check if the bind is used by res or any of the bound+ -- expressions+ dobind (bndr, _) = any (expr_uses_binders [bndr]) (res:bound_exprs)+-- Leave all other expressions unchanged+letremoveunused c expr = return expr++--------------------------------+-- empty let removal+--------------------------------+-- Remove empty (recursive) lets+letremove :: Transform+letremove c (Let (Rec []) res) = change res+-- Leave all other expressions unchanged+letremove c expr = return expr++--------------------------------+-- Simple let binding removal+--------------------------------+-- Remove a = b bindings from let expressions everywhere+letremovesimple :: Transform+letremovesimple = inlinebind (\(b, e) -> Trans.lift $ is_local_var e)++--------------------------------+-- Cast propagation+--------------------------------+-- Try to move casts as much downward as possible.+castprop :: Transform+castprop c (Cast (Let binds expr) ty) = change $ Let binds (Cast expr ty)+castprop c expr@(Cast (Case scrut b _ alts) ty) = change (Case scrut b ty alts')+ where+ alts' = map (\(con, bndrs, expr) -> (con, bndrs, (Cast expr ty))) alts+-- Leave all other expressions unchanged+castprop c expr = return expr++--------------------------------+-- Cast simplification. Mostly useful for state packing and unpacking, but+-- perhaps for others as well.+--------------------------------+castsimpl :: Transform+castsimpl c expr@(Cast val ty) = do+ -- Don't extract values that are already simpl+ local_var <- Trans.lift $ is_local_var val+ -- Don't extract values that are not representable, to prevent loops with+ -- inlinenonrep+ repr <- isRepr val+ if (not local_var) && repr+ then do+ -- Generate a binder for the expression+ id <- Trans.lift $ mkBinderFor val "castval"+ -- Extract the expression+ change $ Let (NonRec id val) (Cast (Var id) ty)+ else+ return expr+-- Leave all other expressions unchanged+castsimpl c expr = return expr++--------------------------------+-- Top level function inlining+--------------------------------+-- This transformation inlines simple top level bindings. Simple+-- currently means that the body is only a single application (though+-- the complexity of the arguments is not currently checked) or that the+-- normalized form only contains a single binding. This should catch most of the+-- cases where a top level function is created that simply calls a type class+-- method with a type and dictionary argument, e.g.+-- fromInteger = GHC.Num.fromInteger (SizedWord D8) $dNum+-- which is later called using simply+-- fromInteger (smallInteger 10)+--+-- These useless wrappers are created by GHC automatically. If we don't+-- inline them, we get loads of useless components cluttering the+-- generated VHDL.+--+-- Note that the inlining could also inline simple functions defined by+-- the user, not just GHC generated functions. It turns out to be near+-- impossible to reliably determine what functions are generated and+-- what functions are user-defined. Instead of guessing (which will+-- inline less than we want) we will just inline all simple functions.+--+-- Only functions that are actually completely applied and bound by a+-- variable in a let expression are inlined. These are the expressions+-- that will eventually generate instantiations of trivial components.+-- By not inlining any other reference, we also prevent looping problems+-- with funextract and inlinedict.+inlinetoplevel :: Transform+inlinetoplevel (LetBinding:_) expr | not (is_fun expr) =+ case collectArgs expr of+ (Var f, args) -> do+ body_maybe <- needsInline f+ case body_maybe of+ Just body -> do+ -- Regenerate all uniques in the to-be-inlined expression+ body_uniqued <- Trans.lift $ genUniques body+ -- And replace the variable reference with the unique'd body.+ change (mkApps body_uniqued args)+ -- No need to inline+ Nothing -> return expr+ -- This is not an application of a binder, leave it unchanged.+ _ -> return expr++-- Leave all other expressions unchanged+inlinetoplevel c expr = return expr++-- | Does the given binder need to be inlined? If so, return the body to+-- be used for inlining.+needsInline :: CoreBndr -> TransformMonad (Maybe CoreExpr)+needsInline f = do+ body_maybe <- Trans.lift $ getGlobalBind f+ case body_maybe of+ -- No body available?+ Nothing -> return Nothing+ Just body -> case CoreSyn.collectArgs body of+ -- The body is some (top level) binder applied to 0 or more+ -- arguments. That should be simple enough to inline.+ (Var f, args) -> return $ Just body+ -- Body is more complicated, try normalizing it+ _ -> do+ norm_maybe <- Trans.lift $ getNormalized_maybe False f+ case norm_maybe of+ -- Noth normalizeable+ Nothing -> return Nothing + Just norm -> case splitNormalizedNonRep norm of+ -- The function has just a single binding, so that's simple+ -- enough to inline.+ (args, [bind], Var res) -> return $ Just norm+ -- More complicated function, don't inline+ _ -> return Nothing+++----------------------------------------------------------------+-- Program structure transformations+----------------------------------------------------------------++--------------------------------+-- η expansion+--------------------------------+-- Make sure all parameters to the normalized functions are named by top+-- level lambda expressions. For this we apply η expansion to the+-- function body (possibly enclosed in some lambda abstractions) while+-- it has a function type. Eventually this will result in a function+-- body consisting of a bunch of nested lambdas containing a+-- non-function value (e.g., a complete application).+eta :: Transform+eta c expr | is_fun expr && not (is_lam expr) && all (== LambdaBody) c = do+ let arg_ty = (fst . Type.splitFunTy . CoreUtils.exprType) expr+ id <- Trans.lift $ mkInternalVar "param" arg_ty+ change (Lam id (App expr (Var id)))+-- Leave all other expressions unchanged+eta c e = return e++--------------------------------+-- Application propagation+--------------------------------+-- Move applications into let and case expressions.+appprop :: Transform+-- Propagate the application into the let+appprop c (App (Let binds expr) arg) = change $ Let binds (App expr arg)+-- Propagate the application into each of the alternatives+appprop c (App (Case scrut b ty alts) arg) = change $ Case scrut b ty' alts'+ where + alts' = map (\(con, bndrs, expr) -> (con, bndrs, (App expr arg))) alts+ ty' = CoreUtils.applyTypeToArg ty arg+-- Leave all other expressions unchanged+appprop c expr = return expr++--------------------------------+-- Let recursification+--------------------------------+-- Make all lets recursive, so other transformations don't need to+-- handle non-recursive lets+letrec :: Transform+letrec c expr@(Let (NonRec bndr val) res) = + change $ Let (Rec [(bndr, val)]) res++-- Leave all other expressions unchanged+letrec c expr = return expr++--------------------------------+-- let flattening+--------------------------------+-- Takes a let that binds another let, and turns that into two nested lets.+-- e.g., from:+-- let b = (let b' = expr' in res') in res+-- to:+-- let b' = expr' in (let b = res' in res)+letflat :: Transform+-- Turn a nonrec let that binds a let into two nested lets.+letflat c (Let (NonRec b (Let binds res')) res) = + change $ Let binds (Let (NonRec b res') res)+letflat c (Let (Rec binds) expr) = do+ -- Flatten each binding.+ binds' <- Utils.concatM $ Monad.mapM flatbind binds+ -- Return the new let. We don't use change here, since possibly nothing has+ -- changed. If anything has changed, flatbind has already flagged that+ -- change.+ return $ Let (Rec binds') expr+ where+ -- Turns a binding of a let into a multiple bindings, or any other binding+ -- into a list with just that binding+ flatbind :: (CoreBndr, CoreExpr) -> TransformMonad [(CoreBndr, CoreExpr)]+ flatbind (b, Let (Rec binds) expr) = change ((b, expr):binds)+ flatbind (b, Let (NonRec b' expr') expr) = change [(b, expr), (b', expr')]+ flatbind (b, expr) = return [(b, expr)]+-- Leave all other expressions unchanged+letflat c expr = return expr++--------------------------------+-- Return value simplification+--------------------------------+-- Ensure the return value of a function follows proper normal form. eta+-- expansion ensures the body starts with lambda abstractions, this+-- transformation ensures that the lambda abstractions always contain a+-- recursive let and that, when the return value is representable, the+-- let contains a local variable reference in its body.++-- Extract the return value from the body of the top level lambdas (of+-- which ther could be zero), unless it is a let expression (in which+-- case the next clause applies).+retvalsimpl c expr | all (== LambdaBody) c && not (is_lam expr) && not (is_let expr) = do+ local_var <- Trans.lift $ is_local_var expr+ repr <- isRepr expr+ if not local_var && repr+ then do+ id <- Trans.lift $ mkBinderFor expr "res" + change $ Let (Rec [(id, expr)]) (Var id)+ else+ return expr+-- Extract the return value from the body of a let expression, which is+-- itself the body of the top level lambdas (of which there could be+-- zero).+retvalsimpl c expr@(Let (Rec binds) body) | all (== LambdaBody) c = do+ -- Don't extract values that are already a local variable, to prevent+ -- loops with ourselves.+ local_var <- Trans.lift $ is_local_var body+ -- Don't extract values that are not representable, to prevent loops with+ -- inlinenonrep+ repr <- isRepr body+ if not local_var && repr+ then do+ id <- Trans.lift $ mkBinderFor body "res" + change $ Let (Rec ((id, body):binds)) (Var id)+ else+ return expr+-- Leave all other expressions unchanged+retvalsimpl c expr = return expr++--------------------------------+-- Representable arguments simplification+--------------------------------+-- Make sure that all arguments of a representable type are simple variables.+appsimpl :: Transform+-- Simplify all representable arguments. Do this by introducing a new Let+-- that binds the argument and passing the new binder in the application.+appsimpl c expr@(App f arg) = do+ -- Check runtime representability+ repr <- isRepr arg+ local_var <- Trans.lift $ is_local_var arg+ if repr && not local_var+ then do -- Extract representable arguments+ id <- Trans.lift $ mkBinderFor arg "arg"+ change $ Let (NonRec id arg) (App f (Var id))+ else -- Leave non-representable arguments unchanged+ return expr+-- Leave all other expressions unchanged+appsimpl c expr = return expr++----------------------------------------------------------------+-- Built-in function transformations+----------------------------------------------------------------++--------------------------------+-- Function-typed argument extraction+--------------------------------+-- This transform takes any function-typed argument that cannot be propagated+-- (because the function that is applied to it is a builtin function), and+-- puts it in a brand new top level binder. This allows us to for example+-- apply map to a lambda expression This will not conflict with inlinenonrep,+-- since that only inlines local let bindings, not top level bindings.+funextract :: Transform+funextract c expr@(App _ _) | is_var fexpr = do+ body_maybe <- Trans.lift $ getGlobalBind f+ case body_maybe of+ -- We don't have a function body for f, so we can perform this transform.+ Nothing -> do+ -- Find the new arguments+ args' <- mapM doarg args+ -- And update the arguments. We use return instead of changed, so the+ -- changed flag doesn't get set if none of the args got changed.+ return $ MkCore.mkCoreApps fexpr args'+ -- We have a function body for f, leave this application to funprop+ Just _ -> return expr+ where+ -- Find the function called and the arguments+ (fexpr, args) = collectArgs expr+ Var f = fexpr+ -- Change any arguments that have a function type, but are not simple yet+ -- (ie, a variable or application). This means to create a new function+ -- for map (\f -> ...) b, but not for map (foo a) b.+ --+ -- We could use is_applicable here instead of is_fun, but I think+ -- arguments to functions could only have forall typing when existential+ -- typing is enabled. Not sure, though.+ doarg arg | not (is_simple arg) && is_fun arg = do+ -- Create a new top level binding that binds the argument. Its body will+ -- be extended with lambda expressions, to take any free variables used+ -- by the argument expression.+ let free_vars = VarSet.varSetElems $ CoreFVs.exprFreeVars arg+ let body = MkCore.mkCoreLams free_vars arg+ id <- Trans.lift $ mkBinderFor body "fun"+ Trans.lift $ addGlobalBind id body+ -- Replace the argument with a reference to the new function, applied to+ -- all vars it uses.+ change $ MkCore.mkCoreApps (Var id) (map Var free_vars)+ -- Leave all other arguments untouched+ doarg arg = return arg++-- Leave all other expressions unchanged+funextract c expr = return expr+++++----------------------------------------------------------------+-- Case normalization transformations+----------------------------------------------------------------++--------------------------------+-- Scrutinee simplification+--------------------------------+-- Make sure the scrutinee of a case expression is a local variable+-- reference.+scrutsimpl :: Transform+-- Don't touch scrutinees that are already simple+scrutsimpl c expr@(Case (Var _) _ _ _) = return expr+-- Replace all other cases with a let that binds the scrutinee and a new+-- simple scrutinee, but only when the scrutinee is representable (to prevent+-- loops with inlinenonrep, though I don't think a non-representable scrutinee+-- will be supported anyway...) +scrutsimpl c expr@(Case scrut b ty alts) = do+ repr <- isRepr scrut+ if repr+ then do+ id <- Trans.lift $ mkBinderFor scrut "scrut"+ change $ Let (NonRec id scrut) (Case (Var id) b ty alts)+ else+ return expr+-- Leave all other expressions unchanged+scrutsimpl c expr = return expr++--------------------------------+-- Scrutinee binder removal+--------------------------------+-- A case expression can have an extra binder, to which the scrutinee is bound+-- after bringing it to WHNF. This is used for forcing evaluation of strict+-- arguments. Since strictness does not matter for us (rather, everything is+-- sort of strict), this binder is ignored when generating VHDL, and must thus+-- be wild in the normal form.+scrutbndrremove :: Transform+-- If the scrutinee is already simple, and the bndr is not wild yet, replace+-- all occurences of the binder with the scrutinee variable.+scrutbndrremove c (Case (Var scrut) bndr ty alts) | bndr_used = do+ alts' <- mapM subs_bndr alts+ change $ Case (Var scrut) wild ty alts'+ where+ is_used (_, _, expr) = expr_uses_binders [bndr] expr+ bndr_used = or $ map is_used alts+ subs_bndr (con, bndrs, expr) = do+ expr' <- substitute bndr (Var scrut) c expr+ return (con, bndrs, expr')+ wild = MkCore.mkWildBinder (Id.idType bndr)+-- Leave all other expressions unchanged+scrutbndrremove c expr = return expr++--------------------------------+-- Case normalization+--------------------------------+-- Turn a case expression with any number of alternatives with any+-- number of non-wild binders into as set of case and let expressions,+-- all of which are in normal form (e.g., a bunch of extractor case+-- expressions to extract all fields from the scrutinee, a number of let+-- bindings to bind each alternative and a single selector case to+-- select the right value.+casesimpl :: Transform+-- This is already a selector case (or, if x does not appear in bndrs, a very+-- simple case statement that will be removed by caseremove below). Just leave+-- it be.+casesimpl c expr@(Case scrut b ty [(con, bndrs, Var x)]) = return expr+-- Make sure that all case alternatives have only wild binders and simple+-- expressions.+-- This is done by creating a new let binding for each non-wild binder, which+-- is bound to a new simple selector case statement and for each complex+-- expression. We do this only for representable types, to prevent loops with+-- inlinenonrep.+casesimpl c expr@(Case scrut bndr ty alts) | not bndr_used = do+ (bindingss, alts') <- (Monad.liftM unzip) $ mapM doalt alts+ let bindings = concat bindingss+ -- Replace the case with a let with bindings and a case+ let newlet = mkNonRecLets bindings (Case scrut bndr ty alts')+ -- If there are no non-wild binders, or this case is already a simple+ -- selector (i.e., a single alt with exactly one binding), already a simple+ -- selector altan no bindings (i.e., no wild binders in the original case),+ -- don't change anything, otherwise, replace the case.+ if null bindings then return expr else change newlet + where+ -- Check if the scrutinee binder is used+ is_used (_, _, expr) = expr_uses_binders [bndr] expr+ bndr_used = or $ map is_used alts+ -- Generate a single wild binder, since they are all the same+ wild = MkCore.mkWildBinder+ -- Wilden the binders of one alt, producing a list of bindings as a+ -- sideeffect.+ doalt :: CoreAlt -> TransformMonad ([(CoreBndr, CoreExpr)], CoreAlt)+ doalt (con, bndrs, expr) = do+ -- Make each binder wild, if possible+ bndrs_res <- Monad.zipWithM dobndr bndrs [0..]+ let (newbndrs, bindings_maybe) = unzip bndrs_res+ -- Extract a complex expression, if possible. For this we check if any of+ -- the new list of bndrs are used by expr. We can't use free_vars here,+ -- since that looks at the old bndrs.+ let uses_bndrs = not $ VarSet.isEmptyVarSet $ CoreFVs.exprSomeFreeVars (`elem` newbndrs) expr+ (exprbinding_maybe, expr') <- doexpr expr uses_bndrs+ -- Create a new alternative+ let newalt = (con, newbndrs, expr')+ let bindings = Maybe.catMaybes (bindings_maybe ++ [exprbinding_maybe])+ return (bindings, newalt)+ where+ -- Make wild alternatives for each binder+ wildbndrs = map (\bndr -> MkCore.mkWildBinder (Id.idType bndr)) bndrs+ -- A set of all the binders that are used by the expression+ free_vars = CoreFVs.exprSomeFreeVars (`elem` bndrs) expr+ -- Look at the ith binder in the case alternative. Return a new binder+ -- for it (either the same one, or a wild one) and optionally a let+ -- binding containing a case expression.+ dobndr :: CoreBndr -> Int -> TransformMonad (CoreBndr, Maybe (CoreBndr, CoreExpr))+ dobndr b i = do+ repr <- isRepr b+ -- Is b wild (e.g., not a free var of expr. Since b is only in scope+ -- in expr, this means that b is unused if expr does not use it.)+ let wild = not (VarSet.elemVarSet b free_vars)+ -- Create a new binding for any representable binder that is not+ -- already wild and is representable (to prevent loops with+ -- inlinenonrep).+ if (not wild) && repr+ then do+ caseexpr <- Trans.lift $ mkSelCase scrut i+ -- Create a new binder that will actually capture a value in this+ -- case statement, and return it.+ return (wildbndrs!!i, Just (b, caseexpr))+ else + -- Just leave the original binder in place, and don't generate an+ -- extra selector case.+ return (b, Nothing)+ -- Process the expression of a case alternative. Accepts an expression+ -- and whether this expression uses any of the binders in the+ -- alternative. Returns an optional new binding and a new expression.+ doexpr :: CoreExpr -> Bool -> TransformMonad (Maybe (CoreBndr, CoreExpr), CoreExpr)+ doexpr expr uses_bndrs = do+ local_var <- Trans.lift $ is_local_var expr+ repr <- isRepr expr+ -- Extract any expressions that do not use any binders from this+ -- alternative, is not a local var already and is representable (to+ -- prevent loops with inlinenonrep).+ if (not uses_bndrs) && (not local_var) && repr+ then do+ id <- Trans.lift $ mkBinderFor expr "caseval"+ -- We don't flag a change here, since casevalsimpl will do that above+ -- based on Just we return here.+ return (Just (id, expr), Var id)+ else+ -- Don't simplify anything else+ return (Nothing, expr)+-- Leave all other expressions unchanged+casesimpl c expr = return expr++--------------------------------+-- Case removal+--------------------------------+-- Remove case statements that have only a single alternative and only wild+-- binders.+caseremove :: Transform+-- Replace a useless case by the value of its single alternative+caseremove c (Case scrut b ty [(con, bndrs, expr)]) | not usesvars = change expr+ -- Find if any of the binders are used by expr+ where usesvars = (not . VarSet.isEmptyVarSet . (CoreFVs.exprSomeFreeVars (`elem` b:bndrs))) expr+-- Leave all other expressions unchanged+caseremove c expr = return expr++--------------------------------+-- Case of known constructor simplification+--------------------------------+-- If a case expressions scrutinizes a datacon application, we can+-- determine which alternative to use and remove the case alltogether.+-- We replace it with a let expression the binds every binder in the+-- alternative bound to the corresponding argument of the datacon. We do+-- this instead of substituting the binders, to prevent duplication of+-- work and preserve sharing wherever appropriate.+knowncase :: Transform+knowncase context expr@(Case scrut@(App _ _) bndr ty alts) | not bndr_used = do+ case collectArgs scrut of+ (Var f, args) -> case Id.isDataConId_maybe f of+ -- Not a dataconstructor? Don't change anything (probably a+ -- function, then)+ Nothing -> return expr+ Just dc -> do+ let (altcon, bndrs, res) = case List.find (\(altcon, bndrs, res) -> altcon == (DataAlt dc)) alts of+ Just alt -> alt -- Return the alternative found+ Nothing -> head alts -- If the datacon is not present, the first must be the default alternative+ -- Double check if we have either the correct alternative, or+ -- the default.+ if altcon /= (DataAlt dc) && altcon /= DEFAULT then error ("Normalize.knowncase: Invalid core, datacon not found in alternatives and DEFAULT alternative is not first? " ++ pprString expr) else return ()+ -- Find out how many arguments to drop (type variables and+ -- predicates like dictionaries).+ let (tvs, preds, _, _) = DataCon.dataConSig dc+ let count = length tvs + length preds+ -- Create a let expression that binds each of the binders in+ -- this alternative to the corresponding argument of the data+ -- constructor.+ let binds = zip bndrs (drop count args)+ change $ Let (Rec binds) res+ _ -> return expr -- Scrutinee is not an application of a var+ where+ is_used (_, _, expr) = expr_uses_binders [bndr] expr+ bndr_used = or $ map is_used alts++-- Leave all other expressions unchanged+knowncase c expr = return expr+++++----------------------------------------------------------------+-- Unrepresentable value removal transformations+----------------------------------------------------------------++--------------------------------+-- Non-representable binding inlining+--------------------------------+-- Remove a = B bindings, with B of a non-representable type, from let+-- expressions everywhere. This means that any value that we can't generate a+-- signal for, will be inlined and hopefully turned into something we can+-- represent.+--+-- This is a tricky function, which is prone to create loops in the+-- transformations. To fix this, we make sure that no transformation will+-- create a new let binding with a non-representable type. These other+-- transformations will just not work on those function-typed values at first,+-- but the other transformations (in particular β-reduction) should make sure+-- that the type of those values eventually becomes representable.+inlinenonrep :: Transform+inlinenonrep = inlinebind ((Monad.liftM not) . isRepr . snd)++--------------------------------+-- Function specialization+--------------------------------+-- Remove all applications to non-representable arguments, by duplicating the+-- function called with the non-representable parameter replaced by the free+-- variables of the argument passed in.+argprop :: Transform+-- Transform any application of a named function (i.e., skip applications of+-- lambda's). Also skip applications that have arguments with free type+-- variables, since we can't inline those.+argprop c expr@(App _ _) | is_var fexpr = do+ -- Find the body of the function called+ body_maybe <- Trans.lift $ getGlobalBind f+ case body_maybe of+ Just body -> do+ -- Process each of the arguments in turn+ (args', changed) <- Writer.listen $ mapM doarg args+ -- See if any of the arguments changed+ case Monoid.getAny changed of+ True -> do+ let (newargs', newparams', oldargs) = unzip3 args'+ let newargs = concat newargs'+ let newparams = concat newparams'+ -- Create a new body that consists of a lambda for all new arguments and+ -- the old body applied to some arguments.+ let newbody = MkCore.mkCoreLams newparams (MkCore.mkCoreApps body oldargs)+ -- Create a new function with the same name but a new body+ newf <- Trans.lift $ mkFunction f newbody++ Trans.lift $ MonadState.modify tsInitStates (\ismap ->+ let init_state_maybe = Map.lookup f ismap in+ case init_state_maybe of+ Nothing -> ismap+ Just init_state -> Map.insert newf init_state ismap)+ -- Replace the original application with one of the new function to the+ -- new arguments.+ change $ MkCore.mkCoreApps (Var newf) newargs+ False ->+ -- Don't change the expression if none of the arguments changed+ return expr+ + -- If we don't have a body for the function called, leave it unchanged (it+ -- should be a primitive function then).+ Nothing -> return expr+ where+ -- Find the function called and the arguments+ (fexpr, args) = collectArgs expr+ Var f = fexpr++ -- Process a single argument and return (args, bndrs, arg), where args are+ -- the arguments to replace the given argument in the original+ -- application, bndrs are the binders to include in the top-level lambda+ -- in the new function body, and arg is the argument to apply to the old+ -- function body.+ doarg :: CoreExpr -> TransformMonad ([CoreExpr], [CoreBndr], CoreExpr)+ doarg arg = do+ repr <- isRepr arg+ bndrs <- Trans.lift getGlobalBinders+ let interesting var = Var.isLocalVar var && (var `notElem` bndrs)+ if not repr && not (is_var arg && interesting (exprToVar arg)) && not (has_free_tyvars arg) + then do+ -- Propagate all complex arguments that are not representable, but not+ -- arguments with free type variables (since those would require types+ -- not known yet, which will always be known eventually).+ -- Find interesting free variables, each of which should be passed to+ -- the new function instead of the original function argument.+ -- + -- Interesting vars are those that are local, but not available from the+ -- top level scope (functions from this module are defined as local, but+ -- they're not local to this function, so we can freely move references+ -- to them into another function).+ let free_vars = VarSet.varSetElems $ CoreFVs.exprSomeFreeVars interesting arg+ -- Mark the current expression as changed+ setChanged+ -- TODO: Clone the free_vars (and update references in arg), since+ -- this might cause conflicts if two arguments that are propagated+ -- share a free variable. Also, we are now introducing new variables+ -- into a function that are not fresh, which violates the binder+ -- uniqueness invariant.+ return (map Var free_vars, free_vars, arg)+ else do+ -- Representable types will not be propagated, and arguments with free+ -- type variables will be propagated later.+ -- Note that we implicitly remove any type variables in the type of+ -- the original argument by using the type of the actual argument+ -- for the new formal parameter.+ -- TODO: preserve original naming?+ id <- Trans.lift $ mkBinderFor arg "param"+ -- Just pass the original argument to the new function, which binds it+ -- to a new id and just pass that new id to the old function body.+ return ([arg], [id], mkReferenceTo id) +-- Leave all other expressions unchanged+argprop c expr = return expr++--------------------------------+-- Non-representable result inlining+--------------------------------+-- This transformation takes a function (top level binding) that has a+-- non-representable result (e.g., a tuple containing a function, or an+-- Integer. The latter can occur in some cases as the result of the+-- fromIntegerT function) and inlines enough of the function to make the+-- result representable again.+--+-- This is done by first normalizing the function and then "inlining"+-- the result. Since no unrepresentable let bindings are allowed in+-- normal form, we can be sure that all free variables of the result+-- expression will be representable (Note that we probably can't+-- guarantee that all representable parts of the expression will be free+-- variables, so we might inline more than strictly needed).+--+-- The new function result will be a tuple containing all free variables+-- of the old result, so the old result can be rebuild at the caller.+--+-- We take care not to inline dictionary id's, which are top level+-- bindings with a non-representable result type as well, since those+-- will never become VHDL signals directly. There is a separate+-- transformation (inlinedict) that specifically inlines dictionaries+-- only when it is useful.+inlinenonrepresult :: Transform++-- Apply to any (application of) a reference to a top level function+-- that is fully applied (i.e., dos not have a function type) but is not+-- representable. We apply in any context, since non-representable+-- expressions are generally left alone and can occur anywhere.+inlinenonrepresult context expr | not (is_fun expr) =+ case collectArgs expr of+ (Var f, args) | not (Id.isDictId f) -> do+ repr <- isRepr expr+ if not repr+ then do+ body_maybe <- Trans.lift $ getNormalized_maybe True f+ case body_maybe of+ Just body -> do+ let (bndrs, binds, res) = splitNormalizedNonRep body+ if has_free_tyvars res + then+ -- Don't touch anything with free type variables, since+ -- we can't return those. We'll wait until argprop+ -- removed those variables.+ return expr+ else do+ -- Get the free local variables of res+ global_bndrs <- Trans.lift getGlobalBinders+ let interesting var = Var.isLocalVar var && (var `notElem` global_bndrs)+ let free_vars = VarSet.varSetElems $ CoreFVs.exprSomeFreeVars interesting res+ let free_var_types = map Id.idType free_vars+ let n_free_vars = length free_vars+ -- Get a tuple datacon to wrap around the free variables+ let fvs_datacon = TysWiredIn.tupleCon BasicTypes.Boxed n_free_vars+ let fvs_datacon_id = DataCon.dataConWorkId fvs_datacon+ -- Let the function now return a tuple with references to+ -- all free variables of the old return value. First pass+ -- all the types of the variables, since tuple+ -- constructors are polymorphic.+ let newres = mkApps (Var fvs_datacon_id) (map Type free_var_types ++ map Var free_vars)+ -- Recreate the function body with the changed return value+ let newbody = mkLams bndrs (Let (Rec binds) newres) + -- Create the new function+ f' <- Trans.lift $ mkFunction f newbody++ -- Call the new function+ let newapp = mkApps (Var f') args+ res_bndr <- Trans.lift $ mkBinderFor newapp "res"+ -- Create extractor case expressions to extract each of the+ -- free variables from the tuple.+ sel_cases <- Trans.lift $ mapM (mkSelCase (Var res_bndr)) [0..n_free_vars-1]++ -- Bind the res_bndr to the result of the new application+ -- and each of the free variables to the corresponding+ -- selector case. Replace the let body with the original+ -- body of the called function (which can still access all+ -- of its free variables, from the let).+ let binds = (res_bndr, newapp):(zip free_vars sel_cases)+ let letexpr = Let (Rec binds) res++ -- Finally, regenarate all uniques in the new expression,+ -- since the free variables could otherwise become+ -- duplicated. It is not strictly necessary to regenerate+ -- res, since we're moving that expression, but it won't+ -- hurt.+ letexpr_uniqued <- Trans.lift $ genUniques letexpr+ change letexpr_uniqued+ Nothing -> return expr+ else+ -- Don't touch representable expressions or (applications of)+ -- dictionary ids.+ return expr+ -- Not a reference to or application of a top level function+ _ -> return expr+-- Leave all other expressions unchanged+inlinenonrepresult c expr = return expr++--------------------------------+-- ClassOp resolution+--------------------------------+-- Resolves any class operation to the actual operation whenever+-- possible. Class methods (as well as parent dictionary selectors) are+-- special "functions" that take a type and a dictionary and evaluate to+-- the corresponding method. A dictionary is nothing more than a+-- special dataconstructor applied to the type the dictionary is for,+-- each of the superclasses and all of the class method definitions for+-- that particular type. Since dictionaries all always inlined (top+-- levels dictionaries are inlined by inlinedict, local dictionaries are+-- inlined by inlinenonrep), we will eventually have something like:+--+-- baz+-- @ CLasH.HardwareTypes.Bit+-- (D:Baz @ CLasH.HardwareTypes.Bit bitbaz)+--+-- Here, baz is the method selector for the baz method, while+-- D:Baz is the dictionary constructor for the Baz and bitbaz is the baz+-- method defined in the Baz Bit instance declaration.+--+-- To resolve this, we can look at the ClassOp IdInfo from the baz Id,+-- which contains the Class it is defined for. From the Class, we can+-- get a list of all selectors (both parent class selectors as well as+-- method selectors). Since the arguments to D:Baz (after the type+-- argument) correspond exactly to this list, we then look up baz in+-- that list and replace the entire expression by the corresponding +-- argument to D:Baz.+--+-- We don't resolve methods that have a builtin translation (such as+-- ==), since the actual implementation is not always (easily)+-- translateable. For example, when deriving ==, GHC generates code+-- using $con2tag functions to translate a datacon to an int and compare+-- that with GHC.Prim.==# . Better to avoid that for now.+classopresolution :: Transform+classopresolution c expr@(App (App (Var sel) ty) dict) | not is_builtin =+ case Id.isClassOpId_maybe sel of+ -- Not a class op selector+ Nothing -> return expr+ Just cls -> case collectArgs dict of+ (_, []) -> return expr -- Dict is not an application (e.g., not inlined yet)+ (Var dictdc, (ty':selectors)) | not (Maybe.isJust (Id.isDataConId_maybe dictdc)) -> return expr -- Dictionary is not a datacon yet (but e.g., a top level binder)+ | tyargs_neq ty ty' -> error $ "Normalize.classopresolution: Applying class selector to dictionary without matching type?\n" ++ pprString expr+ | otherwise ->+ let selector_ids = Class.classSelIds cls in+ -- Find the selector used in the class' list of selectors+ case List.elemIndex sel selector_ids of+ Nothing -> error $ "Normalize.classopresolution: Selector not found in class' selector list? This should not happen!\nExpression: " ++ pprString expr ++ "\nClass: " ++ show cls ++ "\nSelectors: " ++ show selector_ids+ -- Get the corresponding argument from the dictionary+ Just n -> change (selectors!!n)+ (_, _) -> return expr -- Not applying a variable? Don't touch+ where+ -- Compare two type arguments, returning True if they are _not_+ -- equal+ tyargs_neq (Type ty1) (Type ty2) = not $ Type.coreEqType ty1 ty2+ tyargs_neq _ _ = True+ -- Is this a builtin function / method?+ is_builtin = elem (Name.getOccString sel) builtinIds++-- Leave all other expressions unchanged+classopresolution c expr = return expr++--------------------------------+-- Dictionary inlining+--------------------------------+-- Inline all top level dictionaries, that are in a position where+-- classopresolution can actually resolve them. This makes this+-- transformation look similar to classoperesolution below, but we'll+-- keep them separated for clarity. By not inlining other dictionaries,+-- we prevent expression sizes exploding when huge type level integer+-- dictionaries are inlined which can never be expanded (in casts, for+-- example).+inlinedict c expr@(App (App (Var sel) ty) (Var dict)) | not is_builtin && is_classop = do+ body_maybe <- Trans.lift $ getGlobalBind dict+ case body_maybe of+ -- No body available (no source available, or a local variable /+ -- argument)+ Nothing -> return expr+ Just body -> change (App (App (Var sel) ty) body)+ where+ -- Is this a builtin function / method?+ is_builtin = elem (Name.getOccString sel) builtinIds+ -- Are we dealing with a class operation selector?+ is_classop = Maybe.isJust (Id.isClassOpId_maybe sel)++-- Leave all other expressions unchanged+inlinedict c expr = return expr+++{-+--------------------------------+-- Identical let binding merging+--------------------------------+-- Merge two bindings in a let if they are identical +-- TODO: We would very much like to use GHC's CSE module for this, but that+-- doesn't track if something changed or not, so we can't use it properly.+letmerge :: Transform+letmerge c expr@(Let _ _) = do+ let (binds, res) = flattenLets expr+ binds' <- domerge binds+ return $ mkNonRecLets binds' res+ where+ domerge :: [(CoreBndr, CoreExpr)] -> TransformMonad [(CoreBndr, CoreExpr)]+ domerge [] = return []+ domerge (e:es) = do + es' <- mapM (mergebinds e) es+ es'' <- domerge es'+ return (e:es'')++ -- Uses the second bind to simplify the second bind, if applicable.+ mergebinds :: (CoreBndr, CoreExpr) -> (CoreBndr, CoreExpr) -> TransformMonad (CoreBndr, CoreExpr)+ mergebinds (b1, e1) (b2, e2)+ -- Identical expressions? Replace the second binding with a reference to+ -- the first binder.+ | CoreUtils.cheapEqExpr e1 e2 = change $ (b2, Var b1)+ -- Different expressions? Don't change+ | otherwise = return (b2, e2)+-- Leave all other expressions unchanged+letmerge c expr = return expr+-}++--------------------------------+-- End of transformations+--------------------------------+++++-- What transforms to run?+transforms = [ ("inlinedict", inlinedict)+ , ("inlinetoplevel", inlinetoplevel)+ , ("inlinenonrepresult", inlinenonrepresult)+ , ("knowncase", knowncase)+ , ("classopresolution", classopresolution)+ , ("argprop", argprop)+ , ("funextract", funextract)+ , ("eta", eta)+ , ("beta", beta)+ , ("appprop", appprop)+ , ("castprop", castprop)+ , ("letremovesimple", letremovesimple)+ , ("letrec", letrec)+ , ("letremove", letremove)+ , ("retvalsimpl", retvalsimpl)+ , ("letflat", letflat)+ , ("scrutsimpl", scrutsimpl)+ , ("scrutbndrremove", scrutbndrremove)+ , ("casesimpl", casesimpl)+ , ("caseremove", caseremove)+ , ("inlinenonrep", inlinenonrep)+ , ("appsimpl", appsimpl)+ , ("letremoveunused", letremoveunused)+ , ("castsimpl", castsimpl)+ ]++-- | Returns the normalized version of the given function, or an error+-- if it is not a known global binder.+getNormalized ::+ Bool -- ^ Allow the result to be unrepresentable?+ -> CoreBndr -- ^ The function to get+ -> TranslatorSession CoreExpr -- The normalized function body+getNormalized result_nonrep bndr = do+ norm <- getNormalized_maybe result_nonrep bndr+ return $ Maybe.fromMaybe+ (error $ "Normalize.getNormalized: Unknown or non-representable function requested: " ++ show bndr)+ norm++-- | Returns the normalized version of the given function, or Nothing+-- when the binder is not a known global binder or is not normalizeable.+getNormalized_maybe ::+ Bool -- ^ Allow the result to be unrepresentable?+ -> CoreBndr -- ^ The function to get+ -> TranslatorSession (Maybe CoreExpr) -- The normalized function body++getNormalized_maybe result_nonrep bndr = do+ expr_maybe <- getGlobalBind bndr+ normalizeable <- isNormalizeable result_nonrep bndr+ if not normalizeable || Maybe.isNothing expr_maybe+ then+ -- Binder not normalizeable or not found+ return Nothing+ else do+ -- Binder found and is monomorphic. Normalize the expression+ -- and cache the result.+ normalized <- Utils.makeCached bndr tsNormalized $ + normalizeExpr (show bndr) (Maybe.fromJust expr_maybe)+ return (Just normalized)++-- | Normalize an expression+normalizeExpr ::+ String -- ^ What are we normalizing? For debug output only.+ -> CoreSyn.CoreExpr -- ^ The expression to normalize + -> TranslatorSession CoreSyn.CoreExpr -- ^ The normalized expression++normalizeExpr what expr = do+ startcount <- MonadState.get tsTransformCounter + expr_uniqued <- genUniques expr+ -- Do a debug print, if requested+ let expr_uniqued' = Utils.traceIf (normalize_debug >= NormDbgFinal) (what ++ " before normalization:\n\n" ++ showSDoc ( ppr expr_uniqued ) ++ "\n") expr_uniqued+ -- Normalize this expression+ expr' <- dotransforms transforms expr_uniqued'+ endcount <- MonadState.get tsTransformCounter + -- Do a debug print, if requested+ Utils.traceIf (normalize_debug >= NormDbgFinal) (what ++ " after normalization:\n\n" ++ showSDoc ( ppr expr') ++ "\nNeeded " ++ show (endcount - startcount) ++ " transformations to normalize " ++ what) $+ return expr'++-- | Split a normalized expression into the argument binders, top level+-- bindings and the result binder. This function returns an error if+-- the type of the expression is not representable.+splitNormalized ::+ CoreExpr -- ^ The normalized expression+ -> ([CoreBndr], [Binding], CoreBndr)+splitNormalized expr = + case splitNormalizedNonRep expr of+ (args, binds, Var res) -> (args, binds, res)+ _ -> error $ "Normalize.splitNormalized: Not in normal form: " ++ pprString expr ++ "\n"++-- Split a normalized expression, whose type can be unrepresentable.+splitNormalizedNonRep::+ CoreExpr -- ^ The normalized expression+ -> ([CoreBndr], [Binding], CoreExpr)+splitNormalizedNonRep expr = (args, binds, resexpr)+ where+ (args, letexpr) = CoreSyn.collectBinders expr+ (binds, resexpr) = flattenLets letexpr
+ CLasH/Normalize/NormalizeTools.hs view
@@ -0,0 +1,245 @@+-- +-- This module provides functions for program transformations.+--+module CLasH.Normalize.NormalizeTools where++-- Standard modules+import qualified Data.Monoid as Monoid+import qualified Data.Either as Either+import qualified Control.Monad as Monad+import qualified Control.Monad.Trans.Writer as Writer+import qualified Control.Monad.Trans.Class as Trans+import qualified Data.Accessor.Monad.Trans.State as MonadState++-- GHC API+import CoreSyn+import qualified Name+import qualified Id+import qualified CoreSubst+import qualified Type+import qualified CoreUtils+import Outputable ( showSDoc, ppr, nest )++-- Local imports+import CLasH.Normalize.NormalizeTypes+import CLasH.Translator.TranslatorTypes+import CLasH.VHDL.Constants (builtinIds)+import CLasH.Utils+import qualified CLasH.Utils.Core.CoreTools as CoreTools+import qualified CLasH.VHDL.VHDLTools as VHDLTools++-- Apply the given transformation to all expressions in the given expression,+-- including the expression itself.+everywhere :: Transform -> Transform+everywhere trans = applyboth (subeverywhere (everywhere trans)) trans++data NormDbgLevel = + NormDbgNone -- ^ No debugging+ | NormDbgFinal -- ^ Print functions before / after normalization+ | NormDbgApplied -- ^ Print expressions before / after applying transformations+ | NormDbgAll -- ^ Print expressions when a transformation does not apply+ deriving (Eq, Ord)+normalize_debug = NormDbgFinal++-- Applies a transform, optionally showing some debug output.+apply :: (String, Transform) -> Transform+apply (name, trans) ctx expr = do+ -- Apply the transformation and find out if it changed anything+ (expr', any_changed) <- Writer.listen $ trans ctx expr+ let changed = Monoid.getAny any_changed+ -- If it changed, increase the transformation counter + Monad.when changed $ Trans.lift (MonadState.modify tsTransformCounter (+1))+ -- Prepare some debug strings+ let before = showSDoc (nest 4 $ ppr expr) ++ "\nType: \n" ++ (showSDoc $ nest 4 $ ppr $ CoreUtils.exprType expr) ++ "\n"+ let context = "Context: " ++ show ctx ++ "\n"+ let after = showSDoc (nest 4 $ ppr expr') ++ "\nType: \n" ++ (showSDoc $ nest 4 $ ppr $ CoreUtils.exprType expr') ++ "\n"+ traceIf (normalize_debug >= NormDbgApplied && changed) ("Changes when applying transform " ++ name ++ " to:\n" ++ before ++ context ++ "Result:\n" ++ after) $ + traceIf (normalize_debug >= NormDbgAll && not changed) ("No changes when applying transform " ++ name ++ " to:\n" ++ before ++ context) $+ return expr'++-- Apply the first transformation, followed by the second transformation, and+-- keep applying both for as long as expression still changes.+applyboth :: Transform -> Transform -> Transform+applyboth first second context expr = do+ -- Apply the first+ expr' <- first context expr+ -- Apply the second+ (expr'', changed) <- Writer.listen $ second context expr'+ if Monoid.getAny $ changed+ then+ applyboth first second context expr'' + else + return expr''++-- Apply the given transformation to all direct subexpressions (only), not the+-- expression itself.+subeverywhere :: Transform -> Transform+subeverywhere trans c (App a b) = do+ a' <- trans (AppFirst:c) a+ b' <- trans (AppSecond:c) b+ return $ App a' b'++subeverywhere trans c (Let (NonRec b bexpr) expr) = do+ bexpr' <- trans (LetBinding:c) bexpr+ expr' <- trans (LetBody:c) expr+ return $ Let (NonRec b bexpr') expr'++subeverywhere trans c (Let (Rec binds) expr) = do+ expr' <- trans (LetBody:c) expr+ binds' <- mapM transbind binds+ return $ Let (Rec binds') expr'+ where+ transbind :: (CoreBndr, CoreExpr) -> TransformMonad (CoreBndr, CoreExpr)+ transbind (b, e) = do+ e' <- trans (LetBinding:c) e+ return (b, e')++subeverywhere trans c (Lam x expr) = do+ expr' <- trans (LambdaBody:c) expr+ return $ Lam x expr'++subeverywhere trans c (Case scrut b t alts) = do+ scrut' <- trans (Other:c) scrut+ alts' <- mapM transalt alts+ return $ Case scrut' b t alts'+ where+ transalt :: CoreAlt -> TransformMonad CoreAlt+ transalt (con, binders, expr) = do+ expr' <- trans (Other:c) expr+ return (con, binders, expr')++subeverywhere trans c (Var x) = return $ Var x+subeverywhere trans c (Lit x) = return $ Lit x+subeverywhere trans c (Type x) = return $ Type x++subeverywhere trans c (Cast expr ty) = do+ expr' <- trans (Other:c) expr+ return $ Cast expr' ty++subeverywhere trans c expr = error $ "\nNormalizeTools.subeverywhere: Unsupported expression: " ++ show expr++-- Runs each of the transforms repeatedly inside the State monad.+dotransforms :: [(String, Transform)] -> CoreExpr -> TranslatorSession CoreExpr+dotransforms transs expr = do+ (expr', changed) <- Writer.runWriterT $ Monad.foldM (\e trans -> everywhere (apply trans) [] e) expr transs+ if Monoid.getAny changed then dotransforms transs expr' else return expr'++-- Inline all let bindings that satisfy the given condition+inlinebind :: ((CoreBndr, CoreExpr) -> TransformMonad Bool) -> Transform+inlinebind condition context expr@(Let (Rec binds) res) = do+ -- Find all bindings that adhere to the condition+ res_eithers <- mapM docond binds+ case Either.partitionEithers res_eithers of+ -- No replaces? No change+ ([], _) -> return expr+ (replace, others) -> do+ -- Substitute the to be replaced binders with their expression+ newexpr <- do_substitute replace (Let (Rec others) res)+ change newexpr+ where + -- Apply the condition to a let binding and return an Either+ -- depending on whether it needs to be inlined or not.+ docond :: (CoreBndr, CoreExpr) -> TransformMonad (Either (CoreBndr, CoreExpr) (CoreBndr, CoreExpr))+ docond b = do+ res <- condition b+ return $ case res of True -> Left b; False -> Right b++ -- Apply the given list of substitutions to the the given expression+ do_substitute :: [(CoreBndr, CoreExpr)] -> CoreExpr -> TransformMonad CoreExpr+ do_substitute [] expr = return expr+ do_substitute ((bndr, val):reps) expr = do+ -- Perform this substitution in the expression+ expr' <- substitute_clone bndr val context expr+ -- And in the substitution values we will be using next+ reps' <- mapM (subs_bind bndr val) reps+ -- And then perform the remaining substitutions+ do_substitute reps' expr'+ + -- Replace the given binder with the given expression in the+ -- expression oft the given let binding+ subs_bind :: CoreBndr -> CoreExpr -> (CoreBndr, CoreExpr) -> TransformMonad (CoreBndr, CoreExpr)+ subs_bind bndr expr (b, v) = do+ v' <- substitute_clone bndr expr (LetBinding:context) v+ return (b, v')+++-- Leave all other expressions unchanged+inlinebind _ context expr = return expr++-- Sets the changed flag in the TransformMonad, to signify that some+-- transform has changed the result+setChanged :: TransformMonad ()+setChanged = Writer.tell (Monoid.Any True)++-- Sets the changed flag and returns the given value.+change :: a -> TransformMonad a+change val = do+ setChanged+ return val++-- Returns the given value and sets the changed flag if the bool given is+-- True. Note that this will not unset the changed flag if the bool is False.+changeif :: Bool -> a -> TransformMonad a+changeif True val = change val+changeif False val = return val++-- | Creates a transformation that substitutes the given binder with the given+-- expression (This can be a type variable, replace by a Type expression).+-- Does not set the changed flag.+substitute :: CoreBndr -> CoreExpr -> Transform+-- Use CoreSubst to subst a type var in an expression+substitute find repl context expr = do+ let subst = CoreSubst.extendSubst CoreSubst.emptySubst find repl+ return $ CoreSubst.substExpr subst expr ++-- | Creates a transformation that substitutes the given binder with the given+-- expression. This does only work for value expressions! All binders in the+-- expression are cloned before the replacement, to guarantee uniqueness.+substitute_clone :: CoreBndr -> CoreExpr -> Transform+-- If we see the var to find, replace it by a uniqued version of repl+substitute_clone find repl context (Var var) | find == var = do+ repl' <- Trans.lift $ CoreTools.genUniques repl+ change repl'++-- For all other expressions, just look in subexpressions+substitute_clone find repl context expr = subeverywhere (substitute_clone find repl) context expr++-- Is the given expression representable at runtime, based on the type?+isRepr :: (CoreTools.TypedThing t) => t -> TransformMonad Bool+isRepr tything = Trans.lift (isRepr' tything)++isRepr' :: (CoreTools.TypedThing t) => t -> TranslatorSession Bool+isRepr' tything = case CoreTools.getType tything of+ Nothing -> return False+ Just ty -> MonadState.lift tsType $ VHDLTools.isReprType ty ++is_local_var :: CoreSyn.CoreExpr -> TranslatorSession Bool+is_local_var (CoreSyn.Var v) = do+ bndrs <- getGlobalBinders+ return $ v `notElem` bndrs+is_local_var _ = return False++-- Is the given binder defined by the user?+isUserDefined :: CoreSyn.CoreBndr -> Bool+-- System names are certain to not be user defined+isUserDefined bndr | Name.isSystemName (Id.idName bndr) = False+-- Builtin functions are usually not user-defined either (and would+-- break currently if they are...)+isUserDefined bndr = str `notElem` builtinIds+ where+ str = Name.getOccString bndr++-- | Is the given binder normalizable? This means that its type signature can be+-- represented in hardware, which should (?) guarantee that it can be made+-- into hardware. This checks whether all the arguments and (optionally)+-- the return value are+-- representable.+isNormalizeable :: + Bool -- ^ Allow the result to be unrepresentable?+ -> CoreBndr -- ^ The binder to check+ -> TranslatorSession Bool -- ^ Is it normalizeable?+isNormalizeable result_nonrep bndr = do+ let ty = Id.idType bndr+ let (arg_tys, res_ty) = Type.splitFunTys ty+ let check_tys = if result_nonrep then arg_tys else (res_ty:arg_tys) + andM $ mapM isRepr' check_tys
+ CLasH/Normalize/NormalizeTypes.hs view
@@ -0,0 +1,34 @@+module CLasH.Normalize.NormalizeTypes where++-- Standard modules+import qualified Control.Monad.Trans.Writer as Writer+import qualified Data.Monoid as Monoid++-- GHC API+import qualified CoreSyn++-- Local imports+import CLasH.Translator.TranslatorTypes++-- Wrap a writer around a TranslatorSession, to run a single transformation+-- over a single expression and track if the expression was changed.+type TransformMonad = Writer.WriterT Monoid.Any TranslatorSession++-- | In what context does a core expression occur?+data CoreContext = AppFirst -- ^ The expression is the first+ -- argument of an application (i.e.,+ -- it is applied)+ | AppSecond -- ^ The expression is the second+ -- argument of an application+ -- (i.e., something is applied to it)+ | LetBinding -- ^ The expression is bound in a+ -- (recursive or non-recursive) let+ -- expression.+ | LetBody -- ^ The expression is the body of a+ -- let expression+ | LambdaBody -- ^ The expression is the body of a+ -- lambda abstraction+ | Other -- ^ Another context+ deriving (Eq, Show)+-- | Transforms a CoreExpr and keeps track if it has changed.+type Transform = [CoreContext] -> CoreSyn.CoreExpr -> TransformMonad CoreSyn.CoreExpr
+ CLasH/Translator.hs view
@@ -0,0 +1,142 @@+module CLasH.Translator + (+ makeVHDLAnnotations+ ) where++-- Standard Modules+import qualified System.Directory as Directory+import qualified Maybe+import qualified Monad+import qualified System.FilePath as FilePath+import qualified Control.Monad.Trans.State as State+import Text.PrettyPrint.HughesPJ (render)+import Data.Accessor.Monad.Trans.State+import qualified Data.Map as Map+import qualified Data.Time.Clock as Clock+import Debug.Trace++-- GHC API+import qualified CoreSyn+import qualified HscTypes+import qualified UniqSupply++-- VHDL Imports+import qualified Language.VHDL.AST as AST+import qualified Language.VHDL.FileIO as FileIO+import qualified Language.VHDL.Ppr as Ppr++-- Local Imports+import CLasH.Translator.TranslatorTypes+import CLasH.Translator.Annotations+import CLasH.Utils+import CLasH.Utils.GhcTools+import CLasH.VHDL+import CLasH.VHDL.VHDLTools+import CLasH.VHDL.Testbench++-- | Turn Haskell to VHDL, Using the Annotations for Top Entity, Initial State+-- and Test Inputs found in the Files. +makeVHDLAnnotations :: + FilePath -- ^ The GHC Library Dir+ -> [FilePath] -- ^ The FileNames+ -> IO ()+makeVHDLAnnotations libdir filenames =+ makeVHDL libdir filenames finder+ where+ finder = findSpec (hasCLasHAnnotation isTopEntity)+ (hasCLasHAnnotation isInitState)+ (isCLasHAnnotation isInitState)+ (hasCLasHAnnotation isTestInput)++-- | Turn Haskell to VHDL, using the given finder functions to find the Top+-- Entity, Initial State and Test Inputs in the Haskell Files.+makeVHDL ::+ FilePath -- ^ The GHC Library Dir+ -> [FilePath] -- ^ The Filenames+ -> Finder+ -> IO ()+makeVHDL libdir filenames finder = do+ start <- Clock.getCurrentTime+ -- Load the modules+ (cores, env, specs) <- loadModules libdir filenames (Just finder)+ -- Translate to VHDL+ vhdl <- moduleToVHDL env cores specs+ -- Write VHDL to file. Just use the first entity for the name+ let top_entity = head $ Maybe.catMaybes $ map (\(t, _, _) -> t) specs+ let dir = "./vhdl/" ++ (show top_entity) ++ "/"+ prepareDir dir+ mapM_ (writeVHDL dir) vhdl+ end <- Clock.getCurrentTime+ trace ("\nTotal compilation took " ++ show (Clock.diffUTCTime end start)) $+ return ()++-- | Translate the specified entities in the given modules to VHDL.+moduleToVHDL ::+ HscTypes.HscEnv -- ^ The GHC Environment+ -> [HscTypes.CoreModule] -- ^ The Core Modules+ -> [EntitySpec] -- ^ The entities to generate+ -> IO [(AST.VHDLId, AST.DesignFile)]+moduleToVHDL env cores specs = do+ (vhdl, count) <- runTranslatorSession env $ do+ let all_bindings = concatMap (\x -> CoreSyn.flattenBinds (HscTypes.cm_binds x)) cores+ -- Store the bindings we loaded+ tsBindings %= Map.fromList all_bindings+ let all_initstates = concatMap (\x -> case x of (_, Nothing, _) -> []; (_, Just inits, _) -> inits) specs + tsInitStates %= Map.fromList all_initstates+ test_binds <- catMaybesM $ Monad.mapM mkTest specs+ let topbinds = Maybe.catMaybes $ map (\(top, _, _) -> top) specs+ vhdl <- case topbinds of+ [] -> error "Could not find top entity requested"+ tops -> createDesignFiles (tops ++ test_binds)+ count <- get tsTransformCounter + return (vhdl, count)+ mapM_ (putStr . render . Ppr.ppr . snd) vhdl+ putStr $ "Total number of transformations applied: " ++ (show count) ++ "\n"+ return vhdl+ where+ mkTest :: EntitySpec -> TranslatorSession (Maybe CoreSyn.CoreBndr)+ -- Create a testbench for any entry that has test input+ mkTest (_, _, Nothing) = return Nothing+ mkTest (Nothing, _, _) = return Nothing+ mkTest (Just top, _, Just input) = do+ bndr <- createTestbench Nothing cores input top+ return $ Just bndr++-- Run the given translator session. Generates a new UniqSupply for that+-- session.+runTranslatorSession :: HscTypes.HscEnv -> TranslatorSession a -> IO a+runTranslatorSession env session = do+ -- Generate a UniqSupply+ -- Running + -- egrep -r "(initTcRnIf|mkSplitUniqSupply)" .+ -- on the compiler dir of ghc suggests that 'z' is not used to generate+ -- a unique supply anywhere.+ uniqSupply <- UniqSupply.mkSplitUniqSupply 'z'+ let init_typestate = TypeState builtin_types [] Map.empty Map.empty env+ let init_state = TranslatorState uniqSupply init_typestate Map.empty Map.empty 0 Map.empty Map.empty Map.empty 0+ return $ State.evalState session init_state++-- | Prepares the directory for writing VHDL files. This means creating the+-- dir if it does not exist and removing all existing .vhdl files from it.+prepareDir :: String -> IO()+prepareDir dir = do+ -- Create the dir if needed+ Directory.createDirectoryIfMissing True dir+ -- Find all .vhdl files in the directory+ files <- Directory.getDirectoryContents dir+ let to_remove = filter ((==".vhdl") . FilePath.takeExtension) files+ -- Prepend the dirname to the filenames+ let abs_to_remove = map (FilePath.combine dir) to_remove+ -- Remove the files+ mapM_ Directory.removeFile abs_to_remove++-- | Write the given design file to a file with the given name inside the+-- given dir+writeVHDL :: String -> (AST.VHDLId, AST.DesignFile) -> IO ()+writeVHDL dir (name, vhdl) = do+ -- Find the filename+ let fname = dir ++ (AST.fromVHDLId name) ++ ".vhdl"+ -- Write the file+ FileIO.writeDesignFile vhdl fname++-- vim: set ts=8 sw=2 sts=2 expandtab:
+ CLasH/Translator/Annotations.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE DeriveDataTypeable #-}+module CLasH.Translator.Annotations where+ +import qualified Language.Haskell.TH as TH+import Data.Data++data CLasHAnn = TopEntity | InitState TH.Name | TestInput | TestCycles+ deriving (Show, Data, Typeable)+ +isTopEntity :: CLasHAnn -> Bool+isTopEntity TopEntity = True+isTopEntity _ = False++isInitState :: CLasHAnn -> Bool+isInitState (InitState _) = True+isInitState _ = False++isTestInput :: CLasHAnn -> Bool+isTestInput TestInput = True+isTestInput _ = False++isTestCycles :: CLasHAnn -> Bool+isTestCycles TestCycles = True+isTestCycles _ = False
+ CLasH/Translator/TranslatorTypes.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE TemplateHaskell #-}+--+-- Simple module providing some types used by Translator. These are in a+-- separate module to prevent circular dependencies in Pretty for example.+--+module CLasH.Translator.TranslatorTypes where++-- Standard modules+import qualified Control.Monad.Trans.State as State+import qualified Data.Map as Map+import qualified Data.Accessor.Template+import qualified Data.Accessor.Monad.Trans.State as MonadState++-- GHC API+import qualified GHC+import qualified CoreSyn+import qualified Type+import qualified HscTypes+import qualified UniqSupply++-- VHDL Imports+import qualified Language.VHDL.AST as AST++-- Local imports+import CLasH.VHDL.VHDLTypes++-- | A specification of an entity we can generate VHDL for. Consists of the+-- binder of the top level entity, an optional initial state and an optional+-- test input.+type EntitySpec = (Maybe CoreSyn.CoreBndr, Maybe [(CoreSyn.CoreBndr, CoreSyn.CoreBndr)], Maybe CoreSyn.CoreExpr)++-- | A function that knows which parts of a module to compile+type Finder =+ HscTypes.CoreModule -- ^ The module to look at+ -> GHC.Ghc [EntitySpec]++-----------------------------------------------------------------------------+-- The TranslatorSession+-----------------------------------------------------------------------------++-- A orderable equivalent of CoreSyn's Type for use as a map key+newtype OrdType = OrdType Type.Type+instance Eq OrdType where+ (OrdType a) == (OrdType b) = Type.tcEqType a b+instance Ord OrdType where+ compare (OrdType a) (OrdType b) = Type.tcCmpType a b++data HType = AggrType String [HType] |+ EnumType String [String] |+ VecType Int HType |+ UVecType HType |+ SizedWType Int |+ RangedWType Int |+ SizedIType Int |+ BuiltinType String |+ StateType+ deriving (Eq, Ord, Show)++-- A map of a Core type to the corresponding type name, or Nothing when the+-- type would be empty.+type TypeMapRec = Maybe (AST.VHDLId, Maybe (Either AST.TypeDef AST.SubtypeIn))+type TypeMap = Map.Map HType TypeMapRec++-- A map of a vector Core element type and function name to the coressponding+-- VHDLId of the function and the function body.+type TypeFunMap = Map.Map (HType, String) (AST.VHDLId, AST.SubProgBody)++type TfpIntMap = Map.Map OrdType Int+-- A substate that deals with type generation+data TypeState = TypeState {+ -- | A map of Core type -> VHDL Type+ tsTypes_ :: TypeMap,+ -- | A list of type declarations+ tsTypeDecls_ :: [Maybe AST.PackageDecItem],+ -- | A map of vector Core type -> VHDL type function+ tsTypeFuns_ :: TypeFunMap,+ tsTfpInts_ :: TfpIntMap,+ tsHscEnv_ :: HscTypes.HscEnv+}++-- Derive accessors+Data.Accessor.Template.deriveAccessors ''TypeState++-- Define a session+type TypeSession = State.State TypeState+-- A global state for the translator+data TranslatorState = TranslatorState {+ tsUniqSupply_ :: UniqSupply.UniqSupply+ , tsType_ :: TypeState+ , tsBindings_ :: Map.Map CoreSyn.CoreBndr CoreSyn.CoreExpr+ , tsNormalized_ :: Map.Map CoreSyn.CoreBndr CoreSyn.CoreExpr+ , tsEntityCounter_ :: Integer+ , tsEntities_ :: Map.Map CoreSyn.CoreBndr Entity+ , tsArchitectures_ :: Map.Map CoreSyn.CoreBndr (Architecture, [CoreSyn.CoreBndr])+ , tsInitStates_ :: Map.Map CoreSyn.CoreBndr CoreSyn.CoreBndr+ , tsTransformCounter_ :: Int -- ^ How many transformations were applied?+}++-- Derive accessors+Data.Accessor.Template.deriveAccessors ''TranslatorState++type TranslatorSession = State.State TranslatorState++-----------------------------------------------------------------------------+-- Some accessors+-----------------------------------------------------------------------------++-- Does the given binder reference a top level binder in the current+-- module(s)?+isTopLevelBinder :: CoreSyn.CoreBndr -> TranslatorSession Bool+isTopLevelBinder bndr = do+ bindings <- MonadState.get tsBindings+ return $ Map.member bndr bindings++-- Finds the value of a global binding, if available+getGlobalBind :: CoreSyn.CoreBndr -> TranslatorSession (Maybe CoreSyn.CoreExpr)+getGlobalBind bndr = do+ bindings <- MonadState.get tsBindings+ return $ Map.lookup bndr bindings ++-- Adds a new global binding with the given value+addGlobalBind :: CoreSyn.CoreBndr -> CoreSyn.CoreExpr -> TranslatorSession ()+addGlobalBind bndr expr = MonadState.modify tsBindings (Map.insert bndr expr)++-- Returns a list of all global binders+getGlobalBinders :: TranslatorSession [CoreSyn.CoreBndr]+getGlobalBinders = do+ bindings <- MonadState.get tsBindings+ return $ Map.keys bindings++-- vim: set ts=8 sw=2 sts=2 expandtab:
+ CLasH/Utils.hs view
@@ -0,0 +1,69 @@+module CLasH.Utils where++-- Standard Imports+import qualified Maybe+import Data.Accessor+import qualified Data.Accessor.Monad.Trans.State as MonadState+import qualified Data.Map as Map+import qualified Control.Monad as Monad+import qualified Control.Monad.Trans.State as State+import qualified Debug.Trace as Trace+ +-- Make a caching version of a stateful computatation.+makeCached :: (Monad m, Ord k) =>+ k -- ^ The key to use for the cache+ -> Accessor s (Map.Map k v) -- ^ The accessor to get at the cache+ -> State.StateT s m v -- ^ How to compute the value to cache?+ -> State.StateT s m v -- ^ The resulting value, from the cache or freshly+ -- computed.+makeCached key accessor create = do+ cache <- MonadState.get accessor+ case Map.lookup key cache of+ -- Found in cache, just return+ Just value -> return value+ -- Not found, compute it and put it in the cache+ Nothing -> do+ value <- create+ MonadState.modify accessor (Map.insert key value)+ return value++unzipM :: (Monad m) =>+ m [(a, b)]+ -> m ([a], [b])+unzipM = Monad.liftM unzip++catMaybesM :: (Monad m) =>+ m [Maybe a]+ -> m [a]+catMaybesM = Monad.liftM Maybe.catMaybes++concatM :: (Monad m) =>+ m [[a]]+ -> m [a]+concatM = Monad.liftM concat++isJustM :: (Monad m) => m (Maybe a) -> m Bool+isJustM = Monad.liftM Maybe.isJust++andM, orM :: (Monad m) => m [Bool] -> m Bool+andM = Monad.liftM and+orM = Monad.liftM or++-- | Monadic versions of any and all. We reimplement them, since there+-- is no ready-made lifting function for them.+allM, anyM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool+allM f = andM . (mapM f)+anyM f = orM . (mapM f)++mapAccumLM :: (Monad m) => (acc -> x -> m (acc, y)) -> acc -> [x] -> m (acc, [y])+mapAccumLM _ s [] = return (s, [])+mapAccumLM f s (x:xs) = do+ (s', y ) <- f s x+ (s'', ys) <- mapAccumLM f s' xs+ return (s'', y:ys)++-- Trace the given string if the given bool is True, do nothing+-- otherwise.+traceIf :: Bool -> String -> a -> a+traceIf True = Trace.trace+traceIf False = flip const
+ CLasH/Utils/Core/BinderTools.hs view
@@ -0,0 +1,95 @@+--+-- This module contains functions that manipulate binders in various ways.+--+module CLasH.Utils.Core.BinderTools where++-- Standard modules+import qualified Data.Accessor.Monad.Trans.State as MonadState++-- GHC API+import qualified CoreSyn+import qualified Type+import qualified UniqSupply+import qualified Unique+import qualified OccName+import qualified Name+import qualified Module+import qualified Var+import qualified SrcLoc+import qualified IdInfo+import qualified CoreUtils++-- Local imports+import CLasH.Translator.TranslatorTypes++-- Create a new Unique+mkUnique :: TranslatorSession Unique.Unique +mkUnique = do+ us <- MonadState.get tsUniqSupply + let (us', us'') = UniqSupply.splitUniqSupply us+ MonadState.set tsUniqSupply us'+ return $ UniqSupply.uniqFromSupply us''++-- Create a new internal var with the given name and type. A Unique is+-- appended to the given name, to ensure uniqueness (not strictly neccesary,+-- since the Unique is also stored in the name, but this ensures variable+-- names are unique in the output).+mkInternalVar :: String -> Type.Type -> TranslatorSession Var.Var+mkInternalVar str ty = do+ uniq <- mkUnique+ let occname = OccName.mkVarOcc (str ++ show uniq)+ let name = Name.mkInternalName uniq occname SrcLoc.noSrcSpan+ return $ Var.mkLocalVar IdInfo.VanillaId name ty IdInfo.vanillaIdInfo++-- Create a new type variable with the given name and kind. A Unique is+-- appended to the given name, to ensure uniqueness (not strictly neccesary,+-- since the Unique is also stored in the name, but this ensures variable+-- names are unique in the output).+mkTypeVar :: String -> Type.Kind -> TranslatorSession Var.Var+mkTypeVar str kind = do+ uniq <- mkUnique+ let occname = OccName.mkVarOcc (str ++ show uniq)+ let name = Name.mkInternalName uniq occname SrcLoc.noSrcSpan+ return $ Var.mkTyVar name kind++-- Creates a binder for the given expression with the given name. This+-- works for both value and type level expressions, so it can return a Var or+-- TyVar (which is just an alias for Var).+mkBinderFor :: CoreSyn.CoreExpr -> String -> TranslatorSession Var.Var+mkBinderFor (CoreSyn.Type ty) string = mkTypeVar string (Type.typeKind ty)+mkBinderFor expr string = mkInternalVar string (CoreUtils.exprType expr)++-- Creates a reference to the given variable. This works for both a normal+-- variable as well as a type variable+mkReferenceTo :: Var.Var -> CoreSyn.CoreExpr+mkReferenceTo var | Var.isTyVar var = (CoreSyn.Type $ Type.mkTyVarTy var)+ | otherwise = (CoreSyn.Var var)++cloneVar :: Var.Var -> TranslatorSession Var.Var+cloneVar v = do+ uniq <- mkUnique+ -- Swap out the unique, and reset the IdInfo (I'm not 100% sure what it+ -- contains, but vannillaIdInfo is always correct, since it means "no info").+ return $ Var.lazySetIdInfo (Var.setVarUnique v uniq) IdInfo.vanillaIdInfo++-- Creates a new function with the same name as the given binder (but with a+-- new unique) and with the given function body. Returns the new binder for+-- this function.+mkFunction :: CoreSyn.CoreBndr -> CoreSyn.CoreExpr -> TranslatorSession CoreSyn.CoreBndr+mkFunction bndr body = do+ let ty = CoreUtils.exprType body+ id <- cloneVar bndr+ let newid = Var.setVarType id ty+ addGlobalBind newid body+ return newid++-- Returns the full name of a NamedThing, in the forum+-- modulename.occname+getFullString :: Name.NamedThing a => a -> String+getFullString thing = modstr ++ occstr+ where+ name = Name.getName thing+ modstr = case Name.nameModule_maybe name of+ Nothing -> ""+ Just mod -> Module.moduleNameString (Module.moduleName mod) ++ "."+ occstr = Name.getOccString name
+ CLasH/Utils/Core/CoreShow.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE StandaloneDeriving,FlexibleInstances, UndecidableInstances, OverlappingInstances #-}+--+-- This module derives Show instances for CoreSyn types.+--+module CLasH.Utils.Core.CoreShow where++-- GHC API+import qualified BasicTypes+import qualified CoreSyn+import qualified TypeRep+import qualified TyCon+import qualified HsTypes+import qualified HsExpr+import qualified HsBinds+import qualified SrcLoc+import qualified RdrName+import Outputable ( Outputable, OutputableBndr, showSDoc, ppr)++-- Derive Show for core expressions and binders, so we can see the actual+-- structure.+deriving instance (Show b) => Show (CoreSyn.Expr b)+deriving instance (Show b) => Show (CoreSyn.Bind b)+deriving instance Show TypeRep.Type+deriving instance (Show n, OutputableBndr n) => Show (HsTypes.HsType n)+deriving instance (Show n, OutputableBndr n) => Show (HsTypes.ConDeclField n)+deriving instance (Show x) => Show (SrcLoc.Located x)+deriving instance (Show x, OutputableBndr x) => Show (HsExpr.StmtLR x x)+deriving instance (Show x, OutputableBndr x) => Show (HsExpr.HsTupArg x)+deriving instance (Show x, OutputableBndr x) => Show (HsExpr.HsExpr x)+deriving instance Show (RdrName.RdrName)+deriving instance (Show idL, Show idR, OutputableBndr idL, OutputableBndr idR) => Show (HsBinds.HsBindLR idL idR)+deriving instance Show CoreSyn.Note+deriving instance Show TyCon.SynTyConRhs+++-- Implement dummy shows, since deriving them will need loads of other shows+-- as well.+instance Show TypeRep.PredType where+ show t = "_PredType:(" ++ showSDoc (ppr t) ++ ")"+instance Show TyCon.TyCon where+ show t | TyCon.isAlgTyCon t && not (TyCon.isTupleTyCon t) =+ showtc "AlgTyCon" ""+ | TyCon.isCoercionTyCon t =+ showtc "CoercionTyCon" ""+ | TyCon.isSynTyCon t =+ showtc "SynTyCon" (", synTcRhs = " ++ synrhs)+ | TyCon.isTupleTyCon t =+ showtc "TupleTyCon" ""+ | TyCon.isFunTyCon t =+ showtc "FunTyCon" ""+ | TyCon.isPrimTyCon t =+ showtc "PrimTyCon" ""+ | TyCon.isSuperKindTyCon t =+ showtc "SuperKindTyCon" ""+ | otherwise = + "_Nonexistant tycon?:(" ++ showSDoc (ppr t) ++ ")_"+ where+ showtc con extra = "(" ++ con ++ " {tyConName = " ++ name ++ extra ++ ", ...})"+ name = show (TyCon.tyConName t)+ synrhs = show (TyCon.synTyConRhs t)+instance Show BasicTypes.Boxity where+ show b = "_Boxity"+instance Show HsTypes.HsExplicitForAll where+ show b = "_HsExplicitForAll"+instance Show HsExpr.HsArrAppType where+ show b = "_HsArrAppType"+instance Show (HsExpr.MatchGroup x) where+ show b = "_HsMatchGroup"+instance Show (HsExpr.GroupByClause x) where+ show b = "_GroupByClause"+instance Show (HsExpr.HsStmtContext x) where+ show b = "_HsStmtContext"+instance Show (HsBinds.Prag) where+ show b = "_Prag"+instance Show (HsExpr.GRHSs id) where+ show b = "_GRHSs"+++instance (Outputable x) => Show x where+ show x = "__" ++ showSDoc (ppr x) ++ "__"
+ CLasH/Utils/Core/CoreTools.hs view
@@ -0,0 +1,463 @@+{-# LANGUAGE PatternGuards, TypeSynonymInstances #-}+-- | This module provides a number of functions to find out things about Core+-- programs. This module does not provide the actual plumbing to work with+-- Core and Haskell (it uses HsTools for this), but only the functions that+-- know about various libraries and know which functions to call.+module CLasH.Utils.Core.CoreTools where++--Standard modules+import qualified Maybe+import qualified System.IO.Unsafe+import qualified Data.Map as Map+import qualified Data.Accessor.Monad.Trans.State as MonadState++-- GHC API+import qualified GHC+import qualified Type+import qualified TcType+import qualified HsExpr+import qualified HsTypes+import qualified HscTypes+import qualified Name+import qualified Id+import qualified TyCon+import qualified DataCon+import qualified TysWiredIn+import qualified DynFlags+import qualified SrcLoc+import qualified CoreSyn+import qualified Var+import qualified IdInfo+import qualified VarSet+import qualified CoreUtils+import qualified CoreFVs+import qualified Literal+import qualified MkCore+import qualified VarEnv++-- Local imports+import CLasH.Translator.TranslatorTypes+import CLasH.Utils.GhcTools+import CLasH.Utils.Core.BinderTools+import CLasH.Utils.HsTools+import CLasH.Utils.Pretty+import CLasH.Utils+import qualified CLasH.Utils.Core.BinderTools as BinderTools++-- | A single binding, used as a shortcut to simplify type signatures.+type Binding = (CoreSyn.CoreBndr, CoreSyn.CoreExpr)++-- | Evaluate a core Type representing type level int from the tfp+-- library to a real int. Checks if the type really is a Dec type and+-- caches the results.+tfp_to_int :: Type.Type -> TypeSession Int+tfp_to_int ty = do+ hscenv <- MonadState.get tsHscEnv+ let norm_ty = normalize_tfp_int hscenv ty+ case Type.splitTyConApp_maybe norm_ty of+ Just (tycon, args) -> do+ let name = Name.getOccString (TyCon.tyConName tycon)+ case name of+ "Dec" ->+ tfp_to_int' ty+ otherwise -> do+ return $ error ("Callin tfp_to_int on non-dec:" ++ (show ty))+ Nothing -> return $ error ("Callin tfp_to_int on non-dec:" ++ (show ty))++-- | Evaluate a core Type representing type level int from the tfp+-- library to a real int. Caches the results. Do not use directly, use+-- tfp_to_int instead.+tfp_to_int' :: Type.Type -> TypeSession Int+tfp_to_int' ty = do+ lens <- MonadState.get tsTfpInts+ hscenv <- MonadState.get tsHscEnv+ let norm_ty = normalize_tfp_int hscenv ty+ let existing_len = Map.lookup (OrdType norm_ty) lens+ case existing_len of+ Just len -> return len+ Nothing -> do+ let new_len = eval_tfp_int hscenv ty+ MonadState.modify tsTfpInts (Map.insert (OrdType norm_ty) (new_len))+ return new_len+ +-- | Evaluate a core Type representing type level int from the tfp+-- library to a real int. Do not use directly, use tfp_to_int instead.+eval_tfp_int :: HscTypes.HscEnv -> Type.Type -> Int+eval_tfp_int env ty =+ unsafeRunGhc libdir $ do+ GHC.setSession env+ -- Automatically import modules for any fully qualified identifiers+ setDynFlag DynFlags.Opt_ImplicitImportQualified++ let from_int_t_name = mkRdrName "Types.Data.Num.Ops" "fromIntegerT"+ let from_int_t = SrcLoc.noLoc $ HsExpr.HsVar from_int_t_name+ let undef = hsTypedUndef $ coreToHsType ty+ let app = SrcLoc.noLoc $ HsExpr.HsApp (from_int_t) (undef)+ let int_ty = SrcLoc.noLoc $ HsTypes.HsTyVar TysWiredIn.intTyCon_RDR+ let expr = HsExpr.ExprWithTySig app int_ty+ core <- toCore expr+ execCore core+ where+ libdir = DynFlags.topDir dynflags+ dynflags = HscTypes.hsc_dflags env++normalize_tfp_int :: HscTypes.HscEnv -> Type.Type -> Type.Type+normalize_tfp_int env ty =+ System.IO.Unsafe.unsafePerformIO $+ normalizeType env ty++sized_word_len_ty :: Type.Type -> Type.Type+sized_word_len_ty ty = len+ where+ args = case Type.splitTyConApp_maybe ty of+ Just (tycon, args) -> args+ Nothing -> error $ "\nCoreTools.sized_word_len_ty: Not a sized word type: " ++ (pprString ty)+ [len] = args++sized_int_len_ty :: Type.Type -> Type.Type+sized_int_len_ty ty = len+ where+ args = case Type.splitTyConApp_maybe ty of+ Just (tycon, args) -> args+ Nothing -> error $ "\nCoreTools.sized_int_len_ty: Not a sized int type: " ++ (pprString ty)+ [len] = args+ +ranged_word_bound_ty :: Type.Type -> Type.Type+ranged_word_bound_ty ty = len+ where+ args = case Type.splitTyConApp_maybe ty of+ Just (tycon, args) -> args+ Nothing -> error $ "\nCoreTools.ranged_word_bound_ty: Not a sized word type: " ++ (pprString ty)+ [len] = args++tfvec_len_ty :: Type.Type -> Type.Type+tfvec_len_ty ty = len+ where + args = case Type.splitTyConApp_maybe ty of+ Just (tycon, args) -> args+ Nothing -> error $ "\nCoreTools.tfvec_len_ty: Not a vector type: " ++ (pprString ty)+ [len, el_ty] = args+ +-- | Get the element type of a TFVec type+tfvec_elem :: Type.Type -> Type.Type+tfvec_elem ty = el_ty+ where+ args = case Type.splitTyConApp_maybe ty of+ Just (tycon, args) -> args+ Nothing -> error $ "\nCoreTools.tfvec_len: Not a vector type: " ++ (pprString ty)+ [len, el_ty] = args++-- Is the given core expression a lambda abstraction?+is_lam :: CoreSyn.CoreExpr -> Bool+is_lam (CoreSyn.Lam _ _) = True+is_lam _ = False++-- Is the given core expression a let expression?+is_let :: CoreSyn.CoreExpr -> Bool+is_let (CoreSyn.Let _ _) = True+is_let _ = False++-- Is the given core expression of a function type?+is_fun :: CoreSyn.CoreExpr -> Bool+-- Treat Type arguments differently, because exprType is not defined for them.+is_fun (CoreSyn.Type _) = False+is_fun expr = (Type.isFunTy . CoreUtils.exprType) expr++-- Is the given core expression polymorphic (i.e., does it accept type+-- arguments?).+is_poly :: CoreSyn.CoreExpr -> Bool+-- Treat Type arguments differently, because exprType is not defined for them.+is_poly (CoreSyn.Type _) = False+is_poly expr = (Maybe.isJust . Type.splitForAllTy_maybe . CoreUtils.exprType) expr++-- Is the given core expression a variable reference?+is_var :: CoreSyn.CoreExpr -> Bool+is_var (CoreSyn.Var _) = True+is_var _ = False++is_lit :: CoreSyn.CoreExpr -> Bool+is_lit (CoreSyn.Lit _) = True+is_lit _ = False++-- Can the given core expression be applied to something? This is true for+-- applying to a value as well as a type.+is_applicable :: CoreSyn.CoreExpr -> Bool+is_applicable expr = is_fun expr || is_poly expr++-- Is the given core expression a variable or an application?+is_simple :: CoreSyn.CoreExpr -> Bool+is_simple (CoreSyn.App _ _) = True+is_simple (CoreSyn.Var _) = True+is_simple (CoreSyn.Cast expr _) = is_simple expr+is_simple _ = False++-- Does the given CoreExpr have any free type vars?+has_free_tyvars :: CoreSyn.CoreExpr -> Bool+has_free_tyvars = not . VarSet.isEmptyVarSet . (CoreFVs.exprSomeFreeVars Var.isTyVar)++-- Does the given type have any free type vars?+ty_has_free_tyvars :: Type.Type -> Bool+ty_has_free_tyvars = not . VarSet.isEmptyVarSet . Type.tyVarsOfType++-- Does the given CoreExpr have any free local vars?+has_free_vars :: CoreSyn.CoreExpr -> Bool+has_free_vars = not . VarSet.isEmptyVarSet . CoreFVs.exprFreeVars++-- Does the given expression use any of the given binders?+expr_uses_binders :: [CoreSyn.CoreBndr] -> CoreSyn.CoreExpr -> Bool+expr_uses_binders bndrs = not . VarSet.isEmptyVarSet . (CoreFVs.exprSomeFreeVars (`elem` bndrs))++-- Turns a Var CoreExpr into the Id inside it. Will of course only work for+-- simple Var CoreExprs, not complexer ones.+exprToVar :: CoreSyn.CoreExpr -> Var.Id+exprToVar (CoreSyn.Var id) = id+exprToVar expr = error $ "\nCoreTools.exprToVar: Not a var: " ++ show expr++-- Turns a Lit CoreExpr into the Literal inside it.+exprToLit :: CoreSyn.CoreExpr -> Literal.Literal+exprToLit (CoreSyn.Lit lit) = lit+exprToLit expr = error $ "\nCoreTools.exprToLit: Not a lit: " ++ show expr++-- Removes all the type and dictionary arguments from the given argument list,+-- leaving only the normal value arguments. The type given is the type of the+-- expression applied to this argument list.+get_val_args :: Type.Type -> [CoreSyn.CoreExpr] -> [CoreSyn.CoreExpr]+get_val_args ty args = drop n args+ where+ (tyvars, predtypes, _) = TcType.tcSplitSigmaTy ty+ -- The first (length tyvars) arguments should be types, the next + -- (length predtypes) arguments should be dictionaries. We drop this many+ -- arguments, to get at the value arguments.+ n = length tyvars + length predtypes++-- Finds out what literal Integer this expression represents.+getIntegerLiteral :: CoreSyn.CoreExpr -> TranslatorSession Integer+getIntegerLiteral expr =+ case CoreSyn.collectArgs expr of+ (CoreSyn.Var f, [CoreSyn.Lit (Literal.MachInt integer)]) + | getFullString f == "GHC.Integer.smallInteger" -> return integer+ (CoreSyn.Var f, [CoreSyn.Lit (Literal.MachInt64 integer)]) + | getFullString f == "GHC.Integer.int64ToInteger" -> return integer+ (CoreSyn.Var f, [CoreSyn.Lit (Literal.MachWord integer)]) + | getFullString f == "GHC.Integer.wordToInteger" -> return integer+ (CoreSyn.Var f, [CoreSyn.Lit (Literal.MachWord64 integer)]) + | getFullString f == "GHC.Integer.word64ToInteger" -> return integer+ -- fromIntegerT returns the integer corresponding to the type of its+ -- (third) argument. Since it is polymorphic, the type of that+ -- argument is passed as the first argument, so we can just use that+ -- one.+ (CoreSyn.Var f, [CoreSyn.Type dec_ty, dec_dict, CoreSyn.Type num_ty, num_dict, arg]) + | getFullString f == "Types.Data.Num.Ops.fromIntegerT" -> do+ int <- MonadState.lift tsType $ tfp_to_int dec_ty+ return $ toInteger int+ _ -> error $ "CoreTools.getIntegerLiteral: Unsupported Integer literal: " ++ pprString expr++reduceCoreListToHsList :: + [HscTypes.CoreModule] -- ^ The modules where parts of the list are hidden+ -> CoreSyn.CoreExpr -- ^ The refence to atleast one of the nodes+ -> TranslatorSession [CoreSyn.CoreExpr]+reduceCoreListToHsList cores app@(CoreSyn.App _ _) = do {+ ; let { (fun, args) = CoreSyn.collectArgs app+ ; len = length args + } ;+ ; case len of+ 3 -> do {+ ; let topelem = args!!1+ ; case (args!!2) of+ (varz@(CoreSyn.Var id)) -> do {+ ; binds <- mapM (findExpr (isVarName id)) cores+ ; otherelems <- reduceCoreListToHsList cores (head (Maybe.catMaybes binds))+ ; return (topelem:otherelems)+ }+ (appz@(CoreSyn.App _ _)) -> do {+ ; otherelems <- reduceCoreListToHsList cores appz+ ; return (topelem:otherelems)+ }+ otherwise -> return [topelem]+ }+ otherwise -> return []+ }+ where+ isVarName :: Monad m => Var.Var -> Var.Var -> m Bool+ isVarName lookfor bind = return $ (Var.varName lookfor) == (Var.varName bind)++reduceCoreListToHsList _ _ = return []++-- Is the given var the State data constructor?+isStateCon :: Var.Var -> Bool+isStateCon var =+ -- See if it is a DataConWrapId (not DataConWorkId, since State is a+ -- newtype).+ case Id.idDetails var of+ IdInfo.DataConWrapId dc -> + -- See if the datacon is the State datacon from the State type.+ let tycon = DataCon.dataConTyCon dc+ tyname = Name.getOccString tycon+ dcname = Name.getOccString dc+ in case (tyname, dcname) of+ ("State", "State") -> True+ _ -> False+ _ -> False++-- | Is the given type a State type?+isStateType :: Type.Type -> Bool+-- Resolve any type synonyms remaining+isStateType ty | Just ty' <- Type.tcView ty = isStateType ty'+isStateType ty = Maybe.isJust $ do+ -- Split the type. Don't use normal splitAppTy, since that looks through+ -- newtypes, and we want to see the State newtype.+ (typef, _) <- Type.repSplitAppTy_maybe ty+ -- See if the applied type is a type constructor+ (tycon, _) <- Type.splitTyConApp_maybe typef+ if TyCon.isNewTyCon tycon && Name.getOccString tycon == "State"+ then+ Just ()+ else+ Nothing++-- | Does the given TypedThing have a State type?+hasStateType :: (TypedThing t) => t -> Bool+hasStateType expr = case getType expr of+ Nothing -> False+ Just ty -> isStateType ty+++-- | Flattens nested lets into a single list of bindings. The expression+-- passed does not have to be a let expression, if it isn't an empty list of+-- bindings is returned.+flattenLets ::+ CoreSyn.CoreExpr -- ^ The expression to flatten.+ -> ([Binding], CoreSyn.CoreExpr) -- ^ The bindings and resulting expression.+flattenLets (CoreSyn.Let binds expr) = + (bindings ++ bindings', expr')+ where+ -- Recursively flatten the contained expression+ (bindings', expr') =flattenLets expr+ -- Flatten our own bindings to remove the Rec / NonRec constructors+ bindings = CoreSyn.flattenBinds [binds]+flattenLets expr = ([], expr)++-- | Create bunch of nested non-recursive let expressions from the given+-- bindings. The first binding is bound at the highest level (and thus+-- available in all other bindings).+mkNonRecLets :: [Binding] -> CoreSyn.CoreExpr -> CoreSyn.CoreExpr+mkNonRecLets bindings expr = MkCore.mkCoreLets binds expr+ where+ binds = map (uncurry CoreSyn.NonRec) bindings++-- | A class of things that (optionally) have a core Type. The type is+-- optional, since Type expressions don't have a type themselves.+class TypedThing t where+ getType :: t -> Maybe Type.Type++instance TypedThing CoreSyn.CoreExpr where+ getType (CoreSyn.Type _) = Nothing+ getType expr = Just $ CoreUtils.exprType expr++instance TypedThing CoreSyn.CoreBndr where+ getType = return . Id.idType++instance TypedThing Type.Type where+ getType = return . id++-- | Generate new uniques for all binders in the given expression.+-- Does not support making type variables unique, though this could be+-- supported if required (by passing a CoreSubst.Subst instead of VarEnv to+-- genUniques' below).+genUniques :: CoreSyn.CoreExpr -> TranslatorSession CoreSyn.CoreExpr+genUniques = genUniques' VarEnv.emptyVarEnv++-- | A helper function to generate uniques, that takes a VarEnv containing the+-- substitutions already performed.+genUniques' :: VarEnv.VarEnv CoreSyn.CoreBndr -> CoreSyn.CoreExpr -> TranslatorSession CoreSyn.CoreExpr+genUniques' subst (CoreSyn.Var f) = do+ -- Replace the binder with its new value, if applicable.+ let f' = VarEnv.lookupWithDefaultVarEnv subst f f+ return (CoreSyn.Var f')+-- Leave literals untouched+genUniques' subst (CoreSyn.Lit l) = return $ CoreSyn.Lit l+genUniques' subst (CoreSyn.App f arg) = do+ -- Only work on subexpressions+ f' <- genUniques' subst f+ arg' <- genUniques' subst arg+ return (CoreSyn.App f' arg')+-- Don't change type abstractions+genUniques' subst expr@(CoreSyn.Lam bndr res) | CoreSyn.isTyVar bndr = return expr+genUniques' subst (CoreSyn.Lam bndr res) = do+ -- Generate a new unique for the bound variable+ (subst', bndr') <- genUnique subst bndr+ res' <- genUniques' subst' res+ return (CoreSyn.Lam bndr' res')+genUniques' subst (CoreSyn.Let (CoreSyn.NonRec bndr bound) res) = do+ -- Make the binders unique+ (subst', bndr') <- genUnique subst bndr+ bound' <- genUniques' subst' bound+ res' <- genUniques' subst' res+ return $ CoreSyn.Let (CoreSyn.NonRec bndr' bound') res'+genUniques' subst (CoreSyn.Let (CoreSyn.Rec binds) res) = do+ -- Make each of the binders unique+ (subst', bndrs') <- mapAccumLM genUnique subst (map fst binds)+ bounds' <- mapM (genUniques' subst' . snd) binds+ res' <- genUniques' subst' res+ let binds' = zip bndrs' bounds'+ return $ CoreSyn.Let (CoreSyn.Rec binds') res'+genUniques' subst (CoreSyn.Case scrut bndr ty alts) = do+ -- Process the scrutinee with the original substitution, since non of the+ -- binders bound in the Case statement is in scope in the scrutinee.+ scrut' <- genUniques' subst scrut+ -- Generate a new binder for the scrutinee+ (subst', bndr') <- genUnique subst bndr+ -- Process each of the alts+ alts' <- mapM (doalt subst') alts+ return $ CoreSyn.Case scrut' bndr' ty alts'+ where+ doalt subst (con, bndrs, expr) = do+ (subst', bndrs') <- mapAccumLM genUnique subst bndrs+ expr' <- genUniques' subst' expr+ -- Note that we don't return subst', since bndrs are only in scope in+ -- expr.+ return (con, bndrs', expr')+genUniques' subst (CoreSyn.Cast expr coercion) = do+ expr' <- genUniques' subst expr+ -- Just process the casted expression+ return $ CoreSyn.Cast expr' coercion+genUniques' subst (CoreSyn.Note note expr) = do+ expr' <- genUniques' subst expr+ -- Just process the annotated expression+ return $ CoreSyn.Note note expr'+-- Leave types untouched+genUniques' subst expr@(CoreSyn.Type _) = return expr++-- Generate a new unique for the given binder, and extend the given+-- substitution to reflect this.+genUnique :: VarEnv.VarEnv CoreSyn.CoreBndr -> CoreSyn.CoreBndr -> TranslatorSession (VarEnv.VarEnv CoreSyn.CoreBndr, CoreSyn.CoreBndr)+genUnique subst bndr = do+ bndr' <- BinderTools.cloneVar bndr+ -- Replace all occurences of the old binder with a reference to the new+ -- binder.+ let subst' = VarEnv.extendVarEnv subst bndr bndr'+ return (subst', bndr')++-- Create a "selector" case that selects the ith field from a datacon+mkSelCase :: CoreSyn.CoreExpr -> Int -> TranslatorSession CoreSyn.CoreExpr+mkSelCase scrut i = do+ let scrut_ty = CoreUtils.exprType scrut+ case Type.splitTyConApp_maybe scrut_ty of+ -- The scrutinee should have a type constructor. We keep the type+ -- arguments around so we can instantiate the field types below+ Just (tycon, tyargs) -> case TyCon.tyConDataCons tycon of+ -- The scrutinee type should have a single dataconstructor,+ -- otherwise we can't construct a valid selector case.+ [datacon] -> do+ let field_tys = DataCon.dataConInstOrigArgTys datacon tyargs+ -- Create a list of wild binders for the fields we don't want+ let wildbndrs = map MkCore.mkWildBinder field_tys+ -- Create a single binder for the field we want+ sel_bndr <- mkInternalVar "sel" (field_tys!!i)+ -- Create a wild binder for the scrutinee+ let scrut_bndr = MkCore.mkWildBinder scrut_ty+ -- Create the case expression+ let binders = take i wildbndrs ++ [sel_bndr] ++ drop (i+1) wildbndrs+ return $ CoreSyn.Case scrut scrut_bndr scrut_ty [(CoreSyn.DataAlt datacon, binders, CoreSyn.Var sel_bndr)]+ dcs -> error $ "CoreTools.mkSelCase: Scrutinee type must have exactly one datacon. Extracting element " ++ (show i) ++ " from '" ++ pprString scrut ++ "' Datacons: " ++ (show dcs) ++ " Type: " ++ (pprString scrut_ty)+ Nothing -> error $ "CoreTools.mkSelCase: Creating extractor case, but scrutinee has no tycon? Extracting element " ++ (show i) ++ " from '" ++ pprString scrut ++ "'" ++ " Type: " ++ (pprString scrut_ty)
+ CLasH/Utils/GhcTools.hs view
@@ -0,0 +1,249 @@+{-# LANGUAGE ScopedTypeVariables #-}++module CLasH.Utils.GhcTools where+ +-- Standard modules+import qualified Monad+import qualified System.IO.Unsafe+import qualified Language.Haskell.TH as TH+import qualified Maybe++-- GHC API+import qualified Annotations+import qualified CoreSyn+import qualified CoreUtils+import qualified DynFlags+import qualified HscTypes+import qualified GHC+import qualified Name+import qualified Serialized+import qualified Var+import qualified Outputable+import qualified Class++-- Local Imports+import CLasH.Utils.Pretty+import CLasH.Translator.TranslatorTypes+import CLasH.Translator.Annotations+import CLasH.Utils++listBindings :: FilePath -> [FilePath] -> IO ()+listBindings libdir filenames = do+ (cores,_,_) <- loadModules libdir filenames Nothing+ let binds = concatMap (CoreSyn.flattenBinds . HscTypes.cm_binds) cores+ mapM listBinding binds+ putStr "\n=========================\n"+ let classes = concatMap (HscTypes.typeEnvClasses . HscTypes.cm_types) cores+ mapM listClass classes+ return ()++listBinding :: (CoreSyn.CoreBndr, CoreSyn.CoreExpr) -> IO ()+listBinding (b, e) = do+ putStr "\nBinder: "+ putStr $ show b ++ "[" ++ show (Var.varUnique b) ++ "]"+ putStr "\nType of Binder: \n"+ putStr $ Outputable.showSDoc $ Outputable.ppr $ Var.varType b+ putStr "\n\nExpression: \n"+ putStr $ prettyShow e+ putStr "\n\n"+ putStr $ Outputable.showSDoc $ Outputable.ppr e+ putStr "\n\nType of Expression: \n"+ putStr $ Outputable.showSDoc $ Outputable.ppr $ CoreUtils.exprType e+ putStr "\n\n"++listClass :: Class.Class -> IO ()+listClass c = do+ putStr "\nClass: "+ putStr $ show (Class.className c)+ putStr "\nSelectors: "+ putStr $ show (Class.classSelIds c)+ putStr "\n"+ +-- | Show the core structure of the given binds in the given file.+listBind :: FilePath -> [FilePath] -> String -> IO ()+listBind libdir filenames name = do+ (cores,_,_) <- loadModules libdir filenames Nothing+ bindings <- concatM $ mapM (findBinder (hasVarName name)) cores+ mapM_ listBinding bindings+ return ()++-- Change a DynFlag from within the Ghc monad. Strangely enough there seems to+-- be no standard function to do exactly this.+setDynFlag :: DynFlags.DynFlag -> GHC.Ghc ()+setDynFlag dflag = do+ dflags <- GHC.getSessionDynFlags+ let dflags' = DynFlags.dopt_set dflags dflag+ GHC.setSessionDynFlags dflags'+ return ()++-- We don't want the IO monad sprinkled around everywhere, so we hide it.+-- This should be safe as long as we only do simple things in the GhcMonad+-- such as interface lookups and evaluating simple expressions that+-- don't have side effects themselves (Or rather, that don't use+-- unsafePerformIO themselves, since normal side effectful function would+-- just return an IO monad when they are evaluated).+unsafeRunGhc :: FilePath -> GHC.Ghc a -> a+unsafeRunGhc libDir m =+ System.IO.Unsafe.unsafePerformIO $+ GHC.runGhc (Just libDir) $ do+ dflags <- GHC.getSessionDynFlags+ GHC.setSessionDynFlags dflags+ m+ +-- | Loads the given files and turns it into a core module+loadModules ::+ FilePath -- ^ The GHC Library directory + -> [String] -- ^ The files that need to be loaded+ -> Maybe Finder -- ^ What entities to build?+ -> IO ( [HscTypes.CoreModule]+ , HscTypes.HscEnv+ , [EntitySpec]+ ) -- ^ ( The loaded modules, the resulting ghc environment, the entities to build)+loadModules libdir filenames finder =+ GHC.defaultErrorHandler DynFlags.defaultDynFlags $+ GHC.runGhc (Just libdir) $ do+ dflags <- GHC.getSessionDynFlags+ GHC.setSessionDynFlags dflags+ cores <- mapM GHC.compileToCoreModule filenames+ env <- GHC.getSession+ specs <- case finder of+ Nothing -> return []+ Just f -> concatM $ mapM f cores+ return (cores, env, specs)++findBinds ::+ Monad m =>+ (Var.Var -> m Bool)+ -> HscTypes.CoreModule+ -> m (Maybe [CoreSyn.CoreBndr])+findBinds criteria core = do+ binders <- findBinder criteria core+ case binders of+ [] -> return Nothing+ bndrs -> return $ Just $ map fst bndrs++findBind ::+ Monad m =>+ (Var.Var -> m Bool)+ -> HscTypes.CoreModule+ -> m (Maybe CoreSyn.CoreBndr)+findBind criteria core = do+ binders <- findBinds criteria core+ case binders of+ Nothing -> return Nothing+ (Just bndrs) -> return $ Just $ head bndrs++findExprs ::+ Monad m =>+ (Var.Var -> m Bool)+ -> HscTypes.CoreModule+ -> m (Maybe [CoreSyn.CoreExpr])+findExprs criteria core = do+ binders <- findBinder criteria core+ case binders of+ [] -> return Nothing+ bndrs -> return $ Just (map snd bndrs)++findExpr ::+ Monad m =>+ (Var.Var -> m Bool)+ -> HscTypes.CoreModule+ -> m (Maybe CoreSyn.CoreExpr)+findExpr criteria core = do+ exprs <- findExprs criteria core+ case exprs of+ Nothing -> return Nothing+ (Just exprs) -> return $ Just $ head exprs++findAnns ::+ Monad m =>+ (Var.Var -> m [CLasHAnn])+ -> HscTypes.CoreModule+ -> m [CLasHAnn]+findAnns criteria core = do+ let binds = CoreSyn.flattenBinds $ HscTypes.cm_binds core+ anns <- Monad.mapM (criteria . fst) binds+ case anns of+ [] -> return []+ xs -> return $ concat xs++-- | Find a binder in module according to a certain criteria+findBinder :: + Monad m =>+ (Var.Var -> m Bool) -- ^ The criteria to filter the binders on+ -> HscTypes.CoreModule -- ^ The module to be inspected+ -> m [(CoreSyn.CoreBndr, CoreSyn.CoreExpr)] -- ^ The binders to meet the criteria+findBinder criteria core = do+ let binds = CoreSyn.flattenBinds $ HscTypes.cm_binds core+ Monad.filterM (criteria . fst) binds++-- | Determine if a binder has an Annotation meeting a certain criteria+isCLasHAnnotation ::+ GHC.GhcMonad m =>+ (CLasHAnn -> Bool) -- ^ The criteria the Annotation has to meet+ -> Var.Var -- ^ The Binder+ -> m [CLasHAnn] -- ^ Indicates if binder has the Annotation+isCLasHAnnotation clashAnn var = do+ let deserializer = Serialized.deserializeWithData+ let target = Annotations.NamedTarget (Var.varName var)+ (anns :: [CLasHAnn]) <- GHC.findGlobalAnns deserializer target+ let annEnts = filter clashAnn anns+ return annEnts++-- | Determine if a binder has an Annotation meeting a certain criteria+hasCLasHAnnotation ::+ GHC.GhcMonad m =>+ (CLasHAnn -> Bool) -- ^ The criteria the Annotation has to meet+ -> Var.Var -- ^ The Binder+ -> m Bool -- ^ Indicates if binder has the Annotation+hasCLasHAnnotation clashAnn var = do+ anns <- isCLasHAnnotation clashAnn var+ case anns of+ [] -> return False+ xs -> return True++-- | Determine if a binder has a certain name+hasVarName :: + Monad m =>+ String -- ^ The name the binder has to have+ -> Var.Var -- ^ The Binder+ -> m Bool -- ^ Indicate if the binder has the name+hasVarName lookfor bind = return $ lookfor == Name.occNameString (Name.nameOccName $ Name.getName bind)+++findInitStates ::+ (Var.Var -> GHC.Ghc Bool) -> + (Var.Var -> GHC.Ghc [CLasHAnn]) -> + HscTypes.CoreModule -> + GHC.Ghc (Maybe [(CoreSyn.CoreBndr, CoreSyn.CoreBndr)])+findInitStates statec annsc mod = do+ states <- findBinds statec mod+ anns <- findAnns annsc mod+ let funs = Maybe.catMaybes (map extractInits anns)+ exprs' <- mapM (\x -> findBind (hasVarName (TH.nameBase x)) mod) funs+ let exprs = Maybe.catMaybes exprs'+ let inits = zipMWith (\a b -> (a,b)) states exprs+ return inits+ where+ extractInits :: CLasHAnn -> Maybe TH.Name+ extractInits (InitState x) = Just x+ extractInits _ = Nothing+ zipMWith :: (a -> b -> c) -> (Maybe [a]) -> [b] -> (Maybe [c])+ zipMWith _ Nothing _ = Nothing+ zipMWith f (Just as) bs = Just $ zipWith f as bs++-- | Make a complete spec out of a three conditions+findSpec ::+ (Var.Var -> GHC.Ghc Bool) -> (Var.Var -> GHC.Ghc Bool) -> (Var.Var -> GHC.Ghc [CLasHAnn]) -> (Var.Var -> GHC.Ghc Bool)+ -> Finder++findSpec topc statec annsc testc mod = do+ top <- findBind topc mod+ state <- findExprs statec mod+ anns <- findAnns annsc mod+ test <- findExpr testc mod+ inits <- findInitStates statec annsc mod+ return [(top, inits, test)]+ -- case top of+ -- Just t -> return [(t, state, test)]+ -- Nothing -> return error $ "Could not find top entity requested"
+ CLasH/Utils/HsTools.hs view
@@ -0,0 +1,212 @@+module CLasH.Utils.HsTools where++-- Standard modules+import qualified Unsafe.Coerce+import qualified Maybe++-- GHC API+import qualified GHC+import qualified HscMain+import qualified HscTypes+import qualified DynFlags+import qualified FastString+import qualified StringBuffer+import qualified MonadUtils+import Outputable ( showSDoc, ppr )+import qualified Outputable+-- Lexer & Parser, i.e. up to HsExpr+import qualified Lexer+import qualified Parser+-- HsExpr representation, renaming, typechecking and desugaring+-- (i.e., everything up to Core).+import qualified HsSyn+import qualified HsExpr+import qualified HsTypes+import qualified HsBinds+import qualified TcRnMonad+import qualified TcRnTypes+import qualified RnExpr+import qualified RnEnv+import qualified TcExpr+import qualified TcEnv+import qualified TcSimplify+import qualified TcTyFuns+import qualified Desugar+import qualified PrelNames+import qualified Module+import qualified OccName+import qualified RdrName+import qualified Name+import qualified SrcLoc+import qualified LoadIface+import qualified BasicTypes+-- Core representation and handling+import qualified CoreSyn+import qualified Id+import qualified Type+import qualified TyCon++-- | Translate a HsExpr to a Core expression. This does renaming, type+-- checking, simplification of class instances and desugaring. The result is+-- a let expression that holds the given expression and a number of binds that+-- are needed for any type classes used to work. For example, the HsExpr:+-- \x = x == (1 :: Int)+-- will result in the CoreExpr+-- let +-- $dInt = ...+-- (==) = Prelude.(==) Int $dInt +-- in +-- \x = (==) x 1+toCore ::+ HsSyn.HsExpr RdrName.RdrName -- ^ The expression to translate to Core.+ -> GHC.Ghc CoreSyn.CoreExpr -- ^ The resulting core expression.+toCore expr = do+ env <- GHC.getSession+ let icontext = HscTypes.hsc_IC env+ + (binds, tc_expr) <- HscTypes.ioMsgMaybe $ MonadUtils.liftIO $ + -- Translage the TcRn (typecheck-rename) monad into an IO monad+ TcRnMonad.initTcPrintErrors env PrelNames.iNTERACTIVE $ do+ (tc_expr, insts) <- TcRnMonad.getLIE $ do+ -- Rename the expression, resulting in a HsExpr Name+ (rn_expr, freevars) <- RnExpr.rnExpr expr+ -- Typecheck the expression, resulting in a HsExpr Id and a list of+ -- Insts+ (res, _) <- TcExpr.tcInferRho (SrcLoc.noLoc rn_expr)+ return res+ -- Translate the instances into bindings+ --(insts', binds) <- TcSimplify.tcSimplifyRuleLhs insts+ binds <- TcSimplify.tcSimplifyTop insts+ return (binds, tc_expr)+ + -- Create a let expression with the extra binds (for polymorphism etc.) and+ -- the resulting expression.+ let letexpr = SrcLoc.noLoc $ HsExpr.HsLet + (HsBinds.HsValBinds $ HsBinds.ValBindsOut [(BasicTypes.NonRecursive, binds)] [])+ tc_expr+ -- Desugar the expression, resulting in core.+ let rdr_env = HscTypes.ic_rn_gbl_env icontext+ HscTypes.ioMsgMaybe $ Desugar.deSugarExpr env PrelNames.iNTERACTIVE rdr_env HscTypes.emptyTypeEnv letexpr+++-- | Create an Id from a RdrName. Might not work for DataCons...+mkId :: RdrName.RdrName -> GHC.Ghc Id.Id+mkId rdr_name = do+ env <- GHC.getSession+ HscTypes.ioMsgMaybe $ MonadUtils.liftIO $ + -- Translage the TcRn (typecheck-rename) monad in an IO monad+ TcRnMonad.initTcPrintErrors env PrelNames.iNTERACTIVE $ + -- Automatically import all available modules, so fully qualified names+ -- always work+ TcRnMonad.setOptM DynFlags.Opt_ImplicitImportQualified $ do+ -- Lookup a Name for the RdrName. This finds the package (version) in+ -- which the name resides.+ name <- RnEnv.lookupGlobalOccRn rdr_name+ -- Lookup an Id for the Name. This finds out the the type of the thing+ -- we're looking for.+ --+ -- Note that tcLookupId doesn't seem to work for DataCons. See source for+ -- tcLookupId to find out.+ TcEnv.tcLookupId name ++normalizeType ::+ HscTypes.HscEnv+ -> Type.Type+ -> IO Type.Type+normalizeType env ty = do+ (err, nty) <- MonadUtils.liftIO $+ -- Initialize the typechecker monad+ TcRnMonad.initTcPrintErrors env PrelNames.iNTERACTIVE $ do+ -- Normalize the type+ (_, nty) <- TcTyFuns.tcNormaliseFamInst ty+ return nty+ let normalized_ty = Maybe.fromJust nty+ return normalized_ty++-- | Translate a core Type to an HsType. Far from complete so far.+coreToHsType :: Type.Type -> HsTypes.LHsType RdrName.RdrName+-- Translate TyConApps+coreToHsType ty = case Type.splitTyConApp_maybe ty of+ Just (tycon, tys) ->+ foldl (\t a -> SrcLoc.noLoc $ HsTypes.HsAppTy t a) tycon_ty (map coreToHsType tys)+ where+ tycon_name = TyCon.tyConName tycon+ mod_name = Module.moduleName $ Name.nameModule tycon_name+ occ_name = Name.nameOccName tycon_name+ tycon_rdrname = RdrName.mkRdrQual mod_name occ_name+ tycon_ty = SrcLoc.noLoc $ HsTypes.HsTyVar tycon_rdrname+ Nothing -> error "HsTools.coreToHsType Cannot translate non-tycon type"++-- | Evaluate a CoreExpr and return its value. For this to work, the caller+-- should already know the result type for sure, since the result value is+-- unsafely coerced into this type.+execCore :: CoreSyn.CoreExpr -> GHC.Ghc a+execCore expr = do+ -- Setup session flags (yeah, this seems like a noop, but+ -- setSessionDynFlags really does some extra work...)+ dflags <- GHC.getSessionDynFlags+ GHC.setSessionDynFlags dflags+ -- Compile the expressions. This runs in the IO monad, but really wants+ -- to run an IO-monad-inside-a-GHC-monad for some reason. I don't really+ -- understand what it means, but it works.+ env <- GHC.getSession+ let srcspan = SrcLoc.noSrcSpan+ hval <- MonadUtils.liftIO $ HscMain.compileExpr env srcspan expr+ let res = Unsafe.Coerce.unsafeCoerce hval :: Int+ return $ Unsafe.Coerce.unsafeCoerce hval++-- These functions build (parts of) a LHSExpr RdrName.++-- | A reference to the Prelude.undefined function.+hsUndef :: HsExpr.LHsExpr RdrName.RdrName+hsUndef = SrcLoc.noLoc $ HsExpr.HsVar PrelNames.undefined_RDR++-- | A typed reference to the Prelude.undefined function.+hsTypedUndef :: HsTypes.LHsType RdrName.RdrName -> HsExpr.LHsExpr RdrName.RdrName+hsTypedUndef ty = SrcLoc.noLoc $ HsExpr.ExprWithTySig hsUndef ty++-- | Create a qualified RdrName from a module name and a variable name+mkRdrName :: String -> String -> RdrName.RdrName+mkRdrName mod var =+ RdrName.mkRdrQual (Module.mkModuleName mod) (OccName.mkVarOcc var)++-- These three functions are simplified copies of those in HscMain, because+-- those functions are not exported. These versions have all error handling+-- removed.+hscParseType = hscParseThing Parser.parseType+hscParseStmt = hscParseThing Parser.parseStmt++hscParseThing :: Lexer.P thing -> DynFlags.DynFlags -> String -> GHC.Ghc thing+hscParseThing parser dflags str = do+ buf <- MonadUtils.liftIO $ StringBuffer.stringToStringBuffer str+ let loc = SrcLoc.mkSrcLoc (FastString.fsLit "<interactive>") 1 0+ let Lexer.POk _ thing = Lexer.unP parser (Lexer.mkPState buf loc dflags)+ return thing++-- | This function imports the module with the given name, for the renamer /+-- typechecker to use. It also imports any "orphans" and "family instances"+-- from modules included by this module, but not the actual modules+-- themselves. I'm not 100% sure how this works, but it seems that any+-- functions defined in included modules are available just by loading the+-- original module, and by doing this orphan stuff, any (type family or class)+-- instances are available as well.+--+-- Most of the code is based on tcRnImports and rnImportDecl, but those+-- functions do a lot more (which I hope we won't need...).+importModule :: Module.ModuleName -> TcRnTypes.RnM ()+importModule mod = do+ let reason = Outputable.text "Hardcoded import" -- Used for trace output+ let pkg = Nothing+ -- Load the interface.+ iface <- LoadIface.loadSrcInterface reason mod False pkg+ -- Load orphan an familiy instance dependencies as well. I think these+ -- dependencies are needed for the type checker to know all instances. Any+ -- other instances (on other packages) are only useful to the+ -- linker, so we can probably safely ignore them here. Dependencies within+ -- the same package are also listed in deps, but I'm not so sure what to do+ -- with them.+ let deps = HscTypes.mi_deps iface+ let orphs = HscTypes.dep_orphs deps+ let finsts = HscTypes.dep_finsts deps+ LoadIface.loadOrphanModules orphs False+ LoadIface.loadOrphanModules finsts True
+ CLasH/Utils/Pretty.hs view
@@ -0,0 +1,81 @@+module CLasH.Utils.Pretty (prettyShow, pprString, pprStringDebug) where++-- Standard imports+import qualified Data.Map as Map+import Text.PrettyPrint.HughesPJClass++-- GHC API+import qualified CoreSyn+import Outputable ( showSDoc, showSDocDebug, ppr, Outputable, OutputableBndr)++-- VHDL Imports +import qualified Language.VHDL.Ppr as Ppr+import qualified Language.VHDL.AST as AST+import qualified Language.VHDL.AST.Ppr++-- Local imports+import CLasH.VHDL.VHDLTypes+import CLasH.Utils.Core.CoreShow++-- | A version of the default pPrintList method, which uses a custom function+-- f instead of pPrint to print elements.+printList :: (a -> Doc) -> [a] -> Doc+printList f = brackets . fsep . punctuate comma . map f++{-+instance Pretty FuncData where+ pPrint (FuncData flatfunc entity arch) =+ text "Flattened: " $$ nest 15 (ppffunc flatfunc)+ $+$ text "Entity" $$ nest 15 (ppent entity)+ $+$ pparch arch+ where+ ppffunc (Just f) = pPrint f+ ppffunc Nothing = text "Nothing"+ ppent (Just e) = pPrint e+ ppent Nothing = text "Nothing"+ pparch Nothing = text "VHDL architecture not present"+ pparch (Just _) = text "VHDL architecture present"+-}++instance Pretty Entity where+ pPrint (Entity id args res decl) =+ text "Entity: " $$ nest 10 (pPrint id)+ $+$ text "Args: " $$ nest 10 (pPrint args)+ $+$ text "Result: " $$ nest 10 (pPrint res)+ $+$ text "Declaration not shown"++instance (OutputableBndr b, Show b) => Pretty (CoreSyn.Bind b) where+ pPrint (CoreSyn.NonRec b expr) =+ text "NonRec: " $$ nest 10 (prettyBind (b, expr))+ pPrint (CoreSyn.Rec binds) =+ text "Rec: " $$ nest 10 (vcat $ map (prettyBind) binds)++instance (OutputableBndr b, Show b) => Pretty (CoreSyn.Expr b) where+ pPrint = text . show++instance Pretty AST.VHDLId where+ pPrint id = Ppr.ppr id+ +instance Pretty AST.VHDLName where+ pPrint name = Ppr.ppr name++prettyBind :: (Show b, Show e) => (b, e) -> Doc+prettyBind (b, expr) =+ text b' <> text " = " <> text expr'+ where+ b' = show b+ expr' = show expr++instance (Pretty k, Pretty v) => Pretty (Map.Map k v) where+ pPrint = + vcat . map ppentry . Map.toList+ where+ ppentry (k, v) =+ pPrint k <> text " : " $$ nest 15 (pPrint v)++-- Convenience method for turning an Outputable into a string+pprString :: (Outputable x) => x -> String+pprString = showSDoc . ppr++pprStringDebug :: (Outputable x) => x -> String+pprStringDebug = showSDocDebug . ppr
+ CLasH/VHDL.hs view
@@ -0,0 +1,99 @@+--+-- Functions to generate VHDL from FlatFunctions+--+module CLasH.VHDL where++-- Standard modules+import qualified Data.Map as Map+import qualified Maybe+import qualified Control.Arrow as Arrow+import Data.Accessor+import qualified Data.Accessor.Monad.Trans.State as MonadState++-- VHDL Imports+import qualified Language.VHDL.AST as AST++-- GHC API+import qualified CoreSyn++-- Local imports+import CLasH.Translator.TranslatorTypes+import CLasH.VHDL.VHDLTypes+import CLasH.VHDL.VHDLTools+import CLasH.VHDL.Constants+import CLasH.VHDL.Generate++createDesignFiles ::+ [CoreSyn.CoreBndr] -- ^ Top binders+ -> TranslatorSession [(AST.VHDLId, AST.DesignFile)]++createDesignFiles topbndrs = do+ bndrss <- mapM recurseArchitectures topbndrs+ let bndrs = concat bndrss+ lunits <- mapM createLibraryUnit bndrs+ typepackage <- createTypesPackage+ let files = map (Arrow.second $ AST.DesignFile full_context) lunits+ return $ typepackage : files+ where+ full_context =+ mkUseAll ["work", "types"]+ : (mkUseAll ["work"]+ : ieee_context)++ieee_context = [+ AST.Library $ mkVHDLBasicId "IEEE",+ mkUseAll ["IEEE", "std_logic_1164"],+ mkUseAll ["IEEE", "numeric_std"],+ mkUseAll ["std", "textio"]+ ]++-- | Find out which entities are needed for the given top level binders.+recurseArchitectures ::+ CoreSyn.CoreBndr -- ^ The top level binder+ -> TranslatorSession [CoreSyn.CoreBndr] + -- ^ The binders of all needed functions.+recurseArchitectures bndr = do+ -- See what this binder directly uses+ (_, used) <- getArchitecture bndr+ -- Recursively check what each of the used functions uses+ useds <- mapM recurseArchitectures used+ -- And return all of them+ return $ bndr : (concat useds)++-- | Creates the types package, based on the current type state.+createTypesPackage ::+ TranslatorSession (AST.VHDLId, AST.DesignFile) + -- ^ The id and content of the types package+ +createTypesPackage = do+ tyfuns <- MonadState.get (tsType .> tsTypeFuns)+ let tyfun_decls = mkBuiltInShow ++ map snd (Map.elems tyfuns)+ ty_decls_maybes <- MonadState.get (tsType .> tsTypeDecls)+ let ty_decls = Maybe.catMaybes ty_decls_maybes+ let subProgSpecs = map (\(AST.SubProgBody spec _ _) -> AST.PDISS spec) tyfun_decls+ let type_package_dec = AST.LUPackageDec $ AST.PackageDec (mkVHDLBasicId "types") ([tfvec_index_decl] ++ ty_decls ++ subProgSpecs)+ let type_package_body = AST.LUPackageBody $ AST.PackageBody typesId tyfun_decls+ return (mkVHDLBasicId "types", AST.DesignFile ieee_context [type_package_dec, type_package_body])+ where+ tfvec_index_decl = AST.PDISD $ AST.SubtypeDec tfvec_indexTM tfvec_index_def+ tfvec_range = AST.ConstraintRange $ AST.SubTypeRange (AST.PrimLit "-1") (AST.PrimName $ AST.NAttribute $ AST.AttribName (AST.NSimple integerTM) (AST.NSimple highId) Nothing)+ tfvec_index_def = AST.SubtypeIn integerTM (Just tfvec_range)++-- Create a use foo.bar.all statement. Takes a list of components in the used+-- name. Must contain at least two components+mkUseAll :: [String] -> AST.ContextItem+mkUseAll ss = + AST.Use $ from AST.:.: AST.All+ where+ base_prefix = (AST.NSimple $ mkVHDLBasicId $ head ss)+ from = foldl select base_prefix (tail ss)+ select prefix s = AST.NSelected $ prefix AST.:.: (AST.SSimple $ mkVHDLBasicId s)+ +createLibraryUnit ::+ CoreSyn.CoreBndr+ -> TranslatorSession (AST.VHDLId, [AST.LibraryUnit])++createLibraryUnit bndr = do+ entity <- getEntity bndr+ (arch, _) <- getArchitecture bndr+ return (ent_id entity, [AST.LUEntity (ent_dec entity), AST.LUArch arch])
+ CLasH/VHDL/Constants.hs view
@@ -0,0 +1,399 @@+module CLasH.VHDL.Constants where++-- VHDL Imports +import qualified Language.VHDL.AST as AST++-- | A list of all builtin functions. Partly duplicates the name table+-- in VHDL.Generate, but we can't use that map everywhere due to+-- circular dependencie.+builtinIds = [ exId, replaceId, headId, lastId, tailId, initId, takeId, dropId+ , selId, plusgtId, ltplusId, plusplusId, mapId, zipWithId, foldlId+ , foldrId, zipId, unzipId, shiftlId, shiftrId, rotlId, rotrId+ , concatId, reverseId, iteratenId, iterateId, generatenId, generateId+ , emptyId, singletonId, copynId, copyId, lengthTId, nullId+ , hwxorId, hwandId, hworId, hwnotId, equalityId, inEqualityId, ltId+ , lteqId, gtId, gteqId, boolOrId, boolAndId, plusId, timesId+ , negateId, minusId, fromSizedWordId, fromIntegerId, resizeWordId+ , resizeIntId, sizedIntId, smallIntegerId, fstId, sndId, blockRAMId+ , splitId, minimumId, fromRangedWordId + ]+--------------+-- Identifiers+--------------++-- | reset and clock signal identifiers in String form+resetStr, clockStr :: String+resetStr = "resetn"+clockStr = "clock"++-- | reset and clock signal identifiers in basic AST.VHDLId form+resetId, clockId :: AST.VHDLId+resetId = AST.unsafeVHDLBasicId resetStr+clockId = AST.unsafeVHDLBasicId clockStr++integerId :: AST.VHDLId+integerId = AST.unsafeVHDLBasicId "integer"++-- | \"types\" identifier+typesId :: AST.VHDLId+typesId = AST.unsafeVHDLBasicId "types"++-- | work identifier+workId :: AST.VHDLId+workId = AST.unsafeVHDLBasicId "work"++-- | std identifier+stdId :: AST.VHDLId+stdId = AST.unsafeVHDLBasicId "std"+++-- | textio identifier+textioId :: AST.VHDLId+textioId = AST.unsafeVHDLBasicId "textio"++-- | range attribute identifier+rangeId :: AST.VHDLId+rangeId = AST.unsafeVHDLBasicId "range"+++-- | high attribute identifier+highId :: AST.VHDLId+highId = AST.unsafeVHDLBasicId "high"++-- | range attribute identifier+imageId :: AST.VHDLId+imageId = AST.unsafeVHDLBasicId "image"++-- | event attribute identifie+eventId :: AST.VHDLId+eventId = AST.unsafeVHDLBasicId "event"+++-- | default function identifier+defaultId :: AST.VHDLId+defaultId = AST.unsafeVHDLBasicId "default"++-- FSVec function identifiers++-- | ex (operator ! in original Haskell source) function identifier+exId :: String+exId = "!"++-- | sel (function select in original Haskell source) function identifier+selId :: String+selId = "select"+++-- | ltplus (function (<+) in original Haskell source) function identifier+ltplusId :: String+ltplusId = "<+"+++-- | plusplus (function (++) in original Haskell source) function identifier+plusplusId :: String+plusplusId = "++"+++-- | empty function identifier+emptyId :: String+emptyId = "empty"++-- | plusgt (function (+>) in original Haskell source) function identifier+plusgtId :: String+plusgtId = "+>"++-- | singleton function identifier+singletonId :: String+singletonId = "singleton"++-- | length function identifier+lengthId :: String+lengthId = "length"+++-- | isnull (function null in original Haskell source) function identifier+nullId :: String+nullId = "null"+++-- | replace function identifier+replaceId :: String+replaceId = "replace"+++-- | head function identifier+headId :: String+headId = "head"+++-- | last function identifier+lastId :: String+lastId = "last"+++-- | init function identifier+initId :: String+initId = "init"+++-- | tail function identifier+tailId :: String+tailId = "tail"++-- | minimum ftp function identifier+minimumId :: String+minimumId = "minimum"++-- | take function identifier+takeId :: String+takeId = "take"+++-- | drop function identifier+dropId :: String+dropId = "drop"++-- | shiftl function identifier+shiftlId :: String+shiftlId = "shiftl"++-- | shiftr function identifier+shiftrId :: String+shiftrId = "shiftr"++-- | rotl function identifier+rotlId :: String+rotlId = "rotl"++-- | reverse function identifier+rotrId :: String+rotrId = "rotr"++-- | concatenate the vectors in a vector+concatId :: String+concatId = "concat"++-- | reverse function identifier+reverseId :: String+reverseId = "reverse"++-- | iterate function identifier+iterateId :: String+iterateId = "iterate"++-- | iteraten function identifier+iteratenId :: String+iteratenId = "iteraten"++-- | iterate function identifier+generateId :: String+generateId = "generate"++-- | iteraten function identifier+generatenId :: String+generatenId = "generaten"++-- | copy function identifier+copyId :: String+copyId = "copy"++-- | copyn function identifier+copynId :: String+copynId = "copyn"++-- | map function identifier+mapId :: String+mapId = "map"++-- | zipwith function identifier+zipWithId :: String+zipWithId = "zipWith"++-- | foldl function identifier+foldlId :: String+foldlId = "foldl"++-- | foldr function identifier+foldrId :: String+foldrId = "foldr"++-- | zip function identifier+zipId :: String+zipId = "zip"++-- | unzip function identifier+unzipId :: String+unzipId = "unzip"++-- | hwxor function identifier+hwxorId :: String+hwxorId = "hwxor"++-- | hwor function identifier+hworId :: String+hworId = "hwor"++-- | hwnot function identifier+hwnotId :: String+hwnotId = "hwnot"++-- | hwand function identifier+hwandId :: String+hwandId = "hwand"++lengthTId :: String+lengthTId = "lengthT"++fstId :: String+fstId = "fst"++sndId :: String+sndId = "snd"++splitId :: String+splitId = "split"++-- Equality Operations+equalityId :: String+equalityId = "=="++inEqualityId :: String+inEqualityId = "/="++gtId :: String+gtId = ">"++ltId :: String+ltId = "<"++gteqId :: String+gteqId = ">="++lteqId :: String+lteqId = "<="++boolOrId :: String+boolOrId = "||"++boolAndId :: String+boolAndId = "&&"++boolNot :: String+boolNot = "not"++-- Numeric Operations++-- | plus operation identifier+plusId :: String+plusId = "+"++-- | times operation identifier+timesId :: String+timesId = "*"++-- | negate operation identifier+negateId :: String+negateId = "negate"++-- | minus operation identifier+minusId :: String+minusId = "-"++-- | convert sizedword to ranged+fromSizedWordId :: String+fromSizedWordId = "fromUnsigned"++fromRangedWordId :: String+fromRangedWordId = "fromIndex"++toIntegerId :: String+toIntegerId = "to_integer"++fromIntegerId :: String+fromIntegerId = "fromInteger"++toSignedId :: String+toSignedId = "to_signed"++toUnsignedId :: String+toUnsignedId = "to_unsigned"++resizeId :: String+resizeId = "resize"++resizeWordId :: String+resizeWordId = "resizeWord"++resizeIntId :: String+resizeIntId = "resizeInt"++smallIntegerId :: String+smallIntegerId = "smallInteger"++sizedIntId :: String+sizedIntId = "Signed"++tfvecId :: String+tfvecId = "Vector"++blockRAMId :: String+blockRAMId = "blockRAM"++-- | output file identifier (from std.textio)+showIdString :: String+showIdString = "show"++showId :: AST.VHDLId+showId = AST.unsafeVHDLExtId showIdString++-- | write function identifier (from std.textio)+writeId :: AST.VHDLId+writeId = AST.unsafeVHDLBasicId "write"++-- | output file identifier (from std.textio)+outputId :: AST.VHDLId+outputId = AST.unsafeVHDLBasicId "output"++------------------+-- VHDL type marks+------------------++-- | The Bit type mark+bitTM :: AST.TypeMark+bitTM = AST.unsafeVHDLBasicId "Bit"++-- | Stardard logic type mark+std_logicTM :: AST.TypeMark+std_logicTM = AST.unsafeVHDLBasicId "std_logic"++-- | boolean type mark+booleanTM :: AST.TypeMark+booleanTM = AST.unsafeVHDLBasicId "boolean"++-- | fsvec_index AST. TypeMark+tfvec_indexTM :: AST.TypeMark+tfvec_indexTM = AST.unsafeVHDLBasicId "tfvec_index"++-- | natural AST. TypeMark+naturalTM :: AST.TypeMark+naturalTM = AST.unsafeVHDLBasicId "natural"++-- | integer TypeMark+integerTM :: AST.TypeMark+integerTM = AST.unsafeVHDLBasicId "integer"++-- | signed TypeMark+signedTM :: AST.TypeMark+signedTM = AST.unsafeVHDLBasicId "signed"++-- | unsigned TypeMark+unsignedTM :: AST.TypeMark+unsignedTM = AST.unsafeVHDLBasicId "unsigned"++-- | string TypeMark+stringTM :: AST.TypeMark+stringTM = AST.unsafeVHDLBasicId "string"++-- | tup VHDLName suffix+tupVHDLSuffix :: AST.VHDLId -> AST.Suffix+tupVHDLSuffix id = AST.SSimple id
+ CLasH/VHDL/Generate.hs view
@@ -0,0 +1,1634 @@+module CLasH.VHDL.Generate where++-- Standard modules+import qualified Data.List as List+import qualified Data.Map as Map+import qualified Control.Monad as Monad+import qualified Maybe+import qualified Data.Either as Either+import qualified Data.Accessor.Monad.Trans.State as MonadState++-- VHDL Imports+import qualified Language.VHDL.AST as AST++-- GHC API+import qualified CoreSyn+import qualified Type+import qualified Var+import qualified Id+import qualified IdInfo+import qualified Literal+import qualified Name+import qualified TyCon++-- Local imports+import CLasH.Translator.TranslatorTypes+import CLasH.VHDL.Constants+import CLasH.VHDL.VHDLTypes+import CLasH.VHDL.VHDLTools+import CLasH.Utils+import CLasH.Utils.Core.CoreTools+import CLasH.Utils.Pretty+import qualified CLasH.Normalize as Normalize++-----------------------------------------------------------------------------+-- Functions to generate VHDL for user-defined functions.+-----------------------------------------------------------------------------++-- | Create an entity for a given function+getEntity ::+ CoreSyn.CoreBndr+ -> TranslatorSession Entity -- ^ The resulting entity++getEntity fname = makeCached fname tsEntities $ do+ expr <- Normalize.getNormalized False fname+ -- Split the normalized expression+ let (args, binds, res) = Normalize.splitNormalized expr+ -- Generate ports for all non-empty types+ args' <- catMaybesM $ mapM mkMap args+ -- TODO: Handle Nothing+ res' <- mkMap res+ count <- MonadState.get tsEntityCounter + let vhdl_id = mkVHDLBasicId $ varToString fname ++ "Component_" ++ show count+ MonadState.set tsEntityCounter (count + 1)+ let ent_decl = createEntityAST vhdl_id args' res'+ let signature = Entity vhdl_id args' res' ent_decl+ return signature+ where+ mkMap ::+ --[(SignalId, SignalInfo)] + CoreSyn.CoreBndr + -> TranslatorSession (Maybe Port)+ mkMap = (\bndr ->+ let+ --info = Maybe.fromMaybe+ -- (error $ "Signal not found in the name map? This should not happen!")+ -- (lookup id sigmap)+ -- Assume the bndr has a valid VHDL id already+ id = varToVHDLId bndr+ ty = Var.varType bndr+ error_msg = "\nVHDL.createEntity.mkMap: Can not create entity: " ++ pprString fname ++ "\nbecause no type can be created for port: " ++ pprString bndr + in do+ type_mark_maybe <- MonadState.lift tsType $ vhdlTy error_msg ty+ case type_mark_maybe of + Just type_mark -> return $ Just (id, type_mark)+ Nothing -> return Nothing+ )++-- | Create the VHDL AST for an entity+createEntityAST ::+ AST.VHDLId -- ^ The name of the function+ -> [Port] -- ^ The entity's arguments+ -> Maybe Port -- ^ The entity's result+ -> AST.EntityDec -- ^ The entity with the ent_decl filled in as well++createEntityAST vhdl_id args res =+ AST.EntityDec vhdl_id ports+ where+ -- Create a basic Id, since VHDL doesn't grok filenames with extended Ids.+ ports = map (mkIfaceSigDec AST.In) args+ ++ (Maybe.maybeToList res_port)+ ++ [clk_port,resetn_port]+ -- Add a clk port if we have state+ clk_port = AST.IfaceSigDec clockId AST.In std_logicTM+ resetn_port = AST.IfaceSigDec resetId AST.In std_logicTM+ res_port = fmap (mkIfaceSigDec AST.Out) res++-- | Create a port declaration+mkIfaceSigDec ::+ AST.Mode -- ^ The mode for the port (In / Out)+ -> Port -- ^ The id and type for the port+ -> AST.IfaceSigDec -- ^ The resulting port declaration++mkIfaceSigDec mode (id, ty) = AST.IfaceSigDec id mode ty++-- | Create an architecture for a given function+getArchitecture ::+ CoreSyn.CoreBndr -- ^ The function to get an architecture for+ -> TranslatorSession (Architecture, [CoreSyn.CoreBndr])+ -- ^ The architecture for this function++getArchitecture fname = makeCached fname tsArchitectures $ do+ expr <- Normalize.getNormalized False fname+ -- Split the normalized expression+ let (args, binds, res) = Normalize.splitNormalized expr+ + -- Get the entity for this function+ signature <- getEntity fname+ let entity_id = ent_id signature++ -- Create signal declarations for all binders in the let expression, except+ -- for the output port (that will already have an output port declared in+ -- the entity).+ sig_dec_maybes <- mapM (mkSigDec . fst) (filter ((/=res).fst) binds)+ let sig_decs = Maybe.catMaybes sig_dec_maybes+ -- Process each bind, resulting in info about state variables and concurrent+ -- statements.+ (state_vars, sms) <- Monad.mapAndUnzipM dobind binds+ let (in_state_maybes, out_state_maybes) = unzip state_vars+ let (statementss, used_entitiess) = unzip sms+ -- Get initial state, if it's there+ initSmap <- MonadState.get tsInitStates+ let init_state = Map.lookup fname initSmap+ -- Create a state proc, if needed+ (state_proc, resbndr) <- case (Maybe.catMaybes in_state_maybes, Maybe.catMaybes out_state_maybes, init_state) of+ ([in_state], [out_state], Nothing) -> do + nonEmpty <- hasNonEmptyType in_state+ if nonEmpty + then error ("No initial state defined for: " ++ show fname) + else return ([],[])+ ([in_state], [out_state], Just resetval) -> do+ nonEmpty <- hasNonEmptyType in_state+ if nonEmpty + then mkStateProcSm (in_state, out_state, resetval)+ else error ("Initial state defined for function with only substate: " ++ show fname)+ ([], [], Just _) -> error $ "Initial state defined for state-less function: " ++ show fname+ ([], [], Nothing) -> return ([],[])+ (ins, outs, res) -> error $ "Weird use of state in " ++ show fname ++ ". In: " ++ show ins ++ " Out: " ++ show outs+ -- Join the create statements and the (optional) state_proc+ let statements = concat statementss ++ state_proc+ -- Create the architecture+ let arch = AST.ArchBody (mkVHDLBasicId "structural") (AST.NSimple entity_id) (map AST.BDISD sig_decs) statements+ let used_entities = (concat used_entitiess) ++ resbndr+ return (arch, used_entities)+ where+ dobind :: (CoreSyn.CoreBndr, CoreSyn.CoreExpr) -- ^ The bind to process+ -> TranslatorSession ((Maybe CoreSyn.CoreBndr, Maybe CoreSyn.CoreBndr), ([AST.ConcSm], [CoreSyn.CoreBndr]))+ -- ^ ((Input state variable, output state variable), (statements, used entities))+ -- newtype unpacking is just a cast+ dobind (bndr, unpacked@(CoreSyn.Cast packed coercion)) + | hasStateType packed && not (hasStateType unpacked)+ = return ((Just bndr, Nothing), ([], []))+ -- With simplCore, newtype packing is just a cast+ dobind (bndr, packed@(CoreSyn.Cast unpacked@(CoreSyn.Var state) coercion)) + | hasStateType packed && not (hasStateType unpacked)+ = return ((Nothing, Just state), ([], []))+ -- Without simplCore, newtype packing uses a data constructor+ dobind (bndr, (CoreSyn.App (CoreSyn.App (CoreSyn.Var con) (CoreSyn.Type _)) (CoreSyn.Var state))) + | isStateCon con+ = return ((Nothing, Just state), ([], []))+ -- Anything else is handled by mkConcSm+ dobind bind = do+ sms <- mkConcSm bind+ return ((Nothing, Nothing), sms)++mkStateProcSm :: + (CoreSyn.CoreBndr, CoreSyn.CoreBndr, CoreSyn.CoreBndr) -- ^ The current state, new state and reset variables+ -> TranslatorSession ([AST.ConcSm], [CoreSyn.CoreBndr]) -- ^ The resulting statements+mkStateProcSm (old, new, res) = do+ let error_msg = "\nVHDL.mkSigDec: Can not make signal declaration for type: \n" ++ pprString res + type_mark_old_maybe <- MonadState.lift tsType $ vhdlTy error_msg (Var.varType old)+ let type_mark_old = Maybe.fromMaybe + (error $ "\nGenerate.mkStateProcSm: empty type for state? Type: " ++ pprString (Var.varType old))+ type_mark_old_maybe+ type_mark_res_maybe <- MonadState.lift tsType $ vhdlTy error_msg (Var.varType res)+ let type_mark_res' = Maybe.fromMaybe + (error $ "\nGenerate.mkStateProcSm: empty type for initial state? Type: " ++ pprString (Var.varType res))+ type_mark_res_maybe+ let type_mark_res = if type_mark_old == type_mark_res' then+ type_mark_res'+ else + error $ "Initial state has different type than state type, state type: " ++ show type_mark_old ++ ", init type: " ++ show type_mark_res' + let resvalid = mkVHDLExtId $ varToString res ++ "val"+ let resvaldec = AST.BDISD $ AST.SigDec resvalid type_mark_res Nothing+ let reswform = AST.Wform [AST.WformElem (AST.PrimName $ AST.NSimple resvalid) Nothing]+ let res_assign = AST.SigAssign (varToVHDLName old) reswform+ let blocklabel = mkVHDLBasicId "state"+ let statelabel = mkVHDLBasicId "stateupdate"+ let rising_edge = AST.NSimple $ mkVHDLBasicId "rising_edge"+ let wform = AST.Wform [AST.WformElem (AST.PrimName $ varToVHDLName new) Nothing]+ let clk_assign = AST.SigAssign (varToVHDLName old) wform+ let rising_edge_clk = AST.PrimFCall $ AST.FCall rising_edge [Nothing AST.:=>: (AST.ADName $ AST.NSimple clockId)]+ let resetn_is_low = (AST.PrimName $ AST.NSimple resetId) AST.:=: (AST.PrimLit "'0'")+ signature <- getEntity res+ let entity_id = ent_id signature+ let reslabel = "resetval_" ++ ((prettyShow . varToVHDLName) res)+ let portmaps = mkAssocElems [] (AST.NSimple resvalid) signature+ let reset_statement = mkComponentInst reslabel entity_id portmaps+ let clk_statement = [AST.ElseIf rising_edge_clk [clk_assign]]+ let statement = AST.IfSm resetn_is_low [res_assign] clk_statement Nothing+ let stateupdate = AST.CSPSm $ AST.ProcSm statelabel [clockId,resetId,resvalid] [statement]+ let block = AST.CSBSm $ AST.BlockSm blocklabel [] (AST.PMapAspect []) [resvaldec] [reset_statement,stateupdate]+ return ([block],[res])++-- | Transforms a core binding into a VHDL concurrent statement+mkConcSm ::+ (CoreSyn.CoreBndr, CoreSyn.CoreExpr) -- ^ The binding to process+ -> TranslatorSession ([AST.ConcSm], [CoreSyn.CoreBndr]) + -- ^ The corresponding VHDL concurrent statements and entities+ -- instantiated.+++-- Ignore Cast expressions, they should not longer have any meaning as long as+-- the type works out. Throw away state repacking+mkConcSm (bndr, to@(CoreSyn.Cast from ty))+ | hasStateType to && hasStateType from+ = return ([],[])+mkConcSm (bndr, CoreSyn.Cast expr ty) = mkConcSm (bndr, expr)++-- Simple a = b assignments are just like applications, but without arguments.+-- We can't just generate an unconditional assignment here, since b might be a+-- top level binding (e.g., a function with no arguments).+mkConcSm (bndr, CoreSyn.Var v) =+ genApplication (Left bndr) v []++mkConcSm (bndr, app@(CoreSyn.App _ _))= do+ let (CoreSyn.Var f, args) = CoreSyn.collectArgs app+ let valargs = get_val_args (Var.varType f) args+ genApplication (Left bndr) f (map Left valargs)++-- A single alt case must be a selector. This means the scrutinee is a simple+-- variable, the alternative is a dataalt with a single non-wild binder that+-- is also returned.+mkConcSm (bndr, expr@(CoreSyn.Case (CoreSyn.Var scrut) b ty [alt])) + -- Don't generate VHDL for substate extraction+ | hasStateType bndr = return ([], [])+ | otherwise =+ case alt of+ (CoreSyn.DataAlt dc, bndrs, (CoreSyn.Var sel_bndr)) -> do+ nonemptysel <- hasNonEmptyType sel_bndr + if nonemptysel + then do+ bndrs' <- Monad.filterM hasNonEmptyType bndrs+ case List.elemIndex sel_bndr bndrs' of+ Just i -> do+ htypeScrt <- MonadState.lift tsType $ mkHTypeEither (Var.varType scrut)+ htypeBndr <- MonadState.lift tsType $ mkHTypeEither (Var.varType bndr)+ case htypeScrt == htypeBndr of+ True -> do+ let sel_name = varToVHDLName scrut+ let sel_expr = AST.PrimName sel_name+ return ([mkUncondAssign (Left bndr) sel_expr], [])+ otherwise ->+ case htypeScrt of+ Right (AggrType _ _) -> do+ labels <- MonadState.lift tsType $ getFieldLabels (Id.idType scrut)+ let label = labels!!i+ let sel_name = mkSelectedName (varToVHDLName scrut) label+ let sel_expr = AST.PrimName sel_name+ return ([mkUncondAssign (Left bndr) sel_expr], [])+ _ -> do -- error $ "DIE!"+ let sel_name = varToVHDLName scrut+ let sel_expr = AST.PrimName sel_name+ return ([mkUncondAssign (Left bndr) sel_expr], [])+ Nothing -> error $ "\nVHDL.mkConcSM: Not in normal form: Not a selector case: result is not one of the binders\n" ++ (pprString expr)+ else+ -- A selector case that selects a state value, ignore it.+ return ([], [])+ + _ -> error $ "\nVHDL.mkConcSM: Not in normal form: Not a selector case:\n" ++ (pprString expr)++-- Multiple case alt become conditional assignments and have only wild+-- binders in the alts and only variables in the case values and a variable+-- for a scrutinee. We check the constructor of the second alt, since the+-- first is the default case, if there is any.+mkConcSm (bndr, (CoreSyn.Case (CoreSyn.Var scrut) _ _ (alt:alts))) = do+ scrut' <- MonadState.lift tsType $ varToVHDLExpr scrut+ -- Omit first condition, which is the default+ altcons <- MonadState.lift tsType $ mapM (altconToVHDLExpr . (\(con,_,_) -> con)) alts+ let cond_exprs = map (\x -> scrut' AST.:=: x) altcons+ -- Rotate expressions to the left, so that the expression related to the default case is the last+ exprs <- MonadState.lift tsType $ mapM (varToVHDLExpr . (\(_,_,CoreSyn.Var expr) -> expr)) (alts ++ [alt])+ return ([mkAltsAssign (Left bndr) cond_exprs exprs], [])++mkConcSm (_, CoreSyn.Case _ _ _ _) = error "\nVHDL.mkConcSm: Not in normal form: Case statement does not have a simple variable as scrutinee"+mkConcSm (bndr, expr) = error $ "\nVHDL.mkConcSM: Unsupported binding in let expression: " ++ pprString bndr ++ " = " ++ pprString expr++-----------------------------------------------------------------------------+-- Functions to generate VHDL for builtin functions+-----------------------------------------------------------------------------++-- | A function to wrap a builder-like function that expects its arguments to+-- be expressions.+genExprArgs wrap dst func args = do+ args' <- argsToVHDLExprs args+ wrap dst func args'++-- | Turn the all lefts into VHDL Expressions.+argsToVHDLExprs :: [Either CoreSyn.CoreExpr AST.Expr] -> TranslatorSession [AST.Expr]+argsToVHDLExprs = catMaybesM . (mapM argToVHDLExpr)++argToVHDLExpr :: Either CoreSyn.CoreExpr AST.Expr -> TranslatorSession (Maybe AST.Expr)+argToVHDLExpr (Left expr) = MonadState.lift tsType $ do+ let errmsg = "Generate.argToVHDLExpr: Using non-representable type? Should not happen!"+ ty_maybe <- vhdlTy errmsg expr+ case ty_maybe of+ Just _ -> do+ vhdl_expr <- varToVHDLExpr $ exprToVar expr+ return $ Just vhdl_expr+ Nothing -> return Nothing++argToVHDLExpr (Right expr) = return $ Just expr++-- A function to wrap a builder-like function that generates no component+-- instantiations+genNoInsts ::+ (dst -> func -> args -> TranslatorSession [AST.ConcSm])+ -> (dst -> func -> args -> TranslatorSession ([AST.ConcSm], [CoreSyn.CoreBndr]))+genNoInsts wrap dst func args = do+ concsms <- wrap dst func args+ return (concsms, [])++-- | A function to wrap a builder-like function that expects its arguments to+-- be variables.+genVarArgs ::+ (dst -> func -> [Var.Var] -> res)+ -> (dst -> func -> [Either CoreSyn.CoreExpr AST.Expr] -> res)+genVarArgs wrap = genCoreArgs $ \dst func args -> let+ args' = map exprToVar args+ in+ wrap dst func args'++-- | A function to wrap a builder-like function that expects its arguments to+-- be core expressions.+genCoreArgs ::+ (dst -> func -> [CoreSyn.CoreExpr] -> res)+ -> (dst -> func -> [Either CoreSyn.CoreExpr AST.Expr] -> res)+genCoreArgs wrap dst func args = wrap dst func args'+ where+ -- Check (rather crudely) that all arguments are CoreExprs+ args' = case Either.partitionEithers args of + (exprargs, []) -> exprargs+ (exprsargs, rest) -> error $ "\nGenerate.genCoreArgs: expect core expression arguments but found ast exprs:" ++ (show rest)++-- | A function to wrap a builder-like function that produces an expression+-- and expects it to be assigned to the destination.+genExprRes ::+ ((Either CoreSyn.CoreBndr AST.VHDLName) -> func -> [arg] -> TranslatorSession AST.Expr)+ -> ((Either CoreSyn.CoreBndr AST.VHDLName) -> func -> [arg] -> TranslatorSession [AST.ConcSm])+genExprRes wrap dst func args = do+ expr <- wrap dst func args+ return [mkUncondAssign dst expr]++-- | Generate a binary operator application. The first argument should be a+-- constructor from the AST.Expr type, e.g. AST.And.+genOperator2 :: (AST.Expr -> AST.Expr -> AST.Expr) -> BuiltinBuilder +genOperator2 op = genNoInsts $ genExprArgs $ genExprRes (genOperator2' op)+genOperator2' :: (AST.Expr -> AST.Expr -> AST.Expr) -> dst -> CoreSyn.CoreBndr -> [AST.Expr] -> TranslatorSession AST.Expr+genOperator2' op _ f [arg1, arg2] = return $ op arg1 arg2++-- | Generate a unary operator application+genOperator1 :: (AST.Expr -> AST.Expr) -> BuiltinBuilder +genOperator1 op = genNoInsts $ genExprArgs $ genExprRes (genOperator1' op)+genOperator1' :: (AST.Expr -> AST.Expr) -> dst -> CoreSyn.CoreBndr -> [AST.Expr] -> TranslatorSession AST.Expr+genOperator1' op _ f [arg] = return $ op arg++-- | Generate a unary operator application+genNegation :: BuiltinBuilder +genNegation = genNoInsts $ genVarArgs $ genExprRes genNegation'+genNegation' :: dst -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession AST.Expr+genNegation' _ f [arg] = do+ arg1 <- MonadState.lift tsType $ varToVHDLExpr arg+ let ty = Var.varType arg+ let (tycon, args) = Type.splitTyConApp ty+ let name = Name.getOccString (TyCon.tyConName tycon)+ case name of+ "Signed" -> return $ AST.Neg arg1+ otherwise -> error $ "\nGenerate.genNegation': Negation not allowed for type: " ++ show name ++-- | Generate a function call from the destination binder, function name and a+-- list of expressions (its arguments)+genFCall :: Bool -> BuiltinBuilder +genFCall switch = genNoInsts $ genExprArgs $ genExprRes (genFCall' switch)+genFCall' :: Bool -> Either CoreSyn.CoreBndr AST.VHDLName -> CoreSyn.CoreBndr -> [AST.Expr] -> TranslatorSession AST.Expr+genFCall' switch (Left res) f args = do+ let fname = varToString f+ let el_ty = if switch then (Var.varType res) else ((tfvec_elem . Var.varType) res)+ id <- MonadState.lift tsType $ vectorFunId el_ty fname+ return $ AST.PrimFCall $ AST.FCall (AST.NSimple id) $+ map (\exp -> Nothing AST.:=>: AST.ADExpr exp) args+genFCall' _ (Right name) _ _ = error $ "\nGenerate.genFCall': Cannot generate builtin function call assigned to a VHDLName: " ++ show name++genFromSizedWord :: BuiltinBuilder+genFromSizedWord = genNoInsts $ genExprArgs genFromSizedWord'+genFromSizedWord' :: Either CoreSyn.CoreBndr AST.VHDLName -> CoreSyn.CoreBndr -> [AST.Expr] -> TranslatorSession [AST.ConcSm]+genFromSizedWord' (Left res) f args@[arg] =+ return [mkUncondAssign (Left res) arg]+ -- let fname = varToString f+ -- return $ AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLBasicId toIntegerId)) $+ -- map (\exp -> Nothing AST.:=>: AST.ADExpr exp) args+genFromSizedWord' (Right name) _ _ = error $ "\nGenerate.genFromSizedWord': Cannot generate builtin function call assigned to a VHDLName: " ++ show name++genFromRangedWord :: BuiltinBuilder+genFromRangedWord = genNoInsts $ genExprArgs $ genExprRes genFromRangedWord'+genFromRangedWord' :: Either CoreSyn.CoreBndr AST.VHDLName -> CoreSyn.CoreBndr -> [AST.Expr] -> TranslatorSession AST.Expr+genFromRangedWord' (Left res) f [arg] = do {+ ; let { ty = Var.varType res+ ; (tycon, args) = Type.splitTyConApp ty+ ; name = Name.getOccString (TyCon.tyConName tycon)+ } ;+ ; len <- MonadState.lift tsType $ tfp_to_int (sized_word_len_ty ty)+ ; return $ AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLBasicId resizeId))+ [Nothing AST.:=>: AST.ADExpr arg, Nothing AST.:=>: AST.ADExpr( AST.PrimLit (show len))]+ }+genFromRangedWord' (Right name) _ _ = error $ "\nGenerate.genFromRangedWord': Cannot generate builtin function call assigned to a VHDLName: " ++ show name++genResize :: BuiltinBuilder+genResize = genNoInsts $ genExprArgs $ genExprRes genResize'+genResize' :: Either CoreSyn.CoreBndr AST.VHDLName -> CoreSyn.CoreBndr -> [AST.Expr] -> TranslatorSession AST.Expr+genResize' (Left res) f [arg] = do {+ ; let { ty = Var.varType res+ ; (tycon, args) = Type.splitTyConApp ty+ ; name = Name.getOccString (TyCon.tyConName tycon)+ } ;+ ; len <- case name of+ "Signed" -> MonadState.lift tsType $ tfp_to_int (sized_int_len_ty ty)+ "Unsigned" -> MonadState.lift tsType $ tfp_to_int (sized_word_len_ty ty)+ ; return $ AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLBasicId resizeId))+ [Nothing AST.:=>: AST.ADExpr arg, Nothing AST.:=>: AST.ADExpr( AST.PrimLit (show len))]+ }+genResize' (Right name) _ _ = error $ "\nGenerate.genFromSizedWord': Cannot generate builtin function call assigned to a VHDLName: " ++ show name++genTimes :: BuiltinBuilder+genTimes = genNoInsts $ genExprArgs $ genExprRes genTimes'+genTimes' :: Either CoreSyn.CoreBndr AST.VHDLName -> CoreSyn.CoreBndr -> [AST.Expr] -> TranslatorSession AST.Expr+genTimes' (Left res) f [arg1,arg2] = do {+ ; let { ty = Var.varType res+ ; (tycon, args) = Type.splitTyConApp ty+ ; name = Name.getOccString (TyCon.tyConName tycon)+ } ;+ ; len <- case name of+ "Signed" -> MonadState.lift tsType $ tfp_to_int (sized_int_len_ty ty)+ "Unsigned" -> MonadState.lift tsType $ tfp_to_int (sized_word_len_ty ty)+ "Index" -> do { ubound <- MonadState.lift tsType $ tfp_to_int (ranged_word_bound_ty ty)+ ; let bitsize = floor (logBase 2 (fromInteger (toInteger ubound)))+ ; return bitsize+ }+ ; return $ AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLBasicId resizeId))+ [Nothing AST.:=>: AST.ADExpr (arg1 AST.:*: arg2), Nothing AST.:=>: AST.ADExpr( AST.PrimLit (show len))]+ }+genTimes' (Right name) _ _ = error $ "\nGenerate.genTimes': Cannot generate builtin function call assigned to a VHDLName: " ++ show name++-- fromInteger turns an Integer into a Num instance. Since Integer is+-- not representable and is only allowed for literals, the actual+-- Integer should be inlined entirely into the fromInteger argument.+genFromInteger :: BuiltinBuilder+genFromInteger = genNoInsts $ genCoreArgs $ genExprRes genFromInteger'+genFromInteger' :: Either CoreSyn.CoreBndr AST.VHDLName -> CoreSyn.CoreBndr -> [CoreSyn.CoreExpr] -> TranslatorSession AST.Expr+genFromInteger' (Left res) f args = do+ let ty = Var.varType res+ let (tycon, tyargs) = Type.splitTyConApp ty+ let name = Name.getOccString (TyCon.tyConName tycon)+ len <- case name of+ "Signed" -> MonadState.lift tsType $ tfp_to_int (sized_int_len_ty ty)+ "Unsigned" -> MonadState.lift tsType $ tfp_to_int (sized_word_len_ty ty)+ "Index" -> do+ bound <- MonadState.lift tsType $ tfp_to_int (ranged_word_bound_ty ty)+ return $ floor (logBase 2 (fromInteger (toInteger (bound)))) + 1+ let fname = case name of "Signed" -> toSignedId ; "Unsigned" -> toUnsignedId ; "Index" -> toUnsignedId+ case args of+ [integer] -> do -- The type and dictionary arguments are removed by genApplication+ literal <- getIntegerLiteral integer+ return $ AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLBasicId fname))+ [Nothing AST.:=>: AST.ADExpr (AST.PrimLit (show literal)), Nothing AST.:=>: AST.ADExpr( AST.PrimLit (show len))]+ _ -> error $ "\nGenerate.genFromInteger': Wrong number of arguments to genInteger. Applying " ++ pprString f ++ " to " ++ pprString args++genFromInteger' (Right name) _ _ = error $ "\nGenerate.genFromInteger': Cannot generate builtin function call assigned to a VHDLName: " ++ show name++genSizedInt :: BuiltinBuilder+genSizedInt = genFromInteger++{-+-- This function is useful for use with vectorTH, since that generates+-- explicit references to the TFVec constructor (which is normally+-- hidden). Below implementation is probably not current anymore, but+-- kept here in case we start using vectorTH again.+-- | Generate a Builder for the builtin datacon TFVec+genTFVec :: BuiltinBuilder+genTFVec (Left res) f [Left (CoreSyn.Let (CoreSyn.Rec letBinders) letRes)] = do {+ -- Generate Assignments for all the binders+ ; letAssigns <- mapM genBinderAssign letBinders+ -- Generate assignments for the result (which might be another let binding)+ ; (resBinders,resAssignments) <- genResAssign letRes+ -- Get all the Assigned binders+ ; let assignedBinders = Maybe.catMaybes (map fst letAssigns)+ -- Make signal names for all the assigned binders+ ; sigs <- mapM (\x -> MonadState.lift tsType $ varToVHDLExpr x) (assignedBinders ++ resBinders)+ -- Assign all the signals to the resulting vector+ ; let { vecsigns = mkAggregateSignal sigs+ ; vecassign = mkUncondAssign (Left res) vecsigns+ } ;+ -- Generate all the signal declaration for the assigned binders+ ; sig_dec_maybes <- mapM mkSigDec (assignedBinders ++ resBinders)+ ; let { sig_decs = map (AST.BDISD) (Maybe.catMaybes $ sig_dec_maybes)+ -- Setup the VHDL Block+ ; block_label = mkVHDLExtId ("TFVec_" ++ show (varToString res))+ ; block = AST.BlockSm block_label [] (AST.PMapAspect []) sig_decs ((concat (map snd letAssigns)) ++ resAssignments ++ [vecassign])+ } ;+ -- Return the block statement coressponding to the TFVec literal+ ; return $ [AST.CSBSm block]+ }+ where+ genBinderAssign :: (CoreSyn.CoreBndr, CoreSyn.CoreExpr) -> TranslatorSession (Maybe CoreSyn.CoreBndr, [AST.ConcSm])+ -- For now we only translate applications+ genBinderAssign (bndr, app@(CoreSyn.App _ _)) = do+ let (CoreSyn.Var f, args) = CoreSyn.collectArgs app+ let valargs = get_val_args (Var.varType f) args+ apps <- genApplication (Left bndr) f (map Left valargs)+ return (Just bndr, apps)+ genBinderAssign _ = return (Nothing,[])+ genResAssign :: CoreSyn.CoreExpr -> TranslatorSession ([CoreSyn.CoreBndr], [AST.ConcSm])+ genResAssign app@(CoreSyn.App _ letexpr) = do+ case letexpr of+ (CoreSyn.Let (CoreSyn.Rec letbndrs) letres) -> do+ letapps <- mapM genBinderAssign letbndrs+ let bndrs = Maybe.catMaybes (map fst letapps)+ let app = (map snd letapps)+ (vars, apps) <- genResAssign letres+ return ((bndrs ++ vars),((concat app) ++ apps))+ otherwise -> return ([],[])+ genResAssign _ = return ([],[])++genTFVec (Left res) f [Left app@(CoreSyn.App _ _)] = do {+ ; let { elems = reduceCoreListToHsList app+ -- Make signal names for all the binders+ ; binders = map (\expr -> case expr of + (CoreSyn.Var b) -> b+ otherwise -> error $ "\nGenerate.genTFVec: Cannot generate TFVec: " + ++ show res ++ ", with elems:\n" ++ show elems ++ "\n" ++ pprString elems) elems+ } ;+ ; sigs <- mapM (\x -> MonadState.lift tsType $ varToVHDLExpr x) binders+ -- Assign all the signals to the resulting vector+ ; let { vecsigns = mkAggregateSignal sigs+ ; vecassign = mkUncondAssign (Left res) vecsigns+ -- Setup the VHDL Block+ ; block_label = mkVHDLExtId ("TFVec_" ++ show (varToString res))+ ; block = AST.BlockSm block_label [] (AST.PMapAspect []) [] [vecassign]+ } ;+ -- Return the block statement coressponding to the TFVec literal+ ; return $ [AST.CSBSm block]+ }+ +genTFVec (Left name) _ [Left xs] = error $ "\nGenerate.genTFVec: Cannot generate TFVec: " ++ show name ++ ", with elems:\n" ++ show xs ++ "\n" ++ pprString xs++genTFVec (Right name) _ _ = error $ "\nGenerate.genTFVec: Cannot generate TFVec assigned to VHDLName: " ++ show name+-}+-- | Generate a generate statement for the builtin function "map"+genMap :: BuiltinBuilder+genMap (Left res) f [Left mapped_f, Left (CoreSyn.Var arg)] = do {+ -- mapped_f must be a CoreExpr (since we can't represent functions as VHDL+ -- expressions). arg must be a CoreExpr (and should be a CoreSyn.Var), since+ -- we must index it (which we couldn't if it was a VHDL Expr, since only+ -- VHDLNames can be indexed).+ -- Setup the generate scheme+ ; len <- MonadState.lift tsType $ tfp_to_int $ (tfvec_len_ty . Var.varType) res+ -- TODO: Use something better than varToString+ ; let { label = mkVHDLExtId ("mapVector" ++ (varToString res))+ ; n_id = mkVHDLBasicId "n"+ ; n_expr = idToVHDLExpr n_id+ ; range = AST.ToRange (AST.PrimLit "0") (AST.PrimLit $ show (len-1))+ ; genScheme = AST.ForGn n_id range+ -- Create the content of the generate statement: Applying the mapped_f to+ -- each of the elements in arg, storing to each element in res+ ; resname = mkIndexedName (varToVHDLName res) n_expr+ ; argexpr = vhdlNameToVHDLExpr $ mkIndexedName (varToVHDLName arg) n_expr+ ; (CoreSyn.Var real_f, already_mapped_args) = CoreSyn.collectArgs mapped_f+ ; valargs = get_val_args (Var.varType real_f) already_mapped_args+ } ;+ ; (app_concsms, used) <- genApplication (Right resname) real_f (map Left valargs ++ [Right argexpr])+ -- Return the generate statement+ ; return ([AST.CSGSm $ AST.GenerateSm label genScheme [] app_concsms], used)+ }++genMap' (Right name) _ _ = error $ "\nGenerate.genMap': Cannot generate map function call assigned to a VHDLName: " ++ show name+ +genZipWith :: BuiltinBuilder+genZipWith (Left res) f args@[Left zipped_f, Left (CoreSyn.Var arg1), Left (CoreSyn.Var arg2)] = do {+ -- Setup the generate scheme+ ; len <- MonadState.lift tsType $ tfp_to_int $ (tfvec_len_ty . Var.varType) res+ -- TODO: Use something better than varToString+ ; let { label = mkVHDLExtId ("zipWithVector" ++ (varToString res))+ ; n_id = mkVHDLBasicId "n"+ ; n_expr = idToVHDLExpr n_id+ ; range = AST.ToRange (AST.PrimLit "0") (AST.PrimLit $ show (len-1))+ ; genScheme = AST.ForGn n_id range+ -- Create the content of the generate statement: Applying the zipped_f to+ -- each of the elements in arg1 and arg2, storing to each element in res+ ; resname = mkIndexedName (varToVHDLName res) n_expr+ ; (CoreSyn.Var real_f, already_mapped_args) = CoreSyn.collectArgs zipped_f+ ; valargs = get_val_args (Var.varType real_f) already_mapped_args+ ; argexpr1 = vhdlNameToVHDLExpr $ mkIndexedName (varToVHDLName arg1) n_expr+ ; argexpr2 = vhdlNameToVHDLExpr $ mkIndexedName (varToVHDLName arg2) n_expr+ } ;+ ; (app_concsms, used) <- genApplication (Right resname) real_f (map Left valargs ++ [Right argexpr1, Right argexpr2])+ -- Return the generate functions+ ; return ([AST.CSGSm $ AST.GenerateSm label genScheme [] app_concsms], used)+ }++genFoldl :: BuiltinBuilder+genFoldl = genFold True++genFoldr :: BuiltinBuilder+genFoldr = genFold False++genFold :: Bool -> BuiltinBuilder+genFold left = genVarArgs (genFold' left)++genFold' :: Bool -> (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession ([AST.ConcSm], [CoreSyn.CoreBndr])+genFold' left res f args@[folded_f , start ,vec]= do+ len <- MonadState.lift tsType $ tfp_to_int (tfvec_len_ty (Var.varType vec))+ genFold'' len left res f args++genFold'' :: Int -> Bool -> (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession ([AST.ConcSm], [CoreSyn.CoreBndr])+-- Special case for an empty input vector, just assign start to res+genFold'' len left (Left res) _ [_, start, vec] | len == 0 = do+ arg <- MonadState.lift tsType $ varToVHDLExpr start+ return ([mkUncondAssign (Left res) arg], [])+ +genFold'' len left (Left res) f [folded_f, start, vec] = do+ -- The vector length+ --len <- MonadState.lift tsType $ tfp_to_int $ (tfvec_len_ty . Var.varType) vec+ -- An expression for len-1+ let len_min_expr = (AST.PrimLit $ show (len-1))+ -- evec is (TFVec n), so it still needs an element type+ let (nvec, _) = Type.splitAppTy (Var.varType vec)+ -- Put the type of the start value in nvec, this will be the type of our+ -- temporary vector+ let tmp_ty = Type.mkAppTy nvec (Var.varType start)+ let error_msg = "\nGenerate.genFold': Can not construct temp vector for element type: " ++ pprString tmp_ty + -- TODO: Handle Nothing+ Just tmp_vhdl_ty <- MonadState.lift tsType $ vhdlTy error_msg tmp_ty+ -- Setup the generate scheme+ let gen_label = mkVHDLExtId ("foldlVector" ++ (varToString vec))+ let block_label = mkVHDLExtId ("foldlVector" ++ (varToString res))+ let gen_range = if left then AST.ToRange (AST.PrimLit "0") len_min_expr+ else AST.DownRange len_min_expr (AST.PrimLit "0")+ let gen_scheme = AST.ForGn n_id gen_range+ -- Make the intermediate vector+ let tmp_dec = AST.BDISD $ AST.SigDec tmp_id tmp_vhdl_ty Nothing+ -- Create the generate statement+ cells' <- sequence [genFirstCell, genOtherCell]+ let (cells, useds) = unzip cells'+ let gen_sm = AST.GenerateSm gen_label gen_scheme [] (map AST.CSGSm cells)+ -- Assign tmp[len-1] or tmp[0] to res+ let out_assign = mkUncondAssign (Left res) $ vhdlNameToVHDLExpr (if left then+ (mkIndexedName tmp_name (AST.PrimLit $ show (len-1))) else+ (mkIndexedName tmp_name (AST.PrimLit "0"))) + let block = AST.BlockSm block_label [] (AST.PMapAspect []) [tmp_dec] [AST.CSGSm gen_sm, out_assign]+ return ([AST.CSBSm block], concat useds)+ where+ -- An id for the counter+ n_id = mkVHDLBasicId "n"+ n_cur = idToVHDLExpr n_id+ -- An expression for previous n+ n_prev = if left then (n_cur AST.:-: (AST.PrimLit "1"))+ else (n_cur AST.:+: (AST.PrimLit "1"))+ -- An id for the tmp result vector+ tmp_id = mkVHDLBasicId "tmp"+ tmp_name = AST.NSimple tmp_id+ -- Generate parts of the fold+ genFirstCell, genOtherCell :: TranslatorSession (AST.GenerateSm, [CoreSyn.CoreBndr])+ genFirstCell = do+ len <- MonadState.lift tsType $ tfp_to_int $ (tfvec_len_ty . Var.varType) vec+ let cond_label = mkVHDLExtId "firstcell"+ -- if n == 0 or n == len-1+ let cond_scheme = AST.IfGn $ n_cur AST.:=: (if left then (AST.PrimLit "0")+ else (AST.PrimLit $ show (len-1)))+ -- Output to tmp[current n]+ let resname = mkIndexedName tmp_name n_cur+ -- Input from start+ argexpr1 <- MonadState.lift tsType $ varToVHDLExpr start+ -- Input from vec[current n]+ let argexpr2 = vhdlNameToVHDLExpr $ mkIndexedName (varToVHDLName vec) n_cur+ (app_concsms, used) <- genApplication (Right resname) folded_f ( if left then+ [Right argexpr1, Right argexpr2]+ else+ [Right argexpr2, Right argexpr1]+ )+ -- Return the conditional generate part+ return (AST.GenerateSm cond_label cond_scheme [] app_concsms, used)++ genOtherCell = do+ len <- MonadState.lift tsType $ tfp_to_int $ (tfvec_len_ty . Var.varType) vec+ let cond_label = mkVHDLExtId "othercell"+ -- if n > 0 or n < len-1+ let cond_scheme = AST.IfGn $ n_cur AST.:/=: (if left then (AST.PrimLit "0")+ else (AST.PrimLit $ show (len-1)))+ -- Output to tmp[current n]+ let resname = mkIndexedName tmp_name n_cur+ -- Input from tmp[previous n]+ let argexpr1 = vhdlNameToVHDLExpr $ mkIndexedName tmp_name n_prev+ -- Input from vec[current n]+ let argexpr2 = vhdlNameToVHDLExpr $ mkIndexedName (varToVHDLName vec) n_cur+ (app_concsms, used) <- genApplication (Right resname) folded_f ( if left then+ [Right argexpr1, Right argexpr2]+ else+ [Right argexpr2, Right argexpr1]+ )+ -- Return the conditional generate part+ return (AST.GenerateSm cond_label cond_scheme [] app_concsms, used)++-- | Generate a generate statement for the builtin function "zip"+genZip :: BuiltinBuilder+genZip = genNoInsts $ genVarArgs genZip'+genZip' :: (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession [AST.ConcSm]+genZip' (Left res) f args@[arg1, arg2] = do {+ -- Setup the generate scheme+ ; len <- MonadState.lift tsType $ tfp_to_int $ (tfvec_len_ty . Var.varType) res+ -- TODO: Use something better than varToString+ ; let { label = mkVHDLExtId ("zipVector" ++ (varToString res))+ ; n_id = mkVHDLBasicId "n"+ ; n_expr = idToVHDLExpr n_id+ ; range = AST.ToRange (AST.PrimLit "0") (AST.PrimLit $ show (len-1))+ ; genScheme = AST.ForGn n_id range+ ; resname' = mkIndexedName (varToVHDLName res) n_expr+ ; argexpr1 = vhdlNameToVHDLExpr $ mkIndexedName (varToVHDLName arg1) n_expr+ ; argexpr2 = vhdlNameToVHDLExpr $ mkIndexedName (varToVHDLName arg2) n_expr+ } ; + ; labels <- MonadState.lift tsType $ getFieldLabels (tfvec_elem (Var.varType res))+ ; let { resnameA = mkSelectedName resname' (labels!!0)+ ; resnameB = mkSelectedName resname' (labels!!1)+ ; resA_assign = mkUncondAssign (Right resnameA) argexpr1+ ; resB_assign = mkUncondAssign (Right resnameB) argexpr2+ } ;+ -- Return the generate functions+ ; return [AST.CSGSm $ AST.GenerateSm label genScheme [] [resA_assign,resB_assign]]+ }+ +-- | Generate a generate statement for the builtin function "fst"+genFst :: BuiltinBuilder+genFst = genNoInsts $ genVarArgs genFst'+genFst' :: (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession [AST.ConcSm]+genFst' (Left res) f args@[arg] = do {+ ; labels <- MonadState.lift tsType $ getFieldLabels (Var.varType arg)+ ; let { argexpr' = varToVHDLName arg+ ; argexprA = vhdlNameToVHDLExpr $ mkSelectedName argexpr' (labels!!0)+ ; assign = mkUncondAssign (Left res) argexprA+ } ;+ -- Return the generate functions+ ; return [assign]+ }+ +-- | Generate a generate statement for the builtin function "snd"+genSnd :: BuiltinBuilder+genSnd = genNoInsts $ genVarArgs genSnd'+genSnd' :: (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession [AST.ConcSm]+genSnd' (Left res) f args@[arg] = do {+ ; labels <- MonadState.lift tsType $ getFieldLabels (Var.varType arg)+ ; let { argexpr' = varToVHDLName arg+ ; argexprB = vhdlNameToVHDLExpr $ mkSelectedName argexpr' (labels!!1)+ ; assign = mkUncondAssign (Left res) argexprB+ } ;+ -- Return the generate functions+ ; return [assign]+ }+ +-- | Generate a generate statement for the builtin function "unzip"+genUnzip :: BuiltinBuilder+genUnzip = genNoInsts $ genVarArgs genUnzip'+genUnzip' :: (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession [AST.ConcSm]+genUnzip' (Left res) f args@[arg] = do+ let error_msg = "\nGenerate.genUnzip: Cannot generate unzip call: " ++ pprString res ++ " = " ++ pprString f ++ " " ++ pprString arg+ htype <- MonadState.lift tsType $ mkHType error_msg (Var.varType arg)+ -- Prepare a unconditional assignment, for the case when either part+ -- of the unzip is a state variable, which will disappear in the+ -- resulting VHDL, making the the unzip no longer required.+ case htype of+ -- A normal vector containing two-tuples+ VecType _ (AggrType _ [_, _]) -> do {+ -- Setup the generate scheme+ ; len <- MonadState.lift tsType $ tfp_to_int $ (tfvec_len_ty . Var.varType) arg+ -- TODO: Use something better than varToString+ ; let { label = mkVHDLExtId ("unzipVector" ++ (varToString res))+ ; n_id = mkVHDLBasicId "n"+ ; n_expr = idToVHDLExpr n_id+ ; range = AST.ToRange (AST.PrimLit "0") (AST.PrimLit $ show (len-1))+ ; genScheme = AST.ForGn n_id range+ ; resname' = varToVHDLName res+ ; argexpr' = mkIndexedName (varToVHDLName arg) n_expr+ } ;+ ; reslabels <- MonadState.lift tsType $ getFieldLabels (Var.varType res)+ ; arglabels <- MonadState.lift tsType $ getFieldLabels (tfvec_elem (Var.varType arg))+ ; let { resnameA = mkIndexedName (mkSelectedName resname' (reslabels!!0)) n_expr+ ; resnameB = mkIndexedName (mkSelectedName resname' (reslabels!!1)) n_expr+ ; argexprA = vhdlNameToVHDLExpr $ mkSelectedName argexpr' (arglabels!!0)+ ; argexprB = vhdlNameToVHDLExpr $ mkSelectedName argexpr' (arglabels!!1)+ ; resA_assign = mkUncondAssign (Right resnameA) argexprA+ ; resB_assign = mkUncondAssign (Right resnameB) argexprB+ } ;+ -- Return the generate functions+ ; return [AST.CSGSm $ AST.GenerateSm label genScheme [] [resA_assign,resB_assign]]+ }+ -- Both elements of the tuple were state, so they've disappeared. No+ -- need to do anything+ VecType _ (AggrType _ []) -> return []+ -- A vector containing aggregates with more than two elements?+ VecType _ (AggrType _ _) -> error $ "Unzipping a value that is not a vector of two-tuples? Value: " ++ pprString arg ++ "\nType: " ++ pprString (Var.varType arg)+ -- One of the elements of the tuple was state, so there won't be a+ -- tuple (record) in the VHDL output. We can just do a plain+ -- assignment, then.+ VecType _ _ -> do+ argexpr <- MonadState.lift tsType $ varToVHDLExpr arg+ return [mkUncondAssign (Left res) argexpr]+ _ -> error $ "Unzipping a value that is not a vector? Value: " ++ pprString arg ++ "\nType: " ++ pprString (Var.varType arg) ++ "\nhtype: " ++ show htype++genCopy :: BuiltinBuilder +genCopy = genNoInsts genCopy'+genCopy' :: (Either CoreSyn.CoreBndr AST.VHDLName ) -> CoreSyn.CoreBndr -> [Either CoreSyn.CoreExpr AST.Expr] -> TranslatorSession [AST.ConcSm]+genCopy' (Left res) f [arg] = do {+ ; [arg'] <- argsToVHDLExprs [arg]+ ; let { resExpr = AST.Aggregate [AST.ElemAssoc (Just AST.Others) arg']+ ; out_assign = mkUncondAssign (Left res) resExpr+ }+ ; return [out_assign]+ }+ +genConcat :: BuiltinBuilder+genConcat = genNoInsts $ genVarArgs genConcat'+genConcat' :: (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession [AST.ConcSm]+genConcat' (Left res) f args@[arg] = do {+ -- Setup the generate scheme+ ; len1 <- MonadState.lift tsType $ tfp_to_int $ (tfvec_len_ty . Var.varType) arg+ ; let (_, nvec) = Type.splitAppTy (Var.varType arg)+ ; len2 <- MonadState.lift tsType $ tfp_to_int $ tfvec_len_ty nvec+ -- TODO: Use something better than varToString+ ; let { label = mkVHDLExtId ("concatVector" ++ (varToString res))+ ; n_id = mkVHDLBasicId "n"+ ; n_expr = idToVHDLExpr n_id+ ; fromRange = n_expr AST.:*: (AST.PrimLit $ show len2)+ ; genScheme = AST.ForGn n_id range+ -- Create the content of the generate statement: Applying the mapped_f to+ -- each of the elements in arg, storing to each element in res+ ; toRange = (n_expr AST.:*: (AST.PrimLit $ show len2)) AST.:+: (AST.PrimLit $ show (len2-1))+ ; range = AST.ToRange (AST.PrimLit "0") (AST.PrimLit $ show (len1-1))+ ; resname = vecSlice fromRange toRange+ ; argexpr = vhdlNameToVHDLExpr $ mkIndexedName (varToVHDLName arg) n_expr+ ; out_assign = mkUncondAssign (Right resname) argexpr+ } ;+ -- Return the generate statement+ ; return [AST.CSGSm $ AST.GenerateSm label genScheme [] [out_assign]]+ }+ where+ vecSlice init last = AST.NSlice (AST.SliceName (varToVHDLName res) + (AST.ToRange init last))++genIteraten :: BuiltinBuilder+genIteraten dst f args = genIterate dst f (tail args)++genIterate :: BuiltinBuilder+genIterate = genIterateOrGenerate True++genGeneraten :: BuiltinBuilder+genGeneraten dst f args = genGenerate dst f (tail args)++genGenerate :: BuiltinBuilder+genGenerate = genIterateOrGenerate False++genIterateOrGenerate :: Bool -> BuiltinBuilder+genIterateOrGenerate iter = genVarArgs (genIterateOrGenerate' iter)++genIterateOrGenerate' :: Bool -> (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession ([AST.ConcSm], [CoreSyn.CoreBndr])+genIterateOrGenerate' iter (Left res) f args = do+ len <- MonadState.lift tsType $ tfp_to_int ((tfvec_len_ty . Var.varType) res)+ genIterateOrGenerate'' len iter (Left res) f args++genIterateOrGenerate'' :: Int -> Bool -> (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession ([AST.ConcSm], [CoreSyn.CoreBndr])+-- Special case for an empty input vector, just assign start to res+genIterateOrGenerate'' len iter (Left res) _ [app_f, start] | len == 0 = return ([mkUncondAssign (Left res) (AST.PrimLit "\"\"")], [])++genIterateOrGenerate'' len iter (Left res) f [app_f, start] = do+ -- The vector length+ -- len <- MonadState.lift tsType $ tfp_to_int ((tfvec_len_ty . Var.varType) res)+ -- An expression for len-1+ let len_min_expr = (AST.PrimLit $ show (len-1))+ -- -- evec is (TFVec n), so it still needs an element type+ -- let (nvec, _) = splitAppTy (Var.varType vec)+ -- -- Put the type of the start value in nvec, this will be the type of our+ -- -- temporary vector+ let tmp_ty = Var.varType res+ let error_msg = "\nGenerate.genFold': Can not construct temp vector for element type: " ++ pprString tmp_ty + -- TODO: Handle Nothing+ Just tmp_vhdl_ty <- MonadState.lift tsType $ vhdlTy error_msg tmp_ty+ -- Setup the generate scheme+ let gen_label = mkVHDLExtId ("iterateVector" ++ (varToString start))+ let block_label = mkVHDLExtId ("iterateVector" ++ (varToString res))+ let gen_range = AST.ToRange (AST.PrimLit "0") len_min_expr+ let gen_scheme = AST.ForGn n_id gen_range+ -- Make the intermediate vector+ let tmp_dec = AST.BDISD $ AST.SigDec tmp_id tmp_vhdl_ty Nothing+ -- Create the generate statement+ cells' <- sequence [genFirstCell, genOtherCell]+ let (cells, useds) = unzip cells'+ let gen_sm = AST.GenerateSm gen_label gen_scheme [] (map AST.CSGSm cells)+ -- Assign tmp[len-1] or tmp[0] to res+ let out_assign = mkUncondAssign (Left res) $ vhdlNameToVHDLExpr tmp_name + let block = AST.BlockSm block_label [] (AST.PMapAspect []) [tmp_dec] [AST.CSGSm gen_sm, out_assign]+ return ([AST.CSBSm block], concat useds)+ where+ -- An id for the counter+ n_id = mkVHDLBasicId "n"+ n_cur = idToVHDLExpr n_id+ -- An expression for previous n+ n_prev = n_cur AST.:-: (AST.PrimLit "1")+ -- An id for the tmp result vector+ tmp_id = mkVHDLBasicId "tmp"+ tmp_name = AST.NSimple tmp_id+ -- Generate parts of the fold+ genFirstCell, genOtherCell :: TranslatorSession (AST.GenerateSm, [CoreSyn.CoreBndr])+ genFirstCell = do+ let cond_label = mkVHDLExtId "firstcell"+ -- if n == 0 or n == len-1+ let cond_scheme = AST.IfGn $ n_cur AST.:=: (AST.PrimLit "0")+ -- Output to tmp[current n]+ let resname = mkIndexedName tmp_name n_cur+ -- Input from start+ argexpr <- MonadState.lift tsType $ varToVHDLExpr start+ let startassign = mkUncondAssign (Right resname) argexpr+ (app_concsms, used) <- genApplication (Right resname) app_f [Right argexpr]+ -- Return the conditional generate part+ let gensm = AST.GenerateSm cond_label cond_scheme [] (if iter then + [startassign]+ else + app_concsms+ )+ return (gensm, used)++ genOtherCell = do+ let cond_label = mkVHDLExtId "othercell"+ -- if n > 0 or n < len-1+ let cond_scheme = AST.IfGn $ n_cur AST.:/=: (AST.PrimLit "0")+ -- Output to tmp[current n]+ let resname = mkIndexedName tmp_name n_cur+ -- Input from tmp[previous n]+ let argexpr = vhdlNameToVHDLExpr $ mkIndexedName tmp_name n_prev+ (app_concsms, used) <- genApplication (Right resname) app_f [Right argexpr]+ -- Return the conditional generate part+ return (AST.GenerateSm cond_label cond_scheme [] app_concsms, used)++genBlockRAM :: BuiltinBuilder+genBlockRAM = genNoInsts $ genExprArgs genBlockRAM'++genBlockRAM' :: (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [AST.Expr] -> TranslatorSession [AST.ConcSm]+genBlockRAM' (Left res) f args@[data_in,rdaddr,wraddr,wrenable] = do+ -- Get the ram type+ let (tup,data_out) = Type.splitAppTy (Var.varType res)+ let (tup',ramvec) = Type.splitAppTy tup+ let Just realram = Type.coreView ramvec+ let Just (tycon, types) = Type.splitTyConApp_maybe realram+ Just ram_vhdl_ty <- MonadState.lift tsType $ vhdlTy "wtf" (head types)+ -- Make the intermediate vector+ let ram_dec = AST.BDISD $ AST.SigDec ram_id ram_vhdl_ty Nothing+ -- Get the data_out name+ -- reslabels <- MonadState.lift tsType $ getFieldLabels (Var.varType res)+ let resname = varToVHDLName res+ -- let resname = mkSelectedName resname' (reslabels!!0)+ let rdaddr_int = genExprFCall (mkVHDLBasicId toIntegerId) rdaddr+ let argexpr = vhdlNameToVHDLExpr $ mkIndexedName (AST.NSimple ram_id) rdaddr_int+ let assign = mkUncondAssign (Right resname) argexpr+ let block_label = mkVHDLExtId ("blockRAM" ++ (varToString res))+ let block = AST.BlockSm block_label [] (AST.PMapAspect []) [ram_dec] [assign, mkUpdateProcSm]+ return [AST.CSBSm block]+ where+ ram_id = mkVHDLBasicId "ram"+ mkUpdateProcSm :: AST.ConcSm+ mkUpdateProcSm = AST.CSPSm $ AST.ProcSm proclabel [clockId] [statement]+ where+ proclabel = mkVHDLBasicId "updateRAM"+ rising_edge = mkVHDLBasicId "rising_edge"+ wraddr_int = genExprFCall (mkVHDLBasicId toIntegerId) wraddr+ ramloc = mkIndexedName (AST.NSimple ram_id) wraddr_int+ wform = AST.Wform [AST.WformElem data_in Nothing]+ ramassign = AST.SigAssign ramloc wform+ rising_edge_clk = genExprFCall rising_edge (AST.PrimName $ AST.NSimple clockId)+ statement = AST.IfSm (AST.And rising_edge_clk wrenable) [ramassign] [] Nothing+ +genSplit :: BuiltinBuilder+genSplit = genNoInsts $ genVarArgs genSplit'++genSplit' :: (Either CoreSyn.CoreBndr AST.VHDLName) -> CoreSyn.CoreBndr -> [Var.Var] -> TranslatorSession [AST.ConcSm]+genSplit' (Left res) f args@[vecIn] = do {+ ; labels <- MonadState.lift tsType $ getFieldLabels (Var.varType res)+ ; len <- MonadState.lift tsType $ tfp_to_int $ (tfvec_len_ty . Var.varType) vecIn+ ; let { block_label = mkVHDLExtId ("split" ++ (varToString vecIn))+ ; halflen = round ((fromIntegral len) / 2)+ ; rangeL = vecSlice (AST.PrimLit "0") (AST.PrimLit $ show (halflen - 1))+ ; rangeR = vecSlice (AST.PrimLit $ show halflen) (AST.PrimLit $ show (len - 1))+ ; resname = varToVHDLName res+ ; resnameL = mkSelectedName resname (labels!!0)+ ; resnameR = mkSelectedName resname (labels!!1)+ ; argexprL = vhdlNameToVHDLExpr rangeL+ ; argexprR = vhdlNameToVHDLExpr rangeR+ ; out_assignL = mkUncondAssign (Right resnameL) argexprL+ ; out_assignR = mkUncondAssign (Right resnameR) argexprR+ ; block = AST.BlockSm block_label [] (AST.PMapAspect []) [] [out_assignL, out_assignR]+ }+ ; return [AST.CSBSm block]+ }+ where+ vecSlice init last = AST.NSlice (AST.SliceName (varToVHDLName res) + (AST.ToRange init last))+-----------------------------------------------------------------------------+-- Function to generate VHDL for applications+-----------------------------------------------------------------------------+genApplication ::+ (Either CoreSyn.CoreBndr AST.VHDLName) -- ^ Where to store the result?+ -> CoreSyn.CoreBndr -- ^ The function to apply+ -> [Either CoreSyn.CoreExpr AST.Expr] -- ^ The arguments to apply+ -> TranslatorSession ([AST.ConcSm], [CoreSyn.CoreBndr]) + -- ^ The corresponding VHDL concurrent statements and entities+ -- instantiated.+genApplication dst f args = do+ nonemptydst <- case dst of+ Left bndr -> hasNonEmptyType bndr + Right _ -> return True+ if nonemptydst+ then+ if Var.isGlobalId f then+ case Var.idDetails f of+ IdInfo.DataConWorkId dc -> case dst of+ -- It's a datacon. Create a record from its arguments.+ Left bndr -> do+ -- We have the bndr, so we can get at the type+ htype <- MonadState.lift tsType $ mkHTypeEither (Var.varType bndr)+ let argsNostate = filter (\x -> not (either hasStateType (\x -> False) x)) args+ case argsNostate of+ [arg] -> do+ [arg'] <- argsToVHDLExprs [arg]+ return ([mkUncondAssign dst arg'], [])+ otherwise ->+ case htype of+ Right (AggrType _ _) -> do+ labels <- MonadState.lift tsType $ getFieldLabels (Var.varType bndr)+ args' <- argsToVHDLExprs argsNostate+ return (zipWith mkassign labels args', [])+ where+ mkassign :: AST.VHDLId -> AST.Expr -> AST.ConcSm+ mkassign label arg =+ let sel_name = mkSelectedName ((either varToVHDLName id) dst) label in+ mkUncondAssign (Right sel_name) arg+ _ -> do -- error $ "DIE!"+ args' <- argsToVHDLExprs argsNostate+ return ([mkUncondAssign dst (head args')], []) + Right _ -> error "\nGenerate.genApplication(DataConWorkId): Can't generate dataconstructor application without an original binder"+ IdInfo.DataConWrapId dc -> case dst of+ -- It's a datacon. Create a record from its arguments.+ Left bndr ->+ case (Map.lookup (varToString f) globalNameTable) of+ Just (arg_count, builder) ->+ if length args == arg_count then+ builder dst f args+ else+ error $ "\nGenerate.genApplication(DataConWrapId): Incorrect number of arguments to builtin function: " ++ pprString f ++ " Args: " ++ show args+ Nothing -> error $ "\nGenerate.genApplication(DataConWrapId): Can't generate dataconwrapper: " ++ (show dc)+ Right _ -> error "\nGenerate.genApplication(DataConWrapId): Can't generate dataconwrapper application without an original binder"+ IdInfo.VanillaId ->+ -- It's a global value imported from elsewhere. These can be builtin+ -- functions. Look up the function name in the name table and execute+ -- the associated builder if there is any and the argument count matches+ -- (this should always be the case if it typechecks, but just to be+ -- sure...).+ case (Map.lookup (varToString f) globalNameTable) of+ Just (arg_count, builder) ->+ if length args == arg_count then+ builder dst f args+ else+ error $ "\nGenerate.genApplication(VanillaId): Incorrect number of arguments to builtin function: " ++ pprString f ++ " Args: " ++ show args+ Nothing -> do+ top <- isTopLevelBinder f+ if top then+ do+ -- Local binder that references a top level binding. Generate a+ -- component instantiation.+ signature <- getEntity f+ args' <- argsToVHDLExprs args+ let entity_id = ent_id signature+ -- TODO: Using show here isn't really pretty, but we'll need some+ -- unique-ish value...+ let label = "comp_ins_" ++ (either show prettyShow) dst+ let portmaps = mkAssocElems args' ((either varToVHDLName id) dst) signature+ return ([mkComponentInst label entity_id portmaps], [f])+ else+ -- Not a top level binder, so this must be a local variable reference.+ -- It should have a representable type (and thus, no arguments) and a+ -- signal should be generated for it. Just generate an unconditional+ -- assignment here.+ -- FIXME : I DONT KNOW IF THE ABOVE COMMENT HOLDS HERE, SO FOR NOW JUST ERROR!+ -- f' <- MonadState.lift tsType $ varToVHDLExpr f+ -- return $ ([mkUncondAssign dst f'], [])+ do errtype <- case dst of + Left bndr -> do + htype <- MonadState.lift tsType $ mkHTypeEither (Var.varType bndr)+ return (show htype)+ Right vhd -> return $ show vhd+ error ("\nGenerate.genApplication(VanillaId): Using function from another module that is not a known builtin: " ++ (pprString f) ++ "::" ++ errtype) + IdInfo.ClassOpId cls ->+ -- FIXME: Not looking for what instance this class op is called for+ -- Is quite stupid of course.+ case (Map.lookup (varToString f) globalNameTable) of+ Just (arg_count, builder) ->+ if length args == arg_count then+ builder dst f args+ else+ error $ "\nGenerate.genApplication(ClassOpId): Incorrect number of arguments to builtin function: " ++ pprString f ++ " Args: " ++ show args+ Nothing -> error $ "\nGenerate.genApplication(ClassOpId): Using function from another module that is not a known builtin: " ++ pprString f+ details -> error $ "\nGenerate.genApplication: Calling unsupported function " ++ pprString f ++ " with GlobalIdDetails " ++ pprString details+ else do+ top <- isTopLevelBinder f+ if top then+ do+ -- Local binder that references a top level binding. Generate a+ -- component instantiation.+ signature <- getEntity f+ args' <- argsToVHDLExprs args+ let entity_id = ent_id signature+ -- TODO: Using show here isn't really pretty, but we'll need some+ -- unique-ish value...+ let label = "comp_ins_" ++ (either (prettyShow . varToVHDLName) prettyShow) dst+ let portmaps = mkAssocElems args' ((either varToVHDLName id) dst) signature+ return ([mkComponentInst label entity_id portmaps], [f])+ else+ -- Not a top level binder, so this must be a local variable reference.+ -- It should have a representable type (and thus, no arguments) and a+ -- signal should be generated for it. Just generate an unconditional+ -- assignment here.+ do f' <- MonadState.lift tsType $ varToVHDLExpr f+ return ([mkUncondAssign dst f'], [])+ else -- Destination has empty type, don't generate anything+ return ([], [])+-----------------------------------------------------------------------------+-- Functions to generate functions dealing with vectors.+-----------------------------------------------------------------------------++-- Returns the VHDLId of the vector function with the given name for the given+-- element type. Generates -- this function if needed.+vectorFunId :: Type.Type -> String -> TypeSession AST.VHDLId+vectorFunId el_ty fname = do+ let error_msg = "\nGenerate.vectorFunId: Can not construct vector function for element: " ++ pprString el_ty+ -- TODO: Handle the Nothing case?+ elemTM_maybe <- vhdlTy error_msg el_ty+ let elemTM = Maybe.fromMaybe+ (error $ "\nGenerate.vectorFunId: Cannot generate vector function \"" ++ fname ++ "\" for the empty type \"" ++ (pprString el_ty) ++ "\"")+ elemTM_maybe+ -- TODO: This should not be duplicated from mk_vector_ty. Probably but it in+ -- the VHDLState or something.+ let vectorTM = mkVHDLExtId $ "vector_" ++ (AST.fromVHDLId elemTM)+ typefuns <- MonadState.get tsTypeFuns+ el_htype <- mkHType error_msg el_ty+ case Map.lookup (UVecType el_htype, fname) typefuns of+ -- Function already generated, just return it+ Just (id, _) -> return id+ -- Function not generated yet, generate it+ Nothing -> do+ let functions = genUnconsVectorFuns elemTM vectorTM+ case lookup fname functions of+ Just body -> do+ MonadState.modify tsTypeFuns $ Map.insert (UVecType el_htype, fname) (function_id, (fst body))+ mapM_ (vectorFunId el_ty) (snd body)+ return function_id+ Nothing -> error $ "\nGenerate.vectorFunId: I don't know how to generate vector function " ++ fname+ where+ function_id = mkVHDLExtId fname++genUnconsVectorFuns :: AST.TypeMark -- ^ type of the vector elements+ -> AST.TypeMark -- ^ type of the vector+ -> [(String, (AST.SubProgBody, [String]))]+genUnconsVectorFuns elemTM vectorTM = + [ (exId, (AST.SubProgBody exSpec [] [exExpr],[]))+ , (replaceId, (AST.SubProgBody replaceSpec [AST.SPVD replaceVar] [replaceExpr1,replaceExpr2,replaceRet],[]))+ , (lastId, (AST.SubProgBody lastSpec [] [lastExpr],[]))+ , (initId, (AST.SubProgBody initSpec [AST.SPVD initVar] [initExpr, initRet],[]))+ , (minimumId, (AST.SubProgBody minimumSpec [] [minimumExpr],[]))+ , (takeId, (AST.SubProgBody takeSpec [AST.SPVD takeVar] [takeExpr, takeRet],[minimumId]))+ , (dropId, (AST.SubProgBody dropSpec [AST.SPVD dropVar] [dropExpr, dropRet],[]))+ , (plusgtId, (AST.SubProgBody plusgtSpec [AST.SPVD plusgtVar] [plusgtExpr, plusgtRet],[]))+ , (emptyId, (AST.SubProgBody emptySpec [AST.SPVD emptyVar] [emptyExpr],[]))+ , (singletonId, (AST.SubProgBody singletonSpec [AST.SPVD singletonVar] [singletonRet],[]))+ , (copynId, (AST.SubProgBody copynSpec [AST.SPVD copynVar] [copynExpr],[]))+ , (selId, (AST.SubProgBody selSpec [AST.SPVD selVar] [selFor, selRet],[]))+ , (ltplusId, (AST.SubProgBody ltplusSpec [AST.SPVD ltplusVar] [ltplusExpr, ltplusRet],[])) + , (plusplusId, (AST.SubProgBody plusplusSpec [AST.SPVD plusplusVar] [plusplusExpr, plusplusRet],[]))+ , (lengthTId, (AST.SubProgBody lengthTSpec [] [lengthTExpr],[]))+ , (shiftlId, (AST.SubProgBody shiftlSpec [AST.SPVD shiftlVar] [shiftlExpr, shiftlRet], [initId]))+ , (shiftrId, (AST.SubProgBody shiftrSpec [AST.SPVD shiftrVar] [shiftrExpr, shiftrRet], [tailId]))+ , (nullId, (AST.SubProgBody nullSpec [] [nullExpr], []))+ , (rotlId, (AST.SubProgBody rotlSpec [AST.SPVD rotlVar] [rotlExpr, rotlRet], [nullId, lastId, initId]))+ , (rotrId, (AST.SubProgBody rotrSpec [AST.SPVD rotrVar] [rotrExpr, rotrRet], [nullId, tailId, headId]))+ , (reverseId, (AST.SubProgBody reverseSpec [AST.SPVD reverseVar] [reverseFor, reverseRet], []))+ ]+ where + ixPar = AST.unsafeVHDLBasicId "ix"+ vecPar = AST.unsafeVHDLBasicId "vec"+ vec1Par = AST.unsafeVHDLBasicId "vec1"+ vec2Par = AST.unsafeVHDLBasicId "vec2"+ nPar = AST.unsafeVHDLBasicId "n"+ leftPar = AST.unsafeVHDLBasicId "nLeft"+ rightPar = AST.unsafeVHDLBasicId "nRight"+ iId = AST.unsafeVHDLBasicId "i"+ iPar = iId+ aPar = AST.unsafeVHDLBasicId "a"+ fPar = AST.unsafeVHDLBasicId "f"+ sPar = AST.unsafeVHDLBasicId "s"+ resId = AST.unsafeVHDLBasicId "res" + exSpec = AST.Function (mkVHDLExtId exId) [AST.IfaceVarDec vecPar vectorTM,+ AST.IfaceVarDec ixPar unsignedTM] elemTM+ exExpr = AST.ReturnSm (Just $ AST.PrimName $ AST.NIndexed + (AST.IndexedName (AST.NSimple vecPar) [genExprFCall (mkVHDLBasicId toIntegerId) (AST.PrimName $ AST.NSimple ixPar)]))+ replaceSpec = AST.Function (mkVHDLExtId replaceId) [ AST.IfaceVarDec vecPar vectorTM+ , AST.IfaceVarDec iPar unsignedTM+ , AST.IfaceVarDec aPar elemTM+ ] vectorTM + -- variable res : fsvec_x (0 to vec'length-1);+ replaceVar =+ AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ (AST.PrimLit "1")) ]))+ Nothing+ -- res AST.:= vec(0 to i-1) & a & vec(i+1 to length'vec-1)+ replaceExpr1 = AST.NSimple resId AST.:= AST.PrimName (AST.NSimple vecPar)+ replaceExpr2 = AST.NIndexed (AST.IndexedName (AST.NSimple resId) [genExprFCall (mkVHDLBasicId toIntegerId) (AST.PrimName $ AST.NSimple iPar)]) AST.:= AST.PrimName (AST.NSimple aPar)+ replaceRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ vecSlice init last = AST.PrimName (AST.NSlice + (AST.SliceName + (AST.NSimple vecPar) + (AST.ToRange init last)))+ lastSpec = AST.Function (mkVHDLExtId lastId) [AST.IfaceVarDec vecPar vectorTM] elemTM+ -- return vec(vec'length-1);+ lastExpr = AST.ReturnSm (Just (AST.PrimName $ AST.NIndexed (AST.IndexedName + (AST.NSimple vecPar) + [AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) + AST.:-: AST.PrimLit "1"])))+ initSpec = AST.Function (mkVHDLExtId initId) [AST.IfaceVarDec vecPar vectorTM] vectorTM + -- variable res : fsvec_x (0 to vec'length-2);+ initVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ (AST.PrimLit "2")) ]))+ Nothing+ -- resAST.:= vec(0 to vec'length-2)+ initExpr = AST.NSimple resId AST.:= (vecSlice + (AST.PrimLit "0") + (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) + AST.:-: AST.PrimLit "2"))+ initRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ minimumSpec = AST.Function (mkVHDLExtId minimumId) [AST.IfaceVarDec leftPar naturalTM,+ AST.IfaceVarDec rightPar naturalTM ] naturalTM+ minimumExpr = AST.IfSm ((AST.PrimName $ AST.NSimple leftPar) AST.:<: (AST.PrimName $ AST.NSimple rightPar))+ [AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple leftPar)]+ []+ (Just $ AST.Else [minimumExprRet])+ where minimumExprRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple rightPar)+ takeSpec = AST.Function (mkVHDLExtId takeId) [AST.IfaceVarDec nPar naturalTM,+ AST.IfaceVarDec vecPar vectorTM ] vectorTM+ -- variable res : fsvec_x (0 to (minimum (n,vec'length))-1);+ minLength = AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLExtId minimumId)) + [Nothing AST.:=>: AST.ADExpr (AST.PrimName $ AST.NSimple nPar)+ ,Nothing AST.:=>: AST.ADExpr (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing))]+ takeVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (minLength AST.:-:+ (AST.PrimLit "1")) ]))+ Nothing+ -- res AST.:= vec(0 to n-1)+ takeExpr = AST.NSimple resId AST.:= + (vecSlice (AST.PrimLit "0") + (minLength AST.:-: AST.PrimLit "1"))+ takeRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ dropSpec = AST.Function (mkVHDLExtId dropId) [AST.IfaceVarDec nPar naturalTM,+ AST.IfaceVarDec vecPar vectorTM ] vectorTM + -- variable res : fsvec_x (0 to vec'length-n-1);+ dropVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ (AST.PrimName $ AST.NSimple nPar)AST.:-: (AST.PrimLit "1")) ]))+ Nothing+ -- res AST.:= vec(n to vec'length-1)+ dropExpr = AST.NSimple resId AST.:= (vecSlice + (AST.PrimName $ AST.NSimple nPar) + (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) + AST.:-: AST.PrimLit "1"))+ dropRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ plusgtSpec = AST.Function (mkVHDLExtId plusgtId) [AST.IfaceVarDec aPar elemTM,+ AST.IfaceVarDec vecPar vectorTM] vectorTM + -- variable res : fsvec_x (0 to vec'length);+ plusgtVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing))]))+ Nothing+ plusgtExpr = AST.NSimple resId AST.:= + ((AST.PrimName $ AST.NSimple aPar) AST.:&: + (AST.PrimName $ AST.NSimple vecPar))+ plusgtRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ emptySpec = AST.Function (mkVHDLExtId emptyId) [] vectorTM+ emptyVar = + AST.VarDec resId+ (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0") (AST.PrimLit "-1")]))+ Nothing+ emptyExpr = AST.ReturnSm (Just $ AST.PrimName (AST.NSimple resId))+ singletonSpec = AST.Function (mkVHDLExtId singletonId) [AST.IfaceVarDec aPar elemTM ] + vectorTM+ -- variable res : fsvec_x (0 to 0) := (others => a);+ singletonVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0") (AST.PrimLit "0")]))+ (Just $ AST.Aggregate [AST.ElemAssoc (Just AST.Others) + (AST.PrimName $ AST.NSimple aPar)])+ singletonRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ copynSpec = AST.Function (mkVHDLExtId copynId) [AST.IfaceVarDec nPar naturalTM,+ AST.IfaceVarDec aPar elemTM ] vectorTM + -- variable res : fsvec_x (0 to n-1) := (others => a);+ copynVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ ((AST.PrimName (AST.NSimple nPar)) AST.:-:+ (AST.PrimLit "1")) ]))+ (Just $ AST.Aggregate [AST.ElemAssoc (Just AST.Others) + (AST.PrimName $ AST.NSimple aPar)])+ -- return res+ copynExpr = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ selSpec = AST.Function (mkVHDLExtId selId) [AST.IfaceVarDec fPar naturalTM,+ AST.IfaceVarDec sPar naturalTM,+ AST.IfaceVarDec nPar naturalTM,+ AST.IfaceVarDec vecPar vectorTM ] vectorTM+ -- variable res : fsvec_x (0 to n-1);+ selVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ ((AST.PrimName (AST.NSimple nPar)) AST.:-:+ (AST.PrimLit "1")) ])+ )+ Nothing+ -- for i res'range loop+ -- res(i) := vec(f+i*s);+ -- end loop;+ selFor = AST.ForSM iId (AST.AttribRange $ AST.AttribName (AST.NSimple resId) (AST.NSimple rangeId) Nothing) [selAssign]+ -- res(i) := vec(f+i*s);+ selAssign = let origExp = AST.PrimName (AST.NSimple fPar) AST.:+: + (AST.PrimName (AST.NSimple iId) AST.:*: + AST.PrimName (AST.NSimple sPar)) in+ AST.NIndexed (AST.IndexedName (AST.NSimple resId) [AST.PrimName (AST.NSimple iId)]) AST.:=+ (AST.PrimName $ AST.NIndexed (AST.IndexedName (AST.NSimple vecPar) [origExp]))+ -- return res;+ selRet = AST.ReturnSm (Just $ AST.PrimName (AST.NSimple resId))+ ltplusSpec = AST.Function (mkVHDLExtId ltplusId) [AST.IfaceVarDec vecPar vectorTM,+ AST.IfaceVarDec aPar elemTM] vectorTM + -- variable res : fsvec_x (0 to vec'length);+ ltplusVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing))]))+ Nothing+ ltplusExpr = AST.NSimple resId AST.:= + ((AST.PrimName $ AST.NSimple vecPar) AST.:&: + (AST.PrimName $ AST.NSimple aPar))+ ltplusRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ plusplusSpec = AST.Function (mkVHDLExtId plusplusId) [AST.IfaceVarDec vec1Par vectorTM,+ AST.IfaceVarDec vec2Par vectorTM] + vectorTM + -- variable res : fsvec_x (0 to vec1'length + vec2'length -1);+ plusplusVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vec1Par) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:+:+ AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vec2Par) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ AST.PrimLit "1")]))+ Nothing+ plusplusExpr = AST.NSimple resId AST.:= + ((AST.PrimName $ AST.NSimple vec1Par) AST.:&: + (AST.PrimName $ AST.NSimple vec2Par))+ plusplusRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ lengthTSpec = AST.Function (mkVHDLExtId lengthTId) [AST.IfaceVarDec vecPar vectorTM] naturalTM+ lengthTExpr = AST.ReturnSm (Just $ AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing))+ shiftlSpec = AST.Function (mkVHDLExtId shiftlId) [AST.IfaceVarDec vecPar vectorTM,+ AST.IfaceVarDec aPar elemTM ] vectorTM + -- variable res : fsvec_x (0 to vec'length-1);+ shiftlVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ (AST.PrimLit "1")) ]))+ Nothing+ -- res := a & init(vec)+ shiftlExpr = AST.NSimple resId AST.:=+ (AST.PrimName (AST.NSimple aPar) AST.:&:+ (AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLExtId initId)) + [Nothing AST.:=>: AST.ADExpr (AST.PrimName $ AST.NSimple vecPar)]))+ shiftlRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId) + shiftrSpec = AST.Function (mkVHDLExtId shiftrId) [AST.IfaceVarDec vecPar vectorTM,+ AST.IfaceVarDec aPar elemTM ] vectorTM + -- variable res : fsvec_x (0 to vec'length-1);+ shiftrVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ (AST.PrimLit "1")) ]))+ Nothing+ -- res := tail(vec) & a+ shiftrExpr = AST.NSimple resId AST.:=+ ((AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLExtId tailId)) + [Nothing AST.:=>: AST.ADExpr (AST.PrimName $ AST.NSimple vecPar)]) AST.:&:+ (AST.PrimName (AST.NSimple aPar)))+ + shiftrRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId) + nullSpec = AST.Function (mkVHDLExtId nullId) [AST.IfaceVarDec vecPar vectorTM] booleanTM+ -- return vec'length = 0+ nullExpr = AST.ReturnSm (Just $ + AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:=:+ AST.PrimLit "0")+ rotlSpec = AST.Function (mkVHDLExtId rotlId) [AST.IfaceVarDec vecPar vectorTM] vectorTM + -- variable res : fsvec_x (0 to vec'length-1);+ rotlVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ (AST.PrimLit "1")) ]))+ Nothing+ -- if null(vec) then res := vec else res := last(vec) & init(vec)+ rotlExpr = AST.IfSm (AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLExtId nullId)) + [Nothing AST.:=>: AST.ADExpr (AST.PrimName $ AST.NSimple vecPar)])+ [AST.NSimple resId AST.:= (AST.PrimName $ AST.NSimple vecPar)]+ []+ (Just $ AST.Else [rotlExprRet])+ where rotlExprRet = + AST.NSimple resId AST.:= + ((AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLExtId lastId)) + [Nothing AST.:=>: AST.ADExpr (AST.PrimName $ AST.NSimple vecPar)]) AST.:&:+ (AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLExtId initId)) + [Nothing AST.:=>: AST.ADExpr (AST.PrimName $ AST.NSimple vecPar)]))+ rotlRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId) + rotrSpec = AST.Function (mkVHDLExtId rotrId) [AST.IfaceVarDec vecPar vectorTM] vectorTM + -- variable res : fsvec_x (0 to vec'length-1);+ rotrVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ (AST.PrimLit "1")) ]))+ Nothing+ -- if null(vec) then res := vec else res := tail(vec) & head(vec)+ rotrExpr = AST.IfSm (AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLExtId nullId)) + [Nothing AST.:=>: AST.ADExpr (AST.PrimName $ AST.NSimple vecPar)])+ [AST.NSimple resId AST.:= (AST.PrimName $ AST.NSimple vecPar)]+ []+ (Just $ AST.Else [rotrExprRet])+ where rotrExprRet = + AST.NSimple resId AST.:= + ((AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLExtId tailId)) + [Nothing AST.:=>: AST.ADExpr (AST.PrimName $ AST.NSimple vecPar)]) AST.:&:+ (AST.PrimFCall $ AST.FCall (AST.NSimple (mkVHDLExtId headId)) + [Nothing AST.:=>: AST.ADExpr (AST.PrimName $ AST.NSimple vecPar)]))+ rotrRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ reverseSpec = AST.Function (mkVHDLExtId reverseId) [AST.IfaceVarDec vecPar vectorTM] vectorTM+ reverseVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ (AST.PrimLit "1")) ]))+ Nothing+ -- for i in 0 to res'range loop+ -- res(vec'length-i-1) := vec(i);+ -- end loop;+ reverseFor = + AST.ForSM iId (AST.AttribRange $ AST.AttribName (AST.NSimple resId) (AST.NSimple rangeId) Nothing) [reverseAssign]+ -- res(vec'length-i-1) := vec(i);+ reverseAssign = AST.NIndexed (AST.IndexedName (AST.NSimple resId) [destExp]) AST.:=+ (AST.PrimName $ AST.NIndexed (AST.IndexedName (AST.NSimple vecPar) + [AST.PrimName $ AST.NSimple iId]))+ where destExp = AST.PrimName (AST.NAttribute $ AST.AttribName (AST.NSimple vecPar) + (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-: + AST.PrimName (AST.NSimple iId) AST.:-: + (AST.PrimLit "1") + -- return res;+ reverseRet = AST.ReturnSm (Just $ AST.PrimName (AST.NSimple resId))++ +-----------------------------------------------------------------------------+-- A table of builtin functions+-----------------------------------------------------------------------------++-- A function that generates VHDL for a builtin function+type BuiltinBuilder = + (Either CoreSyn.CoreBndr AST.VHDLName) -- ^ The destination signal and it's original type+ -> CoreSyn.CoreBndr -- ^ The function called+ -> [Either CoreSyn.CoreExpr AST.Expr] -- ^ The value arguments passed (excluding type and+ -- dictionary arguments).+ -> TranslatorSession ([AST.ConcSm], [CoreSyn.CoreBndr]) + -- ^ The corresponding VHDL concurrent statements and entities+ -- instantiated.++-- A map of a builtin function to VHDL function builder +type NameTable = Map.Map String (Int, BuiltinBuilder )++-- | The builtin functions we support. Maps a name to an argument count and a+-- builder function. If you add a name to this map, don't forget to add+-- it to VHDL.Constants/builtinIds as well.+globalNameTable :: NameTable+globalNameTable = Map.fromList+ [ (exId , (2, genFCall True ) )+ , (replaceId , (3, genFCall False ) )+ , (headId , (1, genFCall True ) )+ , (lastId , (1, genFCall True ) )+ , (tailId , (1, genFCall False ) )+ , (initId , (1, genFCall False ) )+ , (takeId , (2, genFCall False ) )+ , (dropId , (2, genFCall False ) )+ , (selId , (4, genFCall False ) )+ , (plusgtId , (2, genFCall False ) )+ , (ltplusId , (2, genFCall False ) )+ , (plusplusId , (2, genFCall False ) )+ , (mapId , (2, genMap ) )+ , (zipWithId , (3, genZipWith ) )+ , (foldlId , (3, genFoldl ) )+ , (foldrId , (3, genFoldr ) )+ , (zipId , (2, genZip ) )+ , (unzipId , (1, genUnzip ) )+ , (shiftlId , (2, genFCall False ) )+ , (shiftrId , (2, genFCall False ) )+ , (rotlId , (1, genFCall False ) )+ , (rotrId , (1, genFCall False ) )+ , (concatId , (1, genConcat ) )+ , (reverseId , (1, genFCall False ) )+ , (iteratenId , (3, genIteraten ) )+ , (iterateId , (2, genIterate ) )+ , (generatenId , (3, genGeneraten ) )+ , (generateId , (2, genGenerate ) )+ , (emptyId , (0, genFCall False ) )+ , (singletonId , (1, genFCall False ) )+ , (copynId , (2, genFCall False ) )+ , (copyId , (1, genCopy ) )+ , (lengthTId , (1, genFCall False ) )+ , (nullId , (1, genFCall False ) )+ , (hwxorId , (2, genOperator2 AST.Xor ) )+ , (hwandId , (2, genOperator2 AST.And ) )+ , (hworId , (2, genOperator2 AST.Or ) )+ , (hwnotId , (1, genOperator1 AST.Not ) )+ , (equalityId , (2, genOperator2 (AST.:=:) ) )+ , (inEqualityId , (2, genOperator2 (AST.:/=:) ) )+ , (ltId , (2, genOperator2 (AST.:<:) ) )+ , (lteqId , (2, genOperator2 (AST.:<=:) ) )+ , (gtId , (2, genOperator2 (AST.:>:) ) )+ , (gteqId , (2, genOperator2 (AST.:>=:) ) )+ , (boolOrId , (2, genOperator2 AST.Or ) )+ , (boolAndId , (2, genOperator2 AST.And ) )+ , (boolNot , (1, genOperator1 AST.Not ) )+ , (plusId , (2, genOperator2 (AST.:+:) ) )+ , (timesId , (2, genTimes ) )+ , (negateId , (1, genNegation ) )+ , (minusId , (2, genOperator2 (AST.:-:) ) )+ , (fromSizedWordId , (1, genFromSizedWord ) )+ , (fromRangedWordId , (1, genFromRangedWord ) )+ , (fromIntegerId , (1, genFromInteger ) )+ , (resizeWordId , (1, genResize ) )+ , (resizeIntId , (1, genResize ) )+ , (sizedIntId , (1, genSizedInt ) )+ , (smallIntegerId , (1, genFromInteger ) )+ , (fstId , (1, genFst ) )+ , (sndId , (1, genSnd ) )+ , (blockRAMId , (5, genBlockRAM ) )+ , (splitId , (1, genSplit ) )+ --, (tfvecId , (1, genTFVec ) )+ , (minimumId , (2, error "\nFunction name: \"minimum\" is used internally, use another name"))+ ]
+ CLasH/VHDL/Testbench.hs view
@@ -0,0 +1,173 @@+-- +-- Functions to create a VHDL testbench from a list of test input.+--+module CLasH.VHDL.Testbench where++-- Standard modules+import qualified Control.Monad as Monad+import qualified Maybe+import qualified Data.Map as Map+import qualified Data.Accessor.Monad.Trans.State as MonadState++-- VHDL Imports+import qualified Language.VHDL.AST as AST++-- GHC API+import qualified CoreSyn+import qualified HscTypes+import qualified Var+import qualified TysWiredIn++-- Local imports+import CLasH.Translator.TranslatorTypes+import CLasH.VHDL.Constants+import CLasH.VHDL.Generate+import CLasH.VHDL.VHDLTools+import CLasH.VHDL.VHDLTypes+import CLasH.Normalize+import CLasH.Utils.Core.BinderTools+import CLasH.Utils.Core.CoreTools+import CLasH.Utils++createTestbench :: + Maybe Int -- ^ Number of cycles to simulate+ -> [HscTypes.CoreModule] -- ^ Compiled modules+ -> CoreSyn.CoreExpr -- ^ Input stimuli+ -> CoreSyn.CoreBndr -- ^ Top Entity+ -> TranslatorSession CoreSyn.CoreBndr -- ^ The id of the generated archictecture+createTestbench mCycles cores stimuli top = do+ stimuli' <- reduceCoreListToHsList cores stimuli+ -- Create a binder for the testbench. We use the unit type (), since the+ -- testbench has no outputs and no inputs.+ bndr <- mkInternalVar "testbench" TysWiredIn.unitTy+ let entity = createTestbenchEntity bndr+ MonadState.modify tsEntities (Map.insert bndr entity)+ arch <- createTestbenchArch mCycles stimuli' top entity+ MonadState.modify tsArchitectures (Map.insert bndr arch)+ return bndr++createTestbenchEntity :: + CoreSyn.CoreBndr+ -> Entity+createTestbenchEntity bndr = entity+ where+ vhdl_id = mkVHDLBasicId "testbench"+ -- Create an AST entity declaration with no ports+ ent_decl = AST.EntityDec vhdl_id []+ -- Create a signature with no input and no output ports+ entity = Entity vhdl_id [] undefined ent_decl++createTestbenchArch ::+ Maybe Int -- ^ Number of cycles to simulate+ -> [CoreSyn.CoreExpr] -- ^ Imput stimuli+ -> CoreSyn.CoreBndr -- ^ Top Entity+ -> Entity -- ^ The signature to create an architecture for+ -> TranslatorSession (Architecture, [CoreSyn.CoreBndr])+ -- ^ The architecture and any other entities used.+createTestbenchArch mCycles stimuli top testent= do+ signature <- getEntity top+ let entId = ent_id signature+ iIface = ent_args signature+ oIface = ent_res signature+ iIds = map fst iIface+ let (oId, oDec, oProc) = case oIface of+ Just (id, ty) -> ( id+ , [AST.SigDec id ty Nothing]+ , [createOutputProc [id]])+ -- No output port? Just use undefined for the output id, since it won't be+ -- used by mkAssocElems when there is no output port.+ Nothing -> (undefined, [], [])+ let iDecs = map (\(vId, tm) -> AST.SigDec vId tm Nothing) iIface+ let finalIDecs = iDecs +++ [AST.SigDec clockId std_logicTM (Just $ AST.PrimLit "'0'"),+ AST.SigDec resetId std_logicTM (Just $ AST.PrimLit "'0'")]+ let portmaps = mkAssocElems (map idToVHDLExpr iIds) (AST.NSimple oId) signature+ let mIns = mkComponentInst "totest" entId portmaps+ (stimuliAssigns, stimuliDecs, cycles, used) <- createStimuliAssigns mCycles stimuli (head iIds)+ let finalAssigns = (AST.CSSASm (AST.NSimple resetId AST.:<==:+ AST.ConWforms []+ (AST.Wform [AST.WformElem (AST.PrimLit "'0'") (Just $ AST.PrimLit "0 ns"), AST.WformElem (AST.PrimLit "'1'") (Just $ AST.PrimLit "3 ns")])+ Nothing)) : stimuliAssigns+ let clkProc = createClkProc+ let arch = AST.ArchBody+ (AST.unsafeVHDLBasicId "test")+ (AST.NSimple $ ent_id testent)+ (map AST.BDISD (finalIDecs ++ stimuliDecs ++ oDec))+ (mIns :+ ( (AST.CSPSm clkProc) : (fmap AST.CSPSm oProc) ++ finalAssigns ) )+ return (arch, top : used)++createStimuliAssigns ::+ Maybe Int -- ^ Number of cycles to simulate+ -> [CoreSyn.CoreExpr] -- ^ Input stimuli+ -> AST.VHDLId -- ^ Input signal+ -> TranslatorSession ( [AST.ConcSm]+ , [AST.SigDec]+ , Int+ , [CoreSyn.CoreBndr]) -- ^ (Resulting statements, Needed signals, The number of cycles to simulate, Any entities used)+createStimuliAssigns mCycles [] _ = return ([], [], Maybe.maybe 0 id mCycles, [])++createStimuliAssigns mCycles stimuli signal = do+ let genWformElem time stim = (AST.WformElem stim (Just $ AST.PrimLit (show time ++ " ns")))+ let inputlen = length stimuli+ assigns <- Monad.zipWithM createStimulans stimuli [0..inputlen]+ let (stimuli_sms, resvars, useds) = unzip3 assigns+ sig_dec_maybes <- mapM mkSigDec resvars+ let sig_decs = Maybe.catMaybes sig_dec_maybes+ outps <- mapM (\x -> MonadState.lift tsType (varToVHDLExpr x)) resvars+ let wformelems = zipWith genWformElem [0,10..] outps+ let inassign = AST.CSSASm $ AST.NSimple signal AST.:<==: AST.ConWforms [] (AST.Wform wformelems) Nothing+ case (concat stimuli_sms) of+ [] -> return ([inassign], [], inputlen, concat useds)+ stims -> return (stims ++ [inassign], sig_decs, inputlen, concat useds)++createStimulans ::+ CoreSyn.CoreExpr -- ^ The stimulans+ -> Int -- ^ The cycle for this stimulans+ -> TranslatorSession ( [AST.ConcSm]+ , Var.Var + , [CoreSyn.CoreBndr]) -- ^ (The statement, the variable it assigns to (assumed to be available!), Any entities used by this stimulans)++createStimulans expr cycl = do + -- There must be a let at top level + expr <- normalizeExpr ("test input #" ++ show cycl) expr+ -- Split the normalized expression. It can't have a function type, so match+ -- an empty list of argument binders+ let ([], binds, res) = splitNormalized expr+ (stimulansbindss, useds) <- unzipM $ Monad.mapM mkConcSm binds+ sig_dec_maybes <- mapM (mkSigDec . fst) (filter ((/=res).fst) binds)+ let sig_decs = map (AST.BDISD) (Maybe.catMaybes sig_dec_maybes)+ let block_label = mkVHDLExtId ("testcycle_" ++ (show cycl))+ let block = AST.BlockSm block_label [] (AST.PMapAspect []) sig_decs (concat stimulansbindss)+ case (sig_decs,(concat stimulansbindss)) of+ ([],[]) -> return ([], res, concat useds)+ otherwise -> return ([AST.CSBSm block], res, concat useds)+ +-- | generates a clock process with a period of 10ns+createClkProc :: AST.ProcSm+createClkProc = AST.ProcSm (AST.unsafeVHDLBasicId "clkproc") [] sms+ where sms = -- wait for 5 ns -- (half a cycle)+ [AST.WaitFor $ AST.PrimLit "5 ns",+ -- clk <= not clk;+ AST.NSimple clockId `AST.SigAssign` + AST.Wform [AST.WformElem (AST.Not (AST.PrimName $ AST.NSimple clockId)) Nothing]]++-- | generate the output process+createOutputProc :: [AST.VHDLId] -- ^ output signal+ -> AST.ProcSm +createOutputProc outs = + AST.ProcSm (AST.unsafeVHDLBasicId "writeoutput") + [clockId]+ [AST.IfSm clkPred (writeOuts outs) [] Nothing]+ where clkPred = AST.PrimName (AST.NAttribute $ AST.AttribName (AST.NSimple clockId) + (AST.NSimple eventId)+ Nothing ) `AST.And` + (AST.PrimName (AST.NSimple clockId) AST.:=: AST.PrimLit "'1'")+ writeOuts :: [AST.VHDLId] -> [AST.SeqSm]+ writeOuts [] = []+ writeOuts [i] = [writeOut i (AST.PrimLit "LF")]+ writeOuts (i:is) = writeOut i (AST.PrimLit "HT") : writeOuts is+ writeOut outSig suffix = + genExprPCall2 writeId+ (AST.PrimName $ AST.NSimple outputId)+ ((genExprFCall showId (AST.PrimName $ AST.NSimple outSig)) AST.:&: suffix)
+ CLasH/VHDL/VHDLTools.hs view
@@ -0,0 +1,704 @@+{-# LANGUAGE RelaxedPolyRec #-} -- Needed for vhdl_ty_either', for some reason...+module CLasH.VHDL.VHDLTools where++-- Standard modules+import qualified Maybe+import qualified Data.Either as Either+import qualified Data.List as List+import qualified Data.Char as Char+import qualified Data.Map as Map+import qualified Control.Monad as Monad+import qualified Data.Accessor.Monad.Trans.State as MonadState++-- VHDL Imports+import qualified Language.VHDL.AST as AST++-- GHC API+import qualified CoreSyn+import qualified Name+import qualified OccName+import qualified Var+import qualified Id+import qualified TyCon+import qualified Type+import qualified DataCon+import qualified CoreSubst+import qualified Outputable++-- Local imports+import CLasH.VHDL.VHDLTypes+import CLasH.Translator.TranslatorTypes+import CLasH.Utils.Core.CoreTools+import CLasH.Utils+import CLasH.Utils.Pretty+import CLasH.VHDL.Constants++-----------------------------------------------------------------------------+-- Functions to generate concurrent statements+-----------------------------------------------------------------------------++-- Create an unconditional assignment statement+mkUncondAssign ::+ Either CoreSyn.CoreBndr AST.VHDLName -- ^ The signal to assign to+ -> AST.Expr -- ^ The expression to assign+ -> AST.ConcSm -- ^ The resulting concurrent statement+mkUncondAssign dst expr = mkAssign dst Nothing expr++-- Create a conditional assignment statement+mkCondAssign ::+ Either CoreSyn.CoreBndr AST.VHDLName -- ^ The signal to assign to+ -> AST.Expr -- ^ The condition+ -> AST.Expr -- ^ The value when true+ -> AST.Expr -- ^ The value when false+ -> AST.ConcSm -- ^ The resulting concurrent statement+mkCondAssign dst cond true false = mkAssign dst (Just (cond, true)) false++-- Create a conditional or unconditional assignment statement+mkAssign ::+ Either CoreSyn.CoreBndr AST.VHDLName -- ^ The signal to assign to+ -> Maybe (AST.Expr , AST.Expr) -- ^ Optionally, the condition to test for+ -- and the value to assign when true.+ -> AST.Expr -- ^ The value to assign when false or no condition+ -> AST.ConcSm -- ^ The resulting concurrent statement+mkAssign dst cond false_expr =+ let+ -- I'm not 100% how this assignment AST works, but this gets us what we+ -- want...+ whenelse = case cond of+ Just (cond_expr, true_expr) -> + let + true_wform = AST.Wform [AST.WformElem true_expr Nothing]+ in+ [AST.WhenElse true_wform cond_expr]+ Nothing -> []+ false_wform = AST.Wform [AST.WformElem false_expr Nothing]+ dst_name = case dst of+ Left bndr -> AST.NSimple (varToVHDLId bndr)+ Right name -> name+ assign = dst_name AST.:<==: (AST.ConWforms whenelse false_wform Nothing)+ in+ AST.CSSASm assign++mkAltsAssign ::+ Either CoreSyn.CoreBndr AST.VHDLName -- ^ The signal to assign to+ -> [AST.Expr] -- ^ The conditions+ -> [AST.Expr] -- ^ The expressions+ -> AST.ConcSm -- ^ The Alt assigns+mkAltsAssign dst conds exprs+ | (length conds) /= ((length exprs) - 1) = error "\nVHDLTools.mkAltsAssign: conditions expression mismatch"+ | otherwise =+ let+ whenelses = zipWith mkWhenElse conds exprs+ false_wform = AST.Wform [AST.WformElem (last exprs) Nothing]+ dst_name = case dst of+ Left bndr -> AST.NSimple (varToVHDLId bndr)+ Right name -> name+ assign = dst_name AST.:<==: (AST.ConWforms whenelses false_wform Nothing)+ in+ AST.CSSASm assign+ where+ mkWhenElse :: AST.Expr -> AST.Expr -> AST.WhenElse+ mkWhenElse cond true_expr =+ let+ true_wform = AST.Wform [AST.WformElem true_expr Nothing]+ in+ AST.WhenElse true_wform cond++mkAssocElems :: + [AST.Expr] -- ^ The argument that are applied to function+ -> AST.VHDLName -- ^ The binder in which to store the result+ -> Entity -- ^ The entity to map against.+ -> [AST.AssocElem] -- ^ The resulting port maps+mkAssocElems args res entity =+ arg_maps ++ (Maybe.maybeToList res_map_maybe)+ where+ arg_ports = ent_args entity+ res_port_maybe = ent_res entity+ -- Create an expression of res to map against the output port+ res_expr = vhdlNameToVHDLExpr res+ -- Map each of the input ports+ arg_maps = zipWith mkAssocElem (map fst arg_ports) args+ -- Map the output port, if present+ res_map_maybe = fmap (\port -> mkAssocElem (fst port) res_expr) res_port_maybe++-- | Create an VHDL port -> signal association+mkAssocElem :: AST.VHDLId -> AST.Expr -> AST.AssocElem+mkAssocElem port signal = Just port AST.:=>: (AST.ADExpr signal) ++-- | Create an aggregate signal+mkAggregateSignal :: [AST.Expr] -> AST.Expr+mkAggregateSignal x = AST.Aggregate (map (\z -> AST.ElemAssoc Nothing z) x)++mkComponentInst ::+ String -- ^ The portmap label+ -> AST.VHDLId -- ^ The entity name+ -> [AST.AssocElem] -- ^ The port assignments+ -> AST.ConcSm+mkComponentInst label entity_id portassigns = AST.CSISm compins+ where+ -- We always have a clock port, so no need to map it anywhere but here+ clk_port = mkAssocElem clockId (idToVHDLExpr clockId)+ resetn_port = mkAssocElem resetId (idToVHDLExpr resetId)+ compins = AST.CompInsSm (mkVHDLExtId label) (AST.IUEntity (AST.NSimple entity_id)) (AST.PMapAspect (portassigns ++ [clk_port,resetn_port]))++-----------------------------------------------------------------------------+-- Functions to generate VHDL Exprs+-----------------------------------------------------------------------------++varToVHDLExpr :: Var.Var -> TypeSession AST.Expr+varToVHDLExpr var =+ case Id.isDataConWorkId_maybe var of+ -- This is a dataconstructor.+ Just dc -> dataconToVHDLExpr dc+ -- Not a datacon, just another signal.+ Nothing -> return $ AST.PrimName $ AST.NSimple $ varToVHDLId var++-- Turn a VHDLName into an AST expression+vhdlNameToVHDLExpr = AST.PrimName++-- Turn a VHDL Id into an AST expression+idToVHDLExpr = vhdlNameToVHDLExpr . AST.NSimple++-- Turn a Core expression into an AST expression+exprToVHDLExpr core = varToVHDLExpr (exprToVar core)++-- Turn a alternative constructor into an AST expression. For+-- dataconstructors, this is only the constructor itself, not any arguments it+-- has. Should not be called with a DEFAULT constructor.+altconToVHDLExpr :: CoreSyn.AltCon -> TypeSession AST.Expr+altconToVHDLExpr (CoreSyn.DataAlt dc) = dataconToVHDLExpr dc++altconToVHDLExpr (CoreSyn.LitAlt _) = error "\nVHDL.conToVHDLExpr: Literals not support in case alternatives yet"+altconToVHDLExpr CoreSyn.DEFAULT = error "\nVHDL.conToVHDLExpr: DEFAULT alternative should not occur here!"++-- Turn a datacon (without arguments!) into a VHDL expression.+dataconToVHDLExpr :: DataCon.DataCon -> TypeSession AST.Expr+dataconToVHDLExpr dc = do+ typemap <- MonadState.get tsTypes+ htype_either <- mkHTypeEither (DataCon.dataConRepType dc)+ case htype_either of+ -- No errors+ Right htype -> do+ let dcname = DataCon.dataConName dc+ case htype of+ (BuiltinType "Bit") -> return $ AST.PrimLit $ case Name.getOccString dcname of "High" -> "'1'"; "Low" -> "'0'"+ (BuiltinType "Bool") -> return $ AST.PrimLit $ case Name.getOccString dcname of "True" -> "true"; "False" -> "false"+ otherwise -> do+ let existing_ty = Monad.liftM (fmap fst) $ Map.lookup htype typemap+ case existing_ty of+ Just ty -> do+ let lit = idToVHDLExpr $ mkVHDLExtId $ Name.getOccString dcname+ return lit+ Nothing -> error $ "\nVHDLTools.dataconToVHDLExpr: Trying to make value for non-representable DataCon: " ++ pprString dc+ -- Error when constructing htype+ Left err -> error err++-----------------------------------------------------------------------------+-- Functions dealing with names, variables and ids+-----------------------------------------------------------------------------++-- Creates a VHDL Id from a binder+varToVHDLId ::+ CoreSyn.CoreBndr+ -> AST.VHDLId+varToVHDLId var = mkVHDLExtId (varToString var ++ varToStringUniq var ++ show (lowers $ varToStringUniq var))+ where+ lowers :: String -> Int+ lowers xs = length [x | x <- xs, Char.isLower x]++-- Creates a VHDL Name from a binder+varToVHDLName ::+ CoreSyn.CoreBndr+ -> AST.VHDLName+varToVHDLName = AST.NSimple . varToVHDLId++-- Extracts the binder name as a String+varToString ::+ CoreSyn.CoreBndr+ -> String+varToString = OccName.occNameString . Name.nameOccName . Var.varName++-- Get the string version a Var's unique+varToStringUniq :: Var.Var -> String+varToStringUniq = show . Var.varUnique++-- Extracts the string version of the name+nameToString :: Name.Name -> String+nameToString = OccName.occNameString . Name.nameOccName++-- Shortcut for Basic VHDL Ids.+-- Can only contain alphanumerics and underscores. The supplied string must be+-- a valid basic id, otherwise an error value is returned. This function is+-- not meant to be passed identifiers from a source file, use mkVHDLExtId for+-- that.+mkVHDLBasicId :: String -> AST.VHDLId+mkVHDLBasicId s = + AST.unsafeVHDLBasicId $ (strip_multiscore . strip_leading . strip_invalid) s+ where+ -- Strip invalid characters.+ strip_invalid = filter (`elem` ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ "_.")+ -- Strip leading numbers and underscores+ strip_leading = dropWhile (`elem` ['0'..'9'] ++ "_")+ -- Strip multiple adjacent underscores+ strip_multiscore = concatMap (\cs -> + case cs of + ('_':_) -> "_"+ _ -> cs+ ) . List.group++-- Shortcut for Extended VHDL Id's. These Id's can contain a lot more+-- different characters than basic ids, but can never be used to refer to+-- basic ids.+-- Use extended Ids for any values that are taken from the source file.+mkVHDLExtId :: String -> AST.VHDLId+mkVHDLExtId s = + AST.unsafeVHDLExtId $ strip_invalid s+ where + -- Allowed characters, taken from ForSyde's mkVHDLExtId+ allowed = ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ " \"#&'()*+,./:;<=>_|!$%@?[]^`{}~-"+ strip_invalid = filter (`elem` allowed)++-- Create a record field selector that selects the given label from the record+-- stored in the given binder.+mkSelectedName :: AST.VHDLName -> AST.VHDLId -> AST.VHDLName+mkSelectedName name label =+ AST.NSelected $ name AST.:.: (AST.SSimple label) ++-- Create an indexed name that selects a given element from a vector.+mkIndexedName :: AST.VHDLName -> AST.Expr -> AST.VHDLName+-- Special case for already indexed names. Just add an index+mkIndexedName (AST.NIndexed (AST.IndexedName name indexes)) index =+ AST.NIndexed (AST.IndexedName name (indexes++[index]))+-- General case for other names+mkIndexedName name index = AST.NIndexed (AST.IndexedName name [index])++-----------------------------------------------------------------------------+-- Functions dealing with VHDL types+-----------------------------------------------------------------------------+builtin_types :: TypeMap+builtin_types = + Map.fromList [+ (BuiltinType "Bit", Just (std_logicTM, Nothing)),+ (BuiltinType "Bool", Just (booleanTM, Nothing)) -- TysWiredIn.boolTy+ ]++-- Is the given type representable at runtime?+isReprType :: Type.Type -> TypeSession Bool+isReprType ty = do+ ty_either <- mkHTypeEither ty+ return $ case ty_either of+ Left _ -> False+ Right _ -> True++-- | Turn a Core type into a HType, returning an error using the given+-- error string if the type was not representable.+mkHType :: (TypedThing t, Outputable.Outputable t) => + String -> t -> TypeSession HType+mkHType msg ty = do+ htype_either <- mkHTypeEither ty+ case htype_either of+ Right htype -> return htype+ Left err -> error $ msg ++ err ++-- | Turn a Core type into a HType. Returns either an error message if+-- the type was not representable, or the HType generated.+mkHTypeEither :: (TypedThing t, Outputable.Outputable t) => + t -> TypeSession (Either String HType)+mkHTypeEither tything =+ case getType tything of+ Nothing -> return $ Left $ "\nVHDLTools.mkHTypeEither: Typed thing without a type: " ++ pprString tything+ Just ty -> mkHTypeEither' ty++mkHTypeEither' :: Type.Type -> TypeSession (Either String HType)+mkHTypeEither' ty | ty_has_free_tyvars ty = return $ Left $ "\nVHDLTools.mkHTypeEither': Cannot create type: type has free type variables: " ++ pprString ty+ | isStateType ty = return $ Right StateType+ | otherwise =+ case Type.splitTyConApp_maybe ty of+ Just (tycon, args) -> do+ typemap <- MonadState.get tsTypes+ let name = Name.getOccString (TyCon.tyConName tycon)+ let builtinTyMaybe = Map.lookup (BuiltinType name) typemap + case builtinTyMaybe of+ (Just x) -> return $ Right $ BuiltinType name+ Nothing ->+ case name of+ "Vector" -> do+ let el_ty = tfvec_elem ty+ elem_htype_either <- mkHTypeEither el_ty+ case elem_htype_either of+ -- Could create element type+ Right elem_htype -> do+ len <- tfp_to_int (tfvec_len_ty ty)+ return $ Right $ VecType len elem_htype+ -- Could not create element type+ Left err -> return $ Left $ + "\nVHDLTools.mkHTypeEither': Can not construct vectortype for elementtype: " ++ pprString el_ty ++ err+ "Unsigned" -> do+ len <- tfp_to_int (sized_word_len_ty ty)+ return $ Right $ SizedWType len+ "Signed" -> do+ len <- tfp_to_int (sized_word_len_ty ty)+ return $ Right $ SizedIType len+ "Index" -> do+ bound <- tfp_to_int (ranged_word_bound_ty ty)+ return $ Right $ RangedWType bound+ otherwise ->+ mkTyConHType tycon args+ Nothing -> return $ Left $ "\nVHDLTools.mkHTypeEither': Do not know what to do with type: " ++ pprString ty++mkTyConHType :: TyCon.TyCon -> [Type.Type] -> TypeSession (Either String HType)+mkTyConHType tycon args =+ case TyCon.tyConDataCons tycon of+ -- Not an algebraic type+ [] -> return $ Left $ "VHDLTools.mkTyConHType: Only custom algebraic types are supported: " ++ pprString tycon+ [dc] -> do+ let arg_tys = DataCon.dataConRepArgTys dc+ let real_arg_tys = map (CoreSubst.substTy subst) arg_tys+ let real_arg_tys_nostate = filter (\x -> not (isStateType x)) real_arg_tys+ elem_htys_either <- mapM mkHTypeEither real_arg_tys_nostate+ case Either.partitionEithers elem_htys_either of+ ([], [elem_hty]) ->+ return $ Right elem_hty+ -- No errors in element types+ ([], elem_htys) ->+ return $ Right $ AggrType (nameToString (TyCon.tyConName tycon)) elem_htys+ -- There were errors in element types+ (errors, _) -> return $ Left $+ "\nVHDLTools.mkTyConHType: Can not construct type for: " ++ pprString tycon ++ "\n because no type can be construced for some of the arguments.\n"+ ++ (concat errors)+ dcs -> do+ let arg_tys = concatMap DataCon.dataConRepArgTys dcs+ let real_arg_tys = map (CoreSubst.substTy subst) arg_tys+ case real_arg_tys of+ [] ->+ return $ Right $ EnumType (nameToString (TyCon.tyConName tycon)) (map (nameToString . DataCon.dataConName) dcs)+ xs -> return $ Left $+ "VHDLTools.mkTyConHType: Only enum-like constructor datatypes supported: " ++ pprString dcs ++ "\n"+ where+ tyvars = TyCon.tyConTyVars tycon+ subst = CoreSubst.extendTvSubstList CoreSubst.emptySubst (zip tyvars args)++-- Translate a Haskell type to a VHDL type, generating a new type if needed.+-- Returns an error value, using the given message, when no type could be+-- created. Returns Nothing when the type is valid, but empty.+vhdlTy :: (TypedThing t, Outputable.Outputable t) => + String -> t -> TypeSession (Maybe AST.TypeMark)+vhdlTy msg ty = do+ htype <- mkHType msg ty+ vhdlTyMaybe htype++vhdlTyMaybe :: HType -> TypeSession (Maybe AST.TypeMark)+vhdlTyMaybe htype = do+ typemap <- MonadState.get tsTypes+ -- If not a builtin type, try the custom types+ let existing_ty = Map.lookup htype typemap+ case existing_ty of+ -- Found a type, return it+ Just (Just (t, _)) -> return $ Just t+ Just (Nothing) -> return Nothing+ -- No type yet, try to construct it+ Nothing -> do+ newty <- (construct_vhdl_ty htype)+ MonadState.modify tsTypes (Map.insert htype newty)+ case newty of+ Just (ty_id, ty_def) -> do+ MonadState.modify tsTypeDecls (\typedefs -> typedefs ++ [mktydecl (ty_id, ty_def)])+ return $ Just ty_id+ Nothing -> return Nothing++-- Construct a new VHDL type for the given Haskell type. Returns an error+-- message or the resulting typemark and typedef.+construct_vhdl_ty :: HType -> TypeSession TypeMapRec+-- State types don't generate VHDL+construct_vhdl_ty htype =+ case htype of+ StateType -> return Nothing+ (SizedWType w) -> mkUnsignedTy w+ (SizedIType i) -> mkSignedTy i+ (RangedWType u) -> mkNaturalTy 0 u+ (VecType n e) -> mkVectorTy (VecType n e)+ -- Create a custom type from this tycon+ otherwise -> mkTyconTy htype++-- | Create VHDL type for a custom tycon+mkTyconTy :: HType -> TypeSession TypeMapRec+mkTyconTy htype =+ case htype of+ (AggrType tycon args) -> do+ elemTysMaybe <- mapM vhdlTyMaybe args+ case Maybe.catMaybes elemTysMaybe of+ [] -> -- No non-empty members+ return Nothing+ elem_tys -> do+ let elems = zipWith AST.ElementDec recordlabels elem_tys + let elem_names = concatMap prettyShow elem_tys+ let ty_id = mkVHDLExtId $ tycon ++ elem_names+ let ty_def = AST.TDR $ AST.RecordTypeDef elems+ let tupshow = mkTupleShow elem_tys ty_id+ MonadState.modify tsTypeFuns $ Map.insert (htype, showIdString) (showId, tupshow)+ return $ Just (ty_id, Just $ Left ty_def)+ (EnumType tycon dcs) -> do+ let elems = map mkVHDLExtId dcs+ let ty_id = mkVHDLExtId tycon+ let ty_def = AST.TDE $ AST.EnumTypeDef elems+ let enumShow = mkEnumShow elems ty_id+ MonadState.modify tsTypeFuns $ Map.insert (htype, showIdString) (showId, enumShow)+ return $ Just (ty_id, Just $ Left ty_def)+ otherwise -> error $ "\nVHDLTools.mkTyconTy: Called for HType that is neiter a AggrType or EnumType: " ++ show htype+ where+ -- Generate a bunch of labels for fields of a record+ recordlabels = map (\c -> mkVHDLBasicId [c]) ['A'..'Z']++-- | Create a VHDL vector type+mkVectorTy ::+ HType -- ^ The Haskell type of the Vector+ -> TypeSession TypeMapRec+ -- ^ An error message or The typemark created.++mkVectorTy (VecType len elHType) = do+ typesMap <- MonadState.get tsTypes+ elTyTmMaybe <- vhdlTyMaybe elHType+ case elTyTmMaybe of+ (Just elTyTm) -> do+ let ty_id = mkVHDLExtId $ "vector-"++ (AST.fromVHDLId elTyTm) ++ "-0_to_" ++ (show len)+ let range = AST.ConstraintIndex $ AST.IndexConstraint [AST.ToRange (AST.PrimLit "0") (AST.PrimLit $ show (len - 1))]+ let existing_uvec_ty = fmap (fmap fst) $ Map.lookup (UVecType elHType) typesMap+ case existing_uvec_ty of+ Just (Just t) -> do+ let ty_def = AST.SubtypeIn t (Just range)+ return (Just (ty_id, Just $ Right ty_def))+ Nothing -> do+ let vec_id = mkVHDLExtId $ "vector_" ++ (AST.fromVHDLId elTyTm)+ let vec_def = AST.TDA $ AST.UnconsArrayDef [tfvec_indexTM] elTyTm+ MonadState.modify tsTypes (Map.insert (UVecType elHType) (Just (vec_id, (Just $ Left vec_def))))+ MonadState.modify tsTypeDecls (\typedefs -> typedefs ++ [mktydecl (vec_id, (Just $ Left vec_def))])+ let vecShowFuns = mkVectorShow elTyTm vec_id+ mapM_ (\(id, subprog) -> MonadState.modify tsTypeFuns $ Map.insert (UVecType elHType, id) ((mkVHDLExtId id), subprog)) vecShowFuns+ let ty_def = AST.SubtypeIn vec_id (Just range)+ return (Just (ty_id, Just $ Right ty_def))+ -- Vector of empty elements becomes empty itself.+ Nothing -> return Nothing+mkVectorTy htype = error $ "\nVHDLTools.mkVectorTy: Called for HType that is not a VecType: " ++ show htype++mkNaturalTy ::+ Int -- ^ The minimum bound (> 0)+ -> Int -- ^ The maximum bound (> minimum bound)+ -> TypeSession TypeMapRec+ -- ^ An error message or The typemark created.+mkNaturalTy min_bound max_bound = do+ let bitsize = floor (logBase 2 (fromInteger (toInteger max_bound)))+ let ty_id = mkVHDLExtId $ "natural_" ++ (show min_bound) ++ "_to_" ++ (show max_bound)+ let range = AST.ConstraintIndex $ AST.IndexConstraint [AST.ToRange (AST.PrimLit $ show min_bound) (AST.PrimLit $ show bitsize)]+ let ty_def = AST.SubtypeIn unsignedTM (Just range)+ return (Just (ty_id, Just $ Right ty_def))++mkUnsignedTy ::+ Int -- ^ Haskell type of the unsigned integer+ -> TypeSession TypeMapRec+mkUnsignedTy size = do+ let ty_id = mkVHDLExtId $ "unsigned_" ++ show (size - 1)+ let range = AST.ConstraintIndex $ AST.IndexConstraint [AST.ToRange (AST.PrimLit "0") (AST.PrimLit $ show (size - 1))]+ let ty_def = AST.SubtypeIn unsignedTM (Just range)+ return (Just (ty_id, Just $ Right ty_def))+ +mkSignedTy ::+ Int -- ^ Haskell type of the signed integer+ -> TypeSession TypeMapRec+mkSignedTy size = do+ let ty_id = mkVHDLExtId $ "signed_" ++ show (size - 1)+ let range = AST.ConstraintIndex $ AST.IndexConstraint [AST.ToRange (AST.PrimLit "0") (AST.PrimLit $ show (size - 1))]+ let ty_def = AST.SubtypeIn signedTM (Just range)+ return (Just (ty_id, Just $ Right ty_def))++-- Finds the field labels for VHDL type generated for the given Core type,+-- which must result in a record type.+getFieldLabels :: Type.Type -> TypeSession [AST.VHDLId]+getFieldLabels ty = do+ -- Ensure that the type is generated (but throw away it's VHDLId)+ let error_msg = "\nVHDLTools.getFieldLabels: Can not get field labels, because: " ++ pprString ty ++ "can not be generated." + vhdlTy error_msg ty+ -- Get the types map, lookup and unpack the VHDL TypeDef+ types <- MonadState.get tsTypes+ -- Assume the type for which we want labels is really translatable+ htype <- mkHType error_msg ty+ case Map.lookup htype types of+ Nothing -> error $ "\nVHDLTools.getFieldLabels: Type not found? This should not happen!\nLooking for type: " ++ (pprString ty) ++ "\nhtype: " ++ (show htype) + Just Nothing -> return [] -- The type is empty+ Just (Just (_, Just (Left (AST.TDR (AST.RecordTypeDef elems))))) -> return $ map (\(AST.ElementDec id _) -> id) elems+ Just (Just (_, Just vty)) -> error $ "\nVHDLTools.getFieldLabels: Type not a record type? This should not happen!\nLooking for type: " ++ pprString (ty) ++ "\nhtype: " ++ (show htype) ++ "\nFound type: " ++ (show vty)+ +mktydecl :: (AST.VHDLId, Maybe (Either AST.TypeDef AST.SubtypeIn)) -> Maybe AST.PackageDecItem+mytydecl (_, Nothing) = Nothing+mktydecl (ty_id, Just (Left ty_def)) = Just $ AST.PDITD $ AST.TypeDec ty_id ty_def+mktydecl (ty_id, Just (Right ty_def)) = Just $ AST.PDISD $ AST.SubtypeDec ty_id ty_def++mkTupleShow :: + [AST.TypeMark] -- ^ type of each tuple element+ -> AST.TypeMark -- ^ type of the tuple+ -> AST.SubProgBody+mkTupleShow elemTMs tupleTM = AST.SubProgBody showSpec [] [showExpr]+ where+ tupPar = AST.unsafeVHDLBasicId "tup"+ showSpec = AST.Function showId [AST.IfaceVarDec tupPar tupleTM] stringTM+ showExpr = AST.ReturnSm (Just $+ AST.PrimLit "'('" AST.:&: showMiddle AST.:&: AST.PrimLit "')'")+ where+ showMiddle = if null elemTMs then+ AST.PrimLit "''"+ else+ foldr1 (\e1 e2 -> e1 AST.:&: AST.PrimLit "','" AST.:&: e2) $+ map ((genExprFCall showId).+ AST.PrimName .+ AST.NSelected .+ (AST.NSimple tupPar AST.:.:).+ tupVHDLSuffix)+ (take tupSize recordlabels)+ recordlabels = map (\c -> mkVHDLBasicId [c]) ['A'..'Z']+ tupSize = length elemTMs++mkEnumShow ::+ [AST.VHDLId]+ -> AST.TypeMark+ -> AST.SubProgBody+mkEnumShow elemIds enumTM = AST.SubProgBody showSpec [] [showExpr]+ where+ enumPar = AST.unsafeVHDLBasicId "enum"+ showSpec = AST.Function showId [AST.IfaceVarDec enumPar enumTM] stringTM+ showExpr = AST.ReturnSm (Just $+ AST.PrimLit (show $ tail $ init $ AST.fromVHDLId enumTM))++mkVectorShow ::+ AST.TypeMark -- ^ elemtype+ -> AST.TypeMark -- ^ vectype+ -> [(String,AST.SubProgBody)]+mkVectorShow elemTM vectorTM = + [ (headId, AST.SubProgBody headSpec [] [headExpr])+ , (tailId, AST.SubProgBody tailSpec [AST.SPVD tailVar] [tailExpr, tailRet])+ , (showIdString, AST.SubProgBody showSpec [AST.SPSB doShowDef] [showRet])+ ]+ where+ vecPar = AST.unsafeVHDLBasicId "vec"+ resId = AST.unsafeVHDLBasicId "res"+ headSpec = AST.Function (mkVHDLExtId headId) [AST.IfaceVarDec vecPar vectorTM] elemTM+ -- return vec(0);+ headExpr = AST.ReturnSm (Just (AST.PrimName $ AST.NIndexed (AST.IndexedName + (AST.NSimple vecPar) [AST.PrimLit "0"])))+ vecSlice init last = AST.PrimName (AST.NSlice + (AST.SliceName + (AST.NSimple vecPar) + (AST.ToRange init last)))+ tailSpec = AST.Function (mkVHDLExtId tailId) [AST.IfaceVarDec vecPar vectorTM] vectorTM+ -- variable res : fsvec_x (0 to vec'length-2); + tailVar = + AST.VarDec resId + (AST.SubtypeIn vectorTM+ (Just $ AST.ConstraintIndex $ AST.IndexConstraint + [AST.ToRange (AST.PrimLit "0")+ (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) AST.:-:+ (AST.PrimLit "2")) ]))+ Nothing + -- res AST.:= vec(1 to vec'length-1)+ tailExpr = AST.NSimple resId AST.:= (vecSlice + (AST.PrimLit "1") + (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing) + AST.:-: AST.PrimLit "1"))+ tailRet = AST.ReturnSm (Just $ AST.PrimName $ AST.NSimple resId)+ showSpec = AST.Function showId [AST.IfaceVarDec vecPar vectorTM] stringTM+ doShowId = AST.unsafeVHDLExtId "doshow"+ doShowDef = AST.SubProgBody doShowSpec [] [doShowRet]+ where doShowSpec = AST.Function doShowId [AST.IfaceVarDec vecPar vectorTM] + stringTM+ -- case vec'len is+ -- when 0 => return "";+ -- when 1 => return head(vec);+ -- when others => return show(head(vec)) & ',' &+ -- doshow (tail(vec));+ -- end case;+ doShowRet = + AST.CaseSm (AST.PrimName (AST.NAttribute $ + AST.AttribName (AST.NSimple vecPar) (AST.NSimple $ mkVHDLBasicId lengthId) Nothing))+ [AST.CaseSmAlt [AST.ChoiceE $ AST.PrimLit "0"] + [AST.ReturnSm (Just $ AST.PrimLit "\"\"")],+ AST.CaseSmAlt [AST.ChoiceE $ AST.PrimLit "1"] + [AST.ReturnSm (Just $ + genExprFCall showId + (genExprFCall (mkVHDLExtId headId) (AST.PrimName $ AST.NSimple vecPar)) )],+ AST.CaseSmAlt [AST.Others] + [AST.ReturnSm (Just $ + genExprFCall showId + (genExprFCall (mkVHDLExtId headId) (AST.PrimName $ AST.NSimple vecPar)) AST.:&:+ AST.PrimLit "','" AST.:&:+ genExprFCall doShowId + (genExprFCall (mkVHDLExtId tailId) (AST.PrimName $ AST.NSimple vecPar)) ) ]]+ -- return '<' & doshow(vec) & '>';+ showRet = AST.ReturnSm (Just $ AST.PrimLit "'<'" AST.:&:+ genExprFCall doShowId (AST.PrimName $ AST.NSimple vecPar) AST.:&:+ AST.PrimLit "'>'" )++mkBuiltInShow :: [AST.SubProgBody]+mkBuiltInShow = [ AST.SubProgBody showBitSpec [] [showBitExpr]+ , AST.SubProgBody showBoolSpec [] [showBoolExpr]+ , AST.SubProgBody showSingedSpec [] [showSignedExpr]+ , AST.SubProgBody showUnsignedSpec [] [showUnsignedExpr]+ -- , AST.SubProgBody showNaturalSpec [] [showNaturalExpr]+ ]+ where+ bitPar = AST.unsafeVHDLBasicId "s"+ boolPar = AST.unsafeVHDLBasicId "b"+ signedPar = AST.unsafeVHDLBasicId "sint"+ unsignedPar = AST.unsafeVHDLBasicId "uint"+ -- naturalPar = AST.unsafeVHDLBasicId "nat"+ showBitSpec = AST.Function showId [AST.IfaceVarDec bitPar std_logicTM] stringTM+ -- if s = '1' then return "'1'" else return "'0'"+ showBitExpr = AST.IfSm (AST.PrimName (AST.NSimple bitPar) AST.:=: AST.PrimLit "'1'")+ [AST.ReturnSm (Just $ AST.PrimLit "\"High\"")]+ []+ (Just $ AST.Else [AST.ReturnSm (Just $ AST.PrimLit "\"Low\"")])+ showBoolSpec = AST.Function showId [AST.IfaceVarDec boolPar booleanTM] stringTM+ -- if b then return "True" else return "False"+ showBoolExpr = AST.IfSm (AST.PrimName (AST.NSimple boolPar))+ [AST.ReturnSm (Just $ AST.PrimLit "\"True\"")]+ []+ (Just $ AST.Else [AST.ReturnSm (Just $ AST.PrimLit "\"False\"")])+ showSingedSpec = AST.Function showId [AST.IfaceVarDec signedPar signedTM] stringTM+ showSignedExpr = AST.ReturnSm (Just $+ AST.PrimName $ AST.NAttribute $ AST.AttribName (AST.NSimple integerId) + (AST.NIndexed $ AST.IndexedName (AST.NSimple imageId) [signToInt]) Nothing )+ where+ signToInt = genExprFCall (mkVHDLBasicId toIntegerId) (AST.PrimName $ AST.NSimple signedPar)+ showUnsignedSpec = AST.Function showId [AST.IfaceVarDec unsignedPar unsignedTM] stringTM+ showUnsignedExpr = AST.ReturnSm (Just $+ AST.PrimName $ AST.NAttribute $ AST.AttribName (AST.NSimple integerId) + (AST.NIndexed $ AST.IndexedName (AST.NSimple imageId) [unsignToInt]) Nothing )+ where+ unsignToInt = genExprFCall (mkVHDLBasicId toIntegerId) (AST.PrimName $ AST.NSimple unsignedPar)+ -- showNaturalSpec = AST.Function showId [AST.IfaceVarDec naturalPar naturalTM] stringTM+ -- showNaturalExpr = AST.ReturnSm (Just $+ -- AST.PrimName $ AST.NAttribute $ AST.AttribName (AST.NSimple integerId)+ -- (AST.NIndexed $ AST.IndexedName (AST.NSimple imageId) [AST.PrimName $ AST.NSimple $ naturalPar]) Nothing )+ + +genExprFCall :: AST.VHDLId -> AST.Expr -> AST.Expr+genExprFCall fName args = + AST.PrimFCall $ AST.FCall (AST.NSimple fName) $+ map (\exp -> Nothing AST.:=>: AST.ADExpr exp) [args] ++genExprPCall2 :: AST.VHDLId -> AST.Expr -> AST.Expr -> AST.SeqSm +genExprPCall2 entid arg1 arg2 =+ AST.ProcCall (AST.NSimple entid) $+ map (\exp -> Nothing AST.:=>: AST.ADExpr exp) [arg1,arg2]++mkSigDec :: CoreSyn.CoreBndr -> TranslatorSession (Maybe AST.SigDec)+mkSigDec bndr = do+ let error_msg = "\nVHDL.mkSigDec: Can not make signal declaration for type: \n" ++ pprString bndr + type_mark_maybe <- MonadState.lift tsType $ vhdlTy error_msg (Var.varType bndr)+ case type_mark_maybe of+ Just type_mark -> return $ Just (AST.SigDec (varToVHDLId bndr) type_mark Nothing)+ Nothing -> return Nothing++-- | Does the given thing have a non-empty type?+hasNonEmptyType :: (TypedThing t, Outputable.Outputable t) => + t -> TranslatorSession Bool+hasNonEmptyType thing = MonadState.lift tsType $ isJustM (vhdlTy "hasNonEmptyType: Non representable type?" thing)
+ CLasH/VHDL/VHDLTypes.hs view
@@ -0,0 +1,24 @@+--+-- Some types used by the VHDL module.+--+module CLasH.VHDL.VHDLTypes where++-- VHDL imports+import qualified Language.VHDL.AST as AST++-- A description of a port of an entity+type Port = (AST.VHDLId, AST.TypeMark)++-- A description of a VHDL entity. Contains both the entity itself as well as+-- info on how to map a haskell value (argument / result) on to the entity's+-- ports.+data Entity = Entity { + ent_id :: AST.VHDLId, -- ^ The id of the entity+ ent_args :: [Port], -- ^ A port for each non-empty function argument+ ent_res :: Maybe Port, -- ^ The output port+ ent_dec :: AST.EntityDec -- ^ The complete entity declaration+} deriving (Show);++type Architecture = AST.ArchBody++-- vim: set ts=8 sw=2 sts=2 expandtab:
+ Data/Param/Index.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE TypeFamilies, TypeOperators, ScopedTypeVariables, FlexibleInstances, TemplateHaskell, Rank2Types, FlexibleContexts #-}+module Data.Param.Index+ ( Index+ , fromNaturalT+ , fromUnsigned+ , rangeT+ ) where++import Language.Haskell.TH+import Language.Haskell.TH.Syntax (Lift(..)) +import Data.Bits+import Types+import Types.Data.Num.Decimal.Literals.TH++import Data.Param.Integer++instance NaturalT nT => Lift (Index nT) where+ lift (Index i) = sigE [| (Index i) |] (decIndexT (fromIntegerT (undefined :: nT)))++decIndexT :: Integer -> Q Type+decIndexT n = appT (conT (''Index)) (decLiteralT n)++fromNaturalT :: ( NaturalT n+ , NaturalT upper+ , (n :<=: upper) ~ True ) => n -> Index upper+fromNaturalT x = Index (fromIntegerT x)++fromUnsigned ::+ ( NaturalT nT+ , Integral (Unsigned nT)+ ) => Unsigned nT -> Index ((Pow2 nT) :-: D1)+fromUnsigned unsigned = Index (toInteger unsigned)++rangeT :: Index nT -> nT+rangeT _ = undefined++instance NaturalT nT => Eq (Index nT) where+ (Index x) == (Index y) = x == y+ (Index x) /= (Index y) = x /= y+ +instance NaturalT nT => Show (Index nT) where+ showsPrec prec n =+ showsPrec prec $ toInteger n+ +instance NaturalT nT => Ord (Index nT) where+ a `compare` b = toInteger a `compare` toInteger b + +instance NaturalT nT => Bounded (Index nT) where+ minBound = 0+ maxBound = Index (fromIntegerT (undefined :: nT))+ +instance NaturalT nT => Enum (Index nT) where+ succ x+ | x == maxBound = error $ "Enum.succ{Index " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `succ' of maxBound"+ | otherwise = x + 1+ pred x+ | x == minBound = error $ "Enum.succ{Index " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `pred' of minBound"+ | otherwise = x - 1+ + fromEnum (Index x)+ | x > toInteger (maxBound :: Int) =+ error $ "Enum.fromEnum{Index " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Index greater than maxBound :: Int"+ | x < toInteger (minBound :: Int) =+ error $ "Enum.fromEnum{Index " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Index smaller than minBound :: Int"+ | otherwise =+ fromInteger x+ toEnum x+ | x > fromIntegral (maxBound :: Index nT) =+ error $ "Enum.fromEnum{Index " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Index greater than maxBound :: Index " ++ show (fromIntegerT (undefined :: nT))+ | x < fromIntegral (minBound :: Index nT) =+ error $ "Enum.fromEnum{Index " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Index smaller than minBound :: Index " ++ show (fromIntegerT (undefined :: nT))+ | otherwise =+ fromInteger $ toInteger x+ +instance NaturalT nT => Num (Index nT) where+ (Index a) + (Index b) =+ fromInteger $ a + b+ (Index a) * (Index b) =+ fromInteger $ a * b + (Index a) - (Index b) =+ fromInteger $ a - b+ fromInteger n+ | n > fromIntegerT (undefined :: nT) =+ error $ "Num.fromInteger{Index " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to make Index larger than " ++ show (fromIntegerT (undefined :: nT)) ++ ", n: " ++ show n+ fromInteger n+ | n < 0 =+ error $ "Num.fromInteger{Index " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to make Index smaller than 0, n: " ++ show n+ fromInteger n =+ Index n+ abs s = s+ signum s+ | s == 0 =+ 0+ | otherwise =+ 1++instance NaturalT nT => Real (Index nT) where+ toRational n = toRational $ toInteger n++instance NaturalT nT => Integral (Index nT) where+ a `quotRem` b =+ let (quot, rem) = toInteger a `quotRem` toInteger b+ in (fromInteger quot, fromInteger rem)+ toInteger s@(Index x) = x
+ Data/Param/Integer.hs view
@@ -0,0 +1,13 @@+module Data.Param.Integer+ ( Signed(..)+ , Unsigned(..)+ , Index (..)+ ) where++import Types++newtype (NaturalT nT) => Signed nT = Signed Integer++newtype (NaturalT nT) => Unsigned nT = Unsigned Integer++newtype (NaturalT upper) => Index upper = Index Integer
+ Data/Param/Signed.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE TypeFamilies, TypeOperators, ScopedTypeVariables, FlexibleInstances, TemplateHaskell, Rank2Types, FlexibleContexts #-}+module Data.Param.Signed+ ( Signed+ , resize+ ) where++import Language.Haskell.TH+import Language.Haskell.TH.Syntax (Lift(..))+import Data.Bits+import Types+import Types.Data.Num.Decimal.Literals.TH++import Data.Param.Integer++instance NaturalT nT => Lift (Signed nT) where+ lift (Signed i) = sigE [| (Signed i) |] (decSignedT (fromIntegerT (undefined :: nT)))++decSignedT :: Integer -> Q Type+decSignedT n = appT (conT (''Signed)) (decLiteralT n)++resize :: (NaturalT nT, NaturalT nT') => Signed nT -> Signed nT'+resize a = fromInteger (toInteger a)++sizeT :: Signed nT+ -> nT+sizeT _ = undefined++mask :: forall nT . NaturalT nT+ => nT+ -> Integer+mask _ = bit (fromIntegerT (undefined :: nT)) - 1++signBit :: forall nT . NaturalT nT+ => nT+ -> Int+signBit _ = fromIntegerT (undefined :: nT) - 1++isNegative :: forall nT . NaturalT nT+ => Signed nT+ -> Bool+isNegative (Signed x) =+ testBit x $ signBit (undefined :: nT)++instance NaturalT nT => Eq (Signed nT) where+ (Signed x) == (Signed y) = x == y+ (Signed x) /= (Signed y) = x /= y++instance NaturalT nT => Show (Signed nT) where+ showsPrec prec n =+ showsPrec prec $ toInteger n++instance NaturalT nT => Read (Signed nT) where+ readsPrec prec str =+ [ (fromInteger n, str)+ | (n, str) <- readsPrec prec str ]++instance NaturalT nT => Ord (Signed nT) where+ a `compare` b = toInteger a `compare` toInteger b++instance NaturalT nT => Bounded (Signed nT) where+ minBound = Signed $ negate $ 1 `shiftL` (fromIntegerT (undefined :: nT) - 1)+ maxBound = Signed $ (1 `shiftL` (fromIntegerT (undefined :: nT) - 1)) - 1++instance NaturalT nT => Enum (Signed nT) where+ succ x+ | x == maxBound = error $ "Enum.succ{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `succ' of maxBound"+ | otherwise = x + 1+ pred x+ | x == minBound = error $ "Enum.succ{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `pred' of minBound"+ | otherwise = x - 1+ + fromEnum (Signed x)+ | x > toInteger (maxBound :: Int) =+ error $ "Enum.fromEnum{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Signed greater than maxBound :: Int"+ | x < toInteger (minBound :: Int) =+ error $ "Enum.fromEnum{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Signed smaller than minBound :: Int"+ | otherwise =+ fromInteger x+ toEnum x+ | x' > toInteger (maxBound :: Signed nT) =+ error $ "Enum.fromEnum{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Signed greater than maxBound :: Signed " ++ show (fromIntegerT (undefined :: nT))+ | x' < toInteger (minBound :: Signed nT) =+ error $ "Enum.fromEnum{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Signed smaller than minBound :: Signed " ++ show (fromIntegerT (undefined :: nT))+ | otherwise =+ fromInteger x'+ where x' = toInteger x++instance NaturalT nT => Num (Signed nT) where+ (Signed a) + (Signed b) =+ fromInteger $ a + b+ (Signed a) * (Signed b) =+ fromInteger $ a * b+ negate (Signed n) =+ fromInteger $ (n `xor` mask (undefined :: nT)) + 1+ a - b =+ a + (negate b)+ + fromInteger n+ | n > 0 =+ Signed $ n .&. mask (undefined :: nT)+ fromInteger n+ | n < 0 =+ negate $ fromInteger $ negate n+ fromInteger _ =+ Signed 0+ + abs s+ | isNegative s =+ negate s+ | otherwise =+ s+ signum s+ | isNegative s =+ -1+ | s == 0 =+ 0+ | otherwise =+ 1++instance NaturalT nT => Real (Signed nT) where+ toRational n = toRational $ toInteger n++instance NaturalT nT => Integral (Signed nT) where+ a `quot` b =+ fromInteger $ toInteger a `quot` toInteger b+ a `rem` b =+ fromInteger $ toInteger a `rem` toInteger b+ a `div` b =+ fromInteger $ toInteger a `div` toInteger b+ a `mod` b =+ fromInteger $ toInteger a `mod` toInteger b+ a `quotRem` b =+ let (quot, rem) = toInteger a `quotRem` toInteger b+ in (fromInteger quot, fromInteger rem)+ a `divMod` b =+ let (div, mod) = toInteger a `divMod` toInteger b+ in (fromInteger div, fromInteger mod)+ toInteger s@(Signed x) =+ if isNegative s+ then let Signed x' = negate s in negate x'+ else x++instance NaturalT nT => Bits (Signed nT) where+ (Signed a) .&. (Signed b) = Signed $ a .&. b+ (Signed a) .|. (Signed b) = Signed $ a .|. b+ (Signed a) `xor` Signed b = Signed $ a `xor` b+ complement (Signed x) = Signed $ x `xor` mask (undefined :: nT)+ (Signed x) `shiftL` b+ | b < 0 = error $ "Bits.shiftL{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to shift by negative amount"+ | otherwise =+ Signed $ mask (undefined :: nT) .&. (x `shiftL` b)+ s@(Signed x) `shiftR` b+ | b < 0 = error $ "Bits.shiftR{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to shift by negative amount"+ | isNegative s =+ Signed $ mask (undefined :: nT) .&.+ ((x `shiftR` b) .|. (mask (undefined :: nT) `shiftL` (fromIntegerT (undefined :: nT) - b)))+ | otherwise =+ Signed $ (mask (undefined :: nT)) .&. (x `shiftR` b)+ (Signed a) `rotateL` b+ | b < 0 =+ error $ "Bits.rotateL{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to rotate by negative amount"+ | otherwise =+ Signed $ mask (undefined :: nT) .&.+ ((a `shiftL` b) .|. (a `shiftR` (fromIntegerT (undefined :: nT) - b)))+ (Signed a) `rotateR` b+ | b < 0 =+ error $ "Bits.rotateR{Signed " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to rotate by negative amount"+ | otherwise =+ Signed $ mask (undefined :: nT) .&.+ ((a `shiftR` b) .|. (a `shiftL` (fromIntegerT (undefined :: nT) - b)))+ bitSize _ = fromIntegerT (undefined :: nT)+ isSigned _ = True
+ Data/Param/Unsigned.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE TypeFamilies, TypeOperators, ScopedTypeVariables, FlexibleInstances, TemplateHaskell, Rank2Types, FlexibleContexts #-}+module Data.Param.Unsigned+ ( Unsigned+ , resize+ , fromIndex+ ) where++import Language.Haskell.TH+import Language.Haskell.TH.Syntax (Lift(..))+import Data.Bits+import Types+import Types.Data.Num.Decimal.Literals.TH++import Data.Param.Integer++instance NaturalT nT => Lift (Unsigned nT) where+ lift (Unsigned i) = sigE [| (Unsigned i) |] (decUnsignedT (fromIntegerT (undefined :: nT)))++decUnsignedT :: Integer -> Q Type+decUnsignedT n = appT (conT (''Unsigned)) (decLiteralT n)++fromIndex ::+ ( NaturalT nT+ , NaturalT nT'+ , ((Pow2 nT') :>: nT) ~ True+ , Integral (Index nT)+ ) => Index nT -> Unsigned nT'+fromIndex index = Unsigned (toInteger index)++resize :: (NaturalT nT, NaturalT nT') => Unsigned nT -> Unsigned nT'+resize a = fromInteger (toInteger a)++sizeT :: Unsigned nT+ -> nT+sizeT _ = undefined++mask :: forall nT . NaturalT nT+ => nT+ -> Integer+mask _ = bit (fromIntegerT (undefined :: nT)) - 1++instance NaturalT nT => Eq (Unsigned nT) where+ (Unsigned x) == (Unsigned y) = x == y+ (Unsigned x) /= (Unsigned y) = x /= y++instance NaturalT nT => Show (Unsigned nT) where+ showsPrec prec n =+ showsPrec prec $ toInteger n++instance NaturalT nT => Read (Unsigned nT) where+ readsPrec prec str =+ [ (fromInteger n, str)+ | (n, str) <- readsPrec prec str ]++instance NaturalT nT => Ord (Unsigned nT) where+ a `compare` b = toInteger a `compare` toInteger b++instance NaturalT nT => Bounded (Unsigned nT) where+ minBound = 0+ maxBound = Unsigned $ (1 `shiftL` (fromIntegerT (undefined :: nT))) - 1++instance NaturalT nT => Enum (Unsigned nT) where+ succ x+ | x == maxBound = error $ "Enum.succ{Unsigned " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `succ' of maxBound"+ | otherwise = x + 1+ pred x+ | x == minBound = error $ "Enum.succ{Unsigned " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `pred' of minBound"+ | otherwise = x - 1+ + fromEnum (Unsigned x)+ | x > toInteger (maxBound :: Int) =+ error $ "Enum.fromEnum{Unsigned " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Unsigned greater than maxBound :: Int"+ | x < toInteger (minBound :: Int) =+ error $ "Enum.fromEnum{Unsigned " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Unsigned smaller than minBound :: Int"+ | otherwise =+ fromInteger x+ toEnum x+ | x > fromIntegral (maxBound :: Unsigned nT) =+ error $ "Enum.fromEnum{Unsigned " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Unsigned greater than maxBound :: Unsigned " ++ show (fromIntegerT (undefined :: nT))+ | x < fromIntegral (minBound :: Unsigned nT) =+ error $ "Enum.fromEnum{Unsigned " ++ show (fromIntegerT (undefined :: nT)) ++ "}: tried to take `fromEnum' on Unsigned smaller than minBound :: Unsigned " ++ show (fromIntegerT (undefined :: nT))+ | otherwise =+ fromInteger $ toInteger x++instance NaturalT nT => Num (Unsigned nT) where+ (Unsigned a) + (Unsigned b) =+ fromInteger $ a + b+ (Unsigned a) * (Unsigned b) =+ fromInteger $ a * b+ negate s@(Unsigned n) =+ fromInteger $ (n `xor` mask (sizeT s)) + 1+ a - b =+ a + (negate b)++ fromInteger n+ | n > 0 =+ Unsigned $ n .&. mask (undefined :: nT)+ fromInteger n+ | n < 0 =+ negate $ fromInteger $ negate n+ fromInteger _ =+ Unsigned 0++ abs s = s+ signum s+ | s == 0 =+ 0+ | otherwise =+ 1++instance NaturalT nT => Real (Unsigned nT) where+ toRational n = toRational $ toInteger n++instance NaturalT nT => Integral (Unsigned nT) where+ a `quot` b =+ fromInteger $ toInteger a `quot` toInteger b+ a `rem` b =+ fromInteger $ toInteger a `rem` toInteger b+ a `div` b =+ fromInteger $ toInteger a `div` toInteger b+ a `mod` b =+ fromInteger $ toInteger a `mod` toInteger b+ a `quotRem` b =+ let (quot, rem) = toInteger a `quotRem` toInteger b+ in (fromInteger quot, fromInteger rem)+ a `divMod` b =+ let (div, mod) = toInteger a `divMod` toInteger b+ in (fromInteger div, fromInteger mod)+ toInteger s@(Unsigned x) = x++instance NaturalT nT => Bits (Unsigned nT) where+ (Unsigned a) .&. (Unsigned b) = Unsigned $ a .&. b+ (Unsigned a) .|. (Unsigned b) = Unsigned $ a .|. b+ (Unsigned a) `xor` Unsigned b = Unsigned $ a `xor` b+ complement (Unsigned x) = Unsigned $ x `xor` mask (undefined :: nT)+ s@(Unsigned x) `shiftL` b+ | b < 0 = error $ "Bits.shiftL{Unsigned " ++ show (bitSize s) ++ "}: tried to shift by negative amount"+ | otherwise =+ Unsigned $ mask (undefined :: nT) .&. (x `shiftL` b)+ s@(Unsigned x) `shiftR` b+ | b < 0 = error $ "Bits.shiftR{Unsigned " ++ show (bitSize s) ++ "}: tried to shift by negative amount"+ | otherwise =+ Unsigned $ (x `shiftR` b)+ s@(Unsigned x) `rotateL` b+ | b < 0 =+ error $ "Bits.rotateL{Unsigned " ++ show (bitSize s) ++ "}: tried to rotate by negative amount"+ | otherwise =+ Unsigned $ mask (undefined :: nT) .&.+ ((x `shiftL` b) .|. (x `shiftR` (bitSize s - b)))+ s@(Unsigned x) `rotateR` b+ | b < 0 =+ error $ "Bits.rotateR{Unsigned " ++ show (bitSize s) ++ "}: tried to rotate by negative amount"+ | otherwise =+ Unsigned $ mask (undefined :: nT) .&.+ ((x `shiftR` b) .|. (x `shiftL` (bitSize s - b)))+ bitSize _ = fromIntegerT (undefined :: nT)+ isSigned _ = False
+ Data/Param/Vector.hs view
@@ -0,0 +1,316 @@+{-# LANGUAGE StandaloneDeriving, ExistentialQuantification, ScopedTypeVariables, TemplateHaskell, TypeOperators, TypeFamilies #-}+module Data.Param.Vector+ ( Vector+ , empty+ , (+>)+ , singleton+ , vectorTH+ , unsafeVector+ , readVector+ , length+ , lengthT+ , fromVector+ , null+ , (!)+ , replace+ , head+ , last+ , init+ , tail+ , take+ , drop+ , select+ , (<+)+ , (++)+ , map+ , zipWith+ , foldl+ , foldr+ , zip+ , unzip+ , shiftl+ , shiftr+ , rotl+ , rotr+ , concat+ , reverse+ , iterate+ , iteraten+ , generate+ , generaten+ , copy+ , copyn+ , split+ ) where+ +import Types+import Types.Data.Num+import Types.Data.Num.Decimal.Literals.TH+import Data.Param.Index++import Data.Typeable+import qualified Prelude as P+import Prelude hiding (+ null, length, head, tail, last, init, take, drop, (++), map, foldl, foldr,+ zipWith, zip, unzip, concat, reverse, iterate )+import qualified Data.Foldable as DF (Foldable, foldr)+import qualified Data.Traversable as DT (Traversable(traverse))+import Language.Haskell.TH hiding (Pred)+import Language.Haskell.TH.Syntax (Lift(..))++newtype (NaturalT s) => Vector s a = Vector {unVec :: [a]}+ deriving Eq++-- deriving instance (NaturalT s, Typeable s, Data s, Typeable a, Data a) => Data (TFVec s a)++-- ==========================+-- = Constructing functions =+-- ==========================+ +empty :: Vector D0 a+empty = Vector []++(+>) :: a -> Vector s a -> Vector (Succ s) a+x +> (Vector xs) = Vector (x:xs)++infix 5 +>++singleton :: a -> Vector D1 a+singleton x = x +> empty++-- FIXME: Not the most elegant solution... but it works for now in clash+vectorTH :: (Lift a) => [a] -> ExpQ+-- vectorTH xs = sigE [| (TFVec xs) |] (decTFVecT (toInteger (P.length xs)) xs)+vectorTH [] = [| empty |]+vectorTH [x] = [| singleton x |]+vectorTH (x:xs) = [| x +> $(vectorTH xs) |]++unsafeVector :: NaturalT s => s -> [a] -> Vector s a+unsafeVector l xs+ | fromIntegerT l /= P.length xs =+ error (show 'unsafeVector P.++ ": dynamic/static lenght mismatch")+ | otherwise = Vector xs++readVector :: (Read a, NaturalT s) => String -> Vector s a+readVector = read+ +-- =======================+-- = Observing functions =+-- =======================+length :: forall s a . NaturalT s => Vector s a -> Int+length _ = fromIntegerT (undefined :: s)++lengthT :: NaturalT s => Vector s a -> s+lengthT = undefined++fromVector :: NaturalT s => Vector s a -> [a]+fromVector (Vector xs) = xs++null :: Vector D0 a -> Bool+null _ = True++(!) :: ( PositiveT s+ , NaturalT u+ , (s :>: u) ~ True) => Vector s a -> Index u -> a+(Vector xs) ! i = xs !! (fromInteger (toInteger i))++-- ==========================+-- = Transforming functions =+-- ==========================+replace :: (PositiveT s, NaturalT u, (s :>: u) ~ True) =>+ Vector s a -> Index u -> a -> Vector s a+replace (Vector xs) i y = Vector $ replace' xs (toInteger i) y+ where replace' [] _ _ = []+ replace' (_:xs) 0 y = (y:xs)+ replace' (x:xs) n y = x : (replace' xs (n-1) y)+ +head :: PositiveT s => Vector s a -> a+head = P.head . unVec++tail :: PositiveT s => Vector s a -> Vector (Pred s) a+tail = liftV P.tail++last :: PositiveT s => Vector s a -> a+last = P.last . unVec++init :: PositiveT s => Vector s a -> Vector (Pred s) a+init = liftV P.init++take :: NaturalT i => i -> Vector s a -> Vector (Min s i) a+take i = liftV $ P.take (fromIntegerT i)++drop :: NaturalT i => i -> Vector s a -> Vector (s :-: (Min s i)) a+drop i = liftV $ P.drop (fromIntegerT i)++select :: (NaturalT f, NaturalT s, NaturalT n, (f :<: i) ~ True, + (((s :*: n) :+: f) :<=: i) ~ True) => + f -> s -> n -> Vector i a -> Vector n a+select f s n = liftV (select' f' s' n')+ where (f', s', n') = (fromIntegerT f, fromIntegerT s, fromIntegerT n)+ select' f s n = ((selectFirst0 s n).(P.drop f))+ selectFirst0 :: Int -> Int -> [a] -> [a]+ selectFirst0 s n l@(x:_)+ | n > 0 = x : selectFirst0 s (n-1) (P.drop s l)+ | otherwise = []+ selectFirst0 _ 0 [] = []++(<+) :: Vector s a -> a -> Vector (Succ s) a+(<+) (Vector xs) x = Vector (xs P.++ [x])++(++) :: Vector s a -> Vector s2 a -> Vector (s :+: s2) a+(++) = liftV2 (P.++)++infixl 5 <++infixr 5 ++++map :: (a -> b) -> Vector s a -> Vector s b+map f = liftV (P.map f)++zipWith :: (a -> b -> c) -> Vector s a -> Vector s b -> Vector s c+zipWith f = liftV2 (P.zipWith f)++foldl :: (a -> b -> a) -> a -> Vector s b -> a+foldl f e = (P.foldl f e) . unVec++foldr :: (b -> a -> a) -> a -> Vector s b -> a+foldr f e = (P.foldr f e) . unVec++zip :: Vector s a -> Vector s b -> Vector s (a, b)+zip = liftV2 P.zip++unzip :: Vector s (a, b) -> (Vector s a, Vector s b)+unzip (Vector xs) = let (a,b) = P.unzip xs in (Vector a, Vector b)++shiftl :: (PositiveT s, NaturalT n, n ~ Pred s, s ~ Succ n) => + Vector s a -> a -> Vector s a+shiftl xs x = x +> init xs++shiftr :: (PositiveT s, NaturalT n, n ~ Pred s, s ~ Succ n) => + Vector s a -> a -> Vector s a+shiftr xs x = tail xs <+ x+ +rotl :: forall s a . NaturalT s => Vector s a -> Vector s a+rotl = liftV rotl'+ where vlen = fromIntegerT (undefined :: s)+ rotl' [] = []+ rotl' xs = let (i,[l]) = splitAt (vlen - 1) xs+ in l : i ++rotr :: NaturalT s => Vector s a -> Vector s a+rotr = liftV rotr'+ where+ rotr' [] = []+ rotr' (x:xs) = xs P.++ [x] ++concat :: Vector s1 (Vector s2 a) -> Vector (s1 :*: s2) a+concat = liftV (P.foldr ((P.++).unVec) [])++reverse :: Vector s a -> Vector s a+reverse = liftV P.reverse++iterate :: NaturalT s => (a -> a) -> a -> Vector s a+iterate = iteraten (undefined :: s)++iteraten :: NaturalT s => s -> (a -> a) -> a -> Vector s a+iteraten s f x = let s' = fromIntegerT s in Vector (P.take s' $ P.iterate f x)++generate :: NaturalT s => (a -> a) -> a -> Vector s a+generate = generaten (undefined :: s)++generaten :: NaturalT s => s -> (a -> a) -> a -> Vector s a+generaten s f x = let s' = fromIntegerT s in Vector (P.take s' $ P.tail $ P.iterate f x)++copy :: NaturalT s => a -> Vector s a+copy x = copyn (undefined :: s) x++copyn :: NaturalT s => s -> a -> Vector s a+copyn s x = iteraten s id x++split :: ( NaturalT s+ -- , IsEven s ~ True+ ) => Vector s a -> (Vector (Div2 s) a, Vector (Div2 s) a)+split (Vector xs) = (Vector (P.take vlen xs), Vector (P.drop vlen xs))+ where+ vlen = round ((fromIntegral (P.length xs)) / 2)++-- =============+-- = Instances =+-- =============+instance Show a => Show (Vector s a) where+ showsPrec _ = showV.unVec+ where showV [] = showString "<>"+ showV (x:xs) = showChar '<' . shows x . showl xs+ where showl [] = showChar '>'+ showl (x:xs) = showChar ',' . shows x .+ showl xs++instance (Read a, NaturalT nT) => Read (Vector nT a) where+ readsPrec _ str+ | all fitsLength possibilities = P.map toReadS possibilities+ | otherwise = error (fName P.++ ": string/dynamic length mismatch")+ where + fName = "Data.Param.TFVec.read"+ expectedL = fromIntegerT (undefined :: nT)+ possibilities = readVectorList str+ fitsLength (_, l, _) = l == expectedL+ toReadS (xs, _, rest) = (Vector xs, rest)+ +instance NaturalT s => DF.Foldable (Vector s) where+ foldr = foldr+ +instance NaturalT s => Functor (Vector s) where+ fmap = map++instance NaturalT s => DT.Traversable (Vector s) where + traverse f = (fmap Vector).(DT.traverse f).unVec++instance (Lift a, NaturalT nT) => Lift (Vector nT a) where+ lift (Vector xs) = [| unsafeVectorCoerse+ $(decLiteralV (fromIntegerT (undefined :: nT)))+ (Vector xs) |]++-- ======================+-- = Internal Functions =+-- ======================+liftV :: ([a] -> [b]) -> Vector nT a -> Vector nT' b+liftV f = Vector . f . unVec++liftV2 :: ([a] -> [b] -> [c]) -> Vector s a -> Vector s2 b -> Vector s3 c+liftV2 f a b = Vector (f (unVec a) (unVec b))++splitAtM :: Int -> [a] -> Maybe ([a],[a])+splitAtM n xs = splitAtM' n [] xs+ where splitAtM' 0 xs ys = Just (xs, ys)+ splitAtM' n xs (y:ys) | n > 0 = do+ (ls, rs) <- splitAtM' (n-1) xs ys+ return (y:ls,rs)+ splitAtM' _ _ _ = Nothing++unsafeVectorCoerse :: nT' -> Vector nT a -> Vector nT' a+unsafeVectorCoerse _ (Vector v) = (Vector v)++readVectorList :: Read a => String -> [([a], Int, String)]+readVectorList = readParen' False (\r -> [pr | ("<",s) <- lexVector r,+ pr <- readl s])+ where+ readl s = [([],0,t) | (">",t) <- lexVector s] P.+++ [(x:xs,1+n,u) | (x,t) <- reads s,+ (xs, n, u) <- readl' t]+ readl' s = [([],0,t) | (">",t) <- lexVector s] P.+++ [(x:xs,1+n,v) | (",",t) <- lex s,+ (x,u) <- reads t,+ (xs,n,v) <- readl' u]+ readParen' b g = if b then mandatory else optional+ where optional r = g r P.++ mandatory r+ mandatory r = [(x,n,u) | ("(",s) <- lexVector r,+ (x,n,t) <- optional s,+ (")",u) <- lexVector t]++-- Custom lexer for FSVecs, we cannot use lex directly because it considers+-- sequences of < and > as unique lexemes, and that breaks nested FSVecs, e.g.+-- <<1,2><3,4>>+lexVector :: ReadS String+lexVector ('>':rest) = [(">",rest)]+lexVector ('<':rest) = [("<",rest)]+lexVector str = lex str+
+ LICENSE view
@@ -0,0 +1,25 @@+Copyright (c) 2009 Christiaan Baaij & Matthijs Kooijman+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:+ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+ * Neither the name of the copyright holder nor the+ names of its contributors may be used to endorse or promote products+ derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ clash.cabal view
@@ -0,0 +1,55 @@+name: clash+version: 0.1+build-type: Simple+synopsis: CAES Language for Synchronous Hardware (CLaSH)+description: CLaSH is a tool-chain/language to translate subsets of+ Haskell to synthesizable VHDL. It does this by translating+ the intermediate System Fc (GHC Core) representation to a+ VHDL AST, which is then written to file.+category: Language, Hardware+license: BSD3+license-file: LICENSE+homepage: http://clash.ewi.utwente.nl/+package-url: http://github.com/christiaanb/clash/tree/master/cλash+copyright: Copyright (c) 2009-2010 Christiaan Baaij & + Matthijs Kooijman+author: Christiaan Baaij & Matthijs Kooijman+stability: alpha+maintainer: christiaan.baaij@gmail.com & matthijs@stdin.nl+Cabal-Version: >= 1.2++Library+ build-depends: ghc >= 6.12, pretty, vhdl > 0.1, haskell98, syb,+ data-accessor, containers, base >= 4 && < 5, transformers,+ filepath, template-haskell, data-accessor-template,+ data-accessor-transformers, prettyclass, directory, + tfp, th-lift, time+ + exposed-modules: CLasH.HardwareTypes+ CLasH.Translator+ CLasH.Translator.Annotations+ + other-modules: Data.Param.Integer+ Data.Param.Signed+ Data.Param.Unsigned+ Data.Param.Index+ Data.Param.Vector+ CLasH.Translator.TranslatorTypes+ CLasH.Normalize+ CLasH.Normalize.NormalizeTypes+ CLasH.Normalize.NormalizeTools+ CLasH.VHDL+ CLasH.VHDL.Constants+ CLasH.VHDL.Generate+ CLasH.VHDL.Testbench+ CLasH.VHDL.VHDLTools+ CLasH.VHDL.VHDLTypes+ CLasH.Utils+ CLasH.Utils.GhcTools+ CLasH.Utils.HsTools+ CLasH.Utils.Pretty+ CLasH.Utils.Core.BinderTools+ CLasH.Utils.Core.CoreShow+ CLasH.Utils.Core.CoreTools+ +