package conversion overhaul

- full import and export support
- simplify AST representation of import and export
- allow package-scoped identifiers invoked as subroutines
- use scoped name resolution for identifiers in packages
- merge package item nesting conversion into package conversion
- fix handling of colliding enum items in separate modules
- fix visiting enum item exprs in types
This commit is contained in:
Zachary Snow 2021-01-23 16:50:06 -07:00
parent 40df902887
commit 8eb3a251f7
53 changed files with 1213 additions and 383 deletions

View File

@ -102,8 +102,9 @@ Other:
## Supported Features ## Supported Features
sv2v supports most synthesizable SystemVerilog features. Current notable sv2v supports most synthesizable SystemVerilog features. Current notable
exceptions include `export` and interface arrays. Assertions are also supported, exceptions include `defparam` on interface instances and references to typedefs
but are simply dropped during conversion. within interface instances. Assertions are also supported, but are simply
dropped during conversion.
If you find a bug or have a feature request, please create an issue. Preference If you find a bug or have a feature request, please create an issue. Preference
will be given to issues which include examples or test cases. will be given to issues which include examples or test cases.

View File

@ -31,7 +31,6 @@ import qualified Convert.Logic
import qualified Convert.LogOp import qualified Convert.LogOp
import qualified Convert.MultiplePacked import qualified Convert.MultiplePacked
import qualified Convert.NamedBlock import qualified Convert.NamedBlock
import qualified Convert.NestPI
import qualified Convert.Package import qualified Convert.Package
import qualified Convert.ParamNoDefault import qualified Convert.ParamNoDefault
import qualified Convert.ParamType import qualified Convert.ParamType
@ -86,9 +85,7 @@ phases excludes =
, Convert.Unsigned.convert , Convert.Unsigned.convert
, Convert.SignCast.convert , Convert.SignCast.convert
, Convert.Wildcard.convert , Convert.Wildcard.convert
, Convert.Package.convert
, Convert.Enum.convert , Convert.Enum.convert
, Convert.NestPI.convert
, Convert.ForDecl.convert , Convert.ForDecl.convert
, Convert.Jump.convert , Convert.Jump.convert
, Convert.Foreach.convert , Convert.Foreach.convert
@ -111,7 +108,7 @@ run excludes = foldr (.) id $ phases excludes
convert :: [Job.Exclude] -> Phase convert :: [Job.Exclude] -> Phase
convert excludes = convert excludes =
convert' convert'
. Convert.NestPI.reorder . Convert.Package.convert
. Convert.ParamNoDefault.convert . Convert.ParamNoDefault.convert
where where
convert' :: Phase convert' :: Phase

View File

@ -3,10 +3,11 @@
- -
- Conversion for `enum` - Conversion for `enum`
- -
- This conversion replaces the enum items with localparams declared at the - This conversion replaces the enum items with localparams. The localparams are
- global scope. We leave it to the package item nesting conversion to determine - explicitly sized to match the size of the converted enum type. For packages
- where the generated localparams are needed. The localparams are explicitly - and enums used in the global scope, these localparams are inserted in place.
- sized to match the size of the converted enum type. - For enums used within a module or interface, the localparams are injected as
- needed using a nesting procedure from the package conversion.
- -
- SystemVerilog allows for enums to have any number of the items' values - SystemVerilog allows for enums to have any number of the items' values
- specified or unspecified. If the first one is unspecified, it is 0. All other - specified or unspecified. If the first one is unspecified, it is 0. All other
@ -24,6 +25,7 @@ import Data.List (elemIndices)
import qualified Data.Set as Set import qualified Data.Set as Set
import Convert.ExprUtils import Convert.ExprUtils
import Convert.Package (inject)
import Convert.Traverse import Convert.Traverse
import Language.SystemVerilog.AST import Language.SystemVerilog.AST
@ -36,9 +38,14 @@ convert = map $ concatMap convertDescription
convertDescription :: Description -> [Description] convertDescription :: Description -> [Description]
convertDescription (Package ml name items) = convertDescription (Package ml name items) =
[Package ml name $ concatMap convertPackageItem items] [Package ml name $ concatMap convertPackageItem items]
convertDescription description = convertDescription (description @ Part{}) =
(map PackageItem enumItems) ++ [description'] [Part attrs extern kw lifetime name ports items']
where (description', enumItems) = convertDescription' description where
items' = inject enumItems items -- only keep what's used
Part attrs extern kw lifetime name ports items = description'
(description', enumItems) = convertDescription' description
convertDescription (PackageItem item) =
map PackageItem $ convertPackageItem item
-- explode a package item with its corresponding enum items -- explode a package item with its corresponding enum items
convertPackageItem :: PackageItem -> [PackageItem] convertPackageItem :: PackageItem -> [PackageItem]

View File

@ -1,177 +0,0 @@
{- sv2v
- Author: Zachary Snow <zach@zachjs.com>
-
- Conversion for moving top-level package items into modules
-}
module Convert.NestPI (convert, reorder) where
import Control.Monad.Writer.Strict
import Data.Maybe (mapMaybe)
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Convert.Traverse
import Language.SystemVerilog.AST
type PIs = Map.Map Identifier PackageItem
type Idents = Set.Set Identifier
convert :: [AST] -> [AST]
convert =
map (filter (not . isPI)) . nest
where
nest :: [AST] -> [AST]
nest = traverseFiles
(collectDescriptionsM collectDescriptionM)
(traverseDescriptions . convertDescription)
isPI :: Description -> Bool
isPI (PackageItem Import{}) = False
isPI (PackageItem item) = piName item /= ""
isPI _ = False
reorder :: [AST] -> [AST]
reorder = map $ traverseDescriptions reorderDescription
-- collects packages items missing
collectDescriptionM :: Description -> Writer PIs ()
collectDescriptionM (PackageItem item) = do
case piName item of
"" -> return ()
ident -> tell $ Map.singleton ident item
collectDescriptionM _ = return ()
-- nests packages items missing from modules
convertDescription :: PIs -> Description -> Description
convertDescription pis (orig @ Part{}) =
if Map.null pis
then orig
else Part attrs extern kw lifetime name ports items'
where
Part attrs extern kw lifetime name ports items = orig
items' = addItems pis Set.empty (map addUsedPIs items)
convertDescription _ other = other
-- attempt to fix simple declaration order issues
reorderDescription :: Description -> Description
reorderDescription (Part attrs extern kw lifetime name ports items) =
Part attrs extern kw lifetime name ports items'
where
items' = addItems localPIs Set.empty (map addUsedPIs items)
localPIs = Map.fromList $ mapMaybe toPIElem items
toPIElem :: ModuleItem -> Maybe (Identifier, PackageItem)
toPIElem (MIPackageItem item) = Just (piName item, item)
toPIElem _ = Nothing
reorderDescription other = other
-- iteratively inserts missing package items exactly where they are needed
addItems :: PIs -> Idents -> [(ModuleItem, Idents)] -> [ModuleItem]
addItems pis existingPIs ((item, usedPIs) : items) =
if not $ Set.disjoint existingPIs thisPI then
-- this item was re-imported earlier in the module
addItems pis existingPIs items
else if null itemsToAdd then
-- this item has no additional dependencies
item : addItems pis (Set.union existingPIs thisPI) items
else
-- this item has at least one un-met dependency
addItems pis existingPIs (addUsedPIs chosen : (item, usedPIs) : items)
where
thisPI = execWriter $ collectPIsM item
neededPIs = Set.difference (Set.difference usedPIs existingPIs) thisPI
itemsToAdd = map MIPackageItem $ Map.elems $
Map.restrictKeys pis neededPIs
chosen = head itemsToAdd
addItems _ _ [] = []
-- augment a module item with the set of identifiers it uses
addUsedPIs :: ModuleItem -> (ModuleItem, Idents)
addUsedPIs item =
(item, usedPIs)
where
usedPIs = execWriter $
traverseNestedModuleItemsM (traverseIdentsM writeIdent) item
writeIdent :: Identifier -> Writer Idents Identifier
writeIdent x = tell (Set.singleton x) >> return x
-- writes down the names of package items
collectPIsM :: ModuleItem -> Writer Idents ()
collectPIsM (MIPackageItem item) =
case piName item of
"" -> return ()
ident -> tell $ Set.singleton ident
collectPIsM _ = return ()
-- visits all identifiers in a module item
traverseIdentsM :: Monad m => MapperM m Identifier -> MapperM m ModuleItem
traverseIdentsM identMapper = traverseNodesM
(traverseExprIdentsM identMapper)
(traverseDeclIdentsM identMapper)
(traverseTypeIdentsM identMapper)
(traverseLHSIdentsM identMapper)
(traverseStmtIdentsM identMapper)
-- visits all identifiers in an expression
traverseExprIdentsM :: Monad m => MapperM m Identifier -> MapperM m Expr
traverseExprIdentsM identMapper = fullMapper
where
fullMapper = exprMapper >=> traverseSinglyNestedExprsM fullMapper
exprMapper (Call (Ident x) args) =
identMapper x >>= \x' -> return $ Call (Ident x') args
exprMapper (Ident x) = identMapper x >>= return . Ident
exprMapper other = return other
-- visits all identifiers in a type
traverseTypeIdentsM :: Monad m => MapperM m Identifier -> MapperM m Type
traverseTypeIdentsM identMapper = fullMapper
where
fullMapper = typeMapper
>=> traverseTypeExprsM (traverseExprIdentsM identMapper)
>=> traverseSinglyNestedTypesM fullMapper
typeMapper (Alias x t) = aliasHelper (Alias ) x t
typeMapper (PSAlias p x t) = aliasHelper (PSAlias p ) x t
typeMapper (CSAlias c p x t) = aliasHelper (CSAlias c p) x t
typeMapper other = return other
aliasHelper constructor x t =
identMapper x >>= \x' -> return $ constructor x' t
-- visits all identifiers in an LHS
traverseLHSIdentsM :: Monad m => MapperM m Identifier -> MapperM m LHS
traverseLHSIdentsM identMapper = fullMapper
where
fullMapper = lhsMapper
>=> traverseLHSExprsM (traverseExprIdentsM identMapper)
>=> traverseSinglyNestedLHSsM fullMapper
lhsMapper (LHSIdent x) = identMapper x >>= return . LHSIdent
lhsMapper other = return other
-- visits all identifiers in a statement
traverseStmtIdentsM :: Monad m => MapperM m Identifier -> MapperM m Stmt
traverseStmtIdentsM identMapper = fullMapper
where
fullMapper = stmtMapper
>=> traverseStmtExprsM (traverseExprIdentsM identMapper)
>=> traverseStmtLHSsM (traverseLHSIdentsM identMapper)
>=> traverseSinglyNestedStmtsM fullMapper
stmtMapper (Subroutine (Ident x) args) =
identMapper x >>= \x' -> return $ Subroutine (Ident x') args
stmtMapper other = return other
-- visits all identifiers in a declaration
traverseDeclIdentsM :: Monad m => MapperM m Identifier -> MapperM m Decl
traverseDeclIdentsM identMapper =
traverseDeclExprsM (traverseExprIdentsM identMapper) >=>
traverseDeclTypesM (traverseTypeIdentsM identMapper)
-- returns the "name" of a package item, if it has one
piName :: PackageItem -> Identifier
piName (Function _ _ ident _ _) = ident
piName (Task _ ident _ _) = ident
piName (Typedef _ ident ) = ident
piName (Decl (Variable _ _ ident _ _)) = ident
piName (Decl (Param _ _ ident _)) = ident
piName (Decl (ParamType _ ident _)) = ident
piName (Decl (CommentDecl _)) = ""
piName (Import x y) = show $ Import x y
piName (Export _) = ""
piName (Directive _) = ""

