hardware-edsl-0.1.0.0: src/Language/Embedded/VHDL/Monad.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
-- used for the Ord/Eq inst. of XDeclaration etc.
{-# LANGUAGE StandaloneDeriving #-}
module Language.Embedded.VHDL.Monad (
VHDL
, VHDLT
, VHDLEnv
, emptyVHDLEnv
-- ^ run
, runVHDLT, runVHDL, execVHDLT, execVHDL
-- ^ pretty printing
, prettyVHDL, prettyVHDLT
-- ^ name generation
, freshUnique, newSym, newLabel
-- ^ imports
, newLibrary, newImport
-- ^ declarations
, addPort, addGeneric
, addGlobal, addLocal
, addConcurrent, addSequential
, addType, addComponent
-- ^ statements
, inProcess, inFor, inWhile, inConditional, inCase
-- ^ structures
, entity, architecture, package
-- ^ common things
, interfaceConstant, interfaceSignal, interfaceVariable
, declareConstant, declareSignal, declareVariable
, assignSignal, assignSignalS, assignVariable, assignArray
, unconstrainedArray, constrainedArray
) where
import Language.VHDL
import Control.Applicative ((<$>))
import Control.Monad.Identity (Identity)
import Control.Monad.State (StateT, MonadState, MonadIO)
import qualified Control.Monad.Identity as CMI
import qualified Control.Monad.State as CMS
import Data.Maybe (catMaybes)
import Data.Foldable (toList)
import Data.Functor (fmap)
import Data.List (groupBy)
import Data.Set (Set)
import Data.Map (Map)
import qualified Data.Set as Set
import qualified Data.Map as Map
import Text.PrettyPrint (Doc)
import Prelude hiding (null, not, abs, exp, rem, mod, div, and, or)
import qualified Prelude as P
--------------------------------------------------------------------------------
-- * VHDL monad and environment.
--------------------------------------------------------------------------------
-- | Code generation state
data VHDLEnv = VHDLEnv
{ _unique :: !Integer
, _designs :: [DesignUnit]
, _context :: Set ContextItem
, _types :: Set TypeDeclaration
, _ports :: [InterfaceDeclaration]
, _generics :: [InterfaceDeclaration]
, _components :: Set ComponentDeclaration
, _global :: [BlockDeclarativeItem]
, _local :: [BlockDeclarativeItem]
, _concurrent :: [ConcurrentStatement]
, _sequential :: [SequentialStatement]
}
-- | Initial state during code generation
emptyVHDLEnv = VHDLEnv
{ _unique = 0
, _designs = []
, _context = Set.empty
, _types = Set.empty
, _components = Set.empty
, _ports = []
, _generics = []
, _global = []
, _local = []
, _concurrent = []
, _sequential = []
}
--------------------------------------------------------------------------------
-- * VHDL monad.
-- | Type constraints for the VHDL monads
type MonadV m = (Functor m, Applicative m, Monad m, MonadState VHDLEnv m)
-- | VHDL code generation monad
type VHDL = VHDLT Identity
-- | VHDL code genreation monad transformer.
newtype VHDLT m a = VHDLT { unVGenT :: StateT VHDLEnv m a }
deriving ( Functor
, Applicative
, Monad
, MonadState VHDLEnv
, MonadIO
)
-- | Run the VHDL code generation monad transformer.
runVHDLT :: Monad m => VHDLT m a -> VHDLEnv -> m (a, VHDLEnv)
runVHDLT m = CMS.runStateT (unVGenT m)
-- | -- | Executes the VHDL code generation monad transformer, returning only its final state.
execVHDLT :: Monad m => VHDLT m a -> VHDLEnv -> m VHDLEnv
execVHDLT m = CMS.execStateT (unVGenT m)
-- | Run the VHDL code generation monad.
runVHDL :: VHDL a -> VHDLEnv -> (a, VHDLEnv)
runVHDL m = CMI.runIdentity . runVHDLT m
-- | Executes the VHDL code generation monad, returning only its final state.
execVHDL :: VHDL a -> VHDLEnv -> VHDLEnv
execVHDL m = CMI.runIdentity . execVHDLT m
--------------------------------------------------------------------------------
-- ** Generating uniques.
-- | Generates a unique integer.
freshUnique :: MonadV m => m Integer
freshUnique =
do u <- CMS.gets _unique
CMS.modify (\e -> e { _unique = u + 1 })
return u
-- | Generates a fresh and unique identifier.
newSym :: MonadV m => m Identifier
newSym = do i <- freshUnique; return (Ident $ 'v' : show i)
-- | Generates a fresh and unique label.
newLabel :: MonadV m => m Label
newLabel = do i <- freshUnique; return (Ident $ 'l' : show i)
--------------------------------------------------------------------------------
-- ** VHDL environment updates.
-- | Adds a new library import to the context.
newLibrary :: MonadV m => String -> m ()
newLibrary l = CMS.modify $ \s -> s { _context = Set.insert item (_context s) }
where
item :: ContextItem
item = ContextLibrary (LibraryClause (LogicalNameList [Ident l]))
-- | Adds a new library use clause to the context (with an .ALL suffix by default).
newImport :: MonadV m => String -> m ()
newImport i = CMS.modify $ \s -> s { _context = Set.insert item (_context s) }
where
item :: ContextItem
item = ContextUse (UseClause [SelectedName (PName (NSimple (Ident i))) (SAll)])
-- | Adds a port declaration to the entity.
addPort :: MonadV m => InterfaceDeclaration -> m ()
addPort p = CMS.modify $ \s -> s { _ports = p : (_ports s) }
-- | Adds a generic declaration to the entity.
addGeneric :: MonadV m => InterfaceDeclaration -> m ()
addGeneric g = CMS.modify $ \s -> s { _generics = g : (_generics s) }
-- | Adds a type declaration.
addType :: MonadV m => TypeDeclaration -> m ()
addType t = CMS.modify $ \s -> s { _types = Set.insert t (_types s) }
-- | Adds a component declaration.
addComponent :: MonadV m => ComponentDeclaration -> m ()
addComponent c = CMS.modify $ \s -> s { _components = Set.insert c (_components s) }
-- | Adds a global declaration.
addGlobal :: MonadV m => BlockDeclarativeItem -> m ()
addGlobal g = CMS.modify $ \s -> s { _global = g : (_global s) }
-- | Adds a local declaration.
addLocal :: MonadV m => BlockDeclarativeItem -> m ()
addLocal l = CMS.modify $ \s -> s { _local = l : (_local s) }
-- | Adds a concurrent statement.
addConcurrent :: MonadV m => ConcurrentStatement -> m ()
addConcurrent con = CMS.modify $ \s -> s { _concurrent = con : (_concurrent s) }
-- | Adds a sequential statement.
addSequential :: MonadV m => SequentialStatement -> m ()
addSequential seq = CMS.modify $ \s -> s { _sequential = seq : (_sequential s) }
--------------------------------------------------------------------------------
-- * Concurrent and sequential statements
--------------------------------------------------------------------------------
-- | Runs the given action inside a process.
inProcess :: MonadV m => Label -> [Identifier] -> m a -> m (a, ProcessStatement)
inProcess l is m =
do oldLocals <- CMS.gets _local
oldSequential <- CMS.gets _sequential
CMS.modify $ \e -> e { _local = []
, _sequential = [] }
result <- m
newLocals <- reverse <$> CMS.gets _local
newSequential <- reverse <$> CMS.gets _sequential
CMS.modify $ \e -> e { _local = oldLocals
, _sequential = oldSequential }
return ( result
, ProcessStatement
(Just l) -- label
(False) -- postponed
(sensitivity) -- sensitivitylist
(translate $ merge $ newLocals) -- declarativepart
(newSequential)) -- statementpart
where
sensitivity | P.null is = Nothing
| otherwise = Just $ SensitivityList $ fmap NSimple is
-- | Run program in for loop.
inFor :: MonadV m => Identifier -> Range -> m () -> m (LoopStatement)
inFor i r m =
do oldSequential <- CMS.gets _sequential
CMS.modify $ \e -> e { _sequential = [] }
m
newSequential <- reverse <$> CMS.gets _sequential
CMS.modify $ \e -> e { _sequential = oldSequential }
return $
LoopStatement
(Nothing)
(Just (IterFor (ParameterSpecification
(i)
(DRRange r))))
(newSequential)
-- | Run program inside while loop.
inWhile :: MonadV m => Expression -> m () -> m (LoopStatement)
inWhile cont m =
do oldSequential <- CMS.gets _sequential
CMS.modify $ \e -> e { _sequential = [] }
m
newSequential <- reverse <$> CMS.gets _sequential
CMS.modify $ \e -> e { _sequential = oldSequential }
return $
LoopStatement
(Nothing)
(Just (IterWhile cont))
(newSequential)
-- | Conditional statements.
inConditional :: MonadV m => (Condition, m ()) -> [(Condition, m ())] -> m () -> m (IfStatement)
inConditional (c, m) os e =
do let (cs, ns) = unzip os
oldSequential <- CMS.gets _sequential
CMS.modify $ \e -> e { _sequential = [] }
m' <- contain m
ns' <- mapM contain ns
e' <- contain e
CMS.modify $ \e -> e { _sequential = oldSequential }
return $
IfStatement
(Nothing)
(c, m')
(zip cs ns')
(maybeList e')
where
maybeList :: [SequentialStatement] -> Maybe [SequentialStatement]
maybeList xs
| P.null xs = Nothing
| otherwise = Just xs
-- | Case statements.
inCase :: MonadV m => Expression -> [(Choices, m ())] -> m (CaseStatement)
inCase e choices =
do let (cs, ns) = unzip choices
oldSequential <- CMS.gets _sequential
CMS.modify $ \e -> e { _sequential = [] }
ns' <- mapM contain ns
CMS.modify $ \e -> e { _sequential = oldSequential }
return $
CaseStatement
(Nothing)
(e)
(zipWith CaseStatementAlternative cs ns')
contain :: MonadV m => m () -> m [SequentialStatement]
contain m = do
m -- do
new <- reverse <$> CMS.gets _sequential -- get
CMS.modify $ \e -> e { _sequential = [] } -- reset
return new -- return
--------------------------------------------------------------------------------
-- * Design units
--------------------------------------------------------------------------------
-- ... design unit with context
addDesign :: MonadV m => LibraryUnit -> m ()
addDesign lib =
do ctxt <- CMS.gets _context
dsig <- CMS.gets _designs
let item = DesignUnit (ContextClause (Set.toList ctxt)) lib
CMS.modify $ \s -> s { _designs = item : dsig
, _context = Set.empty
}
-- .. design unit ignoring context
addDesign_ :: MonadV m => LibraryUnit -> m ()
addDesign_ lib = CMS.modify $ \s -> s { _designs = (DesignUnit (ContextClause []) lib) : (_designs s)}
--------------------------------------------------------------------------------
-- ** Architectures
-- | Wraps the given monadic action in an architecture, consuming all global
-- identifiers and concurrent statements it produces. Strings are its entity
-- and architecture names, respectively.
architecture :: MonadV m => Identifier -> Identifier -> m a -> m a
architecture entity name m =
do oldGlobal <- CMS.gets _global
oldConcurrent <- CMS.gets _concurrent
CMS.modify $ \e -> e { _global = []
, _concurrent = [] }
result <- m
newGlobal <- reverse <$> CMS.gets _global
newConcurrent <- reverse <$> CMS.gets _concurrent
addDesign_ $ LibrarySecondary $ SecondaryArchitecture $
ArchitectureBody
(name)
(NSimple entity)
(merge newGlobal)
(newConcurrent)
CMS.modify $ \e -> e { _global = oldGlobal
, _concurrent = oldConcurrent }
return result
--------------------------------------------------------------------------------
-- ** Entities
-- | Declares an entity with the given name by consuming all port-level
-- declaraions and context items produced by running the monadic action.
entity :: MonadV m => Identifier -> m a -> m a
entity name m =
do oldPorts <- CMS.gets _ports
oldGenerics <- CMS.gets _generics
CMS.modify $ \e -> e { _ports = []
, _generics = [] }
result <- m
newPorts <- reverse <$> CMS.gets _ports
newGenerics <- reverse <$> CMS.gets _generics
addDesign $ LibraryPrimary $ PrimaryEntity $
EntityDeclaration
(name)
(EntityHeader
(GenericClause <$> maybeNull newGenerics)
(PortClause <$> maybeNull newPorts))
([])
(Nothing)
CMS.modify $ \e -> e { _ports = oldPorts
, _generics = oldGenerics }
return result
where
maybeNull :: [InterfaceDeclaration] -> Maybe InterfaceList
maybeNull [] = Nothing
maybeNull xs = Just $ InterfaceList $ merge xs
--------------------------------------------------------------------------------
-- ** Packages
-- | Declares a package with the given name by consuming all type declarations
-- produced by running the monadic action.
package :: MonadV m => String -> m a -> m a
package name m =
do oldTypes <- CMS.gets _types
CMS.modify $ \e -> e { _types = Set.empty }
result <- m
newTypes <- CMS.gets _types
addDesign $ LibraryPrimary $ PrimaryPackage $
PackageDeclaration
(Ident name)
(fmap PHDIType $ Set.toList newTypes)
CMS.modify $ \e -> e { _types = oldTypes }
return result
--------------------------------------------------------------------------------
-- * Pretty printing VHDL programs
--------------------------------------------------------------------------------
-- | Runs the VHDL monad and pretty prints its resulting VHDL program.
prettyVHDL :: VHDL a -> Doc
prettyVHDL = CMI.runIdentity . prettyVHDLT
-- | Runs the VHDL monad transformer and pretty prints its resulting VHDL program.
prettyVHDLT :: Monad m => VHDLT m a -> m Doc
prettyVHDLT m = prettyVEnv <$> execVHDLT m emptyVHDLEnv
--------------------------------------------------------------------------------
-- | Pretty print a VHDL environment.
--
-- *** Shouldn't use revers to fix ordering issues! Pair architectures/bodies
-- with their respective entities.
prettyVEnv :: VHDLEnv -> Doc
prettyVEnv env = pp (DesignFile $ types ++ archi)
where
archi = reverse $ _designs env
types = reverse $ designTypes (_types env)
-- *** Scan type declarations for necessary imports instead.
-- *** Types are added in an ugly manner.
designTypes :: Set TypeDeclaration -> [DesignUnit]
designTypes set
| Set.null set = []
| otherwise = _designs . snd $ runVHDL pack emptyVHDLEnv
where
pack :: MonadV m => m ()
pack = package "types" $ do
-- *** Instead of importing every library possible the correct ones
-- should be declared when adding a type through 'addType'.
newLibrary "IEEE"
newImport "IEEE.STD_LOGIC_1164"
newImport "IEEE.NUMERIC_STD"
CMS.modify $ \e -> e { _types = set }
--------------------------------------------------------------------------------
-- * Common things
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- ** Ports/Generic declarations
interfaceConstant :: Identifier -> SubtypeIndication -> Maybe Expression -> InterfaceDeclaration
interfaceConstant i t e = InterfaceConstantDeclaration [i] t e
interfaceSignal :: Identifier -> Mode -> SubtypeIndication -> Maybe Expression -> InterfaceDeclaration
interfaceSignal i m t e = InterfaceSignalDeclaration [i] (Just m) t False e
interfaceVariable :: Identifier -> Mode -> SubtypeIndication -> Maybe Expression -> InterfaceDeclaration
interfaceVariable i m t e = InterfaceVariableDeclaration [i] (Just m) t e
--------------------------------------------------------------------------------
-- ** Array Declarations.
compositeTypeDeclaration :: Identifier -> CompositeTypeDefinition -> TypeDeclaration
compositeTypeDeclaration name t = TDFull (FullTypeDeclaration name (TDComposite t))
unconstrainedArray :: Identifier -> SubtypeIndication -> TypeDeclaration
unconstrainedArray name typ = compositeTypeDeclaration name $
CTDArray (ArrU (UnconstrainedArrayDefinition [] typ))
constrainedArray :: Identifier -> SubtypeIndication -> Range -> TypeDeclaration
constrainedArray name typ range = compositeTypeDeclaration name $
CTDArray (ArrC (ConstrainedArrayDefinition
(IndexConstraint [DRRange range]) typ))
--------------------------------------------------------------------------------
-- ** Global/Local Declarations.
declareConstant :: Identifier -> SubtypeIndication -> Maybe Expression -> BlockDeclarativeItem
declareConstant i t e = BDIConstant $ ConstantDeclaration [i] t e
declareSignal :: Identifier -> SubtypeIndication -> Maybe Expression -> BlockDeclarativeItem
declareSignal i t e = BDISignal $ SignalDeclaration [i] t Nothing e
declareVariable :: Identifier -> SubtypeIndication -> Maybe Expression -> BlockDeclarativeItem
declareVariable i t e = BDIShared $ VariableDeclaration False [i] t e
--------------------------------------------------------------------------------
-- ** Assign Signal/Variable.
assignSignal :: Identifier -> Expression -> ConcurrentStatement
assignSignal i e = ConSignalAss $ CSASCond Nothing False $
ConditionalSignalAssignment
(TargetName (NSimple i))
(Options False Nothing)
(ConditionalWaveforms
([])
( WaveElem [WaveEExp e Nothing]
, Nothing))
assignSignalS :: Identifier -> Expression -> SequentialStatement
assignSignalS i e = SSignalAss $
SignalAssignmentStatement
(Nothing)
(TargetName (NSimple i))
(Nothing)
(WaveElem [WaveEExp e Nothing])
assignVariable :: Identifier -> Expression -> SequentialStatement
assignVariable i e = SVarAss $
VariableAssignmentStatement
(Nothing)
(TargetName (NSimple i))
(e)
assignArray :: Name -> Expression -> SequentialStatement
assignArray i e = SSignalAss $
SignalAssignmentStatement
(Nothing)
(TargetName i)
(Nothing)
(WaveElem [WaveEExp e Nothing])
--------------------------------------------------------------------------------
-- Some helper classes and their instances
--------------------------------------------------------------------------------
--
-- I use BlockDeclarativeItem to represent all declarative items, which means we
-- have to translate them over to their correct VHDL kind when generating an AST
class Merge a
where
-- group two items if this holds
group :: a -> a -> Bool
-- merge in this way
reduce :: [a] -> a
merge :: [a] -> [a]
merge = fmap reduce . groupBy group
instance Merge BlockDeclarativeItem
where
group l r = setBlockIds l [] == setBlockIds r []
reduce bs@(b:_) = setBlockIds b $ concatMap getBlockIds bs
instance Merge InterfaceDeclaration
where
group l r = l { idecl_identifier_list = [] } == r { idecl_identifier_list = [] }
reduce (x:xs) = x { idecl_identifier_list = ids x ++ concatMap ids xs }
where ids = idecl_identifier_list
--------------------------------------------------------------------------------
setBlockIds :: BlockDeclarativeItem -> [Identifier] -> BlockDeclarativeItem
setBlockIds (BDIConstant c) is = BDIConstant $ c { const_identifier_list = is }
setBlockIds (BDISignal s) is = BDISignal $ s { signal_identifier_list = is }
setBlockIds (BDIShared v) is = BDIShared $ v { var_identifier_list = is }
setBlockIds (BDIFile f) is = BDIFile $ f { fd_identifier_list = is }
setBlockIds x _ = x
getBlockIds :: BlockDeclarativeItem -> [Identifier]
getBlockIds (BDIConstant c) = const_identifier_list c
getBlockIds (BDISignal s) = signal_identifier_list s
getBlockIds (BDIShared v) = var_identifier_list v
getBlockIds (BDIFile f) = fd_identifier_list f
class Declarative a
where
-- lists are used so we can fail without having to throw errors
translate :: [BlockDeclarativeItem] -> [a]
instance Declarative ProcessDeclarativeItem
where
translate = catMaybes . fmap tryProcess
-- | Try to transform the declarative item into a process item
tryProcess :: BlockDeclarativeItem -> Maybe (ProcessDeclarativeItem)
tryProcess (BDIConstant c) = Just $ PDIConstant c
tryProcess (BDIShared v) = Just $ PDIVariable v
tryProcess (BDIFile f) = Just $ PDIFile f
tryProcess _ = Nothing
--------------------------------------------------------------------------------
-- Ord instance for use in sets
--------------------------------------------------------------------------------
--
-- *** These break the Ord rules but seems to be needed for Set.
deriving instance Ord ContextItem
deriving instance Ord LibraryClause
deriving instance Ord LogicalNameList
deriving instance Ord UseClause
instance Ord TypeDeclaration
where
compare (TDFull l) (TDFull r) = compare (ftd_identifier l) (ftd_identifier r)
compare (TDPartial l) (TDPartial r) = compare l r
compare (TDFull l) _ = GT
compare (TDPartial l) _ = LT
deriving instance Ord IncompleteTypeDeclaration
instance Ord ComponentDeclaration
where
compare l r = compare (comp_identifier l) (comp_identifier r)
deriving instance Ord SubtypeIndication
deriving instance Ord TypeMark
instance Ord Constraint
where
compare (CRange a) (CRange b) = compare a b
compare _ _ = error "Ord not supported for index constraints"
deriving instance Ord RangeConstraint
instance Ord Range
where
compare (RSimple a b c) (RSimple x y z) =
case compare a x of
GT -> GT
LT -> LT
EQ -> case compare b y of
GT -> GT
LT -> LT
EQ -> case compare c z of
GT -> GT
LT -> LT
EQ -> EQ
compare _ _ = error "Ord not supported for attribute ranges"
deriving instance Ord Direction
deriving instance Ord Expression
deriving instance Ord Relation
deriving instance Ord ShiftExpression
deriving instance Ord SimpleExpression
deriving instance Ord Term
deriving instance Ord Factor
instance Ord Primary
where
compare (PrimName a) (PrimName x) = compare a x
deriving instance Ord LogicalOperator
deriving instance Ord RelationalOperator
deriving instance Ord ShiftOperator
deriving instance Ord AddingOperator
deriving instance Ord Sign
deriving instance Ord MultiplyingOperator
deriving instance Ord MiscellaneousOperator
deriving instance Ord Identifier
instance Ord Name
where
compare (NSimple a) (NSimple x) = compare a x
compare (NSelect a) (NSelect x) = compare a x
compare (NIndex a) (NIndex x) = compare a x
compare (NSlice a) (NSlice x) = compare a x
compare (NAttr a) (NAttr x) = compare a x
deriving instance Ord StringLiteral
deriving instance Ord SelectedName
instance Ord Suffix
where
compare (SSimple a) (SSimple x) = compare a x
compare (SChar a) (SChar x) = compare a x
compare (SAll) (SAll) = EQ
compare _ _ = error "Ord not supported for operator symbols"
deriving instance Ord CharacterLiteral
deriving instance Ord IndexedName
deriving instance Ord SliceName
deriving instance Ord DiscreteRange
instance Ord Prefix
where
compare (PName a) (PName x) = compare a x
compare _ _ = error "Ord not supported for function names"
deriving instance Ord AttributeName
deriving instance Ord Signature
--------------------------------------------------------------------------------