2 -- Functions to generate VHDL from FlatFunctions
6 import qualified Data.Foldable as Foldable
7 import qualified Data.List as List
8 import qualified Data.Map as Map
10 import qualified Control.Monad as Monad
11 import qualified Control.Arrow as Arrow
12 import qualified Data.Traversable as Traversable
13 import qualified Data.Monoid as Monoid
17 import qualified TysWiredIn
19 import qualified TyCon
20 import Outputable ( showSDoc, ppr )
22 import qualified ForSyDe.Backend.VHDL.AST as AST
27 import TranslatorTypes
30 getDesignFiles :: [FuncData] -> [AST.DesignFile]
31 getDesignFiles funcs =
32 map (AST.DesignFile context) units
34 units = filter (not.null) $ map getLibraryUnits funcs
36 AST.Library $ mkVHDLId "IEEE",
37 AST.Use $ (AST.NSimple $ mkVHDLId "IEEE.std_logic_1164") AST.:.: AST.All]
39 -- | Create an entity for a given function
41 HsFunction -- | The function signature
42 -> FuncData -- | The function data collected so far
43 -> Maybe Entity -- | The resulting entity. Should return the existing
44 --- Entity for builtin functions.
46 createEntity hsfunc fdata =
47 case fdata ^. fdFlatFunc of
48 -- Skip (builtin) functions without a FlatFunction
49 Nothing -> fdata ^. fdEntity
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'
66 Just $ Entity entity_id args' res' (Just ent_decl') pkg_decl
69 [(SignalId, SignalInfo)]
71 -> ([AST.TypeDec], Maybe (AST.VHDLId, AST.TypeMark))
73 if isPortSigUse $ sigUse info
75 let (decs, type_mark) = vhdl_ty ty in
76 (decs, Just (mkVHDLId nm, type_mark))
78 (Monoid.mempty, Nothing)
80 info = Maybe.fromMaybe
81 (error $ "Signal not found in the name map? This should not happen!")
84 (error $ "Signal not named? This should not happen!")
88 -- | Create the VHDL AST for an entity
90 HsFunction -- | The signature of the function we're working with
91 -> [VHDLSignalMap] -- | The entity's arguments
92 -> VHDLSignalMap -- | The entity's result
93 -> AST.EntityDec -- | The entity with the ent_decl filled in as well
95 createEntityAST hsfunc args res =
96 AST.EntityDec vhdl_id ports
98 vhdl_id = mkEntityId hsfunc
99 ports = concatMap (mapToPorts AST.In) args
100 ++ mapToPorts AST.Out res
102 mapToPorts :: AST.Mode -> VHDLSignalMap -> [AST.IfaceSigDec]
104 Maybe.catMaybes $ map (mkIfaceSigDec mode) (Foldable.toList m)
105 -- Add a clk port if we have state
106 clk_port = if hasState hsfunc
108 [AST.IfaceSigDec (mkVHDLId "clk") AST.In VHDL.std_logic_ty]
112 -- | Create a port declaration
114 AST.Mode -- | The mode for the port (In / Out)
115 -> Maybe (AST.VHDLId, AST.TypeMark) -- | The id and type for the port
116 -> Maybe AST.IfaceSigDec -- | The resulting port declaration
118 mkIfaceSigDec mode (Just (id, ty)) = Just $ AST.IfaceSigDec id mode ty
119 mkIfaceSigDec _ Nothing = Nothing
121 -- | Generate a VHDL entity name for the given hsfunc
123 -- TODO: This doesn't work for functions with multiple signatures!
124 mkVHDLId $ hsFuncName hsfunc
126 -- | Create an architecture for a given function
127 createArchitecture ::
128 FuncMap -- ^ The functions in the current session
129 -> HsFunction -- ^ The function signature
130 -> FuncData -- ^ The function data collected so far
131 -> Maybe AST.ArchBody -- ^ The architecture for this function
133 createArchitecture funcs hsfunc fdata =
134 case fdata ^. fdFlatFunc of
135 -- Skip (builtin) functions without a FlatFunction
136 Nothing -> fdata ^. fdArch
137 -- Create an architecture for all other functions
140 sigs = flat_sigs flatfunc
141 args = flat_args flatfunc
142 res = flat_res flatfunc
143 defs = flat_defs flatfunc
144 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 (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 = zipWith (mkConcSm funcs sigs) defs [0..]
154 procs = map mkStateProcSm (makeStatePairs flatfunc)
155 procs' = map AST.CSPSm procs
157 Just $ AST.ArchBody (mkVHDLId "structural") (AST.NSimple entity_id) (map AST.BDISD sig_decs) (statements ++ procs')
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 FuncMap -- ^ The functions in the current session
208 -> [(SignalId, SignalInfo)] -- ^ The signals in the current architecture
209 -> SigDef -- ^ The signal definition
210 -> Int -- ^ A number that will be unique for all
211 -- concurrent statements in the architecture.
212 -> AST.ConcSm -- ^ The corresponding VHDL component instantiation.
214 mkConcSm funcs sigs (FApp hsfunc args res) num =
216 fdata_maybe = Map.lookup hsfunc funcs
217 fdata = Maybe.fromMaybe
218 (error $ "Using function '" ++ (prettyShow hsfunc) ++ "' that is not in the session? This should not happen!")
220 entity = Maybe.fromMaybe
221 (error $ "Using function '" ++ (prettyShow hsfunc) ++ "' without entity declaration? This should not happen!")
223 entity_id = ent_id entity
224 label = (AST.fromVHDLId entity_id) ++ "_" ++ (show num)
225 -- Add a clk port if we have state
226 clk_port = Maybe.fromJust $ mkAssocElem (Just $ mkVHDLId "clk") "clk"
227 portmaps = mkAssocElems sigs args res entity ++ (if hasState hsfunc then [clk_port] else [])
229 AST.CSISm $ AST.CompInsSm (mkVHDLId label) (AST.IUEntity (AST.NSimple entity_id)) (AST.PMapAspect portmaps)
231 mkConcSm _ sigs (UncondDef src dst) _ =
233 src_expr = vhdl_expr src
234 src_wform = AST.Wform [AST.WformElem src_expr Nothing]
235 dst_name = AST.NSimple (getSignalId $ signalInfo sigs dst)
236 assign = dst_name AST.:<==: (AST.ConWforms [] src_wform Nothing)
240 vhdl_expr (Left id) = mkIdExpr sigs id
241 vhdl_expr (Right expr) =
244 (mkIdExpr sigs id) AST.:=: (AST.PrimLit lit)
248 (mkIdExpr sigs a) AST.:=: (mkIdExpr sigs b)
250 mkConcSm _ sigs (CondDef cond true false dst) _ =
252 cond_expr = mkIdExpr sigs cond
253 true_expr = mkIdExpr sigs true
254 false_expr = mkIdExpr sigs false
255 false_wform = AST.Wform [AST.WformElem false_expr Nothing]
256 true_wform = AST.Wform [AST.WformElem true_expr Nothing]
257 whenelse = AST.WhenElse true_wform cond_expr
258 dst_name = AST.NSimple (getSignalId $ signalInfo sigs dst)
259 assign = dst_name AST.:<==: (AST.ConWforms [whenelse] false_wform Nothing)
263 -- | Turn a SignalId into a VHDL Expr
264 mkIdExpr :: [(SignalId, SignalInfo)] -> SignalId -> AST.Expr
266 let src_name = AST.NSimple (getSignalId $ signalInfo sigs id) in
267 AST.PrimName src_name
270 [(SignalId, SignalInfo)] -- | The signals in the current architecture
271 -> [SignalMap] -- | The signals that are applied to function
272 -> SignalMap -- | the signals in which to store the function result
273 -> Entity -- | The entity to map against.
274 -> [AST.AssocElem] -- | The resulting port maps
276 mkAssocElems sigmap args res entity =
277 -- Create the actual AssocElems
278 Maybe.catMaybes $ zipWith mkAssocElem ports sigs
280 -- Turn the ports and signals from a map into a flat list. This works,
281 -- since the maps must have an identical form by definition. TODO: Check
283 arg_ports = concat (map Foldable.toList (ent_args entity))
284 res_ports = Foldable.toList (ent_res entity)
285 arg_sigs = (concat (map Foldable.toList args))
286 res_sigs = Foldable.toList res
287 -- Extract the id part from the (id, type) tuple
288 ports = (map (fmap fst) (arg_ports ++ res_ports))
289 -- Translate signal numbers into names
290 sigs = (map (lookupSigName sigmap) (arg_sigs ++ res_sigs))
292 -- | Look up a signal in the signal name map
293 lookupSigName :: [(SignalId, SignalInfo)] -> SignalId -> String
294 lookupSigName sigs sig = name
296 info = Maybe.fromMaybe
297 (error $ "Unknown signal " ++ (show sig) ++ " used? This should not happen!")
299 name = Maybe.fromMaybe
300 (error $ "Unnamed signal " ++ (show sig) ++ " used? This should not happen!")
303 -- | Create an VHDL port -> signal association
304 mkAssocElem :: Maybe AST.VHDLId -> String -> Maybe AST.AssocElem
305 mkAssocElem (Just port) signal = Just $ Just port AST.:=>: (AST.ADName (AST.NSimple (mkVHDLId signal)))
306 mkAssocElem Nothing _ = Nothing
308 -- | Extracts the generated entity id from the given funcdata
309 getEntityId :: FuncData -> Maybe AST.VHDLId
311 case fdata ^. fdEntity of
313 Just e -> case ent_decl e of
315 Just (AST.EntityDec id _) -> Just id
318 FuncData -- | A function from the session
319 -> [AST.LibraryUnit] -- | The entity, architecture and optional package for the function
321 getLibraryUnits fdata =
322 case fdata ^. fdEntity of
328 case fdata ^. fdArch of
331 [AST.LUEntity decl, AST.LUArch arch]
332 ++ (Maybe.maybeToList (fmap AST.LUPackageDec $ ent_pkg_decl ent))
334 -- | The VHDL Bit type
335 bit_ty :: AST.TypeMark
336 bit_ty = AST.unsafeVHDLBasicId "Bit"
338 -- | The VHDL Boolean type
339 bool_ty :: AST.TypeMark
340 bool_ty = AST.unsafeVHDLBasicId "Boolean"
342 -- | The VHDL std_logic
343 std_logic_ty :: AST.TypeMark
344 std_logic_ty = AST.unsafeVHDLBasicId "std_logic"
346 -- Translate a Haskell type to a VHDL type
347 vhdl_ty :: Type.Type -> ([AST.TypeDec], AST.TypeMark)
348 vhdl_ty ty = Maybe.fromMaybe
349 (error $ "Unsupported Haskell type: " ++ (showSDoc $ ppr ty))
352 -- Translate a Haskell type to a VHDL type, optionally generating a type
353 -- declaration for the type.
354 vhdl_ty_maybe :: Type.Type -> Maybe ([AST.TypeDec], AST.TypeMark)
356 if Type.coreEqType ty TysWiredIn.boolTy
360 case Type.splitTyConApp_maybe ty of
361 Just (tycon, args) ->
362 let name = TyCon.tyConName tycon in
363 -- TODO: Do something more robust than string matching
364 case Name.getOccString name of
365 "Bit" -> Just ([], std_logic_ty)
369 -- TODO: Find actual number
370 ty_id = mkVHDLId ("vector_" ++ (show len))
372 range = AST.IndexConstraint [AST.ToRange (AST.PrimLit "0") (AST.PrimLit "16")]
373 ty_def = AST.TDA $ AST.ConsArrayDef range std_logic_ty
374 ty_dec = AST.TypeDec ty_id ty_def
376 Just ([ty_dec], ty_id)
381 mkVHDLId :: String -> AST.VHDLId
383 AST.unsafeVHDLBasicId $ (strip_multiscore . strip_invalid) s
385 -- Strip invalid characters.
386 strip_invalid = filter (`elem` ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ "_.")
387 -- Strip multiple adjacent underscores
388 strip_multiscore = concat . map (\cs ->