View File

@ -1,32 +1,32 @@
{-# LANGUAGE TupleSections #-}
{- sv2v {- sv2v
- Author: Zachary Snow <zach@zachjs.com> - Author: Zachary Snow <zach@zachjs.com>
- -
- Conversion for packages, exports, and imports - Conversion for packages and global declarations
- -
- TODO: We do not yet handle exports. - This conversion first makes a best-effort pass at resolving any simple
- TODO: The scoping rules are not being entirely followed yet. - declaration ordering issues in the input. Many conversions require that
- TODO: Explicit imports may introduce name conflicts because of carried items. - declarations precede their first usage.
- -
- The SystemVerilog scoping rules for exports and imports are not entirely - The main phase elaborates packages and resolves imported identifiers. An
- trivial. We do not explicitly handle the "error" scenarios detailed Table - identifier (perhaps implicitly) referring to `P::X` is rewritten to `P_X`.
- 26-1 of Section 26-3 of IEEE 1800-2017. Users generally shouldn't be relying - This conversion assumes such renaming will not cause conflicts. The full
- on this tool to catch and report such wild naming conflicts that are outlined - semantics of imports and exports are followed.
- there.
- -
- Summary: - Finally, because Verilog doesn't allow declarations to exist outside of
- * In scopes which have a local declaration of an identifier, that identifier - modules, declarations within packages and in the global scope are injected
- refers to that local declaration. - into modules and interfaces as needed.
- * If there is no local declaration, the identifier refers to the imported
- declaration.
- * If there is an explicit import of that identifier, the identifier refers to
- the imported declaration.
- * Usages of conflicting wildcard imports are not allowed.
-} -}
module Convert.Package (convert) where module Convert.Package
( convert
, inject
) where
import Control.Monad.State.Strict import Control.Monad.State.Strict
import Control.Monad.Writer.Strict import Control.Monad.Writer.Strict
import Data.List (insert, intercalate)
import Data.Maybe (mapMaybe)
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import qualified Data.Set as Set import qualified Data.Set as Set
@ -34,112 +34,296 @@ import Convert.Scoper
import Convert.Traverse import Convert.Traverse
import Language.SystemVerilog.AST import Language.SystemVerilog.AST
type Packages = Map.Map Identifier PackageItems type Packages = Map.Map Identifier Package
type PackageItems = [(Identifier, PackageItem)] type Package = (IdentStateMap, [PackageItem])
type Idents = Set.Set Identifier type Idents = Set.Set Identifier
type PIs = Map.Map Identifier PackageItem
convert :: [AST] -> [AST] convert :: [AST] -> [AST]
convert = step convert files =
map (traverseDescriptions $ convertDescription pis) files'
where where
step :: [AST] -> [AST] (files', packages') = convertPackages files
step curr = pis = Map.fromList $
if next == curr concatMap (concatMap toPackageItems . snd) $
then curr filter (not . Map.null . fst) $
else step next Map.elems packages'
toPackageItems :: PackageItem -> [(Identifier, PackageItem)]
toPackageItems item = map (, item) (piNames item)
-- utility for inserting package items into a set of module items as needed
inject :: [PackageItem] -> [ModuleItem] -> [ModuleItem]
inject packageItems items =
addItems localPIs Set.empty (map addUsedPIs items)
where where
next = traverseFiles localPIs = Map.fromList $ concatMap toPIElem packageItems
(collectDescriptionsM collectDescriptionM) toPIElem :: PackageItem -> [(Identifier, PackageItem)]
convertFile curr toPIElem item = map (, item) (piNames item)
convertFile :: Packages -> AST -> AST -- collect packages and global package items
convertFile packages ast = collectPackageM :: Description -> Writer (Packages, [PackageItem]) ()
(++) globalItems $ collectPackageM (PackageItem item) =
filter (not . isCollected) $ when (not $ null $ piNames item) $
concatMap (traverseDescription packages) $ tell (Map.empty, [item])
ast collectPackageM (Package _ name items) =
tell (Map.singleton name (Map.empty, items), [])
collectPackageM _ = return ()
-- elaborate all packages and their usages
convertPackages :: [AST] -> ([AST], Packages)
convertPackages files =
(files', packages')
where where
globalItems = map PackageItem $ (files', ([], packages')) = runState op ([], packages)
concatMap (uncurry globalPackageItems) $ Map.toList packages op = mapM (traverseDescriptionsM traverseDescriptionM) files
isCollected :: Description -> Bool packages = Map.insert "" (Map.empty, globalItems) realPackages
isCollected (Package _ name _) = Map.member name packages (realPackages, globalItems) =
isCollected _ = False execWriter $ mapM (collectDescriptionsM collectPackageM) files
globalPackageItems :: Identifier -> PackageItems -> [PackageItem] type PackagesState = State ([Identifier], Packages)
globalPackageItems name items =
prefixPackageItems name (packageItemIdents items) (map snd items)
packageItemIdents :: PackageItems -> Idents traverseDescriptionM :: Description -> PackagesState Description
packageItemIdents items = traverseDescriptionM (PackageItem item) = do
Set.union return $ PackageItem $ case piNames item of
(Set.fromList $ map fst items) [] -> item
(Set.unions $ map (packageItemSubIdents . snd) items) idents -> Decl $ CommentDecl $ "removed " ++ show idents
traverseDescriptionM (Package _ name _) =
return $ PackageItem $ Decl $ CommentDecl $ "removed package " ++ show name
traverseDescriptionM (Part attrs extern kw liftetime name ports items) = do
(_, items') <- processItems name "" items
return $ Part attrs extern kw liftetime name ports items'
data IdentState
= Available [Identifier]
| Imported Identifier
| Declared
deriving Eq
isImported :: IdentState -> Bool
isImported Imported{} = True
isImported _ = False
isDeclared :: IdentState -> Bool
isDeclared Declared{} = True
isDeclared _ = False
type IdentStateMap = Map.Map Identifier IdentState
type Scope = ScoperT IdentState PackagesState
-- produce the partial mapping for a particular export and ensure its validity
resolveExport
:: IdentStateMap -> Identifier -> Identifier -> PackagesState IdentStateMap
resolveExport mapping "" "" =
return $ Map.filter isImported mapping
resolveExport mapping pkg "" =
fmap (Map.mapMaybeWithKey checkExport . fst) (findPackage pkg)
where where
packageItemSubIdents :: PackageItem -> Idents checkExport :: Identifier -> IdentState -> Maybe IdentState
packageItemSubIdents (Typedef (Enum _ enumItems _) _) = checkExport ident exportedIdentState =
Set.fromList $ map fst enumItems if localIdentState == expectedIdentState
packageItemSubIdents _ = Set.empty then localIdentState
else Nothing
where
localIdentState = Map.lookup ident mapping
expectedIdentState = Just $ Imported $
toRootPackage pkg exportedIdentState
resolveExport mapping pkg ident =
case Map.lookup ident mapping of
Just (Imported importedPkg) -> do
exportedPkg <- resolveRootPackage pkg ident
if importedPkg == exportedPkg
then return $ Map.singleton ident $ Imported importedPkg
else error $ "export of " ++ pkg ++ "::" ++ ident
++ " differs from import of " ++ importedPkg
++ "::" ++ ident
_ -> error $ "export of " ++ pkg ++ "::" ++ ident
++ ", but " ++ ident ++ " was never imported"
prefixPackageItems :: Identifier -> Idents -> [PackageItem] -> [PackageItem] -- lookup the state of the identifier only within the current scope
prefixPackageItems packageName idents items = lookupLocalIdentState :: Identifier -> Scope (Maybe IdentState)
map unwrap $ evalScoper lookupLocalIdentState =
fmap (fmap thd3) . lookupLocalIdentM
where thd3 (_, _, c) = c
-- make a particular identifier within a package available for import
wildcardImport :: Identifier -> Identifier -> Scope ()
wildcardImport pkg ident = do
rootPkg <- lift $ resolveRootPackage pkg ident
maybeIdentState <- lookupLocalIdentState ident
insertElem ident $
case maybeIdentState of
Nothing -> Available [rootPkg]
Just Declared -> Declared
Just (Imported existingRootPkg) -> Imported existingRootPkg
Just (Available rootPkgs) ->
if elem rootPkg rootPkgs
then Available rootPkgs
else Available $ insert rootPkg rootPkgs
-- make all exported identifiers within a package available for import
wildcardImports :: Identifier -> Scope ()
wildcardImports pkg = do
(exports, _) <- lift $ findPackage pkg
_ <- mapM (wildcardImport pkg) (Map.keys exports)
return ()
-- resolve and store an explicit (non-wildcard) import
explicitImport :: Identifier -> Identifier -> Scope ()
explicitImport pkg ident = do
rootPkg <- lift $ resolveRootPackage pkg ident
maybeIdentState <- lookupLocalIdentState ident
insertElem ident $
case maybeIdentState of
Nothing -> Imported rootPkg
Just Declared ->
error $ "import of " ++ pkg ++ "::" ++ ident
++ " conflicts with prior declaration of " ++ ident
Just Available{} -> Imported rootPkg
Just (Imported otherPkg) ->
if otherPkg == rootPkg
then Imported rootPkg
else error $ "import of " ++ pkg ++ "::" ++ ident
++ " conflicts with prior import of "
++ otherPkg ++ "::" ++ ident
-- main logic responsible for translating packages, resolving imports and
-- exports, and rewriting identifiers referring to package declarations
processItems :: Identifier -> Identifier -> [ModuleItem]
-> PackagesState (IdentStateMap, [ModuleItem])
processItems topName packageName moduleItems = do
(moduleItems', scopes) <- runScoperT
traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM
packageName $ map (wrap . initialPrefix) items topName (reorderItems moduleItems)
let rawIdents = extractMapping scopes
externalIdentMaps <- mapM (resolveExportMI rawIdents) moduleItems
let externalIdents = Map.unions externalIdentMaps
let declaredIdents = Map.filter isDeclared rawIdents
let exports = Map.union declaredIdents externalIdents
let exports' = if null packageName
then rawIdents
else exports
seq exports return (exports', moduleItems')
where where
wrap :: PackageItem -> ModuleItem -- produces partial mappings of exported identifiers, while also
wrap = MIPackageItem -- checking the validity of the exports
unwrap :: ModuleItem -> PackageItem resolveExportMI :: IdentStateMap -> ModuleItem -> PackagesState IdentStateMap
unwrap (MIPackageItem item) = item resolveExportMI mapping (MIPackageItem (item @ (Export pkg ident))) =
unwrap _ = error "unwrap invariant violated" if null packageName
then error $ "invalid " ++ (init $ show item)
++ " outside of package"
else resolveExport mapping pkg ident
resolveExportMI _ _ = return Map.empty
initialPrefix :: PackageItem -> PackageItem -- declare an identifier, prefixing it if within a package
initialPrefix item = prefixIdent :: Identifier -> Scope Identifier
case item of prefixIdent x = do
Function a b x c d -> Function a b (prefix x) c d inProcedure <- withinProcedureM
Task a x c d -> Task a (prefix x) c d maybeIdentState <- lookupLocalIdentState x
Typedef a x -> Typedef a (prefix x) case maybeIdentState of
Decl (Variable a b x c d) -> Decl (Variable a b (prefix x) c d) Just (Imported rootPkg) -> error $ "declaration of " ++ x
Decl (Param a b x c ) -> Decl (Param a b (prefix x) c ) ++ " conflicts with prior import of " ++ rootPkg
Decl (ParamType a x b ) -> Decl (ParamType a (prefix x) b ) ++ "::" ++ x
other -> other _ -> do
insertElem x Declared
if inProcedure || null packageName
then return x
else return $ packageName ++ '_' : x
prefix :: Identifier -> Identifier -- check the global scope for declarations or imports
prefix x = resolveGlobalIdent :: Identifier -> Scope Identifier
if Set.member x idents resolveGlobalIdent x = do
then packageName ++ '_' : x (exports, _) <- lift $ findPackage ""
else x case Map.lookup x exports of
prefixM :: Identifier -> Scoper () Identifier Nothing -> return x
prefixM x = do Just identState -> do
-- inject the exact state outside of the module scope,
-- allowing wildcard imports to be handled correctly
insertElem [Access x Nil] identState
resolveIdent x
-- remap an identifier if needed based on declarations, explicit
-- imports, or names available for import
resolveIdent :: Identifier -> Scope Identifier
resolveIdent x = do
details <- lookupElemM x details <- lookupElemM x
if details == Nothing case details of
then return $ prefix x Nothing ->
else return x if null topName
then return x
else resolveGlobalIdent x
Just ([_, _], _, Declared) ->
if null packageName
then return x
else return $ packageName ++ '_' : x
Just (_, _, Declared) -> return x
Just (_, _, Imported rootPkg) ->
return $ rootPkg ++ '_' : x
Just (accesses, _, Available [rootPkg]) -> do
insertElem accesses $ Imported rootPkg
return $ rootPkg ++ '_' : x
Just (_, _, Available rootPkgs) ->
error $ "identifier " ++ show x
++ " ambiguously refers to the definitions in any of "
++ intercalate ", " rootPkgs
traverseDeclM :: Decl -> Scoper () Decl traversePackageItemM :: PackageItem -> Scope PackageItem
-- TODO: fold this in with type parameters
traversePackageItemM (Typedef t x) = do
t' <- traverseTypeM t
x' <- prefixIdent x
t'' <- traverseNestedTypesM (traverseTypeExprsM traverseExprM) t'
return $ Typedef t'' x'
traversePackageItemM (orig @ (Import pkg ident)) = do
if null ident
then wildcardImports pkg
else explicitImport pkg ident
return $ Decl $ CommentDecl $ "removed " ++ show orig
traversePackageItemM (orig @ (Export pkg ident)) = do
() <- when (not (null pkg || null ident)) $ do
localName <- resolveIdent ident
rootPkg <- lift $ resolveRootPackage pkg ident
localName `seq` rootPkg `seq` return ()
return $ Decl $ CommentDecl $ "removed " ++ show orig
traversePackageItemM other = return other
traverseDeclM :: Decl -> Scope Decl
traverseDeclM decl = do traverseDeclM decl = do
case decl of decl' <- case decl of
Variable _ _ x _ _ -> insertElem x () Variable d t x a e -> declHelp x $ \x' -> Variable d t x' a e
Param _ _ x _ -> insertElem x () Param p t x e -> declHelp x $ \x' -> Param p t x' e
ParamType _ x _ -> insertElem x () ParamType p x t -> declHelp x $ \x' -> ParamType p x' t
CommentDecl{} -> return () CommentDecl c -> return $ CommentDecl c
traverseDeclTypesM traverseTypeM decl >>= traverseDeclTypesM traverseTypeM decl' >>=
traverseDeclExprsM traverseExprM traverseDeclExprsM traverseExprM
where declHelp x f = prefixIdent x >>= return . f
traverseTypeM :: Type -> Scoper () Type traverseTypeM :: Type -> Scope Type
traverseTypeM (PSAlias p x rs) = do
x' <- lift $ resolvePSIdent p x
return $ Alias x' rs
traverseTypeM (Alias x rs) = traverseTypeM (Alias x rs) =
prefixM x >>= \x' -> return $ Alias x' rs resolveIdent x >>= \x' -> return $ Alias x' rs
traverseTypeM (Enum t enumItems rs) = do traverseTypeM (Enum t enumItems rs) = do
enumItems' <- mapM prefixEnumItem enumItems enumItems' <- mapM prefixEnumItem enumItems
return $ Enum t enumItems' rs return $ Enum t enumItems' rs
where prefixEnumItem (x, e) = prefixM x >>= \x' -> return (x', e) where prefixEnumItem (x, e) = prefixIdent x >>= \x' -> return (x', e)
traverseTypeM other = traverseSinglyNestedTypesM traverseTypeM other traverseTypeM other = traverseSinglyNestedTypesM traverseTypeM other
traverseExprM (Ident x) = prefixM x >>= return . Ident traverseExprM (PSIdent p x) = do
x' <- lift $ resolvePSIdent p x
return $ Ident x'
traverseExprM (Ident x) = resolveIdent x >>= return . Ident
traverseExprM other = traverseSinglyNestedExprsM traverseExprM other traverseExprM other = traverseSinglyNestedExprsM traverseExprM other
traverseLHSM (LHSIdent x) = prefixM x >>= return . LHSIdent traverseLHSM (LHSIdent x) = resolveIdent x >>= return . LHSIdent
traverseLHSM other = traverseSinglyNestedLHSsM traverseLHSM other traverseLHSM other = traverseSinglyNestedLHSsM traverseLHSM other
traverseGenItemM = error "not possible" traverseGenItemM = traverseGenItemExprsM traverseExprM
traverseModuleItemM = traverseModuleItemM (MIPackageItem item) = do
item' <- traversePackageItemM item
return $ MIPackageItem item'
traverseModuleItemM other =
traverseModuleItemM' other
traverseModuleItemM' =
traverseTypesM traverseTypeM >=> traverseTypesM traverseTypeM >=>
traverseExprsM traverseExprM >=> traverseExprsM traverseExprM >=>
traverseLHSsM traverseLHSM traverseLHSsM traverseLHSM
@ -147,84 +331,200 @@ prefixPackageItems packageName idents items =
traverseStmtExprsM traverseExprM >=> traverseStmtExprsM traverseExprM >=>
traverseStmtLHSsM traverseLHSM traverseStmtLHSsM traverseLHSM
collectDescriptionM :: Description -> Writer Packages () -- locate a package by name, processing its contents if necessary
collectDescriptionM (Package _ name items) = findPackage :: Identifier -> PackagesState Package
if any isImport items findPackage packageName = do
then return () (stack, packages) <- get
else tell $ Map.singleton name itemList let maybePackage = Map.lookup packageName packages
where assertMsg (maybePackage /= Nothing) $
itemList = concatMap toPackageItems items "could not find package " ++ show packageName
toPackageItems :: PackageItem -> PackageItems -- because this conversion doesn't enforce declaration ordering of packages,
toPackageItems item = -- it must check for dependency loops to avoid infinite recursion
case piName item of let first : rest = reverse $ packageName : stack
"" -> [] assertMsg (not $ elem packageName stack) $
x -> [(x, item)] "package dependency loop: " ++ show first ++ " depends on "
isImport :: PackageItem -> Bool ++ intercalate ", which depends on " (map show rest)
isImport (Import _ _) = True let Just (package @ (exports, _))= maybePackage
isImport _ = False if Map.null exports
collectDescriptionM _ = return () then do
-- process and resolve this package
put (packageName : stack, packages)
package' <- processPackage packageName $ snd package
packages' <- gets snd
put (stack, Map.insert packageName package' packages')
return package'
else return package
traverseDescription :: Packages -> Description -> [Description] -- helper for elaborating a package when it is first referenced
traverseDescription packages (PackageItem (Import x y)) = processPackage :: Identifier -> [PackageItem] -> PackagesState Package
map (\(MIPackageItem item) -> PackageItem item) items processPackage packageName packageItems = do
(exports, moduleItems') <- processItems packageName packageName wrapped
let packageItems' = map unwrap moduleItems'
let package' = (exports, packageItems')
return package'
where where
orig = Part [] False Module Inherit "DNE" [] wrapped = map wrap packageItems
[MIPackageItem $ Import x y] wrap :: PackageItem -> ModuleItem
[orig'] = traverseDescription packages orig wrap = MIPackageItem
Part [] False Module Inherit "DNE" [] items = orig' unwrap :: ModuleItem -> PackageItem
traverseDescription packages description = unwrap packageItem = item
[description'] where MIPackageItem item = packageItem
-- resolve a package scoped identifier to its unique global name
resolvePSIdent :: Identifier -> Identifier -> PackagesState Identifier
resolvePSIdent packageName itemName = do
rootPkg <- resolveRootPackage packageName itemName
return $ rootPkg ++ '_' : itemName
-- determines the root package contained the given package scoped identifier
resolveRootPackage :: Identifier -> Identifier -> PackagesState Identifier
resolveRootPackage packageName itemName = do
(exports, _) <- findPackage packageName
let maybeIdentState = Map.lookup itemName exports
assertMsg (maybeIdentState /= Nothing) $
"could not find " ++ show itemName ++ " in package " ++ show packageName
let Just identState = maybeIdentState
return $ toRootPackage packageName identState
-- errors with the given message when the check is false
assertMsg :: Monad m => Bool -> String -> m ()
assertMsg check msg = when (not check) $ error msg
-- helper for taking an ident which is either declared or exported form a
-- package and determine its true root source package
toRootPackage :: Identifier -> IdentState -> Identifier
toRootPackage sourcePackage identState =
if identState == Declared
then sourcePackage
else rootPackage
where Imported rootPackage = identState
-- nests packages items missing from modules
convertDescription :: PIs -> Description -> Description
convertDescription pis (orig @ Part{}) =
if Map.null pis
then orig
else Part attrs extern kw lifetime name ports items'
where where
description' = traverseModuleItems Part attrs extern kw lifetime name ports items = orig
(traverseModuleItem existingItemNames packages) items' = addItems pis Set.empty (map addUsedPIs items)
description convertDescription _ other = other
existingItemNames = execWriter $
collectModuleItemsM writePIName description
writePIName :: ModuleItem -> Writer Idents ()
writePIName (MIPackageItem (Import _ (Just x))) =
tell $ Set.singleton x
writePIName (MIPackageItem item) =
case piName item of
"" -> return ()
x -> tell $ Set.singleton x
writePIName _ = return ()
traverseModuleItem :: Idents -> Packages -> ModuleItem -> ModuleItem -- attempt to fix simple declaration order issues
traverseModuleItem existingItemNames packages (MIPackageItem (Import x y)) = reorderItems :: [ModuleItem] -> [ModuleItem]
if Map.member x packages reorderItems items =
then Generate $ map (GenModuleItem . MIPackageItem) itemsRenamed addItems localPIs Set.empty (map addUsedPIs items)
else MIPackageItem $ Import x y
where where
packageItems = packages Map.! x localPIs = Map.fromList $ concat $ mapMaybe toPIElem items
namesToAvoid = case y of toPIElem :: ModuleItem -> Maybe [(Identifier, PackageItem)]
Nothing -> existingItemNames toPIElem (MIPackageItem item) = Just $ map (, item) (piNames item)
Just ident -> Set.delete ident existingItemNames toPIElem _ = Nothing
itemsRenamed =
prefixPackageItems x namesToAvoid -- iteratively inserts missing package items exactly where they are needed
(map snd packageItems) addItems :: PIs -> Idents -> [(ModuleItem, Idents)] -> [ModuleItem]
traverseModuleItem _ _ item = addItems pis existingPIs ((item, usedPIs) : items) =
(traverseExprs $ traverseNestedExprs traverseExpr) $ if not $ Set.disjoint existingPIs thisPI then
(traverseTypes $ traverseNestedTypes traverseType) $ -- this item was re-imported earlier in the module
item addItems pis existingPIs items
else if null itemsToAdd then
-- this item has no additional dependencies
item : addItems pis (Set.union existingPIs thisPI) items
else
-- this item has at least one un-met dependency
addItems pis existingPIs (addUsedPIs chosen : (item, usedPIs) : items)
where where
thisPI = case item of
MIPackageItem packageItem ->
Set.fromList $ piNames packageItem
_ -> Set.empty
neededPIs = Set.difference (Set.difference usedPIs existingPIs) thisPI
itemsToAdd = map MIPackageItem $ Map.elems $
Map.restrictKeys pis neededPIs
chosen = head itemsToAdd
addItems _ _ [] = []
traverseExpr :: Expr -> Expr -- augment a module item with the set of identifiers it uses
traverseExpr (PSIdent x y) = Ident $ x ++ "_" ++ y addUsedPIs :: ModuleItem -> (ModuleItem, Idents)
traverseExpr other = other addUsedPIs item =
(item, usedPIs)
where
usedPIs = execWriter $
traverseNestedModuleItemsM (traverseIdentsM writeIdent) item
writeIdent :: Identifier -> Writer Idents Identifier
writeIdent x = tell (Set.singleton x) >> return x
traverseType :: Type -> Type -- visits all identifiers in a module item
traverseType (PSAlias ps xx rs) = Alias (ps ++ "_" ++ xx) rs traverseIdentsM :: Monad m => MapperM m Identifier -> MapperM m ModuleItem
traverseType other = other traverseIdentsM identMapper = traverseNodesM
(traverseExprIdentsM identMapper)
(traverseDeclIdentsM identMapper)
(traverseTypeIdentsM identMapper)
(traverseLHSIdentsM identMapper)
(traverseStmtIdentsM identMapper)
-- returns the "name" of a package item, if it has one -- visits all identifiers in an expression
piName :: PackageItem -> Identifier traverseExprIdentsM :: Monad m => MapperM m Identifier -> MapperM m Expr
piName (Function _ _ ident _ _) = ident traverseExprIdentsM identMapper = fullMapper
piName (Task _ ident _ _) = ident where
piName (Typedef _ ident ) = ident fullMapper = exprMapper >=> traverseSinglyNestedExprsM fullMapper
piName (Decl (Variable _ _ ident _ _)) = ident exprMapper (Call (Ident x) args) =
piName (Decl (Param _ _ ident _)) = ident identMapper x >>= \x' -> return $ Call (Ident x') args
piName (Decl (ParamType _ ident _)) = ident exprMapper (Ident x) = identMapper x >>= return . Ident
piName (Decl (CommentDecl _)) = "" exprMapper other = return other
piName (Import _ _) = ""
piName (Export _) = "" -- visits all identifiers in a type
piName (Directive _) = "" traverseTypeIdentsM :: Monad m => MapperM m Identifier -> MapperM m Type
traverseTypeIdentsM identMapper = fullMapper
where
fullMapper = typeMapper
>=> traverseTypeExprsM (traverseExprIdentsM identMapper)
>=> traverseSinglyNestedTypesM fullMapper
typeMapper (Alias x t) = aliasHelper (Alias ) x t
typeMapper (PSAlias p x t) = aliasHelper (PSAlias p ) x t
typeMapper (CSAlias c p x t) = aliasHelper (CSAlias c p) x t
typeMapper other = return other
aliasHelper constructor x t =
identMapper x >>= \x' -> return $ constructor x' t
-- visits all identifiers in an LHS
traverseLHSIdentsM :: Monad m => MapperM m Identifier -> MapperM m LHS
traverseLHSIdentsM identMapper = fullMapper
where
fullMapper = lhsMapper
>=> traverseLHSExprsM (traverseExprIdentsM identMapper)
>=> traverseSinglyNestedLHSsM fullMapper
lhsMapper (LHSIdent x) = identMapper x >>= return . LHSIdent
lhsMapper other = return other
-- visits all identifiers in a statement
traverseStmtIdentsM :: Monad m => MapperM m Identifier -> MapperM m Stmt
traverseStmtIdentsM identMapper = fullMapper
where
fullMapper = stmtMapper
>=> traverseStmtExprsM (traverseExprIdentsM identMapper)
>=> traverseStmtLHSsM (traverseLHSIdentsM identMapper)
>=> traverseSinglyNestedStmtsM fullMapper
stmtMapper (Subroutine (Ident x) args) =
identMapper x >>= \x' -> return $ Subroutine (Ident x') args
stmtMapper other = return other
-- visits all identifiers in a declaration
traverseDeclIdentsM :: Monad m => MapperM m Identifier -> MapperM m Decl
traverseDeclIdentsM identMapper =
traverseDeclExprsM (traverseExprIdentsM identMapper) >=>
traverseDeclTypesM (traverseTypeIdentsM identMapper)
-- returns any names defined by a package item
piNames :: PackageItem -> [Identifier]
piNames (Function _ _ ident _ _) = [ident]
piNames (Task _ ident _ _) = [ident]
piNames (Decl (Variable _ _ ident _ _)) = [ident]
piNames (Decl (Param _ _ ident _)) = [ident]
piNames (Decl (ParamType _ ident _)) = [ident]
piNames (Decl (CommentDecl _)) = []
piNames (Import x y) = [show $ Import x y]
piNames (Export x y) = [show $ Export x y]
piNames (Directive _) = []
piNames (Typedef (Enum _ enumItems _) ident) =
ident : map fst enumItems
piNames (Typedef _ ident) = [ident]

