We now output VHDL types in the correct order
[matthijs/master-project/cλash.git] / VHDLTools.hs
1 module VHDLTools where
2
3 -- Standard modules
4 import qualified Maybe
5 import qualified Data.List as List
6 import qualified Data.Map as Map
7 import qualified Control.Monad as Monad
8 import qualified Control.Arrow as Arrow
9 import qualified Data.Monoid as Monoid
10 import Data.Accessor
11 import Debug.Trace
12
13 -- ForSyDe
14 import qualified ForSyDe.Backend.VHDL.AST as AST
15
16 -- GHC API
17 import CoreSyn
18 import qualified Name
19 import qualified OccName
20 import qualified Var
21 import qualified Id
22 import qualified TyCon
23 import qualified Type
24 import qualified DataCon
25 import qualified CoreSubst
26
27 -- Local imports
28 import VHDLTypes
29 import CoreTools
30 import Pretty
31 import Constants
32
33 -----------------------------------------------------------------------------
34 -- Functions to generate concurrent statements
35 -----------------------------------------------------------------------------
36
37 -- Create an unconditional assignment statement
38 mkUncondAssign ::
39   Either CoreBndr AST.VHDLName -- ^ The signal to assign to
40   -> AST.Expr -- ^ The expression to assign
41   -> AST.ConcSm -- ^ The resulting concurrent statement
42 mkUncondAssign dst expr = mkAssign dst Nothing expr
43
44 -- Create a conditional assignment statement
45 mkCondAssign ::
46   Either CoreBndr AST.VHDLName -- ^ The signal to assign to
47   -> AST.Expr -- ^ The condition
48   -> AST.Expr -- ^ The value when true
49   -> AST.Expr -- ^ The value when false
50   -> AST.ConcSm -- ^ The resulting concurrent statement
51 mkCondAssign dst cond true false = mkAssign dst (Just (cond, true)) false
52
53 -- Create a conditional or unconditional assignment statement
54 mkAssign ::
55   Either CoreBndr AST.VHDLName -> -- ^ The signal to assign to
56   Maybe (AST.Expr , AST.Expr) -> -- ^ Optionally, the condition to test for
57                                  -- and the value to assign when true.
58   AST.Expr -> -- ^ The value to assign when false or no condition
59   AST.ConcSm -- ^ The resulting concurrent statement
60 mkAssign dst cond false_expr =
61   let
62     -- I'm not 100% how this assignment AST works, but this gets us what we
63     -- want...
64     whenelse = case cond of
65       Just (cond_expr, true_expr) -> 
66         let 
67           true_wform = AST.Wform [AST.WformElem true_expr Nothing] 
68         in
69           [AST.WhenElse true_wform cond_expr]
70       Nothing -> []
71     false_wform = AST.Wform [AST.WformElem false_expr Nothing]
72     dst_name  = case dst of
73       Left bndr -> AST.NSimple (varToVHDLId bndr)
74       Right name -> name
75     assign    = dst_name AST.:<==: (AST.ConWforms whenelse false_wform Nothing)
76   in
77     AST.CSSASm assign
78
79 mkAssocElems :: 
80   [AST.Expr]                    -- | The argument that are applied to function
81   -> AST.VHDLName               -- | The binder in which to store the result
82   -> Entity                     -- | The entity to map against.
83   -> [AST.AssocElem]            -- | The resulting port maps
84 mkAssocElems args res entity =
85     -- Create the actual AssocElems
86     zipWith mkAssocElem ports sigs
87   where
88     -- Turn the ports and signals from a map into a flat list. This works,
89     -- since the maps must have an identical form by definition. TODO: Check
90     -- the similar form?
91     arg_ports = ent_args entity
92     res_port  = ent_res entity
93     -- Extract the id part from the (id, type) tuple
94     ports     = map fst (res_port : arg_ports)
95     -- Translate signal numbers into names
96     sigs      = (vhdlNameToVHDLExpr res : args)
97
98 -- | Create an VHDL port -> signal association
99 mkAssocElem :: AST.VHDLId -> AST.Expr -> AST.AssocElem
100 mkAssocElem port signal = Just port AST.:=>: (AST.ADExpr signal) 
101
102 -- | Create an VHDL port -> signal association
103 mkAssocElemIndexed :: AST.VHDLId -> AST.VHDLId -> AST.VHDLId -> AST.AssocElem
104 mkAssocElemIndexed port signal index = Just port AST.:=>: (AST.ADName (AST.NIndexed (AST.IndexedName 
105                       (AST.NSimple signal) [AST.PrimName $ AST.NSimple index])))
106
107 mkComponentInst ::
108   String -- ^ The portmap label
109   -> AST.VHDLId -- ^ The entity name
110   -> [AST.AssocElem] -- ^ The port assignments
111   -> AST.ConcSm
112 mkComponentInst label entity_id portassigns = AST.CSISm compins
113   where
114     -- We always have a clock port, so no need to map it anywhere but here
115     clk_port = mkAssocElem (mkVHDLExtId "clk") (idToVHDLExpr $ mkVHDLExtId "clk")
116     compins = AST.CompInsSm (mkVHDLExtId label) (AST.IUEntity (AST.NSimple entity_id)) (AST.PMapAspect (portassigns ++ [clk_port]))
117
118 -----------------------------------------------------------------------------
119 -- Functions to generate VHDL Exprs
120 -----------------------------------------------------------------------------
121
122 -- Turn a variable reference into a AST expression
123 varToVHDLExpr :: Var.Var -> AST.Expr
124 varToVHDLExpr var = 
125   case Id.isDataConWorkId_maybe var of
126     Just dc -> dataconToVHDLExpr dc
127     -- This is a dataconstructor.
128     -- Not a datacon, just another signal. Perhaps we should check for
129     -- local/global here as well?
130     -- Sadly so.. tfp decimals are types, not data constructors, but instances
131     -- should still be translated to integer literals. It is probebly not the
132     -- best solution to translate them here.
133     -- FIXME: Find a better solution for translating instances of tfp integers
134     Nothing -> 
135         let 
136           ty  = Var.varType var
137           res = case Type.splitTyConApp_maybe ty of
138                   Just (tycon, args) ->
139                     case Name.getOccString (TyCon.tyConName tycon) of
140                       "Dec" -> AST.PrimLit $ (show (eval_tfp_int ty))
141                       otherwise -> AST.PrimName $ AST.NSimple $ varToVHDLId var
142         in
143           res
144
145 -- Turn a VHDLName into an AST expression
146 vhdlNameToVHDLExpr = AST.PrimName
147
148 -- Turn a VHDL Id into an AST expression
149 idToVHDLExpr = vhdlNameToVHDLExpr . AST.NSimple
150
151 -- Turn a Core expression into an AST expression
152 exprToVHDLExpr = varToVHDLExpr . exprToVar
153
154 -- Turn a alternative constructor into an AST expression. For
155 -- dataconstructors, this is only the constructor itself, not any arguments it
156 -- has. Should not be called with a DEFAULT constructor.
157 altconToVHDLExpr :: CoreSyn.AltCon -> AST.Expr
158 altconToVHDLExpr (DataAlt dc) = dataconToVHDLExpr dc
159
160 altconToVHDLExpr (LitAlt _) = error "\nVHDL.conToVHDLExpr: Literals not support in case alternatives yet"
161 altconToVHDLExpr DEFAULT = error "\nVHDL.conToVHDLExpr: DEFAULT alternative should not occur here!"
162
163 -- Turn a datacon (without arguments!) into a VHDL expression.
164 dataconToVHDLExpr :: DataCon.DataCon -> AST.Expr
165 dataconToVHDLExpr dc = AST.PrimLit lit
166   where
167     tycon = DataCon.dataConTyCon dc
168     tyname = TyCon.tyConName tycon
169     dcname = DataCon.dataConName dc
170     lit = case Name.getOccString tyname of
171       -- TODO: Do something more robust than string matching
172       "Bit"      -> case Name.getOccString dcname of "High" -> "'1'"; "Low" -> "'0'"
173       "Bool" -> case Name.getOccString dcname of "True" -> "true"; "False" -> "false"
174
175 -----------------------------------------------------------------------------
176 -- Functions dealing with names, variables and ids
177 -----------------------------------------------------------------------------
178
179 -- Creates a VHDL Id from a binder
180 varToVHDLId ::
181   CoreSyn.CoreBndr
182   -> AST.VHDLId
183 varToVHDLId = mkVHDLExtId . varToString
184
185 -- Creates a VHDL Name from a binder
186 varToVHDLName ::
187   CoreSyn.CoreBndr
188   -> AST.VHDLName
189 varToVHDLName = AST.NSimple . varToVHDLId
190
191 -- Extracts the binder name as a String
192 varToString ::
193   CoreSyn.CoreBndr
194   -> String
195 varToString = OccName.occNameString . Name.nameOccName . Var.varName
196
197 -- Get the string version a Var's unique
198 varToStringUniq :: Var.Var -> String
199 varToStringUniq = show . Var.varUnique
200
201 -- Extracts the string version of the name
202 nameToString :: Name.Name -> String
203 nameToString = OccName.occNameString . Name.nameOccName
204
205 -- Shortcut for Basic VHDL Ids.
206 -- Can only contain alphanumerics and underscores. The supplied string must be
207 -- a valid basic id, otherwise an error value is returned. This function is
208 -- not meant to be passed identifiers from a source file, use mkVHDLExtId for
209 -- that.
210 mkVHDLBasicId :: String -> AST.VHDLId
211 mkVHDLBasicId s = 
212   AST.unsafeVHDLBasicId $ (strip_multiscore . strip_leading . strip_invalid) s
213   where
214     -- Strip invalid characters.
215     strip_invalid = filter (`elem` ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ "_.")
216     -- Strip leading numbers and underscores
217     strip_leading = dropWhile (`elem` ['0'..'9'] ++ "_")
218     -- Strip multiple adjacent underscores
219     strip_multiscore = concat . map (\cs -> 
220         case cs of 
221           ('_':_) -> "_"
222           _ -> cs
223       ) . List.group
224
225 -- Shortcut for Extended VHDL Id's. These Id's can contain a lot more
226 -- different characters than basic ids, but can never be used to refer to
227 -- basic ids.
228 -- Use extended Ids for any values that are taken from the source file.
229 mkVHDLExtId :: String -> AST.VHDLId
230 mkVHDLExtId s = 
231   AST.unsafeVHDLExtId $ strip_invalid s
232   where 
233     -- Allowed characters, taken from ForSyde's mkVHDLExtId
234     allowed = ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ " \"#&\\'()*+,./:;<=>_|!$%@?[]^`{}~-"
235     strip_invalid = filter (`elem` allowed)
236
237 -- Create a record field selector that selects the given label from the record
238 -- stored in the given binder.
239 mkSelectedName :: AST.VHDLName -> AST.VHDLId -> AST.VHDLName
240 mkSelectedName name label =
241    AST.NSelected $ name AST.:.: (AST.SSimple label) 
242
243 -- Create an indexed name that selects a given element from a vector.
244 mkIndexedName :: AST.VHDLName -> AST.Expr -> AST.VHDLName
245 -- Special case for already indexed names. Just add an index
246 mkIndexedName (AST.NIndexed (AST.IndexedName name indexes)) index =
247  AST.NIndexed (AST.IndexedName name (indexes++[index]))
248 -- General case for other names
249 mkIndexedName name index = AST.NIndexed (AST.IndexedName name [index])
250
251 -----------------------------------------------------------------------------
252 -- Functions dealing with VHDL types
253 -----------------------------------------------------------------------------
254
255 -- | Maps the string name (OccName) of a type to the corresponding VHDL type,
256 -- for a few builtin types.
257 builtin_types = 
258   Map.fromList [
259     ("Bit", std_logicTM),
260     ("Bool", booleanTM), -- TysWiredIn.boolTy
261     ("Dec", integerTM)
262   ]
263
264 -- Translate a Haskell type to a VHDL type, generating a new type if needed.
265 vhdl_ty :: String -> Type.Type -> VHDLSession AST.TypeMark
266 vhdl_ty msg ty = do
267   typemap <- getA vsTypes
268   let builtin_ty = do -- See if this is a tycon and lookup its name
269         (tycon, args) <- Type.splitTyConApp_maybe ty
270         let name = Name.getOccString (TyCon.tyConName tycon)
271         Map.lookup name builtin_types
272   -- If not a builtin type, try the custom types
273   let existing_ty = (fmap fst) $ Map.lookup (OrdType ty) typemap
274   case Monoid.getFirst $ Monoid.mconcat (map Monoid.First [builtin_ty, existing_ty]) of
275     -- Found a type, return it
276     Just t -> return t
277     -- No type yet, try to construct it
278     Nothing -> do
279       newty_maybe <- (construct_vhdl_ty msg ty)
280       case newty_maybe of
281         Just (ty_id, ty_def) -> do
282           -- TODO: Check name uniqueness
283           modA vsTypes (Map.insert (OrdType ty) (ty_id, ty_def))
284           modA vsTypeDecls (\typedefs -> typedefs ++ [mktydecl (ty_id, ty_def)]) 
285           return ty_id
286         Nothing -> error $ msg ++ "\nVHDLTools.vhdl_ty: Unsupported Haskell type: " ++ pprString ty ++ "\n"
287
288 -- Construct a new VHDL type for the given Haskell type.
289 construct_vhdl_ty :: String -> Type.Type -> VHDLSession (Maybe (AST.TypeMark, Either AST.TypeDef AST.SubtypeIn))
290 construct_vhdl_ty msg ty = do
291   case Type.splitTyConApp_maybe ty of
292     Just (tycon, args) -> do
293       let name = Name.getOccString (TyCon.tyConName tycon)
294       case name of
295         "TFVec" -> do
296           res <- mk_vector_ty ty
297           return $ Just $ (Arrow.second Right) res
298         -- "SizedWord" -> do
299         --   res <- mk_vector_ty (sized_word_len ty) ty
300         --   return $ Just $ (Arrow.second Left) res
301         "RangedWord" -> do 
302           res <- mk_natural_ty 0 (ranged_word_bound ty)
303           return $ Just $ (Arrow.second Right) res
304         -- Create a custom type from this tycon
305         otherwise -> mk_tycon_ty msg tycon args
306     Nothing -> return $ Nothing
307
308 -- | Create VHDL type for a custom tycon
309 mk_tycon_ty :: String -> TyCon.TyCon -> [Type.Type] -> VHDLSession (Maybe (AST.TypeMark, Either AST.TypeDef AST.SubtypeIn))
310 mk_tycon_ty msg tycon args =
311   case TyCon.tyConDataCons tycon of
312     -- Not an algebraic type
313     [] -> error $ "\nVHDLTools.mk_tycon_ty: Only custom algebraic types are supported: " ++ pprString tycon
314     [dc] -> do
315       let arg_tys = DataCon.dataConRepArgTys dc
316       -- TODO: CoreSubst docs say each Subs can be applied only once. Is this a
317       -- violation? Or does it only mean not to apply it again to the same
318       -- subject?
319       let real_arg_tys = map (CoreSubst.substTy subst) arg_tys
320       let error_msg = msg ++ "\nVHDLTools.mk_tycon_ty: Can not construct type for: " ++ pprString tycon ++ "\n because no type can be construced for one of the arguments."
321       elem_tys <- mapM (vhdl_ty error_msg) real_arg_tys
322       let elems = zipWith AST.ElementDec recordlabels elem_tys
323       -- For a single construct datatype, build a record with one field for
324       -- each argument.
325       -- TODO: Add argument type ids to this, to ensure uniqueness
326       -- TODO: Special handling for tuples?
327       let elem_names = concat $ map prettyShow elem_tys
328       let ty_id = mkVHDLExtId $ nameToString (TyCon.tyConName tycon) ++ elem_names
329       let ty_def = AST.TDR $ AST.RecordTypeDef elems
330       return $ Just (ty_id, Left ty_def)
331     dcs -> error $ "\nVHDLTools.mk_tycon_ty: Only single constructor datatypes supported: " ++ pprString tycon
332   where
333     -- Create a subst that instantiates all types passed to the tycon
334     -- TODO: I'm not 100% sure that this is the right way to do this. It seems
335     -- to work so far, though..
336     tyvars = TyCon.tyConTyVars tycon
337     subst = CoreSubst.extendTvSubstList CoreSubst.emptySubst (zip tyvars args)
338     -- Generate a bunch of labels for fields of a record
339     recordlabels = map (\c -> mkVHDLBasicId [c]) ['A'..'Z']
340
341 -- | Create a VHDL vector type
342 mk_vector_ty ::
343   Type.Type -- ^ The Haskell type of the Vector
344   -> VHDLSession (AST.TypeMark, AST.SubtypeIn) -- The typemark created.
345
346 mk_vector_ty ty = do
347   types_map <- getA vsTypes
348   let (nvec_l, nvec_el) = Type.splitAppTy ty
349   let (nvec, leng) = Type.splitAppTy nvec_l
350   let vec_ty = Type.mkAppTy nvec nvec_el
351   let len = tfvec_len ty
352   let el_ty = tfvec_elem ty
353   let error_msg = "\nVHDLTools.mk_vector_ty: Can not construct vectortype for elementtype: " ++ pprString el_ty 
354   el_ty_tm <- vhdl_ty error_msg el_ty
355   let ty_id = mkVHDLExtId $ "vector-"++ (AST.fromVHDLId el_ty_tm) ++ "-0_to_" ++ (show len)
356   let range = AST.ConstraintIndex $ AST.IndexConstraint [AST.ToRange (AST.PrimLit "0") (AST.PrimLit $ show (len - 1))]
357   let existing_elem_ty = (fmap fst) $ Map.lookup (OrdType vec_ty) types_map
358   case existing_elem_ty of
359     Just t -> do
360       let ty_def = AST.SubtypeIn t (Just range)
361       return (ty_id, ty_def)
362     Nothing -> do
363       let vec_id = mkVHDLExtId $ "vector_" ++ (AST.fromVHDLId el_ty_tm)
364       let vec_def = AST.TDA $ AST.UnconsArrayDef [tfvec_indexTM] el_ty_tm
365       modA vsTypes (Map.insert (OrdType vec_ty) (vec_id, (Left vec_def)))
366       modA vsTypeDecls (\typedefs -> typedefs ++ [mktydecl (vec_id, (Left vec_def))]) 
367       let ty_def = AST.SubtypeIn vec_id (Just range)
368       return (ty_id, ty_def)
369
370 mk_natural_ty ::
371   Int -- ^ The minimum bound (> 0)
372   -> Int -- ^ The maximum bound (> minimum bound)
373   -> VHDLSession (AST.TypeMark, AST.SubtypeIn) -- The typemark created.
374 mk_natural_ty min_bound max_bound = do
375   let ty_id = mkVHDLExtId $ "nat_" ++ (show min_bound) ++ "_to_" ++ (show max_bound)
376   let range = AST.ConstraintRange $ AST.SubTypeRange (AST.PrimLit $ (show min_bound)) (AST.PrimLit $ (show max_bound))
377   let ty_def = AST.SubtypeIn naturalTM (Just range)
378   return (ty_id, ty_def)
379
380 -- Finds the field labels for VHDL type generated for the given Core type,
381 -- which must result in a record type.
382 getFieldLabels :: Type.Type -> VHDLSession [AST.VHDLId]
383 getFieldLabels ty = do
384   -- Ensure that the type is generated (but throw away it's VHDLId)
385   let error_msg = "\nVHDLTools.getFieldLabels: Can not get field labels, because: " ++ pprString ty ++ "can not be generated." 
386   vhdl_ty error_msg ty
387   -- Get the types map, lookup and unpack the VHDL TypeDef
388   types <- getA vsTypes
389   case Map.lookup (OrdType ty) types of
390     Just (_, Left (AST.TDR (AST.RecordTypeDef elems))) -> return $ map (\(AST.ElementDec id _) -> id) elems
391     _ -> error $ "\nVHDL.getFieldLabels: Type not found or not a record type? This should not happen! Type: " ++ (show ty)
392     
393 mktydecl :: (AST.VHDLId, Either AST.TypeDef AST.SubtypeIn) -> AST.PackageDecItem
394 mktydecl (ty_id, Left ty_def) = AST.PDITD $ AST.TypeDec ty_id ty_def
395 mktydecl (ty_id, Right ty_def) = AST.PDISD $ AST.SubtypeDec ty_id ty_def