co-feldspar-0.1.0.1: src/Feldspar/Hardware/Compile.hs
{-# language GADTs #-}
{-# language TypeOperators #-}
{-# language FlexibleContexts #-}
{-# language ConstraintKinds #-}
{-# language ScopedTypeVariables #-}
{-# language MultiParamTypeClasses #-}
module Feldspar.Hardware.Compile where
import Feldspar.Sugar
import Feldspar.Representation
import Feldspar.Hardware.Primitive
import Feldspar.Hardware.Primitive.Backend
import Feldspar.Hardware.Expression
import Feldspar.Hardware.Representation
import Feldspar.Hardware.Optimize
import Feldspar.Hardware.Frontend (HSig)
import Data.Struct
-- hmm..
import qualified Language.Embedded.Hardware.Interface.AXI as Hard (FreePrim(..))
import Control.Monad.Identity
import Control.Monad.Reader
import Data.Constraint hiding (Sub)
import Data.Proxy
import Data.Map (Map)
import qualified Data.Map as Map
-- operational-higher.
import Control.Monad.Operational.Higher (Program, ProgramT)
import qualified Control.Monad.Operational.Higher as Oper
-- syntactic.
import Language.Syntactic hiding (Nil)
import Language.Syntactic.Functional hiding (Binding (..))
import Language.Syntactic.Functional.Tuple
import qualified Language.Syntactic as Syn
-- hardware-edsl.
import Language.Embedded.Hardware (Signal, FreeExp (..))
import Language.Embedded.Hardware.Command (Signal)
import qualified Language.Embedded.Hardware as Hard
import qualified Language.Embedded.Hardware.Command as Hard
--------------------------------------------------------------------------------
-- * Hardware compiler.
--------------------------------------------------------------------------------
-- | Target hardware instructions.
type TargetCMD
= Hard.VariableCMD
Oper.:+: Hard.ArrayCMD
Oper.:+: Hard.VArrayCMD
Oper.:+: Hard.LoopCMD
Oper.:+: Hard.ConditionalCMD
Oper.:+: Hard.ComponentCMD
Oper.:+: Hard.SignalCMD
Oper.:+: Hard.ProcessCMD
--
Oper.:+: Hard.VHDLCMD
-- | Target monad during translation.
type TargetT m = ReaderT Env (ProgramT TargetCMD (Oper.Param2 Prim HardwarePrimType) m)
-- | Monad for translated programs.
type ProgH = Program TargetCMD (Oper.Param2 Prim HardwarePrimType)
--------------------------------------------------------------------------------
-- ** Compilation of expressions.
-- | Struct expression.
type VExp = Struct HardwarePrimType Prim
-- | Struct expression with hidden result type.
data VExp' where
VExp' :: Struct HardwarePrimType Prim a -> VExp'
newRefV :: Monad m => HTypeRep a -> String -> TargetT m (Struct HardwarePrimType Hard.Variable a)
newRefV t base = lift $ mapStructA (const (Hard.newNamedVariable base)) t
initRefV :: Monad m => String -> VExp a -> TargetT m (Struct HardwarePrimType Hard.Variable a)
initRefV base = lift . mapStructA (Hard.initNamedVariable base)
getRefV :: Monad m => Struct HardwarePrimType Hard.Variable a -> TargetT m (VExp a)
getRefV = lift . mapStructA Hard.getVariable
setRefV :: Monad m => Struct HardwarePrimType Hard.Variable a -> VExp a -> TargetT m ()
setRefV r = lift . sequence_ . zipListStruct Hard.setVariable r
unsafeFreezeRefV :: Monad m => Struct HardwarePrimType Hard.Variable a -> TargetT m (VExp a)
unsafeFreezeRefV = lift . mapStructA Hard.unsafeFreezeVariable
--------------------------------------------------------------------------------
type Env = Map Name VExp'
localAlias :: MonadReader Env m => Name -> VExp a -> m b -> m b
localAlias v e = local (Map.insert v (VExp' e))
lookAlias :: MonadReader Env m => HTypeRep a -> Name -> m (VExp a)
lookAlias t v = do
env <- ask
return $ case Map.lookup v env of
Nothing -> error $ "lookAlias: variable " ++ show v ++ " not in scope."
Just (VExp' e) ->
case hardwareTypeEq t (hardwareTypeRep e) of Just Dict -> e
--------------------------------------------------------------------------------
translateExp :: forall m a . Monad m => HExp a -> TargetT m (VExp a)
translateExp = goAST . optimize . unHExp
where
goAST :: ASTF HardwareDomain b -> TargetT m (VExp b)
goAST = simpleMatch (\(s :&: ValT t) -> go t s)
goSmallAST :: HardwarePrimType b => ASTF HardwareDomain b -> TargetT m (Prim b)
goSmallAST = fmap extractNode . goAST
go :: HTypeRep (DenResult sig)
-> HardwareConstructs sig
-> Syn.Args (AST HardwareDomain) sig
-> TargetT m (VExp (DenResult sig))
go t lit Syn.Nil
| Just (Lit a) <- prj lit
= return $ mapStruct (litE . runIdentity) $ toStruct t a
-- go t lit Syn.Nil
-- | Just (Literal a) <- prj lit
-- = return $ mapStruct (litE . runIdentity) $ toStruct t a
go t var Syn.Nil
| Just (FreeVar v) <- prj var
= return $ Node $ sugarSymPrim $ FreeVar v
go t var Syn.Nil
| Just (VarT v) <- prj var
= lookAlias t v
go t lt (a :* (lam :$ body) :* Syn.Nil)
| Just (Let tag) <- prj lt
, Just (LamT v) <- prj lam
= do let base = if Prelude.null tag then "let" else tag
r <- initRefV base =<< goAST a
a' <- unsafeFreezeRefV r
localAlias v a' $ goAST body
go t tup (a :* b :* Syn.Nil)
| Just Pair <- prj tup =
Branch <$> goAST a <*> goAST b
go t sel (ab :* Syn.Nil)
| Just Fst <- prj sel = do
branch <- goAST ab
case branch of (Branch a _) -> return a
| Just Snd <- prj sel = do
branch <- goAST ab
case branch of (Branch _ b) -> return b
go ty cond (b :* t :* f :* Syn.Nil)
| Just Cond <- prj cond = do
res <- newRefV ty "b"
b' <- goSmallAST b
ReaderT $ \env -> Hard.iff b'
(flip runReaderT env $ setRefV res =<< goAST t)
(flip runReaderT env $ setRefV res =<< goAST f)
unsafeFreezeRefV res
go t op (a :* Syn.Nil)
| Just Neg <- prj op = liftStruct (sugarSymPrim Neg) <$> goAST a
| Just Not <- prj op = liftStruct (sugarSymPrim Not) <$> goAST a
| Just I2N <- prj op = liftStruct (sugarSymPrim I2N) <$> goAST a
| Just (Cast f) <- prj op =
liftStruct (sugarSymPrim (Cast f)) <$> goAST a
| Just BitCompl <- prj op =
liftStruct (sugarSymPrim BitCompl) <$> goAST a
go t op (a :* b :* Syn.Nil)
| Just Add <- prj op = liftStruct2 (sugarSymPrim Add) <$> goAST a <*> goAST b
| Just Sub <- prj op = liftStruct2 (sugarSymPrim Sub) <$> goAST a <*> goAST b
| Just Mul <- prj op = liftStruct2 (sugarSymPrim Mul) <$> goAST a <*> goAST b
| Just Div <- prj op = liftStruct2 (sugarSymPrim Div) <$> goAST a <*> goAST b
| Just Mod <- prj op = liftStruct2 (sugarSymPrim Mod) <$> goAST a <*> goAST b
| Just Eq <- prj op = liftStruct2 (sugarSymPrim Eq) <$> goAST a <*> goAST b
| Just And <- prj op = liftStruct2 (sugarSymPrim And) <$> goAST a <*> goAST b
| Just Or <- prj op = liftStruct2 (sugarSymPrim Or) <$> goAST a <*> goAST b
| Just Lt <- prj op = liftStruct2 (sugarSymPrim Lt) <$> goAST a <*> goAST b
| Just Lte <- prj op = liftStruct2 (sugarSymPrim Lte) <$> goAST a <*> goAST b
| Just Gt <- prj op = liftStruct2 (sugarSymPrim Gt) <$> goAST a <*> goAST b
| Just Gte <- prj op = liftStruct2 (sugarSymPrim Gte) <$> goAST a <*> goAST b
| Just BitAnd <- prj op =
liftStruct2 (sugarSymPrim BitAnd) <$> goAST a <*> goAST b
| Just BitOr <- prj op =
liftStruct2 (sugarSymPrim BitOr) <$> goAST a <*> goAST b
| Just BitXor <- prj op =
liftStruct2 (sugarSymPrim BitXor) <$> goAST a <*> goAST b
| Just ShiftL <- prj op =
liftStruct2 (sugarSymPrim ShiftL) <$> goAST a <*> goAST b
| Just ShiftR <- prj op =
liftStruct2 (sugarSymPrim ShiftR) <$> goAST a <*> goAST b
| Just RotateL <- prj op =
liftStruct2 (sugarSymPrim RotateL) <$> goAST a <*> goAST b
| Just RotateR <- prj op =
liftStruct2 (sugarSymPrim RotateR) <$> goAST a <*> goAST b
go t loop (min :* max :* init :* (lami :$ (lams :$ body)) :* Syn.Nil)
| Just ForLoop <- prj loop
, Just (LamT iv) <- prj lami
, Just (LamT sv) <- prj lams = do
min' <- goSmallAST min
max' <- goSmallAST max
state <- initRefV "state" =<< goAST init
let int = Hard.cast (fromIntegral)
ReaderT $ \env -> Hard.for (int min') (int max') $ \i ->
flip runReaderT env $ do
s <- case t of
Node _ -> unsafeFreezeRefV state
_ -> getRefV state
let i' = Hard.cast' (proxyE min') i
s' <- localAlias iv (Node i') $ localAlias sv s $ goAST body
setRefV state s'
unsafeFreezeRefV state
where
proxyE :: Prim x -> Proxy x
proxyE _ = Proxy
go _ arrIx (i :* Syn.Nil)
| Just (ArrIx arr) <- prj arrIx = do
i' <- goSmallAST i
return $ Node $ sugarSymPrim (ArrIx arr) i'
go _ s _ = error $ "hardware translation handling for symbol " ++ Syn.renderSym s ++ " is missing."
unsafeTranslateSmallExp :: Monad m => HExp a -> TargetT m (Prim a)
unsafeTranslateSmallExp a = do
node <- translateExp a
case node of Node b -> return b
translate :: Hardware a -> ProgH a
translate = flip runReaderT Map.empty . Oper.reexpressEnv unsafeTranslateSmallExp . unHardware
--------------------------------------------------------------------------------
translateHSig :: HSig a -> Hard.Sig TargetCMD Prim HardwarePrimType Identity a
translateHSig (Hard.Ret prg) = Hard.Ret (translate (Hardware prg))
translateHSig (Hard.SSig n m sf) = Hard.SSig n m (translateHSig . sf)
translateHSig (Hard.SArr n m l af) = Hard.SArr n m l (translateHSig . af)
--------------------------------------------------------------------------------
-- * Interpretation of hardware programs.
--------------------------------------------------------------------------------
compile :: Hardware () -> String
compile = Hard.compile . translate
icompile :: Hardware () -> IO ()
icompile = Hard.icompile . translate
--------------------------------------------------------------------------------
compileSig :: HSig a -> String
compileSig = Hard.compileSig . translateHSig
icompileSig :: HSig a -> IO ()
icompileSig = Hard.icompileSig . translateHSig
--------------------------------------------------------------------------------
-- Compiler that wraps a hardware component in an AXI-lite framework.
instance Hard.FreePrim Prim HardwarePrimType
where
witPred _ Dict = Dict
compileAXILite :: HSig a -> String
compileAXILite = Hard.compileAXILite . translateHSig
icompileAXILite :: HSig a -> IO ()
icompileAXILite = putStrLn . compileAXILite
--------------------------------------------------------------------------------