View File

@ -27,6 +27,7 @@ module Convert.Scoper
, ScoperT , ScoperT
, evalScoper , evalScoper
, evalScoperT , evalScoperT
, runScoperT
, partScoper , partScoper
, partScoperT , partScoperT
, insertElem , insertElem
@ -36,9 +37,12 @@ module Convert.Scoper
, Access(..) , Access(..)
, ScopeKey , ScopeKey
, Scopes , Scopes
, extractMapping
, embedScopes , embedScopes
, withinProcedure , withinProcedure
, withinProcedureM , withinProcedureM
, lookupLocalIdent
, lookupLocalIdentM
, scopeModuleItemT , scopeModuleItemT
, Replacements , Replacements
) where ) where
@ -82,6 +86,12 @@ data Scopes a = Scopes
, sInjected :: [ModuleItem] , sInjected :: [ModuleItem]
} deriving Show } deriving Show
extractMapping :: Scopes a -> Map.Map Identifier a
extractMapping =
Map.mapMaybe eElement .
eMapping . snd .
Map.findMin . sMapping
embedScopes :: Monad m => (Scopes a -> b -> c) -> b -> ScoperT a m c embedScopes :: Monad m => (Scopes a -> b -> c) -> b -> ScoperT a m c
embedScopes func x = do embedScopes func x = do
scopes <- get scopes <- get
@ -142,13 +152,26 @@ exprToAccesses (Dot e x) = do
Just $ accesses ++ [Access x Nil] Just $ accesses ++ [Access x Nil]
exprToAccesses _ = Nothing exprToAccesses _ = Nothing
insertElem :: Monad m => Identifier -> a -> ScoperT a m () class ScopePath k where
insertElem name element = do toTiers :: Scopes a -> k -> [Tier]
instance ScopePath Identifier where
toTiers scopes name = sCurrent scopes ++ [Tier name ""]
instance ScopePath [Access] where
toTiers _ = map toTier
where
toTier :: Access -> Tier
toTier (Access x Nil) = Tier x ""
toTier (Access x iy) = Tier x y
where Ident y = iy
insertElem :: Monad m => ScopePath k => k -> a -> ScoperT a m ()
insertElem key element = do
s <- get s <- get
let current = sCurrent s
let mapping = sMapping s let mapping = sMapping s
let entry = Entry (Just element) "" Map.empty let entry = Entry (Just element) "" Map.empty
let mapping' = setScope (current ++ [Tier name ""]) entry mapping let mapping' = setScope (toTiers s key) entry mapping
put $ s { sMapping = mapping' } put $ s { sMapping = mapping' }
injectItem :: Monad m => ModuleItem -> ScoperT a m () injectItem :: Monad m => ModuleItem -> ScoperT a m ()
@ -218,6 +241,19 @@ lookupAccesses scopes accesses = do
let side = resolveInScope (sMapping scopes) [] accesses let side = resolveInScope (sMapping scopes) [] accesses
if isNothing deep then side else deep if isNothing deep then side else deep
lookupLocalIdent :: Scopes a -> Identifier -> LookupResult a
lookupLocalIdent scopes ident = do
(replacements, element) <- directResolve (sMapping scopes) accesses
Just (accesses, replacements, element)
where
accesses = map toAccess (sCurrent scopes) ++ [Access ident Nil]
toAccess :: Tier -> Access
toAccess (Tier x "") = Access x Nil
toAccess (Tier x y) = Access x (Ident y)
lookupLocalIdentM :: Monad m => Identifier -> ScoperT a m (LookupResult a)
lookupLocalIdentM = embedScopes lookupLocalIdent
withinProcedureM :: Monad m => ScoperT a m Bool withinProcedureM :: Monad m => ScoperT a m Bool
withinProcedureM = gets sProcedure withinProcedureM = gets sProcedure
@ -245,8 +281,23 @@ evalScoperT
-> Identifier -> Identifier
-> [ModuleItem] -> [ModuleItem]
-> m [ModuleItem] -> m [ModuleItem]
evalScoperT declMapper moduleItemMapper genItemMapper stmtMapper topName items = evalScoperT declMapper moduleItemMapper genItemMapper stmtMapper topName items = do
evalStateT operation initialState (items', _) <- runScoperT
declMapper moduleItemMapper genItemMapper stmtMapper
topName items
return items'
runScoperT
:: forall a m. Monad m
=> MapperM (ScoperT a m) Decl
-> MapperM (ScoperT a m) ModuleItem
-> MapperM (ScoperT a m) GenItem
-> MapperM (ScoperT a m) Stmt
-> Identifier
-> [ModuleItem]
-> m ([ModuleItem], Scopes a)
runScoperT declMapper moduleItemMapper genItemMapper stmtMapper topName items =
runStateT operation initialState
where where
operation :: ScoperT a m [ModuleItem] operation :: ScoperT a m [ModuleItem]
operation = do operation = do

View File

@ -613,8 +613,8 @@ traverseNodesM exprMapper declMapper typeMapper lhsMapper stmtMapper =
return $ MIPackageItem $ Directive c return $ MIPackageItem $ Directive c
moduleItemMapper (MIPackageItem (Import x y)) = moduleItemMapper (MIPackageItem (Import x y)) =
return $ MIPackageItem $ Import x y return $ MIPackageItem $ Import x y
moduleItemMapper (MIPackageItem (Export x)) = moduleItemMapper (MIPackageItem (Export x y)) =
return $ MIPackageItem $ Export x return $ MIPackageItem $ Export x y
moduleItemMapper (AssertionItem (mx, a)) = do moduleItemMapper (AssertionItem (mx, a)) = do
a' <- traverseAssertionStmtsM stmtMapper a a' <- traverseAssertionStmtsM stmtMapper a
a'' <- traverseAssertionExprsM exprMapper a' a'' <- traverseAssertionExprsM exprMapper a'
@ -864,6 +864,11 @@ traverseTypeExprsM exprMapper =
let pm' = zip (map fst pm) vals' let pm' = zip (map fst pm) vals'
rs' <- mapM (mapBothM exprMapper) rs rs' <- mapM (mapBothM exprMapper) rs
return $ CSAlias ps pm' xx rs' return $ CSAlias ps pm' xx rs'
typeMapper (Enum t enumItems rs) = do
enumItems' <- mapM enumItemMapper enumItems
rs' <- mapM (mapBothM exprMapper) rs
return $ Enum t enumItems' rs'
where enumItemMapper (x, e) = exprMapper e >>= \e' -> return (x, e')
typeMapper t = do typeMapper t = do
let (tf, rs) = typeRanges t let (tf, rs) = typeRanges t
rs' <- mapM (mapBothM exprMapper) rs rs' <- mapM (mapBothM exprMapper) rs

