Add the new HsTools module.
[matthijs/master-project/cλash.git] / VHDL.hs
diff --git a/VHDL.hs b/VHDL.hs
index 8fe3cfec27eccab45adcf492b715936bbbf2db40..b577110db761643e37d83fa0e7caad7a4a7f061b 100644 (file)
--- a/VHDL.hs
+++ b/VHDL.hs
@@ -3,6 +3,7 @@
 --
 module VHDL where
 
 --
 module VHDL where
 
+-- Standard modules
 import qualified Data.Foldable as Foldable
 import qualified Data.List as List
 import qualified Data.Map as Map
 import qualified Data.Foldable as Foldable
 import qualified Data.List as List
 import qualified Data.Map as Map
@@ -13,15 +14,18 @@ import qualified Control.Monad.Trans.State as State
 import qualified Data.Traversable as Traversable
 import qualified Data.Monoid as Monoid
 import Data.Accessor
 import qualified Data.Traversable as Traversable
 import qualified Data.Monoid as Monoid
 import Data.Accessor
+import qualified Data.Accessor.MonadState as MonadState
 
 
+-- ForSyDe
+import qualified ForSyDe.Backend.VHDL.AST as AST
+
+-- GHC API
 import qualified Type
 import qualified Type
-import qualified TysWiredIn
 import qualified Name
 import qualified TyCon
 import Outputable ( showSDoc, ppr )
 
 import qualified Name
 import qualified TyCon
 import Outputable ( showSDoc, ppr )
 
-import qualified ForSyDe.Backend.VHDL.AST as AST
-
+-- Local imports
 import VHDLTypes
 import Flatten
 import FlattenTypes
 import VHDLTypes
 import Flatten
 import FlattenTypes
@@ -34,15 +38,22 @@ createDesignFiles ::
   -> [(AST.VHDLId, AST.DesignFile)]
 
 createDesignFiles flatfuncmap =
   -> [(AST.VHDLId, AST.DesignFile)]
 
 createDesignFiles flatfuncmap =
-  -- TODO: Output types
-  map (Arrow.second $ AST.DesignFile context) units
+  (mkVHDLId "types", AST.DesignFile ieee_context [type_package]) :
+  map (Arrow.second $ AST.DesignFile full_context) units
+  
   where
     init_session = VHDLSession Map.empty builtin_funcs
     (units, final_session) = 
       State.runState (createLibraryUnits flatfuncmap) init_session
   where
     init_session = VHDLSession Map.empty builtin_funcs
     (units, final_session) = 
       State.runState (createLibraryUnits flatfuncmap) init_session
-    context = [
-      AST.Library $ mkVHDLId "IEEE",
-      AST.Use $ (AST.NSimple $ mkVHDLId "IEEE.std_logic_1164") AST.:.: AST.All]
+    ty_decls = Map.elems (final_session ^. vsTypes)
+    ieee_context = [
+        AST.Library $ mkVHDLId "IEEE",
+        AST.Use $ (AST.NSimple $ mkVHDLId "IEEE.std_logic_1164") AST.:.: AST.All
+      ]
+    full_context =
+      (AST.Use $ (AST.NSimple $ mkVHDLId "work.types") AST.:.: AST.All)
+      : ieee_context
+    type_package = AST.LUPackageDec $ AST.PackageDec (mkVHDLId "types") (map (AST.PDITD . snd) ty_decls)
 
 createLibraryUnits ::
   FlatFuncMap
 
 createLibraryUnits ::
   FlatFuncMap
@@ -66,38 +77,25 @@ createEntity ::
   -> FlatFunction -- | The FlatFunction
   -> VHDLState AST.EntityDec -- | The resulting entity
 
   -> FlatFunction -- | The FlatFunction
   -> VHDLState AST.EntityDec -- | The resulting entity
 
