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 Control.Monad.Trans.State as State
13 import qualified Data.Traversable as Traversable
14 import qualified Data.Monoid as Monoid
18 import qualified TysWiredIn
20 import qualified TyCon
21 import Outputable ( showSDoc, ppr )
23 import qualified ForSyDe.Backend.VHDL.AST as AST
28 import TranslatorTypes
33 -> [(AST.VHDLId, AST.DesignFile)]
35 createDesignFiles flatfuncmap =
37 map (Arrow.second $ AST.DesignFile context) units
39 init_session = VHDLSession Map.empty Map.empty
40 (units, final_session) =
41 State.runState (createLibraryUnits flatfuncmap) init_session
43 AST.Library $ mkVHDLId "IEEE",
44 AST.Use $ (AST.NSimple $ mkVHDLId "IEEE.std_logic_1164") AST.:.: AST.All]
48 -> VHDLState [(AST.VHDLId, [AST.LibraryUnit])]
50 createLibraryUnits flatfuncmap = do
51 let hsfuncs = Map.keys flatfuncmap
52 let flatfuncs = Map.elems flatfuncmap
53 entities <- Monad.zipWithM createEntity hsfuncs flatfuncs
54 archs <- Monad.zipWithM createArchitecture hsfuncs flatfuncs
57 let AST.EntityDec id _ = ent in
58 (id, [AST.LUEntity ent, AST.LUArch arch])
62 -- | Create an entity for a given function
64 HsFunction -- | The function signature
65 -> FlatFunction -- | The FlatFunction
66 -> VHDLState AST.EntityDec -- | The resulting entity
68 createEntity hsfunc flatfunc =
70 sigs = flat_sigs flatfunc
71 args = flat_args flatfunc
72 res = flat_res flatfunc
73 (ty_decls, args') = Traversable.traverse (Traversable.traverse (mkMap sigs)) args
74 (ty_decls', res') = Traversable.traverse (mkMap sigs) res
75 -- TODO: Unique ty_decls
76 ent_decl' = createEntityAST hsfunc args' res'
77 pkg_id = mkVHDLId $ (AST.fromVHDLId entity_id) ++ "_types"
78 pkg_decl = if null ty_decls && null ty_decls'
80 else Just $ AST.PackageDec pkg_id (map AST.PDITD $ ty_decls ++ ty_decls')
81 -- TODO: Output package
82 AST.EntityDec entity_id _ = ent_decl'
83 signature = Entity entity_id args' res'
85 modA vsSignatures (Map.insert hsfunc signature)
89 [(SignalId, SignalInfo)]
91 -> ([AST.TypeDec], Maybe (AST.VHDLId, AST.TypeMark))
93 if isPortSigUse $ sigUse info
95 let (decs, type_mark) = vhdl_ty ty in
96 (decs, Just (mkVHDLId nm, type_mark))
98 (Monoid.mempty, Nothing)
100 info = Maybe.fromMaybe
101 (error $ "Signal not found in the name map? This should not happen!")
104 (error $ "Signal not named? This should not happen!")
108 -- | Create the VHDL AST for an entity
110 HsFunction -- | The signature of the function we're working with
111 -> [VHDLSignalMap] -- | The entity's arguments
112 -> VHDLSignalMap -- | The entity's result
113 -> AST.EntityDec -- | The entity with the ent_decl filled in as well
115 createEntityAST hsfunc args res =
116 AST.EntityDec vhdl_id ports
118 vhdl_id = mkEntityId hsfunc
119 ports = concatMap (mapToPorts AST.In) args
120 ++ mapToPorts AST.Out res
122 mapToPorts :: AST.Mode -> VHDLSignalMap -> [AST.IfaceSigDec]
124 Maybe.catMaybes $ map (mkIfaceSigDec mode) (Foldable.toList m)
125 -- Add a clk port if we have state
126 clk_port = if hasState hsfunc
128 [AST.IfaceSigDec (mkVHDLId "clk") AST.In VHDL.std_logic_ty]
132 -- | Create a port declaration
134 AST.Mode -- | The mode for the port (In / Out)
135 -> Maybe (AST.VHDLId, AST.TypeMark) -- | The id and type for the port
136 -> Maybe AST.IfaceSigDec -- | The resulting port declaration
138 mkIfaceSigDec mode (Just (id, ty)) = Just $ AST.IfaceSigDec id mode ty
139 mkIfaceSigDec _ Nothing = Nothing
141 -- | Generate a VHDL entity name for the given hsfunc
143 -- TODO: This doesn't work for functions with multiple signatures!
144 mkVHDLId $ hsFuncName hsfunc
146 -- | Create an architecture for a given function
147 createArchitecture ::
148 HsFunction -- ^ The function signature
149 -> FlatFunction -- ^ The FlatFunction
150 -> VHDLState AST.ArchBody -- ^ The architecture for this function
152 createArchitecture hsfunc flatfunc = do
153 signaturemap <- getA vsSignatures
154 let signature = Maybe.fromMaybe
155 (error $ "Generating architecture for function " ++ (prettyShow hsfunc) ++ "without signature? This should not happen!")
156 (Map.lookup hsfunc signaturemap)
157 let entity_id = ent_id signature
158 -- Create concurrent statements for all signal definitions
159 let statements = zipWith (mkConcSm signaturemap sigs) defs [0..]
160 return $ AST.ArchBody (mkVHDLId "structural") (AST.NSimple entity_id) (map AST.BDISD sig_decs) (statements ++ procs')
162 sigs = flat_sigs flatfunc
163 args = flat_args flatfunc
164 res = flat_res flatfunc
165 defs = flat_defs flatfunc
166 -- Create signal declarations for all internal and state signals
167 (ty_decls, sig_decs) = Arrow.second Maybe.catMaybes $ Traversable.traverse (mkSigDec . snd) sigs
168 -- TODO: Unique ty_decls
169 -- TODO: Store ty_decls somewhere
170 procs = map mkStateProcSm (makeStatePairs flatfunc)
171 procs' = map AST.CSPSm procs
173 -- | Looks up all pairs of old state, new state signals, together with
174 -- the state id they represent.
175 makeStatePairs :: FlatFunction -> [(StateId, SignalInfo, SignalInfo)]
176 makeStatePairs flatfunc =
177 [(Maybe.fromJust $ oldStateId $ sigUse old_info, old_info, new_info)
178 | old_info <- map snd (flat_sigs flatfunc)
179 , new_info <- map snd (flat_sigs flatfunc)
180 -- old_info must be an old state (and, because of the next equality,
181 -- new_info must be a new state).
182 , Maybe.isJust $ oldStateId $ sigUse old_info
183 -- And the state numbers must match
184 , (oldStateId $ sigUse old_info) == (newStateId $ sigUse new_info)]
186 -- Replace the second tuple element with the corresponding SignalInfo
187 --args_states = map (Arrow.second $ signalInfo sigs) args
188 mkStateProcSm :: (StateId, SignalInfo, SignalInfo) -> AST.ProcSm
189 mkStateProcSm (num, old, new) =
190 AST.ProcSm label [clk] [statement]
192 label = mkVHDLId $ "state_" ++ (show num)
194 rising_edge = AST.NSimple $ mkVHDLId "rising_edge"
195 wform = AST.Wform [AST.WformElem (AST.PrimName $ AST.NSimple $ getSignalId new) Nothing]
196 assign = AST.SigAssign (AST.NSimple $ getSignalId old) wform
197 rising_edge_clk = AST.PrimFCall $ AST.FCall rising_edge [Nothing AST.:=>: (AST.ADName $ AST.NSimple clk)]
198 statement = AST.IfSm rising_edge_clk [assign] [] Nothing
200 mkSigDec :: SignalInfo -> ([AST.TypeDec], Maybe AST.SigDec)
202 let use = sigUse info in
203 if isInternalSigUse use || isStateSigUse use then
204 let (ty_decls, type_mark) = vhdl_ty ty in
205 (ty_decls, Just $ AST.SigDec (getSignalId info) type_mark Nothing)
211 -- | Creates a VHDL Id from a named SignalInfo. Errors out if the SignalInfo
213 getSignalId :: SignalInfo -> AST.VHDLId
215 mkVHDLId $ Maybe.fromMaybe
216 (error $ "Unnamed signal? This should not happen!")
219 -- | Transforms a signal definition into a VHDL concurrent statement
221 SignatureMap -- ^ The interfaces of functions in the session
222 -> [(SignalId, SignalInfo)] -- ^ The signals in the current architecture
223 -> SigDef -- ^ The signal definition
224 -> Int -- ^ A number that will be unique for all
225 -- concurrent statements in the architecture.
226 -> AST.ConcSm -- ^ The corresponding VHDL component instantiation.
228 mkConcSm signatures sigs (FApp hsfunc args res) num =
230 signature = Maybe.fromMaybe
231 (error $ "Using function '" ++ (prettyShow hsfunc) ++ "' without signature? This should not happen!")
232 (Map.lookup hsfunc signatures)
233 entity_id = ent_id signature
234 label = (AST.fromVHDLId entity_id) ++ "_" ++ (show num)
235 -- Add a clk port if we have state
236 clk_port = Maybe.fromJust $ mkAssocElem (Just $ mkVHDLId "clk") "clk"
237 portmaps = mkAssocElems sigs args res signature ++ (if hasState hsfunc then [clk_port] else [])
239 AST.CSISm $ AST.CompInsSm (mkVHDLId label) (AST.IUEntity (AST.NSimple entity_id)) (AST.PMapAspect portmaps)
241 mkConcSm _ sigs (UncondDef src dst) _ =
243 src_expr = vhdl_expr src
244 src_wform = AST.Wform [AST.WformElem src_expr Nothing]
245 dst_name = AST.NSimple (getSignalId $ signalInfo sigs dst)
246 assign = dst_name AST.:<==: (AST.ConWforms [] src_wform Nothing)
250 vhdl_expr (Left id) = mkIdExpr sigs id
251 vhdl_expr (Right expr) =
254 (mkIdExpr sigs id) AST.:=: (AST.PrimLit lit)
258 (mkIdExpr sigs a) AST.:=: (mkIdExpr sigs b)
260 mkConcSm _ sigs (CondDef cond true false dst) _ =
262 cond_expr = mkIdExpr sigs cond
263 true_expr = mkIdExpr sigs true
264 false_expr = mkIdExpr sigs false
265 false_wform = AST.Wform [AST.WformElem false_expr Nothing]
266 true_wform = AST.Wform [AST.WformElem true_expr Nothing]
267 whenelse = AST.WhenElse true_wform cond_expr
268 dst_name = AST.NSimple (getSignalId $ signalInfo sigs dst)
269 assign = dst_name AST.:<==: (AST.ConWforms [whenelse] false_wform Nothing)
273 -- | Turn a SignalId into a VHDL Expr
274 mkIdExpr :: [(SignalId, SignalInfo)] -> SignalId -> AST.Expr
276 let src_name = AST.NSimple (getSignalId $ signalInfo sigs id) in
277 AST.PrimName src_name
280 [(SignalId, SignalInfo)] -- | The signals in the current architecture
281 -> [SignalMap] -- | The signals that are applied to function
282 -> SignalMap -- | the signals in which to store the function result
283 -> Entity -- | The entity to map against.
284 -> [AST.AssocElem] -- | The resulting port maps
286 mkAssocElems sigmap args res entity =
287 -- Create the actual AssocElems
288 Maybe.catMaybes $ zipWith mkAssocElem ports sigs
290 -- Turn the ports and signals from a map into a flat list. This works,
291 -- since the maps must have an identical form by definition. TODO: Check
293 arg_ports = concat (map Foldable.toList (ent_args entity))
294 res_ports = Foldable.toList (ent_res entity)
295 arg_sigs = (concat (map Foldable.toList args))
296 res_sigs = Foldable.toList res
297 -- Extract the id part from the (id, type) tuple
298 ports = (map (fmap fst) (arg_ports ++ res_ports))
299 -- Translate signal numbers into names
300 sigs = (map (lookupSigName sigmap) (arg_sigs ++ res_sigs))
302 -- | Look up a signal in the signal name map
303 lookupSigName :: [(SignalId, SignalInfo)] -> SignalId -> String
304 lookupSigName sigs sig = name
306 info = Maybe.fromMaybe
307 (error $ "Unknown signal " ++ (show sig) ++ " used? This should not happen!")
309 name = Maybe.fromMaybe
310 (error $ "Unnamed signal " ++ (show sig) ++ " used? This should not happen!")
313 -- | Create an VHDL port -> signal association
314 mkAssocElem :: Maybe AST.VHDLId -> String -> Maybe AST.AssocElem
315 mkAssocElem (Just port) signal = Just $ Just port AST.:=>: (AST.ADName (AST.NSimple (mkVHDLId signal)))
316 mkAssocElem Nothing _ = Nothing
318 -- | The VHDL Bit type
319 bit_ty :: AST.TypeMark
320 bit_ty = AST.unsafeVHDLBasicId "Bit"
322 -- | The VHDL Boolean type
323 bool_ty :: AST.TypeMark
324 bool_ty = AST.unsafeVHDLBasicId "Boolean"
326 -- | The VHDL std_logic
327 std_logic_ty :: AST.TypeMark
328 std_logic_ty = AST.unsafeVHDLBasicId "std_logic"
330 -- Translate a Haskell type to a VHDL type
331 vhdl_ty :: Type.Type -> ([AST.TypeDec], AST.TypeMark)
332 vhdl_ty ty = Maybe.fromMaybe
333 (error $ "Unsupported Haskell type: " ++ (showSDoc $ ppr ty))
336 -- Translate a Haskell type to a VHDL type, optionally generating a type
337 -- declaration for the type.
338 vhdl_ty_maybe :: Type.Type -> Maybe ([AST.TypeDec], AST.TypeMark)
340 if Type.coreEqType ty TysWiredIn.boolTy
344 case Type.splitTyConApp_maybe ty of
345 Just (tycon, args) ->
346 let name = TyCon.tyConName tycon in
347 -- TODO: Do something more robust than string matching
348 case Name.getOccString name of
349 "Bit" -> Just ([], std_logic_ty)
353 -- TODO: Find actual number
354 ty_id = mkVHDLId ("vector_" ++ (show len))
356 range = AST.IndexConstraint [AST.ToRange (AST.PrimLit "0") (AST.PrimLit "16")]
357 ty_def = AST.TDA $ AST.ConsArrayDef range std_logic_ty
358 ty_dec = AST.TypeDec ty_id ty_def
360 Just ([ty_dec], ty_id)
365 mkVHDLId :: String -> AST.VHDLId
367 AST.unsafeVHDLBasicId $ (strip_multiscore . strip_invalid) s
369 -- Strip invalid characters.
370 strip_invalid = filter (`elem` ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ "_.")
371 -- Strip multiple adjacent underscores
372 strip_multiscore = concat . map (\cs ->