View File

@ -12,7 +12,6 @@ module Language.SystemVerilog.AST.Description
, Lifetime (..) , Lifetime (..)
) where ) where
import Data.Maybe (fromMaybe)
import Text.Printf (printf) import Text.Printf (printf)
import Language.SystemVerilog.AST.ShowHelp import Language.SystemVerilog.AST.ShowHelp
@ -56,8 +55,8 @@ data PackageItem
= Typedef Type Identifier = Typedef Type Identifier
| Function Lifetime Type Identifier [Decl] [Stmt] | Function Lifetime Type Identifier [Decl] [Stmt]
| Task Lifetime Identifier [Decl] [Stmt] | Task Lifetime Identifier [Decl] [Stmt]
| Import Identifier (Maybe Identifier) | Import Identifier Identifier
| Export (Maybe (Identifier, Maybe Identifier)) | Export Identifier Identifier
| Decl Decl | Decl Decl
| Directive String | Directive String
deriving Eq deriving Eq
@ -70,12 +69,15 @@ instance Show PackageItem where
show (Task ml x i b) = show (Task ml x i b) =
printf "task %s%s;\n%s\nendtask" printf "task %s%s;\n%s\nendtask"
(showPad ml) x (showBlock i b) (showPad ml) x (showBlock i b)
show (Import x y) = printf "import %s::%s;" x (fromMaybe "*" y) show (Import x y) = printf "import %s::%s;" x (showWildcard y)
show (Export Nothing) = "export *::*"; show (Export x y) = printf "export %s::%s;" (showWildcard x) (showWildcard y)
show (Export (Just (x, y))) = printf "export %s::%s;" x (fromMaybe "*" y)
show (Decl decl) = show decl show (Decl decl) = show decl
show (Directive str) = str show (Directive str) = str
showWildcard :: Identifier -> String
showWildcard "" = "*"
showWildcard x = x
data PartKW data PartKW
= Module = Module
| Interface | Interface

