sv2v/src/Convert/Interface.hs

472 lines
22 KiB
Haskell
Raw Normal View History

2019-03-07 02:30:47 +01:00
{- sv2v
- Author: Zachary Snow <zach@zachjs.com>
-
- Conversion for interfaces
-}
module Convert.Interface (convert) where
2020-06-20 20:39:57 +02:00
import Data.Maybe (mapMaybe)
2019-03-07 02:30:47 +01:00
import Control.Monad.Writer
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
2019-03-07 02:30:47 +01:00
import Convert.Traverse
import Language.SystemVerilog.AST
type Interface = ([Identifier], [ModuleItem])
type Interfaces = Map.Map Identifier Interface
2020-06-20 20:39:57 +02:00
type Module = ([Identifier], [(Identifier, Type)])
type Modules = Map.Map Identifier Module
type Instances = Map.Map Identifier Identifier
type Modports = Map.Map Identifier (Identifier, Identifier)
2019-03-07 02:30:47 +01:00
convert :: [AST] -> [AST]
convert =
2020-04-14 04:23:03 +02:00
map (filter $ not . isInterface) .
repeatedConverter
2019-03-07 02:30:47 +01:00
where
2020-04-14 04:23:03 +02:00
repeatedConverter :: [AST] -> [AST]
repeatedConverter files =
if files == files'
then files
else repeatedConverter files'
where
files' =
traverseFiles (collectDescriptionsM collectDesc)
(map . uncurry convertDescription)
files
-- we can only collect/map non-extern interfaces
collectDesc :: Description -> Writer (Interfaces, Modules) ()
collectDesc (orig @ (Part _ False kw _ name ports items)) = do
if kw == Interface then
if all fullyResolved items
then tell (Map.singleton name (ports, items), Map.empty)
else return ()
else tell (Map.empty, Map.singleton name (params, decls))
2019-10-21 04:06:10 +02:00
where
params = map fst $ parameters items
decls = execWriter $
collectModuleItemsM (collectDeclsM collectDecl) orig
collectDecl :: Decl -> Writer [(Identifier, Type)] ()
2019-10-21 04:06:10 +02:00
collectDecl (Variable _ t ident _ _) =
tell [(ident, t)]
2019-10-21 04:06:10 +02:00
collectDecl _ = return ()
2019-03-07 02:30:47 +01:00
collectDesc _ = return ()
isInterface :: Description -> Bool
isInterface (Part _ False Interface _ _ _ items) =
all fullyResolved items
2019-03-07 02:30:47 +01:00
isInterface _ = False
-- returns whether a ModuleItem still contains TypeOf
fullyResolved :: ModuleItem -> Bool
fullyResolved =
not . any isTypeOf . execWriter .
collectNestedModuleItemsM (collectTypesM collectType)
where
collectType :: Type -> Writer [Type] ()
collectType t = tell [t]
isTypeOf TypeOf{} = True
isTypeOf _ = False
2019-03-07 02:30:47 +01:00
convertDescription :: Interfaces -> Modules -> Description -> Description
convertDescription interfaces modules (Part attrs extern Module lifetime name ports items) =
Part attrs extern Module lifetime name ports' items'
2019-03-07 02:30:47 +01:00
where
items' =
map (traverseNestedModuleItems $ traverseExprs' ExcludeTFs (traverseNestedExprs $ convertExpr instances modports)) $
map (traverseNestedModuleItems $ traverseLHSs' ExcludeTFs (traverseNestedLHSs $ convertLHS instances modports)) $
map (traverseNestedModuleItems mapInterface) $
items
ports' = concatMap convertPort ports
2019-03-07 02:30:47 +01:00
-- collect the interface type of all interface instances in this module
(instances, modports) = execWriter $ mapM
2020-06-20 20:39:57 +02:00
(collectNestedModuleItemsM collectInstanceM) items
collectInstanceM :: ModuleItem -> Writer (Instances, Modports) ()
collectInstanceM (MIPackageItem (Decl (Variable _ t ident _ _))) =
case t of
InterfaceT interfaceName (Just modportName) [] ->
if Map.member interfaceName interfaces
2020-06-20 20:39:57 +02:00
then writeModport interfaceName modportName
else return ()
Alias Nothing interfaceName [] ->
2020-06-20 20:39:57 +02:00
if Map.member interfaceName interfaces
then writeModport interfaceName ""
else return ()
_ -> return ()
2020-06-20 20:39:57 +02:00
where
writeModport :: Identifier -> Identifier ->
Writer (Instances, Modports) ()
writeModport interfaceName modportName =
tell (Map.empty, Map.singleton ident modport)
where modport = (interfaceName, modportName)
collectInstanceM (Instance part _ ident [] _) =
2019-03-07 02:30:47 +01:00
if Map.member part interfaces
then tell (Map.singleton ident part, Map.empty)
2019-03-07 02:30:47 +01:00
else return ()
2020-06-20 20:39:57 +02:00
collectInstanceM _ = return ()
2019-03-07 02:30:47 +01:00
mapInterface :: ModuleItem -> ModuleItem
2020-06-20 20:39:57 +02:00
mapInterface (orig @ (MIPackageItem (Decl (Variable _ _ ident _ _)))) =
2019-10-21 04:06:10 +02:00
-- expand instantiation of a modport
2020-06-20 20:39:57 +02:00
if Map.member ident modports
then Generate $ map GenModuleItem $
filter shouldKeep interfaceItems ++ map makePortDecl
2020-06-20 20:39:57 +02:00
modportDecls
else orig
where
2020-06-20 20:39:57 +02:00
Just (interfaceName, modportName) = Map.lookup ident modports
interfaceItems = prefixInterface ident $
2020-06-20 20:39:57 +02:00
snd $ lookupInterface interfaceName
modportDecls = lookupModport interfaceItems modportName
shouldKeep (MIPackageItem (Decl Param{})) = True
shouldKeep (MIPackageItem Task{}) = True
shouldKeep (MIPackageItem Function{}) = True
shouldKeep _ = False
makePortDecl :: ModportDecl -> ModuleItem
makePortDecl (dir, port, typ, _) =
2020-06-20 20:39:57 +02:00
MIPackageItem $ Decl $ Variable dir typ port' [] Nil
where port' = if null modportName
then port
else ident ++ '_' : port
2020-06-18 04:01:59 +02:00
mapInterface (Instance part params ident [] instancePorts) =
2019-10-21 04:06:10 +02:00
-- expand modport port bindings
2019-03-07 02:30:47 +01:00
case Map.lookup part interfaces of
Just interface ->
2019-10-21 04:06:10 +02:00
-- inline instantiation of an interface
Generate $ map GenModuleItem $
inlineInterface interface (ident, params, instancePorts)
Nothing ->
if Map.member part modules
2020-06-18 04:01:59 +02:00
then Instance part params' ident [] expandedPorts
else Instance part params ident [] instancePorts
where
expandedBindings = map (uncurry $ expandPortBinding part) (zip instancePorts [0..])
expandedPorts = concatMap snd expandedBindings
Just (moduleParamNames, _) = Map.lookup part modules
addedParams = concatMap fst expandedBindings
paramsNamed = resolveParams moduleParamNames params
params' =
if null addedParams
then params
else paramsNamed ++ addedParams
mapInterface (orig @ (MIPackageItem (Function _ _ _ decls _))) =
convertTF decls orig
mapInterface (orig @ (MIPackageItem (Task _ _ decls _))) =
convertTF decls orig
mapInterface other = other
2019-03-07 02:30:47 +01:00
convertTF :: [Decl] -> ModuleItem -> ModuleItem
2019-10-21 04:06:10 +02:00
convertTF decls =
traverseExprs (traverseNestedExprs $ convertExpr its mps) .
traverseLHSs (traverseNestedLHSs $ convertLHS its mps)
where
2020-06-14 21:56:09 +02:00
locals = Set.fromList $ map declVarIdent decls
its = Map.withoutKeys instances locals
mps = Map.withoutKeys modports locals
2020-06-14 21:56:09 +02:00
declVarIdent :: Decl -> Identifier
declVarIdent (Variable _ _ x _ _) = x
declVarIdent _ = ""
expandPortBinding :: Identifier -> PortBinding -> Int -> ([ParamBinding], [PortBinding])
expandPortBinding moduleName ("", binding) idx =
case Map.lookup moduleName modules of
Nothing -> error $ "could not find module: " ++ moduleName
Just (_, decls) ->
if idx < length decls
then expandPortBinding moduleName
(fst $ decls !! idx, binding) idx
else error $ "could not infer port for "
++ show binding ++ " in module " ++ show moduleName
2020-06-14 21:56:09 +02:00
expandPortBinding _ (origBinding @ (portName, Dot (Ident instanceName) modportName)) _ =
2019-10-21 04:06:10 +02:00
-- expand instance modport bound to a modport
2020-06-20 20:39:57 +02:00
if Map.member instanceName instances
then expandPortBinding' interfaceName portName instanceName
2020-06-20 20:39:57 +02:00
modportDecls
else ([], [origBinding])
2019-10-21 04:06:10 +02:00
where
interfaceName = instances Map.! instanceName
2020-06-20 20:39:57 +02:00
interfaceItems = snd $ lookupInterface interfaceName
modportDecls = lookupModport interfaceItems modportName
expandPortBinding moduleName (origBinding @ (portName, Ident ident)) _ =
2019-10-21 04:06:10 +02:00
case (instances Map.!? ident, modports Map.!? ident) of
(Nothing, Nothing) -> ([], [origBinding])
(Just interfaceName, _) ->
2019-10-21 04:06:10 +02:00
-- given entire interface, but just bound to a modport
if Map.notMember moduleName modules then
error $ "could not find module " ++ show moduleName
else
expandPortBinding' interfaceName portName ident
2020-06-20 20:39:57 +02:00
modportDecls
where
Just (_, decls) = Map.lookup moduleName modules
portType =
case lookup portName decls of
Nothing -> error $ "could not find port "
++ show portName ++ " in module "
++ show moduleName
Just t -> t
2020-06-20 20:39:57 +02:00
interfaceItems = snd $ lookupInterface interfaceName
modportDecls = lookupModport interfaceItems modportName
modportName = case portType of
InterfaceT _ (Just x) [] -> x
Alias Nothing _ [] -> ""
_ -> error $ "can't deduce modport for interface "
++ interfaceName ++ " bound to port "
++ portName ++ " of module " ++ moduleName
(_, Just (interfaceName, modportName)) ->
2019-10-21 04:06:10 +02:00
-- modport directly bound to a modport
expandPortBinding' interfaceName portName ident
(map redirect modportDecls)
2020-06-20 20:39:57 +02:00
where
interfaceItems = snd $ lookupInterface interfaceName
modportDecls = lookupModport interfaceItems modportName
redirect (d, x, t, _) = (d, x, t, Ident x)
expandPortBinding _ other _ = ([], [other])
2019-03-07 02:30:47 +01:00
expandPortBinding' :: Identifier -> Identifier -> Identifier ->
[ModportDecl] -> ([ParamBinding], [PortBinding])
expandPortBinding' interfaceName portName instanceName modportDecls =
(paramBindings, portBindings)
where
paramBindings = map toParamBinding interfaceParamNames
2020-06-20 20:39:57 +02:00
interfaceItems = snd $ lookupInterface interfaceName
interfaceParamNames = map fst $ parameters interfaceItems
toParamBinding x = (portName ++ '_' : x, Right $ Ident $ instanceName ++ '_' : x)
portBindings = map toPortBinding modportDecls
toPortBinding (_, x, _, e) = (x', e')
where
x' = portName ++ '_' : x
2020-06-14 21:56:09 +02:00
e' = traverseNestedExprs prefixExpr e
prefixExpr :: Expr -> Expr
prefixExpr (Ident x) = Ident (instanceName ++ '_' : x)
prefixExpr other = other
2020-06-20 20:39:57 +02:00
lookupInterface :: Identifier -> Interface
lookupInterface interfaceName =
case Map.lookup interfaceName interfaces of
Just res -> res
Nothing -> error $ "could not find interface " ++ show interfaceName
lookupModport :: [ModuleItem] -> Identifier -> [ModportDecl]
lookupModport interfaceItems "" = impliedModport interfaceItems
lookupModport interfaceItems modportName =
case Map.lookup modportName modportMap of
Just modportDecls -> modportDecls
Nothing -> error $ "could not find modport " ++ show modportName
2019-03-07 02:30:47 +01:00
where
modportMap = execWriter $
mapM (collectNestedModuleItemsM collectModport) $
interfaceItems
collectModport :: ModuleItem -> Writer (Map.Map Identifier [ModportDecl]) ()
collectModport (Modport ident l) = tell $ Map.singleton ident l
2019-03-07 02:30:47 +01:00
collectModport _ = return ()
2020-06-20 20:39:57 +02:00
impliedModport :: [ModuleItem] -> [ModportDecl]
impliedModport =
execWriter . mapM (collectNestedModuleItemsM collectModportDecls)
where
collectModportDecls :: ModuleItem -> Writer [ModportDecl] ()
collectModportDecls (MIPackageItem (Decl (Variable d t x _ _))) =
tell [(d', x, t, Ident x)]
where d' = if d == Local then Inout else d
collectModportDecls _ = return ()
convertExpr :: Instances -> Modports -> Expr -> Expr
convertExpr its mps (orig @ (Dot (Ident x) y)) =
if Map.member x mps || Map.member x its
then Ident (x ++ "_" ++ y)
else orig
convertExpr _ _ other = other
convertLHS :: Instances -> Modports -> LHS -> LHS
convertLHS its mps (orig @ (LHSDot (LHSIdent x) y)) =
if Map.member x mps || Map.member x its
then LHSIdent (x ++ "_" ++ y)
else orig
convertLHS _ _ other = other
convertPort :: Identifier -> [Identifier]
convertPort ident =
case Map.lookup ident modports of
Nothing -> [ident]
2020-06-20 20:39:57 +02:00
Just (interfaceName, modportName) ->
map (\(_, x, _, _) ->
ident ++ "_" ++ x) modportDecls
where
interfaceItems = snd $ lookupInterface interfaceName
modportDecls = lookupModport interfaceItems modportName
convertDescription _ _ other = other
2019-03-07 02:30:47 +01:00
-- add a prefix to all standard identifiers in a module item
2020-02-10 03:57:09 +01:00
prefixModuleItems :: (Identifier -> Identifier) -> ModuleItem -> ModuleItem
2019-03-07 02:30:47 +01:00
prefixModuleItems prefix =
2020-02-10 03:57:09 +01:00
prefixOtherItem .
2019-03-07 02:30:47 +01:00
traverseDecls prefixDecl .
traverseExprs (traverseNestedExprs prefixExpr) .
traverseLHSs (traverseNestedLHSs prefixLHS )
2019-03-07 02:30:47 +01:00
where
prefixDecl :: Decl -> Decl
2020-06-14 21:56:09 +02:00
prefixDecl (Variable d t x a e) = Variable d t (prefix x) a e
prefixDecl (Param s t x e) = Param s t (prefix x) e
prefixDecl (ParamType s x mt) = ParamType s (prefix x) mt
prefixDecl (CommentDecl c) = CommentDecl c
2019-03-07 02:30:47 +01:00
prefixExpr :: Expr -> Expr
2020-02-10 03:57:09 +01:00
prefixExpr (Ident x) = Ident (prefix x)
2019-03-07 02:30:47 +01:00
prefixExpr other = other
prefixLHS :: LHS -> LHS
2020-02-10 03:57:09 +01:00
prefixLHS (LHSIdent x) = LHSIdent (prefix x)
2019-03-07 02:30:47 +01:00
prefixLHS other = other
2020-04-14 04:23:03 +02:00
prefixOtherItem :: ModuleItem -> ModuleItem
2020-02-10 03:57:09 +01:00
prefixOtherItem (MIPackageItem item) =
MIPackageItem $ prefixPackageItem prefix item
2020-04-14 04:23:03 +02:00
prefixOtherItem (Instance m params name rs ports) =
Instance m params (prefix name) rs ports
2020-02-10 03:57:09 +01:00
prefixOtherItem (Genvar x) = Genvar $ prefix x
prefixOtherItem other = other
-- add a prefix to all standard identifiers in a package item
2020-02-10 03:57:09 +01:00
prefixPackageItem :: (Identifier -> Identifier) -> PackageItem -> PackageItem
prefixPackageItem prefix (Function lifetime t x decls stmts) =
Function lifetime t x' decls stmts
2020-02-10 03:57:09 +01:00
where x' = prefix x
prefixPackageItem prefix (Task lifetime x decls stmts) =
Task lifetime x' decls stmts
2020-02-10 03:57:09 +01:00
where x' = prefix x
prefixPackageItem _ other = other
2019-03-07 02:30:47 +01:00
2020-02-10 03:57:09 +01:00
-- collect all identifiers defined within a module item
collectIdentsM :: ModuleItem -> Writer (Set.Set Identifier) ()
collectIdentsM (MIPackageItem (Function _ _ x _ _)) = tell $ Set.singleton x
collectIdentsM (MIPackageItem (Task _ x _ _)) = tell $ Set.singleton x
2020-04-14 04:23:03 +02:00
collectIdentsM (Instance _ _ x _ _) = tell $ Set.singleton x
2020-02-10 03:57:09 +01:00
collectIdentsM (Genvar x) = tell $ Set.singleton x
collectIdentsM item = collectDeclsM collectDecl item
where
collectDecl :: Decl -> Writer (Set.Set Identifier) ()
collectDecl (Variable _ _ x _ _) = tell $ Set.singleton x
collectDecl (Param _ _ x _) = tell $ Set.singleton x
collectDecl (ParamType _ x _) = tell $ Set.singleton x
collectDecl (CommentDecl _) = return ()
2019-03-07 02:30:47 +01:00
-- convert an interface instantiation into a series of equivalent module items
inlineInterface :: Interface -> (Identifier, [ParamBinding], [PortBinding]) -> [ModuleItem]
inlineInterface (ports, items) (instanceName, instanceParams, instancePorts) =
(:) comment $
flip (++) portBindings $
2019-03-07 02:30:47 +01:00
map (traverseNestedModuleItems removeModport) $
2019-04-23 23:12:56 +02:00
map (traverseNestedModuleItems removeDeclDir) $
itemsPrefixed
2019-03-07 02:30:47 +01:00
where
comment = MIPackageItem $ Decl $ CommentDecl $
"expanded instance: " ++ instanceName
2019-03-07 02:30:47 +01:00
prefix = instanceName ++ "_"
itemsPrefixed =
map (traverseDecls overrideParam) $
prefixInterface instanceName items
2019-03-07 02:30:47 +01:00
origInstancePortNames = map fst instancePorts
instancePortExprs = map snd instancePorts
instancePortNames =
map (prefix ++) $
if all ("" ==) origInstancePortNames
then ports
else origInstancePortNames
portBindings =
mapMaybe portBindingItem $
2019-03-07 02:30:47 +01:00
zip instancePortNames instancePortExprs
2019-04-23 23:12:56 +02:00
removeDeclDir :: ModuleItem -> ModuleItem
2020-06-14 21:56:09 +02:00
removeDeclDir (MIPackageItem (Decl (Variable _ t x a e))) =
MIPackageItem $ Decl $ Variable Local t' x a e
where t' = case t of
Implicit Unspecified rs ->
IntegerVector TLogic Unspecified rs
_ -> t
2019-04-23 23:12:56 +02:00
removeDeclDir other = other
2019-03-07 02:30:47 +01:00
removeModport :: ModuleItem -> ModuleItem
removeModport (Modport x _) =
MIPackageItem $ Decl $ CommentDecl $ "removed modport " ++ x
2019-03-07 02:30:47 +01:00
removeModport other = other
interfaceParamNames = map fst $ parameters items
instanceParamMap = Map.mapKeys (prefix ++) $
Map.fromList $ resolveParams interfaceParamNames instanceParams
overrideParam :: Decl -> Decl
overrideParam (Param Parameter t x e) =
case Map.lookup x instanceParamMap of
Nothing -> Param Parameter t x e
Just (Right e') -> Param Parameter t x e'
Just (Left t') ->
error $ "interface param expected expression, found type: "
++ show t'
overrideParam (ParamType Parameter x mt) =
case Map.lookup x instanceParamMap of
Nothing -> ParamType Parameter x mt
Just (Left t') -> ParamType Parameter x (Just t')
Just (Right e') ->
error $ "interface param expected type, found expression: "
++ show e'
overrideParam other = other
portBindingItem :: PortBinding -> Maybe ModuleItem
2020-06-14 21:56:09 +02:00
portBindingItem (_, Nil) = Nothing
portBindingItem (ident, expr) =
Just $ if declDirs Map.! ident == Input
2020-03-21 02:13:57 +01:00
then Assign AssignOptionNone (LHSIdent ident) expr
else Assign AssignOptionNone (toLHS expr) (Ident ident)
declDirs = execWriter $
mapM (collectDeclsM collectDeclDir) itemsPrefixed
collectDeclDir :: Decl -> Writer (Map.Map Identifier Direction) ()
collectDeclDir (Variable dir _ ident _ _) =
if dir /= Local
then tell $ Map.singleton ident dir
else return ()
collectDeclDir _ = return ()
toLHS :: Expr -> LHS
toLHS expr =
case exprToLHS expr of
Just lhs -> lhs
Nothing -> error $ "trying to bind an interface output to " ++
show expr ++ " but that can't be an LHS"
-- convert an interface instantiation into a series of equivalent module items
prefixInterface :: Identifier -> [ModuleItem] -> [ModuleItem]
prefixInterface instanceName items =
map prefixItem items
where
prefix = instanceName ++ "_"
idents = execWriter $
mapM (collectNestedModuleItemsM collectIdentsM) items
prefixIfNecessary :: Identifier -> Identifier
prefixIfNecessary x =
if Set.member x idents
then prefix ++ x
else x
prefixItem = traverseNestedModuleItems $
prefixModuleItems prefixIfNecessary
-- give a set of param bindings explicit names
resolveParams :: [Identifier] -> [ParamBinding] -> [ParamBinding]
resolveParams available bindings =
map (uncurry resolveParam) $ zip bindings [0..]
where
resolveParam :: ParamBinding -> Int -> ParamBinding
resolveParam ("", e) idx =
if idx < length available
then (available !! idx, e)
else error $ "interface param binding " ++ (show e)
++ " is out of range"
resolveParam other _ = other
-- given a list of module items, produces the parameters in order
parameters :: [ModuleItem] -> [(Identifier, Decl)]
parameters =
execWriter . mapM (collectNestedModuleItemsM $ collectDeclsM collectDeclM)
where
collectDeclM :: Decl -> Writer [(Identifier, Decl)] ()
collectDeclM (decl @ (Param Parameter _ x _)) = tell [(x, decl)]
collectDeclM (decl @ (ParamType Parameter x _)) = tell [(x, decl)]
collectDeclM _ = return ()