packages feed

imperative-edsl-vhdl-0.3.2: src/Language/Embedded/VHDL/Command.hs

{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE KindSignatures        #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE TypeOperators         #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}

module Language.Embedded.VHDL.Command
  ( SequentialCMD
  , ConcurrentCMD
  , HeaderCMD
  , compile

    -- sequential statements
  , constantL, variableL, fileL
  , (<==), (==:)
  , iff, switch, when, ifThen

    -- concurrent statements
  , constantG, signalG, variableG, fileG
  , process

    -- header statements
  , constantPort, constantGeneric
  , signalPort, signalGeneric
  , variablePort, variableGeneric
  , filePort, fileGeneric
  , entity
  , architecture
  , library
  , imports
  ) where

import Language.VHDL (Identifier(..), Label, Expression, Mode(..))
import qualified Language.VHDL as V

import Language.Embedded.VHDL.Monad      (VHDLT, VHDL)
import Language.Embedded.VHDL.Monad.Type (Type, Kind)
import Language.Embedded.VHDL.Interface
import qualified Language.Embedded.VHDL.Monad       as M
import qualified Language.Embedded.VHDL.Monad.Type  as T
import qualified Language.Embedded.VHDL.Expression.Hoist as H

import Control.Arrow (second)
import Control.Monad.Identity           hiding (when)
import Control.Monad.Operational.Higher hiding (when)
import Control.Applicative
import Data.Typeable
import Data.ALaCarte

--------------------------------------------------------------------------------
-- *
--------------------------------------------------------------------------------

instance CompileExp exp => Interp (SequentialCMD exp) VHDL
  where
    interp = compileSequential

instance CompileExp exp => Interp (ConcurrentCMD exp) VHDL
  where
    interp = compileConcurrent

instance CompileExp exp => Interp (HeaderCMD exp) VHDL
  where
    interp = compileHeader

-- | Compile an VHDL program into a pretty printed text
compile :: (Interp instr VHDL, HFunctor instr) => Program instr a -> String
compile = show . M.prettyVHDL . interpret

--------------------------------------------------------------------------------

-- | Compile if 'exp' is set
compEM
  :: forall exp a.
     ( PredicateExp exp a
     , CompileExp   exp)
  => Maybe (exp a)
  -> VHDL (Maybe Expression)
compEM = maybe (return Nothing) (>>= return . Just) . fmap compE

-- | Compile hidden type
compTM
  :: forall exp a.
     ( PredicateExp exp a
     , CompileExp   exp)
  => Maybe (exp a)
  -> VHDL Type
compTM _ = compT (undefined :: exp a)

--------------------------------------------------------------------------------
-- ** Sequential commands offered by VHDL

data SequentialCMD (exp :: * -> *) (prog :: * -> *) a
  where
    Local
      :: PredicateExp exp a
      => Kind
      -> Maybe (exp a)
      -> SequentialCMD exp prog Identifier

    Assignment
      :: PredicateExp exp a
      => Identifier
      -> Kind
      -> exp a
      -> SequentialCMD exp prog ()

    If
      :: PredicateExp exp Bool
      => (exp Bool, prog ())     -- if
      -> [(exp Bool,  prog ())]  -- else-ifs
      -> prog ()                 -- else
      -> SequentialCMD exp prog ()

    Case
      :: PredicateExp exp a
      => exp a
      -> [(exp a, prog ())]
      -> Maybe (prog ())
      -> SequentialCMD exp prog ()
    
instance HFunctor (SequentialCMD exp)
  where
    hfmap _ (Local k e  )      = Local k e
    hfmap _ (Assignment i k e) = Assignment i k e
    hfmap f (If (b, t) os e)   = If (b, f t) (map (second f) os) (f e)
    hfmap f (Case e cs d)      = Case e (map (second f) cs) (fmap f d)

type instance IExp (SequentialCMD e)       = e
type instance IExp (SequentialCMD e :+: i) = e

--------------------------------------------------------------------------------

-- | Declare local constands/variables/files
constantL, variableL, fileL
  :: (SequentialCMD (IExp instr) :<: instr, PredicateExp (IExp instr) a)
  => Maybe (IExp instr a)
  -> ProgramT instr m Identifier
constantL = singleE . Local T.Constant
variableL = singleE . Local T.Variable
fileL     = singleE . Local T.File

-- | Assign a signal to some expression
(<==) :: (SequentialCMD (IExp instr) :<: instr, PredicateExp (IExp instr) a)
      => Identifier
      -> IExp instr a
      -> ProgramT instr m ()
(<==) i = singleE . Assignment i T.Signal

-- | Assign a variable to some expression
(==:) :: (SequentialCMD (IExp instr) :<: instr, PredicateExp (IExp instr) a)
      => Identifier
      -> IExp instr a
      -> ProgramT instr m ()
(==:) i = singleE . Assignment i T.Variable

-- | Conventional if statement
iff 
  :: (SequentialCMD (IExp instr) :<: instr, PredicateExp (IExp instr) Bool)
  =>  (IExp instr Bool, ProgramT instr m ())
  -> [(IExp instr Bool, ProgramT instr m ())]
  -> ProgramT instr m ()
  -> ProgramT instr m ()
iff th eif el = singleE $ If th eif el

-- | Conventional switch (or case) statement
switch
  :: (SequentialCMD (IExp instr) :<: instr, PredicateExp (IExp instr) a)
  => IExp instr a
  -> [(IExp instr a, ProgramT instr m ())]
  -> Maybe (ProgramT instr m ())
  -> ProgramT instr m ()
switch e choices def = singleE $ Case e choices def

--------------------------------------------------------------------------------

-- | Guards a program by some predicate
when
  :: ( SequentialCMD (IExp instr) :<: instr
     , PredicateExp  (IExp instr) Bool
     , Monad m)
  => IExp instr Bool
  -> ProgramT instr m ()
  -> ProgramT instr m ()
when b prg = singleE $ If (b, prg) [] (return ())

-- | Standard 'if .. then .. else ..' statement
ifThen
  :: ( SequentialCMD (IExp instr) :<: instr
     , PredicateExp  (IExp instr) Bool)
  => IExp instr Bool
  -> ProgramT instr m ()
  -> ProgramT instr m ()
  -> ProgramT instr m ()
ifThen b th el = singleE $ If (b, th) [] el

--------------------------------------------------------------------------------

compileSequential :: CompileExp exp => SequentialCMD exp VHDL a -> VHDL a
compileSequential (Local k e) =
  do v <- compEM e
     t <- compTM e
     i <- M.newSym
     M.addLocal $ case k of
       T.Constant -> M.declConstant i t v
       T.Signal   -> M.declSignal   i t v
       T.Variable -> M.declVariable i t v
     return i
compileSequential (Assignment i k e) =
  do v <- compE e
     M.addSequential $ case k of
       T.Signal -> M.assignSignal   i v
       _        -> M.assignVariable i v
compileSequential (If (b, th) eif els) =
  do let (cs, es) = unzip eif
     v  <- compE b
     bs <- mapM compE cs
     s  <- M.inConditional (v, th) (zip bs es) els
     M.addSequential $ V.SIf s
compileSequential (Case e choices def) =
  do let (cs, es) = unzip choices
     v  <- compE e
     bs <- mapM (compE >=> return . lower) cs
     s  <- M.inCase v (others def $ zip bs es)
     M.addSequential $ V.SCase s
  where
    lower :: V.Expression -> V.Choices
    lower exp = V.Choices . (:[]) . V.ChoiceSimple $
      case exp of
        (V.EAnd  rels) -> head' rels
        (V.EOr   rels) -> head' rels
        (V.EXor  rels) -> head' rels
        (V.ENand r _)  -> drop' r
        (V.ENor  r _)  -> drop' r
        (V.EXnor rels) -> head' rels
      where
        head' :: [V.Relation] -> V.SimpleExpression
        head' [] = H.lift $ V.PrimLit V.LitNull
        head' xs = drop' (head xs)

        drop' :: V.Relation -> V.SimpleExpression
        drop' (V.Relation (V.ShiftExpression x _) _) = x

    others :: Maybe x -> [(V.Choices, x)] -> [(V.Choices, x)]
    others (Nothing) cs = cs
    others (Just d)  cs = cs ++ [(V.Choices [V.ChoiceOthers], d)]

--------------------------------------------------------------------------------
-- ** Concurrent commands offered by VHDL

data ConcurrentCMD exp (prog :: * -> *) a
  where
    Global
      :: PredicateExp exp a  
      => Kind
      -> Maybe (exp a)
      -> ConcurrentCMD exp prog (Identifier)

    Process
      :: Label
      -> [Identifier]
      -> prog ()
      -> ConcurrentCMD exp prog ()

    PortMap
      :: [Identifier] -> ConcurrentCMD exp prog (Identifier)

instance HFunctor (ConcurrentCMD exp)
  where
    hfmap _ (Global  k e)    = Global  k e
    hfmap f (Process l is p) = Process l is (f p)
    hfmap _ (PortMap is)     = PortMap is

type instance IExp (ConcurrentCMD e)       = e
type instance IExp (ConcurrentCMD e :+: i) = e

--------------------------------------------------------------------------------

-- | Declare global constands/variables/files
constantG, signalG, variableG, fileG
  :: ( ConcurrentCMD (IExp instr) :<: instr
     , PredicateExp  (IExp instr) a)
  => Maybe (IExp instr a)
  -> ProgramT instr m Identifier
constantG = singleE . Global T.Constant
signalG   = singleE . Global T.Signal
variableG = singleE . Global T.Variable
fileG     = singleE . Global T.File

-- | Declare a process
process
  :: (ConcurrentCMD (IExp instr) :<: instr)
  => String
  -> [Identifier]
  -> ProgramT instr m ()
  -> ProgramT instr m ()
process i is = singleE . Process (Ident i) is

--------------------------------------------------------------------------------

compileConcurrent :: CompileExp exp => ConcurrentCMD exp VHDL a -> VHDL a
compileConcurrent (Global k e) =
  do v <- compEM e
     t <- compTM e
     i <- M.newSym
     M.addGlobal $ case k of
       T.Constant -> M.declConstant i t v
       T.Signal   -> M.declSignal   i t v 
       T.Variable -> M.declVariable i t v
     return i
compileConcurrent (Process l is p) =
  do (a, process) <- M.inProcess l is p
     M.addConcurrent (V.ConProcess process)
     return a
compileConcurrent (PortMap is) =
  do let ads = fmap (V.ADSignal . V.NSimple) is
     n   <- M.newSym
     lbl <- M.newLabel
     M.addConcurrent (M.portMap lbl n ads)
     return n
     
--------------------------------------------------------------------------------
-- ** Entity declaration related commands offered by VHDL

data DeclKind = Port | Generic

data Record a = Record Identifier

data Array  a = Array  Identifier

data HeaderCMD exp (prog :: * -> *) a
  where
    DeclarePort
      :: PredicateExp exp a
      => DeclKind
      -> Kind
      -> Mode
      -> Maybe (exp a)
      -> HeaderCMD exp prog Identifier
      
    DeclareRecord
      :: [(Identifier, Type)]
      -> HeaderCMD exp prog (Record a)
         
    DeclareArray
      :: HeaderCMD exp prog (Array a)

    -- ^ ...
    Entity
      :: Identifier
      -> prog a
      -> HeaderCMD exp prog a

    -- ^ ...
    Architecture
      :: Identifier -- architecture's name
      -> Identifier -- entity's name
      -> prog a
      -> HeaderCMD exp prog a

    Library :: String -> HeaderCMD exp prog ()
    Import  :: String -> HeaderCMD exp prog ()

instance HFunctor (HeaderCMD exp)
  where
    hfmap _ (DeclarePort d k m e) = DeclarePort d k m e
    hfmap _ (DeclareRecord rs)    = DeclareRecord rs
    hfmap _ (DeclareArray)        = DeclareArray
    hfmap f (Entity e p)          = Entity e (f p)
    hfmap f (Architecture a e p)  = Architecture a e (f p)
    hfmap _ (Library s)           = Library s
    hfmap _ (Import s)            = Import s

type instance IExp (HeaderCMD e)       = e
type instance IExp (HeaderCMD e :+: i) = e

--------------------------------------------------------------------------------

-- | Declare constands/signal/variables/files ports and generics.
constantPort, constantGeneric, signalPort, signalGeneric, variablePort, variableGeneric, filePort, fileGeneric
  :: (HeaderCMD (IExp instr) :<: instr, PredicateExp (IExp instr) a)
  => Mode
  -> Maybe (IExp instr a)
  -> ProgramT instr m Identifier
constantPort    m = singleE . DeclarePort Port    T.Constant m
constantGeneric m = singleE . DeclarePort Generic T.Constant m
signalPort      m = singleE . DeclarePort Port    T.Signal   m
signalGeneric   m = singleE . DeclarePort Generic T.Signal   m
variablePort    m = singleE . DeclarePort Port    T.Variable m
variableGeneric m = singleE . DeclarePort Generic T.Variable m
filePort        m = singleE . DeclarePort Port    T.File     m
fileGeneric     m = singleE . DeclarePort Generic T.File     m

-- | Declare an entity.
entity
  :: (HeaderCMD (IExp instr) :<: instr)
  => String
  -> ProgramT instr m a
  -> ProgramT instr m a
entity name = singleE . Entity (Ident name)

-- | Declare an architecture.
architecture
  :: (HeaderCMD (IExp instr) :<: instr)
  => String
  -> String
  -> ProgramT instr m a
  -> ProgramT instr m a
architecture name entity = singleE . Architecture (Ident name) (Ident entity)

-- | Imports a library.
library :: (HeaderCMD (IExp instr) :<: instr) => String -> ProgramT instr m ()
library = singleE . Library

-- | Imports a library module.
imports :: (HeaderCMD (IExp instr) :<: instr) => String -> ProgramT instr m ()
imports = singleE . Import

--------------------------------------------------------------------------------

compileHeader :: CompileExp exp => HeaderCMD exp VHDL a -> VHDL a
compileHeader (DeclarePort Port k m e) =
  do v <- compEM e
     t <- compTM e
     i <- M.newSym
     M.addPort $ case k of
       T.Constant -> M.interfaceConstant i   t v
       T.Signal   -> M.interfaceSignal   i m t v
       T.Variable -> M.interfaceVariable i m t v
     return i
compileHeader (DeclarePort Generic k m e) =
  do v <- compEM e
     t <- compTM e
     i <- M.newSym
     M.addGeneric $ case k of
       T.Constant -> M.interfaceConstant i   t v
       T.Signal   -> M.interfaceSignal   i m t v
       T.Variable -> M.interfaceVariable i m t v
     return i
compileHeader (DeclareRecord rs) =
  do i <- M.newSym
     r <- return $ M.declRecord i rs
     undefined
compileHeader (DeclareArray) =
  do error "imperative-vhdl: arrays are not yet supported."
compileHeader (Entity       name        prg) = M.entity name prg
compileHeader (Architecture name entity prg) = M.architecture name entity prg
compileHeader (Library s)                    = M.newLibrary s
compileHeader (Import s)                     = M.newImport s

--------------------------------------------------------------------------------