View File

@ -843,8 +843,8 @@ NonDeclPackageItem :: { [PackageItem] }
| "function" Lifetime "void" Identifier TFItems DeclsAndStmts "endfunction" opt(Tag) { [Task $2 $4 (map defaultFuncInput $ $5 ++ fst $6) (snd $6)] } | "function" Lifetime "void" Identifier TFItems DeclsAndStmts "endfunction" opt(Tag) { [Task $2 $4 (map defaultFuncInput $ $5 ++ fst $6) (snd $6)] }
| "task" Lifetime Identifier TFItems DeclsAndStmts "endtask" opt(Tag) { [Task $2 $3 (map defaultFuncInput $ $4 ++ fst $5) (snd $5)] } | "task" Lifetime Identifier TFItems DeclsAndStmts "endtask" opt(Tag) { [Task $2 $3 (map defaultFuncInput $ $4 ++ fst $5) (snd $5)] }
| "import" PackageImportItems ";" { map (uncurry Import) $2 } | "import" PackageImportItems ";" { map (uncurry Import) $2 }
| "export" PackageImportItems ";" { map (Export . Just) $2 } | "export" PackageImportItems ";" { map (uncurry Export) $2 }
| "export" "*" "::" "*" ";" { [Export Nothing] } -- "Nothing" being no restrictions | "export" "*" "::" "*" ";" { [Export "" ""] }
| ForwardTypedef ";" { $1 } | ForwardTypedef ";" { $1 }
| TimeunitsDeclaration { $1 } | TimeunitsDeclaration { $1 }
| Directive { [Directive $1] } | Directive { [Directive $1] }
@ -872,12 +872,12 @@ DefaultNetType :: { String }
: NetType { show $1 } : NetType { show $1 }
| Identifier { $1 } | Identifier { $1 }
PackageImportItems :: { [(Identifier, Maybe Identifier)] } PackageImportItems :: { [(Identifier, Identifier)] }
: PackageImportItem { [$1] } : PackageImportItem { [$1] }
| PackageImportItems "," PackageImportItem { $1 ++ [$3] } | PackageImportItems "," PackageImportItem { $1 ++ [$3] }
PackageImportItem :: { (Identifier, Maybe Identifier) } PackageImportItem :: { (Identifier, Identifier) }
: Identifier "::" Identifier { ($1, Just $3) } : Identifier "::" Identifier { ($1, $3) }
| Identifier "::" "*" { ($1, Nothing) } | Identifier "::" "*" { ($1, "") }
FuncRetAndName :: { (Type, Identifier) } FuncRetAndName :: { (Type, Identifier) }
: Type Identifier { ($1 , $2) } : Type Identifier { ($1 , $2) }
@ -987,6 +987,8 @@ StmtAsgn :: { Stmt }
| IncOrDecOperator LHS ";" { Asgn (AsgnOp $1) Nothing $2 (RawNum 1) } | IncOrDecOperator LHS ";" { Asgn (AsgnOp $1) Nothing $2 (RawNum 1) }
| LHS ";" { Subroutine (lhsToExpr $1) (Args [] []) } | LHS ";" { Subroutine (lhsToExpr $1) (Args [] []) }
| LHS CallArgs ";" { Subroutine (lhsToExpr $1) $2 } | LHS CallArgs ";" { Subroutine (lhsToExpr $1) $2 }
| Identifier "::" Identifier ";" { Subroutine (PSIdent $1 $3) (Args [] []) }
| Identifier "::" Identifier CallArgs ";" { Subroutine (PSIdent $1 $3) $4 }
StmtNonAsgn :: { Stmt } StmtNonAsgn :: { Stmt }
: StmtBlock(BlockKWSeq, "end" ) { $1 } : StmtBlock(BlockKWSeq, "end" ) { $1 }
| StmtBlock(BlockKWPar, "join") { $1 } | StmtBlock(BlockKWPar, "join") { $1 }