-createEntity hsfunc flatfunc = 
-      let 
-        sigs    = flat_sigs flatfunc
-        args    = flat_args flatfunc
-        res     = flat_res  flatfunc
-        (ty_decls, args') = Traversable.traverse (Traversable.traverse (mkMap sigs)) args
-        (ty_decls', res') = Traversable.traverse (mkMap sigs) res
-        -- TODO: Unique ty_decls
-        ent_decl' = createEntityAST hsfunc args' res'
-        pkg_id = mkVHDLId $ (AST.fromVHDLId entity_id) ++ "_types"
-        pkg_decl = if null ty_decls && null ty_decls'
-          then Nothing
-          else Just $ AST.PackageDec pkg_id (map AST.PDITD $ ty_decls ++ ty_decls')
-        -- TODO: Output package
-        AST.EntityDec entity_id _ = ent_decl' 
-        signature = Entity entity_id args' res'
-      in do
-        modA vsSignatures (Map.insert hsfunc signature)
-        return ent_decl'
+createEntity hsfunc flatfunc = do
+      let sigs    = flat_sigs flatfunc
+      let args    = flat_args flatfunc
+      let res     = flat_res  flatfunc
+      args' <- Traversable.traverse (Traversable.traverse (mkMap sigs)) args
+      res' <- Traversable.traverse (mkMap sigs) res
+      let ent_decl' = createEntityAST hsfunc args' res'
+      let AST.EntityDec entity_id _ = ent_decl' 
+      let signature = Entity entity_id args' res'
+      modA vsSignatures (Map.insert hsfunc signature)
+      return ent_decl'
   where
     mkMap :: 
       [(SignalId, SignalInfo)] 
       -> SignalId 
   where
     mkMap :: 
       [(SignalId, SignalInfo)] 
       -> SignalId 
-      -> ([AST.TypeDec], Maybe (AST.VHDLId, AST.TypeMark))
-    mkMap sigmap id =
-      if isPortSigUse $ sigUse info
-        then
-          let (decs, type_mark) = vhdl_ty ty in
-          (decs, Just (mkVHDLId nm, type_mark))
-        else
-          (Monoid.mempty, Nothing)
-      where
+      -> VHDLState VHDLSignalMapElement
+    -- We only need the vsTypes element from the state
+    mkMap sigmap = MonadState.lift vsTypes . (\id ->
+      let
         info = Maybe.fromMaybe
           (error $ "Signal not found in the name map? This should not happen!")
           (lookup id sigmap)
         info = Maybe.fromMaybe
           (error $ "Signal not found in the name map? This should not happen!")
           (lookup id sigmap)
@@ -105,6 +103,14 @@ createEntity hsfunc flatfunc =
           (error $ "Signal not named? This should not happen!")
           (sigName info)
         ty = sigTy info
           (error $ "Signal not named? This should not happen!")
           (sigName info)
         ty = sigTy info
+      in
+        if isPortSigUse $ sigUse info
+          then do
+            type_mark <- vhdl_ty ty
+            return $ Just (mkVHDLId nm, type_mark)
+          else
+            return $ Nothing
+       )
 
   -- | Create the VHDL AST for an entity
 createEntityAST ::
 
   -- | Create the VHDL AST for an entity
 createEntityAST ::
@@ -156,7 +162,10 @@ createArchitecture hsfunc flatfunc = do
         (error $ "Generating architecture for function " ++ (prettyShow hsfunc) ++ "without signature? This should not happen!")
         (Map.lookup hsfunc signaturemap)
   let entity_id = ent_id signature
         (error $ "Generating architecture for function " ++ (prettyShow hsfunc) ++ "without signature? This should not happen!")
         (Map.lookup hsfunc signaturemap)
   let entity_id = ent_id signature
-    -- Create concurrent statements for all signal definitions
+  -- Create signal declarations for all internal and state signals
+  sig_dec_maybes <- mapM (mkSigDec' . snd) sigs
+  let sig_decs = Maybe.catMaybes $ sig_dec_maybes
+  -- Create concurrent statements for all signal definitions
   let statements = zipWith (mkConcSm signaturemap sigs) defs [0..]
   return $ AST.ArchBody (mkVHDLId "structural") (AST.NSimple entity_id) (map AST.BDISD sig_decs) (statements ++ procs')
   where
   let statements = zipWith (mkConcSm signaturemap sigs) defs [0..]
   return $ AST.ArchBody (mkVHDLId "structural") (AST.NSimple entity_id) (map AST.BDISD sig_decs) (statements ++ procs')
   where
@@ -164,12 +173,10 @@ createArchitecture hsfunc flatfunc = do
     args = flat_args flatfunc
     res  = flat_res  flatfunc
     defs = flat_defs flatfunc
     args = flat_args flatfunc
     res  = flat_res  flatfunc
     defs = flat_defs flatfunc
-    -- Create signal declarations for all internal and state signals
-    (ty_decls, sig_decs)  = Arrow.second Maybe.catMaybes $ Traversable.traverse (mkSigDec . snd) sigs
-    -- TODO: Unique ty_decls
-    -- TODO: Store ty_decls somewhere
     procs = map mkStateProcSm (makeStatePairs flatfunc)
     procs' = map AST.CSPSm procs
     procs = map mkStateProcSm (makeStatePairs flatfunc)
     procs' = map AST.CSPSm procs
+    -- mkSigDec only uses vsTypes from the state
+    mkSigDec' = MonadState.lift vsTypes . mkSigDec
 
 -- | Looks up all pairs of old state, new state signals, together with
 --   the state id they represent.
 
 -- | Looks up all pairs of old state, new state signals, together with
 --   the state id they represent.
@@ -198,14 +205,14 @@ mkStateProcSm (num, old, new) =
     rising_edge_clk = AST.PrimFCall $ AST.FCall rising_edge [Nothing AST.:=>: (AST.ADName $ AST.NSimple clk)]
     statement   = AST.IfSm rising_edge_clk [assign] [] Nothing
 
     rising_edge_clk = AST.PrimFCall $ AST.FCall rising_edge [Nothing AST.:=>: (AST.ADName $ AST.NSimple clk)]
     statement   = AST.IfSm rising_edge_clk [assign] [] Nothing
 
-mkSigDec :: SignalInfo -> ([AST.TypeDec], Maybe AST.SigDec)
+mkSigDec :: SignalInfo -> TypeState (Maybe AST.SigDec)
 mkSigDec info =
   let use = sigUse info in
 mkSigDec info =
   let use = sigUse info in
-  if isInternalSigUse use || isStateSigUse use then
-    let (ty_decls, type_mark) = vhdl_ty ty in
-    (ty_decls, Just $ AST.SigDec (getSignalId info) type_mark Nothing)
+  if isInternalSigUse use || isStateSigUse use then do
+    type_mark <- vhdl_ty ty
+    return $ Just (AST.SigDec (getSignalId info) type_mark Nothing)
   else
   else
-    ([], Nothing)
+    return Nothing
   where
     ty = sigTy info
 
   where
     ty = sigTy info
 
@@ -329,38 +336,58 @@ std_logic_ty :: AST.TypeMark
 std_logic_ty = AST.unsafeVHDLBasicId "std_logic"
 
 -- Translate a Haskell type to a VHDL type
 std_logic_ty = AST.unsafeVHDLBasicId "std_logic"
 
 -- Translate a Haskell type to a VHDL type
-vhdl_ty :: Type.Type -> ([AST.TypeDec], AST.TypeMark)
-vhdl_ty ty = Maybe.fromMaybe
-  (error $ "Unsupported Haskell type: " ++ (showSDoc $ ppr ty))
-  (vhdl_ty_maybe ty)
-
--- Translate a Haskell type to a VHDL type, optionally generating a type
--- declaration for the type.
-vhdl_ty_maybe :: Type.Type -> Maybe ([AST.TypeDec], AST.TypeMark)
-vhdl_ty_maybe ty =
-  if Type.coreEqType ty TysWiredIn.boolTy
-    then
-      Just ([], bool_ty)
-    else
-      case Type.splitTyConApp_maybe ty of
-        Just (tycon, args) ->
-          let name = TyCon.tyConName tycon in
-            -- TODO: Do something more robust than string matching
-            case Name.getOccString name of
-              "Bit"      -> Just ([], std_logic_ty)
-              "FSVec"    ->
-                let 
-                  [len, el_ty] = args 
-                  -- TODO: Find actual number
-                  ty_id = mkVHDLId ("vector_" ++ (show len))
-                  -- TODO: Use el_ty
-                  range = AST.IndexConstraint [AST.ToRange (AST.PrimLit "0") (AST.PrimLit "16")]
-                  ty_def = AST.TDA $ AST.ConsArrayDef range std_logic_ty
-                  ty_dec = AST.TypeDec ty_id ty_def
-                in
-                  Just ([ty_dec], ty_id)
-              otherwise  -> Nothing
-        otherwise -> Nothing
+vhdl_ty :: Type.Type -> TypeState AST.TypeMark
+vhdl_ty ty = do
+  typemap <- State.get
+  let builtin_ty = do -- See if this is a tycon and lookup its name
+        (tycon, args) <- Type.splitTyConApp_maybe ty
+        let name = Name.getOccString (TyCon.tyConName tycon)
+        Map.lookup name builtin_types
+  -- If not a builtin type, try the custom types
+  let existing_ty = (fmap fst) $ Map.lookup (OrdType ty) typemap
+  case Monoid.getFirst $ Monoid.mconcat (map Monoid.First [builtin_ty, existing_ty]) of
+    -- Found a type, return it
+    Just t -> return t
+    -- No type yet, try to construct it
+    Nothing -> do
+      let new_ty = do
+            -- Use the Maybe Monad for failing when one of these fails
+            (tycon, args) <- Type.splitTyConApp_maybe ty
+            let name = Name.getOccString (TyCon.tyConName tycon)
+            case name of
+              "FSVec" -> Just $ mk_fsvec_ty ty args
+              otherwise -> Nothing
+      -- Return new_ty when a new type was successfully created
+      Maybe.fromMaybe 
+        (error $ "Unsupported Haskell type: " ++ (showSDoc $ ppr ty))
+        new_ty
+
+-- | Create a VHDL type belonging to a FSVec Haskell type
+mk_fsvec_ty ::
+  Type.Type -- ^ The Haskell type to create a VHDL type for
+  -> [Type.Type] -- ^ Type arguments to the FSVec type constructor
+  -> TypeState AST.TypeMark -- The typemark created.
+
+mk_fsvec_ty ty args = do
+  -- Assume there are two type arguments
+  let [len, el_ty] = args 
+  -- TODO: Find actual number
+  -- Construct the type id, but filter out dots (since these are not allowed).
+  let ty_id = mkVHDLId $ filter (/='.') ("vector_" ++ (show len))
+  -- TODO: Use el_ty
+  let range = AST.IndexConstraint [AST.ToRange (AST.PrimLit "0") (AST.PrimLit "16")]
+  let ty_def = AST.TDA $ AST.ConsArrayDef range std_logic_ty
+  let ty_dec = AST.TypeDec ty_id ty_def
+  -- TODO: Check name uniqueness
+  State.modify (Map.insert (OrdType ty) (ty_id, ty_dec))
+  return ty_id
+
+
+builtin_types = 
+  Map.fromList [
+    ("Bit", std_logic_ty),
+    ("Bool", bool_ty) -- TysWiredIn.boolTy
+  ]
 
 -- Shortcut
 mkVHDLId :: String -> AST.VHDLId
 
 -- Shortcut
 mkVHDLId :: String -> AST.VHDLId
@@ -402,3 +429,5 @@ builtin_funcs = mkBuiltins
 --   a VHDLSignalMap
 toVHDLSignalMap :: HsValueMap (String, AST.TypeMark) -> VHDLSignalMap
 toVHDLSignalMap = fmap (\(name, ty) -> Just (mkVHDLId name, ty))
 --   a VHDLSignalMap
 toVHDLSignalMap :: HsValueMap (String, AST.TypeMark) -> VHDLSignalMap
 toVHDLSignalMap = fmap (\(name, ty) -> Just (mkVHDLId name, ty))
+
+-- vim: set ts=8 sw=2 sts=2 expandtab: