2 -- Functions to generate VHDL from FlatFunctions
6 import qualified Data.Foldable as Foldable
7 import qualified Data.List as List
9 import qualified Control.Monad as Monad
10 import qualified Control.Arrow as Arrow
11 import qualified Data.Traversable as Traversable
12 import qualified Data.Monoid as Monoid
15 import qualified TysWiredIn
17 import qualified TyCon
18 import Outputable ( showSDoc, ppr )
20 import qualified ForSyDe.Backend.VHDL.AST as AST
25 import TranslatorTypes
28 getDesignFiles :: VHDLState [AST.DesignFile]
30 -- Extract the library units generated from all the functions in the
33 let units = Maybe.mapMaybe getLibraryUnits funcs
35 AST.Library $ mkVHDLId "IEEE",
36 AST.Use $ (AST.NSimple $ mkVHDLId "IEEE.std_logic_1164") AST.:.: AST.All]
37 return $ map (AST.DesignFile context) units
39 -- | Create an entity for a given function
41 HsFunction -- | The function signature
42 -> FuncData -- | The function data collected so far
45 createEntity hsfunc fdata =
46 let func = flatFunc fdata in
48 -- Skip (builtin) functions without a FlatFunction
49 Nothing -> do return ()
50 -- Create an entity for all other functions
53 sigs = flat_sigs flatfunc
54 args = flat_args flatfunc
55 res = flat_res flatfunc
56 (ty_decls, args') = Traversable.traverse (Traversable.traverse (mkMap sigs)) args
57 (ty_decls', res') = Traversable.traverse (mkMap sigs) res
58 -- TODO: Unique ty_decls
59 ent_decl' = createEntityAST hsfunc args' res'
60 pkg_id = mkVHDLId $ (AST.fromVHDLId entity_id) ++ "_types"
61 pkg_decl = if null ty_decls && null ty_decls'
63 else Just $ AST.PackageDec pkg_id (map AST.PDITD $ ty_decls ++ ty_decls')
64 AST.EntityDec entity_id _ = ent_decl'
65 entity' = Entity entity_id args' res' (Just ent_decl') pkg_decl
67 setEntity hsfunc entity'
70 [(SignalId, SignalInfo)]
72 -> ([AST.TypeDec], Maybe (AST.VHDLId, AST.TypeMark))
74 if isPortSigUse $ sigUse info
76 let (decs, type_mark) = vhdl_ty ty in
77 (decs, Just (mkVHDLId nm, type_mark))
79 (Monoid.mempty, Nothing)
81 info = Maybe.fromMaybe
82 (error $ "Signal not found in the name map? This should not happen!")
85 (error $ "Signal not named? This should not happen!")
89 -- | Create the VHDL AST for an entity
91 HsFunction -- | The signature of the function we're working with
92 -> [VHDLSignalMap] -- | The entity's arguments
93 -> VHDLSignalMap -- | The entity's result
94 -> AST.EntityDec -- | The entity with the ent_decl filled in as well
96 createEntityAST hsfunc args res =
97 AST.EntityDec vhdl_id ports
99 vhdl_id = mkEntityId hsfunc
100 ports = concatMap (mapToPorts AST.In) args
101 ++ mapToPorts AST.Out res
103 mapToPorts :: AST.Mode -> VHDLSignalMap -> [AST.IfaceSigDec]
105 Maybe.catMaybes $ map (mkIfaceSigDec mode) (Foldable.toList m)
106 -- Add a clk port if we have state
107 clk_port = if hasState hsfunc
109 [AST.IfaceSigDec (mkVHDLId "clk") AST.In VHDL.std_logic_ty]
113 -- | Create a port declaration
115 AST.Mode -- | The mode for the port (In / Out)
116 -> Maybe (AST.VHDLId, AST.TypeMark) -- | The id and type for the port
117 -> Maybe AST.IfaceSigDec -- | The resulting port declaration
119 mkIfaceSigDec mode (Just (id, ty)) = Just $ AST.IfaceSigDec id mode ty
120 mkIfaceSigDec _ Nothing = Nothing
122 -- | Generate a VHDL entity name for the given hsfunc
124 -- TODO: This doesn't work for functions with multiple signatures!
125 mkVHDLId $ hsFuncName hsfunc
127 -- | Create an architecture for a given function
128 createArchitecture ::
129 HsFunction -- | The function signature
130 -> FuncData -- | The function data collected so far
133 createArchitecture hsfunc fdata =
134 let func = flatFunc fdata in
136 -- Skip (builtin) functions without a FlatFunction
137 Nothing -> do return ()
138 -- Create an architecture for all other functions
140 let sigs = flat_sigs flatfunc
141 let args = flat_args flatfunc
142 let res = flat_res flatfunc
143 let defs = flat_defs flatfunc
144 let entity_id = Maybe.fromMaybe
145 (error $ "Building architecture without an entity? This should not happen!")
147 -- Create signal declarations for all signals that are not in args and
149 let (ty_decls, sig_decs) = Arrow.second Maybe.catMaybes $ Traversable.traverse (mkSigDec . snd) sigs
150 -- TODO: Unique ty_decls
151 -- TODO: Store ty_decls somewhere
152 -- Create concurrent statements for all signal definitions
153 statements <- mapM (mkConcSm sigs) defs
154 let procs = map mkStateProcSm (makeStatePairs flatfunc)
155 let procs' = map AST.CSPSm procs
156 let arch = AST.ArchBody (mkVHDLId "structural") (AST.NSimple entity_id) (map AST.BDISD sig_decs) (statements ++ procs')
157 setArchitecture hsfunc arch
159 -- | Looks up all pairs of old state, new state signals, together with
160 -- the state id they represent.
161 makeStatePairs :: FlatFunction -> [(StateId, SignalInfo, SignalInfo)]
162 makeStatePairs flatfunc =
163 [(Maybe.fromJust $ oldStateId $ sigUse old_info, old_info, new_info)
164 | old_info <- map snd (flat_sigs flatfunc)
165 , new_info <- map snd (flat_sigs flatfunc)
166 -- old_info must be an old state (and, because of the next equality,
167 -- new_info must be a new state).
168 , Maybe.isJust $ oldStateId $ sigUse old_info
169 -- And the state numbers must match
170 , (oldStateId $ sigUse old_info) == (newStateId $ sigUse new_info)]
172 -- Replace the second tuple element with the corresponding SignalInfo
173 --args_states = map (Arrow.second $ signalInfo sigs) args
174 mkStateProcSm :: (StateId, SignalInfo, SignalInfo) -> AST.ProcSm
175 mkStateProcSm (num, old, new) =
176 AST.ProcSm label [clk] [statement]
178 label = mkVHDLId $ "state_" ++ (show num)
180 rising_edge = AST.NSimple $ mkVHDLId "rising_edge"
181 wform = AST.Wform [AST.WformElem (AST.PrimName $ AST.NSimple $ getSignalId new) Nothing]
182 assign = AST.SigAssign (AST.NSimple $ getSignalId old) wform
183 rising_edge_clk = AST.PrimFCall $ AST.FCall rising_edge [Nothing AST.:=>: (AST.ADName $ AST.NSimple clk)]
184 statement = AST.IfSm rising_edge_clk [assign] [] Nothing
186 mkSigDec :: SignalInfo -> ([AST.TypeDec], Maybe AST.SigDec)
188 let use = sigUse info in
189 if isInternalSigUse use || isStateSigUse use then
190 let (ty_decls, type_mark) = vhdl_ty ty in
191 (ty_decls, Just $ AST.SigDec (getSignalId info) type_mark Nothing)
197 -- | Creates a VHDL Id from a named SignalInfo. Errors out if the SignalInfo
199 getSignalId :: SignalInfo -> AST.VHDLId
201 mkVHDLId $ Maybe.fromMaybe
202 (error $ "Unnamed signal? This should not happen!")
205 -- | Transforms a signal definition into a VHDL concurrent statement
207 [(SignalId, SignalInfo)] -- | The signals in the current architecture
208 -> SigDef -- | The signal definition
209 -> VHDLState AST.ConcSm -- | The corresponding VHDL component instantiation.
211 mkConcSm sigs (FApp hsfunc args res) = do
212 fdata_maybe <- getFunc hsfunc
213 let fdata = Maybe.fromMaybe
214 (error $ "Using function '" ++ (prettyShow hsfunc) ++ "' that is not in the session? This should not happen!")
216 let entity = Maybe.fromMaybe
217 (error $ "Using function '" ++ (prettyShow hsfunc) ++ "' without entity declaration? This should not happen!")
219 let entity_id = ent_id entity
220 label <- uniqueName (AST.fromVHDLId entity_id)
221 -- Add a clk port if we have state
222 let clk_port = Maybe.fromJust $ mkAssocElem (Just $ mkVHDLId "clk") "clk"
223 let portmaps = mkAssocElems sigs args res entity ++ (if hasState hsfunc then [clk_port] else [])
224 return $ AST.CSISm $ AST.CompInsSm (mkVHDLId label) (AST.IUEntity (AST.NSimple entity_id)) (AST.PMapAspect portmaps)
226 mkConcSm sigs (UncondDef src dst) = do
227 let src_expr = vhdl_expr src
228 let src_wform = AST.Wform [AST.WformElem src_expr Nothing]
229 let dst_name = AST.NSimple (getSignalId $ signalInfo sigs dst)
230 let assign = dst_name AST.:<==: (AST.ConWforms [] src_wform Nothing)
231 return $ AST.CSSASm assign
233 vhdl_expr (Left id) = mkIdExpr sigs id
234 vhdl_expr (Right expr) =
237 (mkIdExpr sigs id) AST.:=: (AST.PrimLit lit)
241 (mkIdExpr sigs a) AST.:=: (mkIdExpr sigs b)
243 mkConcSm sigs (CondDef cond true false dst) = do
244 let cond_expr = mkIdExpr sigs cond
245 let true_expr = mkIdExpr sigs true
246 let false_expr = mkIdExpr sigs false
247 let false_wform = AST.Wform [AST.WformElem false_expr Nothing]
248 let true_wform = AST.Wform [AST.WformElem true_expr Nothing]
249 let whenelse = AST.WhenElse true_wform cond_expr
250 let dst_name = AST.NSimple (getSignalId $ signalInfo sigs dst)
251 let assign = dst_name AST.:<==: (AST.ConWforms [whenelse] false_wform Nothing)
252 return $ AST.CSSASm assign
254 -- | Turn a SignalId into a VHDL Expr
255 mkIdExpr :: [(SignalId, SignalInfo)] -> SignalId -> AST.Expr
257 let src_name = AST.NSimple (getSignalId $ signalInfo sigs id) in
258 AST.PrimName src_name
261 [(SignalId, SignalInfo)] -- | The signals in the current architecture
262 -> [SignalMap] -- | The signals that are applied to function
263 -> SignalMap -- | the signals in which to store the function result
264 -> Entity -- | The entity to map against.
265 -> [AST.AssocElem] -- | The resulting port maps
267 mkAssocElems sigmap args res entity =
268 -- Create the actual AssocElems
269 Maybe.catMaybes $ zipWith mkAssocElem ports sigs
271 -- Turn the ports and signals from a map into a flat list. This works,
272 -- since the maps must have an identical form by definition. TODO: Check
274 arg_ports = concat (map Foldable.toList (ent_args entity))
275 res_ports = Foldable.toList (ent_res entity)
276 arg_sigs = (concat (map Foldable.toList args))
277 res_sigs = Foldable.toList res
278 -- Extract the id part from the (id, type) tuple
279 ports = (map (fmap fst) (arg_ports ++ res_ports))
280 -- Translate signal numbers into names
281 sigs = (map (lookupSigName sigmap) (arg_sigs ++ res_sigs))
283 -- | Look up a signal in the signal name map
284 lookupSigName :: [(SignalId, SignalInfo)] -> SignalId -> String
285 lookupSigName sigs sig = name
287 info = Maybe.fromMaybe
288 (error $ "Unknown signal " ++ (show sig) ++ " used? This should not happen!")
290 name = Maybe.fromMaybe
291 (error $ "Unnamed signal " ++ (show sig) ++ " used? This should not happen!")
294 -- | Create an VHDL port -> signal association
295 mkAssocElem :: Maybe AST.VHDLId -> String -> Maybe AST.AssocElem
296 mkAssocElem (Just port) signal = Just $ Just port AST.:=>: (AST.ADName (AST.NSimple (mkVHDLId signal)))
297 mkAssocElem Nothing _ = Nothing
299 -- | Extracts the generated entity id from the given funcdata
300 getEntityId :: FuncData -> Maybe AST.VHDLId
302 case funcEntity fdata of
304 Just e -> case ent_decl e of
306 Just (AST.EntityDec id _) -> Just id
309 (HsFunction, FuncData) -- | A function from the session
310 -> Maybe [AST.LibraryUnit] -- | The entity, architecture and optional package for the function
312 getLibraryUnits (hsfunc, fdata) =
313 case funcEntity fdata of
319 case funcArch fdata of
323 [AST.LUEntity decl, AST.LUArch arch]
324 ++ (Maybe.maybeToList (fmap AST.LUPackageDec $ ent_pkg_decl ent))
326 -- | The VHDL Bit type
327 bit_ty :: AST.TypeMark
328 bit_ty = AST.unsafeVHDLBasicId "Bit"
330 -- | The VHDL Boolean type
331 bool_ty :: AST.TypeMark
332 bool_ty = AST.unsafeVHDLBasicId "Boolean"
334 -- | The VHDL std_logic
335 std_logic_ty :: AST.TypeMark
336 std_logic_ty = AST.unsafeVHDLBasicId "std_logic"
338 -- Translate a Haskell type to a VHDL type
339 vhdl_ty :: Type.Type -> ([AST.TypeDec], AST.TypeMark)
340 vhdl_ty ty = Maybe.fromMaybe
341 (error $ "Unsupported Haskell type: " ++ (showSDoc $ ppr ty))
344 -- Translate a Haskell type to a VHDL type, optionally generating a type
345 -- declaration for the type.
346 vhdl_ty_maybe :: Type.Type -> Maybe ([AST.TypeDec], AST.TypeMark)
348 if Type.coreEqType ty TysWiredIn.boolTy
352 case Type.splitTyConApp_maybe ty of
353 Just (tycon, args) ->
354 let name = TyCon.tyConName tycon in
355 -- TODO: Do something more robust than string matching
356 case Name.getOccString name of
357 "Bit" -> Just ([], std_logic_ty)
361 -- TODO: Find actual number
362 ty_id = mkVHDLId ("vector_" ++ (show len))
364 range = AST.IndexConstraint [AST.ToRange (AST.PrimLit "0") (AST.PrimLit "16")]
365 ty_def = AST.TDA $ AST.ConsArrayDef range std_logic_ty
366 ty_dec = AST.TypeDec ty_id ty_def
368 Just ([ty_dec], ty_id)
373 mkVHDLId :: String -> AST.VHDLId
375 AST.unsafeVHDLBasicId $ (strip_multiscore . strip_invalid) s
377 -- Strip invalid characters.
378 strip_invalid = filter (`elem` ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ "_.")
379 -- Strip multiple adjacent underscores
380 strip_multiscore = concat . map (\cs ->