View File

@ -81,7 +81,6 @@ executable sv2v
Convert.LogOp Convert.LogOp
Convert.MultiplePacked Convert.MultiplePacked
Convert.NamedBlock Convert.NamedBlock
Convert.NestPI
Convert.Package Convert.Package
Convert.ParamNoDefault Convert.ParamNoDefault
Convert.ParamType Convert.ParamType

18
test/basic/enum_dupe.sv Normal file
View File

@ -0,0 +1,18 @@
module ExampleA;
typedef enum logic {
A = 1,
B = 0,
C = 2
} Enum;
Enum x = A;
initial $display("ExampleA: x=%b, A=%b, B=%b", x, A, B);
endmodule
module ExampleB;
typedef enum logic {
A = 0,
B = 1
} Enum;
Enum x = A;
initial $display("ExampleB: x=%b, A=%b, B=%b", x, A, B);
endmodule

14
test/basic/enum_dupe.v Normal file
View File

@ -0,0 +1,14 @@
module ExampleA;
localparam [0:0] A = 1;
localparam [0:0] B = 0;
reg x = A;
initial $display("ExampleA: x=%b, A=%b, B=%b", x, A, B);
endmodule
module ExampleB;
localparam [0:0] A = 0;
localparam [0:0] B = 1;
reg x = A;
initial $display("ExampleB: x=%b, A=%b, B=%b", x, A, B);
endmodule

View File

@ -0,0 +1,4 @@
module top;
ExampleA a();
ExampleB b();
endmodule

View File

@ -0,0 +1,18 @@
package Q;
localparam W = 5;
localparam unrelated = 1;
endpackage
package P;
import Q::*;
export Q::W;
endpackage
module Example
import P::*;
(
input logic [W - 1:0] inp
);
import Q::unrelated;
initial $display("%b %0d %0d", inp, $bits(inp), unrelated);
endmodule

View File

@ -0,0 +1,6 @@
module Example(inp);
localparam W = 5;
localparam unrelated = 1;
input wire [W - 1:0] inp;
initial $display("%b %0d %0d", inp, $bits(inp), unrelated);
endmodule

View File

@ -0,0 +1,3 @@
module top;
Example e(5'b00000);
endmodule

View File

@ -0,0 +1,11 @@
package Pkg;
localparam integer X = func(1);
function automatic integer func;
input integer inp;
func = inp * 2;
endfunction
endpackage
module top;
initial $display(Pkg::X);
endmodule

View File

@ -0,0 +1,8 @@
module top;
function automatic integer func;
input integer inp;
func = inp * 2;
endfunction
localparam integer X = func(1);
initial $display(X);
endmodule

View File

@ -0,0 +1,13 @@
package P;
localparam X = 1;
localparam Y = 2;
typedef enum {
A = X,
B = Y
} Enum;
endpackage
module top;
import P::*;
initial $display("%0d %0d %0d", X, A, B);
endmodule

View File

@ -0,0 +1,7 @@
module top;
localparam X = 1;
localparam Y = 2;
localparam A = X;
localparam B = Y;
initial $display("%0d %0d %0d", X, A, B);
endmodule

View File

@ -0,0 +1,10 @@
package PkgA;
localparam Foo = 1;
endpackage
package PkgB;
export PkgA::*;
localparam Bar = 2;
endpackage
module top;
initial $display(PkgB::Bar);
endmodule

View File

@ -0,0 +1,4 @@
module top;
localparam Bar = 2;
initial $display(Bar);
endmodule

View File

@ -0,0 +1,23 @@
package P;
localparam X = 1;
endpackage
package Q;
import P::X;
export P::*;
localparam Y = 2;
endpackage
package R;
import Q::X;
export Q::*;
localparam Z = 3;
endpackage
package S;
import P::X;
import Q::Y;
import R::Z;
export *::*;
endpackage
module top;
import S::*;
initial $display(X, Y, Z);
endmodule

View File

@ -0,0 +1,6 @@
module top;
localparam X = 1;
localparam Y = 2;
localparam Z = 3;
initial $display(X, Y, Z);
endmodule

View File

@ -0,0 +1,14 @@
package PkgA;
localparam X = 1;
localparam Y = 2;
endpackage
package PkgB;
localparam X = 3;
localparam Z = 4;
endpackage
import PkgA::*;
import PkgB::*;
localparam X = 5;
module top;
initial $display(X, Y, Z);
endmodule

View File

@ -0,0 +1,6 @@
module top;
localparam X = 5;
localparam Y = 2;
localparam Z = 4;
initial $display(X, Y, Z);
endmodule

172
test/basic/package_scope.sv Normal file
View File

@ -0,0 +1,172 @@
`define DUMP(key) initial $display(`"key %0d`", X);
package P;
localparam X = 1;
endpackage
package Q;
localparam X = 2;
endpackage
module ExampleA;
import P::*;
localparam X = 3;
`DUMP(A)
endmodule
module ExampleB;
localparam X = 3;
import P::*;
`DUMP(B)
endmodule
module ExampleC;
import P::*;
`DUMP(C)
endmodule
module ExampleD;
import Q::*;
`DUMP(D)
endmodule
module ExampleE;
import P::*;
import Q::X;
`DUMP(E)
endmodule
module ExampleF;
import Q::X;
import P::*;
`DUMP(F)
endmodule
module ExampleG;
import P::*;
import Q::*;
// allowed but can't reference C
endmodule
package R;
import P::X;
export P::X;
endpackage
package S;
import P::X;
export R::X; // oof but it's allowed
endpackage
module ExampleH;
import R::*;
import S::*;
`DUMP(H)
endmodule
module ExampleI;
import R::X;
import S::X;
`DUMP(I)
endmodule
module ExampleJ;
import R::*;
`DUMP(J)
import S::X;
endmodule
module ExampleK;
import P::X;
if (1) begin : blk1
import P::X;
`DUMP(K1)
end
if (1) begin : blk2
import Q::X;
`DUMP(K2)
end
if (1) begin : blk3
localparam X = 3;
`DUMP(K3)
end
if (1) begin : blk4
import Q::*;
`DUMP(K4)
end
`DUMP(K0)
endmodule
module ExampleL;
import P::X;
import R::X;
`DUMP(L)
endmodule
package T;
import P::X;
export P::*;
endpackage
package U;
import P::*;
export P::X;
endpackage
package V;
import P::*;
export P::*;
localparam Y = X;
endpackage
package W;
import P::*;
export P::*;
task help;
$display("W::help() %0d", X);
endtask
endpackage
module ExampleM;
if (1) begin : blk1
import T::X;
`DUMP(M1)
end
if (1) begin : blk2
import U::X;
`DUMP(M2)
end
if (1) begin : blk3
import V::X;
`DUMP(M3)
end
if (1) begin : blk4
import W::X;
`DUMP(M4)
initial W::help;
initial W::help();
end
endmodule
module ExampleN;
import P::*;
if (1) begin : blk1
import P::X;
`DUMP(N1)
end
import Q::X;
`DUMP(N2)
endmodule
module ExampleO;
import P::*;
if (1) begin : blk1
import P::*;
`DUMP(O1)
end
import Q::X;
`DUMP(O2)
endmodule
module top;
endmodule

View File

@ -0,0 +1,30 @@
module top;
initial begin
$display("A 3");
$display("B 3");
$display("C 1");
$display("D 2");
$display("E 2");
$display("F 2");
// G doesn't print
$display("H 1");
$display("I 1");
$display("J 1");
$display("K1 1");
$display("K2 2");
$display("K3 3");
$display("K4 2");
$display("K0 1");
$display("L 1");
$display("M1 1");
$display("M2 1");
$display("M3 1");
$display("M4 1");
$display("W::help() 1");
$display("W::help() 1");
$display("N1 1");
$display("N2 2");
$display("O1 1");
$display("O2 2");
end
endmodule

View File

@ -0,0 +1,9 @@
// pattern: invalid export Pkg::Foo outside of package
package Pkg;
localparam Foo = 1;
endpackage
import Pkg::Foo;
export Pkg::Foo;
module top;
initial $display(Foo);
endmodule

View File

@ -0,0 +1,9 @@
// pattern: invalid export Pkg::Foo outside of package
package Pkg;
localparam Foo = 1;
endpackage
module top;
import Pkg::Foo;
export Pkg::Foo;
initial $display(Foo);
endmodule

View File

@ -0,0 +1,9 @@
// pattern: invalid export \*::\* outside of package
package Pkg;
localparam Foo = 1;
endpackage
import Pkg::Foo;
export *::*;
module top;
initial $display(Foo);
endmodule

View File

@ -0,0 +1,4 @@
// pattern: could not find package "PackageThatDoesNotExist"
module top;
import PackageThatDoesNotExist::*;
endmodule

View File

@ -0,0 +1,9 @@
// pattern: could not find "ItemThatDoesNotExist" in package "Pkg"
package Pkg;
localparam Foo = 1;
endpackage
module top;
import Pkg::ItemThatDoesNotExist;
endmodule

View File

@ -0,0 +1,12 @@
// pattern: could not find "X" in package "Q"
package P;
localparam X = 1;
endpackage
package Q;
import P::*;
export P::*;
localparam Y = P::X;
endpackage
module top;
initial $display(Q::X);
endmodule

View File

@ -0,0 +1,13 @@
// pattern: could not find "X" in package "Q"
package P;
localparam X = 1;
localparam Y = 2;
endpackage
package Q;
import P::*;
export *::*;
localparam Z = P::Y;
endpackage
module top;
initial $display(Q::X);
endmodule

View File

@ -0,0 +1,11 @@
// pattern: export of PkgA::Bar, but Bar was never imported
package PkgA;
localparam Bar = 2;
endpackage
package PkgB;
export PkgA::Bar;
localparam Foo = 1;
endpackage
module top;
initial $display(PkgB::Foo);
endmodule

View File

@ -0,0 +1,16 @@
// pattern: export of Bar::Foo differs from import of Foo::Foo
package Bar;
localparam Bar = 1;
localparam Foo = 3;
endpackage
package Foo;
localparam Foo = 2;
endpackage
package Pkg;
import Foo::Foo;
import Bar::Bar;
export Bar::Foo;
endpackage
module top;
initial $display(Pkg::Foo);
endmodule

View File

@ -0,0 +1,11 @@
// pattern: could not find "Foo" in package "Bar"
package Bar;
localparam Bar = 1;
endpackage
package Pkg;
import Bar::*;
export Bar::Foo;
endpackage
module top;
initial $display(Pkg::Foo);
endmodule

View File

@ -0,0 +1,10 @@
// pattern: could not find package "PackageThatDoesNotExist"
package Wrap;
import PackageThatDoesNotExist::*;
localparam Foo = Bar;
endpackage
module top;
import Wrap::*;
endmodule

View File

@ -0,0 +1,13 @@
// pattern: could not find "ItemThatDoesNotExist" in package "Pkg"
package Pkg;
localparam Foo = 1;
endpackage
package Wrap;
localparam Foo = Pkg::ItemThatDoesNotExist;
endpackage
module top;
import Wrap::*;
endmodule

View File

@ -0,0 +1,12 @@
// pattern: package dependency loop: "PkgA" depends on "PkgB", which depends on "PkgA"
package PkgA;
import PkgB::Foo;
export PkgB::Foo;
endpackage
package PkgB;
import PkgA::Foo;
export PkgA::Foo;
endpackage
module top;
initial $display(PkgA::Foo);
endmodule

View File

@ -0,0 +1,16 @@
// pattern: package dependency loop: "PkgA" depends on "PkgC", which depends on "PkgB", which depends on "PkgA"
package PkgA;
import PkgC::Foo;
export PkgC::Foo;
endpackage
package PkgB;
import PkgA::Foo;
export PkgA::Foo;
endpackage
package PkgC;
import PkgB::Foo;
export PkgB::Foo;
endpackage
module top;
initial $display(PkgA::Foo);
endmodule

View File

@ -0,0 +1,12 @@
// pattern: identifier "X" ambiguously refers to the definitions in any of P, Q
package P;
localparam X = 1;
endpackage
package Q;
localparam X = 2;
endpackage
module top;
import P::*;
import Q::*;
initial $display(X);
endmodule

View File

@ -0,0 +1,11 @@
// pattern: import of Q::X conflicts with prior import of P::X
package P;
localparam X = 1;
endpackage
package Q;
localparam X = 2;
endpackage
module top;
import P::X;
import Q::X;
endmodule

View File

@ -0,0 +1,8 @@
// pattern: declaration of X conflicts with prior import of P::X
package P;
localparam X = 1;
endpackage
module top;
import P::X;
localparam X = 2;
endmodule

View File

@ -0,0 +1,8 @@
// pattern: import of P::X conflicts with prior declaration of X
package P;
localparam X = 1;
endpackage
module top;
localparam X = 2;
import P::X;
endmodule

View File

@ -0,0 +1,12 @@
// pattern: import of P::X conflicts with prior import of Q::X
package P;
localparam X = 1;
endpackage
package Q;
localparam X = 2;
endpackage
module top;
import Q::*;
initial $display(X); // imports Q::X
import P::X; // illegal
endmodule

View File

@ -0,0 +1,19 @@
// pattern: import of Q::X conflicts with prior import of P::X
package P;
localparam X = 1;
endpackage
package Q;
localparam X = 2;
endpackage
package W;
import P::*;
export P::*;
task help;
$display("W::help() %0d", X);
endtask
import Q::X;
endpackage
module top;
import W::*;
initial $display(X);
endmodule

View File

@ -0,0 +1,15 @@
// pattern: import of Q::X conflicts with prior import of P::X
package P;
localparam X = 1;
endpackage
package Q;
localparam X = 2;
endpackage
module top;
import P::*;
if (1) begin : blk1
// forces import of P::X at the top level
initial $display(X);
end
import Q::X; // illegal
endmodule

View File

@ -0,0 +1,12 @@
// pattern: identifier "X" ambiguously refers to the definitions in any of PkgA, PkgB
package PkgA;
localparam X = 1;
endpackage
package PkgB;
localparam X = 3;
endpackage
import PkgA::*;
import PkgB::*;
module top;
initial $display(X);
endmodule

View File

@ -0,0 +1,8 @@
// pattern: package dependency loop: "Pkg" depends on "Pkg"
package Pkg;
localparam Foo = 1;
export Pkg::Foo;
endpackage
module top;
initial $display(Pkg::Foo);
endmodule

View File

@ -0,0 +1,9 @@
// pattern: package dependency loop: "P" depends on "P"
package P;
import P::*;
localparam Foo = 1;
endpackage
module top;
import P::*;
initial $display(Foo);
endmodule

View File

@ -0,0 +1,8 @@
// pattern: package dependency loop: "P" depends on "P"
package P;
localparam Foo = P::Foo;
endpackage
module top;
import P::*;
initial $display(Foo);
endmodule