sv2v/src/Convert/Interface.hs

851 lines
37 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, disambiguate) where
2019-03-07 02:30:47 +01:00
import Data.List (intercalate, (\\))
import Data.Maybe (isJust, isNothing, mapMaybe)
2020-08-12 01:14:18 +02:00
import Control.Monad.Writer.Strict
import Text.Read (readMaybe)
2019-03-07 02:30:47 +01:00
import qualified Data.Map.Strict as Map
import Convert.ExprUtils (endianCondExpr)
import Convert.Scoper
2019-03-07 02:30:47 +01:00
import Convert.Traverse
import Language.SystemVerilog.AST
data PartInfo = PartInfo
{ pKind :: PartKW
, pPorts :: [Identifier]
, pItems :: [ModuleItem]
}
type PartInfos = Map.Map Identifier PartInfo
type ModportInstances = [(Identifier, (Identifier, Identifier))]
type ModportBinding = (Identifier, (Substitutions, Expr))
type Substitutions = [(Expr, Expr)]
2019-03-07 02:30:47 +01:00
convert :: [Identifier] -> [AST] -> [AST]
convert tops files =
if needsFlattening
then files
else traverseFiles
(collectDescriptionsM collectPart)
(map . convertDescription tops)
files
2019-03-07 02:30:47 +01:00
where
-- multidimensional instances need to be flattened before this
-- conversion can proceed
needsFlattening =
getAny $ execWriter $ mapM (collectDescriptionsM checkPart) files
checkPart :: Description -> Writer Any ()
checkPart (Part _ _ _ _ _ _ items) =
mapM (collectNestedModuleItemsM checkItem) items >> return ()
checkPart _ = return ()
checkItem :: ModuleItem -> Writer Any ()
checkItem (Instance _ _ _ rs _) = when (length rs > 1) $ tell $ Any True
checkItem _ = return ()
-- we can only collect/map non-extern interfaces and modules
collectPart :: Description -> Writer PartInfos ()
collectPart (Part _ False kw _ name ports items) =
tell $ Map.singleton name $ PartInfo kw ports items
collectPart _ = return ()
-- disambiguate typenames from interface names
disambiguate :: [AST] -> [AST]
disambiguate = traverseFiles
(collectDescriptionsM collectPart)
(map . disambiguateDescription)
-- disambiguate any typenames within a description
disambiguateDescription :: PartInfos -> Description -> Description
disambiguateDescription parts (Part att ext kw lif name ports items) =
Part att ext kw lif name ports $ map traverseModuleItem items
where
typeNames = getTypeNames items
traverseModuleItem :: ModuleItem -> ModuleItem
traverseModuleItem (MIAttr attr item) =
MIAttr attr $ traverseModuleItem item
traverseModuleItem (MIPackageItem (Decl (Variable d t x a e))) =
MIPackageItem $ Decl $ Variable d (traverseType t) x a e
traverseModuleItem other = other
traverseType :: Type -> Type
traverseType (Alias interfaceName rs) =
if isInterface interfaceName && not (elem interfaceName typeNames)
then InterfaceT interfaceName "" rs
else Alias interfaceName rs
traverseType orig@(InterfaceT interfaceName _ _) =
if null interfaceName || isInterface interfaceName
then orig
else error $ "declaration type " ++ show orig ++ " appears to "
++ "refer to an interface that isn't defined"
traverseType other = other
isInterface :: Identifier -> Bool
isInterface partName =
fmap pKind (Map.lookup partName parts) == Just Interface
disambiguateDescription _ other = other
-- get all of the typenames declared anywhere in the top-level module items
getTypeNames :: [ModuleItem] -> [Identifier]
getTypeNames (MIAttr _ item : rest) = getTypeNames $ item : rest
getTypeNames (Generate genItems : rest) =
getTypeNames $ genModuleItems genItems ++ rest
getTypeNames (MIPackageItem (Decl (ParamType _ name _)) : rest) =
name : getTypeNames rest
getTypeNames (_ : rest) = getTypeNames rest
getTypeNames [] = []
-- get the top-level (i.e., un-scoped) module items within a generate block
genModuleItems :: [GenItem] -> [ModuleItem]
genModuleItems (GenModuleItem item : rest) =
item : genModuleItems rest
genModuleItems (_ : rest) = genModuleItems rest
genModuleItems [] = []
topInterfaceError :: String -> String -> a
topInterfaceError name issue = error $
"Specified top module " ++ name ++ " " ++ issue ++ ". Please " ++
"instantiate it somewhere and use that as your top module instead."
convertDescription :: [Identifier] -> PartInfos -> Description -> Description
convertDescription tops _ (Part _ _ Interface _ name _ _)
| elem name tops =
topInterfaceError name "is an interface"
| otherwise =
PackageItem $ Decl $ CommentDecl $ "removed interface: " ++ name
convertDescription tops parts (Part att ext Module lif name ports items) =
if null $ extractModportInstances name $ PartInfo Module ports items then
Part att ext Module lif name ports items'
else if elem name tops then
topInterfaceError name "has interface ports"
else
PackageItem $ Decl $ CommentDecl $
"removed module with interface ports: " ++ name
2019-03-07 02:30:47 +01:00
where
items' = evalScoper $ scopeModuleItems scoper name items
scoper = scopeModuleItem traverseDeclM traverseModuleItemM return return
traverseDeclM :: Decl -> Scoper [ModportDecl] Decl
traverseDeclM decl = do
case decl of
2023-05-09 05:55:58 +02:00
Variable _ t x _ _ -> checkDeclType t x >> insertElem x DeclVal
Net _ _ _ t x _ _ -> checkDeclType t x >> insertElem x DeclVal
Param _ t x _ -> checkDeclType t x >> insertElem x DeclVal
ParamType _ x _ -> insertElem x DeclVal
CommentDecl{} -> return ()
return decl
-- check for module or interface names used as type names
2023-05-09 05:55:58 +02:00
checkDeclType :: Type -> Identifier -> Scoper a ()
checkDeclType (Alias typeName _) declName
| isNothing (readMaybe declName :: Maybe Int)
, Just part <- Map.lookup typeName parts = do
maybeType <- lookupElemM typeName
when (isNothing maybeType) $ scopedErrorM $
"declaration " ++ declName ++ " uses " ++ show (pKind part)
++ " name " ++ typeName ++ " where a type name is expected"
checkDeclType _ _ = return ()
lookupIntfElem :: Scopes [ModportDecl] -> Expr -> LookupResult [ModportDecl]
lookupIntfElem modports expr =
case lookupElem modports expr of
Just (_, _, DeclVal) -> Nothing
other -> other
traverseModuleItemM :: ModuleItem -> Scoper [ModportDecl] ModuleItem
traverseModuleItemM (Modport modportName modportDecls) =
insertElem modportName modportDecls >> return (Generate [])
traverseModuleItemM instanceItem@Instance{} = do
modports <- embedScopes (\l () -> l) ()
if isNothing maybePartInfo then
return instanceItem
else if partKind == Interface then
-- inline instantiation of an interface
scoper $ Generate $ map GenModuleItem $
inlineInstance modports rs []
partItems part instanceName paramBindings portBindings
else if null modportInstances then
return instanceItem
else do
-- inline instantiation of a module
let modportBindings = getModportBindings modports
let unconnected = map fst modportInstances \\
map fst modportBindings
if not (null unconnected)
then scopedErrorM $ "instance " ++ instanceName ++ " of "
++ part ++ " has unconnected interface ports: "
++ intercalate ", " unconnected
else scoper $ Generate $ map GenModuleItem $
inlineInstance modports rs modportBindings partItems
part instanceName paramBindings portBindings
2020-06-20 20:39:57 +02:00
where
Instance part paramBindings instanceName rs portBindings =
instanceItem
maybePartInfo = Map.lookup part parts
Just partInfo = maybePartInfo
PartInfo partKind _ partItems = partInfo
modportInstances = extractModportInstances part partInfo
getModportBindings modports = mapMaybe
(inferModportBinding modports modportInstances) $
map (second $ addImpliedSlice modports) portBindings
second f = \(a, b) -> (a, f b)
traverseModuleItemM other = return other
-- add explicit slices for bindings of entire modport instance arrays
addImpliedSlice :: Scopes [ModportDecl] -> Expr -> Expr
addImpliedSlice modports orig@(Dot expr modportName) =
case lookupIntfElem modports (InstArrKey expr) of
Just (_, _, InstArrVal l r) ->
Dot (Range expr NonIndexed (l, r)) modportName
_ -> orig
addImpliedSlice modports expr =
case lookupIntfElem modports (InstArrKey expr) of
Just (_, _, InstArrVal l r) ->
Range expr NonIndexed (l, r)
_ -> expr
-- elaborates and resolves provided modport bindings
inferModportBinding :: Scopes [ModportDecl] -> ModportInstances ->
PortBinding -> Maybe ModportBinding
inferModportBinding modports modportInstances (portName, expr) =
if maybeInfo == Nothing
then Nothing
else Just (portName, modportBinding)
where
2024-04-16 14:40:26 +02:00
modportBinding =
( substitutions
, scopeExprWithScopes modports $ replaceBit modportE
)
substitutions =
genSubstitutions modports base instanceE modportE
maybeInfo =
lookupModportBinding modports modportInstances portName bitd
Just (instanceE, modportE) = maybeInfo
(exprUndot, bitd) = case expr of
Dot subExpr x -> (subExpr, Dot bitdUndot x)
_ -> (expr, bitdUndot)
bitdUndot = case exprUndot of
Range subExpr _ _ -> Bit subExpr taggedOffset
Bit subExpr _ -> Bit subExpr untaggedOffset
_ -> exprUndot
bitReplacement = case exprUndot of
Range _ mode range -> \e -> Range e mode range
Bit _ idx -> flip Bit idx
_ -> id
base = case exprUndot of
Range{} -> Bit (Ident portName) Tag
_ -> Ident portName
untaggedOffset = Ident $ modportBaseName portName
taggedOffset = BinOp Add Tag untaggedOffset
replaceBit :: Expr -> Expr
replaceBit (Bit subExpr idx) =
if idx == untaggedOffset || idx == taggedOffset
then bitReplacement subExpr
else Bit subExpr idx
replaceBit (Dot subExpr x) =
Dot (replaceBit subExpr) x
replaceBit (Ident x) = Ident x
replaceBit _ = error "replaceBit invariant violated"
-- determines the underlying modport and interface instances associated
-- with the given port binding, if it is a modport binding
lookupModportBinding :: Scopes [ModportDecl] -> ModportInstances
-> Identifier -> Expr -> Maybe (Expr, Expr)
lookupModportBinding modports modportInstances portName expr =
if bindingIsModport then
-- provided specific instance modport
foundModport expr
else if bindingIsBundle && portIsBundle then
-- bundle bound to a generic bundle
foundModport expr
else if bindingIsBundle && not portIsBundle then
-- given entire interface, but just bound to a modport
foundModport $ Dot expr modportName
else if modportInstance /= Nothing then
scopedError modports $ "could not resolve modport binding "
++ show expr ++ " for port " ++ portName ++ " of type "
++ showModportType interfaceName modportName
else
Nothing
where
bindingIsModport = lookupIntfElem modports expr /= Nothing
bindingIsBundle = lookupIntfElem modports (Dot expr "") /= Nothing
portIsBundle = null modportName
modportInstance = lookup portName modportInstances
(interfaceName, modportName) =
case modportInstance of
Just x -> x
Nothing -> scopedError modports $
"can't deduce modport for interface " ++ show expr
++ " bound to port " ++ portName
foundModport modportE =
if (null interfaceName || bInterfaceName == interfaceName)
&& (null modportName || bModportName == modportName)
then Just (instanceE, qualifyModport modportE)
else scopedError modports msg
where
bModportName =
case modportE of
Dot _ x -> x
_ -> ""
instanceE = findInstance modportE
Just (_, _, InterfaceTypeVal bInterfaceName) =
lookupIntfElem modports $ InterfaceTypeKey
(findInstance modportE)
msg = "port " ++ portName ++ " has type "
++ showModportType interfaceName modportName
++ ", but the binding " ++ show expr ++ " has type "
++ showModportType bInterfaceName bModportName
findInstance :: Expr -> Expr
findInstance e =
case lookupIntfElem modports (Dot e "") of
Nothing -> case e of
Bit e' _ -> findInstance e'
Dot e' _ -> findInstance e'
_ -> error "internal invariant violated"
Just (accesses, _, _) -> accessesToExpr $ init accesses
qualifyModport :: Expr -> Expr
qualifyModport e =
accessesToExpr $
case lookupIntfElem modports e of
Just (accesses, _, _) -> accesses
Nothing ->
case lookupIntfElem modports (Dot e "") of
Just (accesses, _, _) -> init accesses
Nothing -> scopedError modports $
"could not find modport " ++ show e
showModportType :: Identifier -> Identifier -> String
showModportType "" "" = "generic interface"
showModportType intf "" = intf
showModportType intf modp = intf ++ '.' : modp
-- expand a modport binding into a series of expression substitutions
genSubstitutions :: Scopes [ModportDecl] -> Expr -> Expr -> Expr
-> [(Expr, Expr)]
genSubstitutions modports baseE instanceE modportE =
(baseE, instanceE) :
map toPortBinding modportDecls
2019-10-21 04:06:10 +02:00
where
a = lookupIntfElem modports modportE
b = lookupIntfElem modports (Dot modportE "")
Just (_, replacements, modportDecls) =
if a == Nothing then b else a
toPortBinding (_, x, e) = (x', e')
where
x' = Dot baseE x
e' = replaceInExpr replacements e
-- association list of modport instances in the given module body
extractModportInstances :: Identifier -> PartInfo -> ModportInstances
extractModportInstances part partInfo =
execWriter $ runScoperT $ scopeModuleItems collector part decls
where
collector = scopeModuleItem checkDecl return return return
decls = filter isDecl $ pItems partInfo
checkDecl :: Decl -> ScoperT () (Writer ModportInstances) Decl
checkDecl decl@(Variable _ t x _ _) =
if maybeInfo == Nothing then
return decl
else if elem x (pPorts partInfo) then
tell [(x, info)] >> return decl
else
scopedErrorM $
"Modport not in port list: " ++ show t ++ " " ++ x
++ ". Is this an interface missing a port list?"
2020-06-20 20:39:57 +02:00
where
maybeInfo = extractModportInfo t
Just info = maybeInfo
checkDecl decl = return decl
2019-03-07 02:30:47 +01:00
extractModportInfo :: Type -> Maybe (Identifier, Identifier)
2021-06-02 21:25:02 +02:00
extractModportInfo (InterfaceT interfaceName modportName _) =
Just (interfaceName, modportName)
extractModportInfo _ = Nothing
convertDescription _ _ other = other
isDecl :: ModuleItem -> Bool
isDecl (MIPackageItem Decl{}) = True
isDecl _ = False
-- produce the implicit modport decls for an interface bundle
impliedModport :: [ModuleItem] -> [ModportDecl]
impliedModport =
execWriter . mapM
(collectNestedModuleItemsM $ collectDeclsM collectModportDecls)
where
collectModportDecls :: Decl -> Writer [ModportDecl] ()
collectModportDecls (Variable _ _ x _ _) =
tell [(Inout, x, Ident x)]
collectModportDecls (Net _ _ _ _ x _ _) =
tell [(Inout, x, Ident x)]
collectModportDecls _ = return ()
-- convert an interface-bound module instantiation or an interface instantiation
-- into a series of equivalent inlined module items
inlineInstance :: Scopes [ModportDecl] -> [Range] -> [ModportBinding]
-> [ModuleItem] -> Identifier -> Identifier -> [ParamBinding]
-> [PortBinding] -> [ModuleItem]
inlineInstance global ranges modportBindings items partName
instanceName instanceParams instancePorts =
comment :
map (MIPackageItem . Decl) bindingBaseParams ++
map (MIPackageItem . Decl) parameterBinds ++
wrapInstance instanceName items'
: portBindings
where
items' = evalScoper $ scopeModuleItems scoper partName $
map (traverseNestedModuleItems rewriteItem) $
if null modportBindings
then itemsChecked ++ infoModports
else itemsChecked
itemsChecked = checkBeforeInline global partName items checkErrMsg
infoModports = [typeModport, dimensionModport, bundleModport]
scoper = scopeModuleItem
traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM
key = shortHash (partName, instanceName)
-- synthetic modports to be collected and removed after inlining
bundleModport = Modport "" (impliedModport items)
dimensionModport = if not isArray
then Generate []
else InstArrEncoded arrayLeft arrayRight
typeModport = InterfaceTypeEncoded partName
inlineKind =
if null modportBindings
then "interface"
else "module"
comment = MIPackageItem $ Decl $ CommentDecl $
"expanded " ++ inlineKind ++ " instance: " ++ instanceName
portBindings =
wrapPortBindings $
map portBindingItem $
filter ((/= Nil) . snd) $
filter notSubstituted instancePorts
notSubstituted :: PortBinding -> Bool
notSubstituted (portName, _) =
lookup portName modportBindings == Nothing
wrapPortBindings :: [ModuleItem] -> [ModuleItem]
wrapPortBindings =
if isArray
then (\x -> [x]) . wrapInstance blockName
else id
where blockName = instanceName ++ "_port_bindings"
rewriteItem :: ModuleItem -> ModuleItem
rewriteItem =
traverseDecls $
removeModportInstance .
removeDeclDir .
overrideParam
traverseDeclM :: Decl -> Scoper () Decl
traverseDeclM decl = do
case decl of
Variable _ _ x _ _ -> insertElem x ()
Net _ _ _ _ x _ _ -> insertElem x ()
Param _ _ x _ -> insertElem x ()
ParamType _ x _ -> insertElem x ()
CommentDecl{} -> return ()
traverseDeclExprsM traverseExprM decl
traverseModuleItemM :: ModuleItem -> Scoper () ModuleItem
traverseModuleItemM item@Modport{} =
traverseExprsM (scopeExpr >=> traverseExprM) item
traverseModuleItemM item@(Instance _ _ x _ _) =
insertElem x () >> traverseExprsM traverseExprM item
traverseModuleItemM item =
traverseExprsM traverseExprM item >>=
traverseLHSsM traverseLHSM
traverseGenItemM :: GenItem -> Scoper () GenItem
traverseGenItemM item@(GenFor (x, _) _ _ _) = do
-- don't want to be scoped in modports
insertElem x ()
item' <- traverseGenItemExprsM traverseExprM item
removeElem x
return item'
traverseGenItemM item =
traverseGenItemExprsM traverseExprM item
traverseStmtM :: Stmt -> Scoper () Stmt
traverseStmtM =
traverseStmtExprsM traverseExprM >=>
traverseStmtLHSsM traverseLHSM
-- used for replacing usages of modports in the module being inlined
modportSubstitutions = concatMap (fst . snd) modportBindings
lhsReplacements = map (\(x, y) -> (toLHS x, toLHS y)) exprReplacements
exprReplacements = filter ((/= Nil) . snd) modportSubstitutions
-- LHSs are replaced using simple substitutions
traverseLHSM :: LHS -> Scoper () LHS
traverseLHSM =
fmap replaceLHS .
embedScopes tagLHS
tagLHS :: Scopes () -> LHS -> LHS
tagLHS scopes lhs
| lookupElem scopes lhs /= Nothing =
LHSDot (renamePartLHS lhs) "@"
| Just portName <- partScopedModportRef $ lhsToExpr lhs =
LHSIdent portName
| otherwise =
traverseSinglyNestedLHSs (tagLHS scopes) lhs
renamePartLHS :: LHS -> LHS
renamePartLHS (LHSDot (LHSIdent x) y) =
if x == partName
then LHSDot scopedInstanceLHS y
else LHSDot (LHSIdent x) y
renamePartLHS lhs = traverseSinglyNestedLHSs renamePartLHS lhs
replaceLHS :: LHS -> LHS
replaceLHS (LHSDot lhs "@") = lhs
replaceLHS (LHSDot (LHSBit lhs elt) field) =
case lookup (LHSDot (LHSBit lhs Tag) field) lhsReplacements of
Just resolved -> replaceLHSArrTag elt resolved
Nothing -> LHSDot (replaceLHS $ LHSBit lhs elt) field
replaceLHS lhs =
case lookup lhs lhsReplacements of
Just lhs' -> lhs'
Nothing -> traverseSinglyNestedLHSs replaceLHS lhs
replaceLHSArrTag :: Expr -> LHS -> LHS
replaceLHSArrTag =
traverseNestedLHSs . (traverseLHSExprs . replaceArrTag)
-- top-level expressions may be modports bound to other modports
traverseExprM :: Expr -> Scoper () Expr
traverseExprM =
fmap replaceExpr .
embedScopes tagExpr
tagExpr :: Scopes () -> Expr -> Expr
tagExpr scopes expr
| lookupElem scopes expr /= Nothing =
Dot (renamePartExpr expr) "@"
| Just portName <- partScopedModportRef expr =
Ident portName
| otherwise =
visitExprsStep (tagExpr scopes) expr
renamePartExpr :: Expr -> Expr
renamePartExpr (Dot (Ident x) y) =
if x == partName
then Dot scopedInstanceExpr y
else Dot (Ident x) y
renamePartExpr expr = visitExprsStep renamePartExpr expr
replaceExpr :: Expr -> Expr
replaceExpr (Dot expr "@") = expr
replaceExpr (Ident x) =
case lookup x modportBindings of
Just (_, m) -> m
Nothing -> Ident x
replaceExpr expr =
replaceExpr' expr
replaceExpr' :: Expr -> Expr
replaceExpr' (Dot expr "@") = expr
replaceExpr' (Dot (Bit expr elt) field) =
case lookup (Dot (Bit expr Tag) field) exprReplacements of
Just resolved -> replaceArrTag (replaceExpr' elt) resolved
Nothing -> Dot (replaceExpr' $ Bit expr elt) field
replaceExpr' (Bit expr elt) =
case lookup (Bit expr Tag) exprReplacements of
Just resolved -> replaceArrTag (replaceExpr' elt) resolved
Nothing -> Bit (replaceExpr' expr) (replaceExpr' elt)
replaceExpr' expr@(Dot Ident{} _) =
case lookup expr exprReplacements of
Just expr' -> expr'
Nothing -> visitExprsStep replaceExprAny expr
replaceExpr' (Ident x) = Ident x
replaceExpr' expr = replaceExprAny expr
replaceExprAny :: Expr -> Expr
replaceExprAny expr =
case lookup expr exprReplacements of
Just expr' -> expr'
Nothing -> visitExprsStep replaceExpr' expr
replaceArrTag :: Expr -> Expr -> Expr
replaceArrTag replacement Tag = replacement
replaceArrTag replacement expr =
visitExprsStep (replaceArrTag replacement) expr
partScopedModportRef :: Expr -> Maybe Identifier
partScopedModportRef (Dot (Ident x) y) =
if x == partName && lookup y modportBindings /= Nothing
then Just y
else Nothing
partScopedModportRef _ = Nothing
visitExprsStep :: (Expr -> Expr) -> Expr -> Expr
visitExprsStep exprMapper =
traverseSinglyNestedExprs exprMapper
. traverseExprTypes (traverseNestedTypes typeMapper)
where typeMapper = traverseTypeExprs exprMapper
checkErrMsg :: String -> String
checkErrMsg exprStr = "inlining instance \"" ++ instanceName
++ "\" of " ++ inlineKind ++ " \"" ++ partName
++ "\" would make expression \"" ++ exprStr ++ "\" used in \""
++ instanceName ++ "\" resolvable when it wasn't previously"
-- unambiguous reference to the current instance
scopedInstanceRaw = accessesToExpr $ localAccesses global instanceName
scopedInstanceExpr =
if isArray
then Bit scopedInstanceRaw (Ident loopVar)
else scopedInstanceRaw
Just scopedInstanceLHS = exprToLHS scopedInstanceExpr
removeModportInstance :: Decl -> Decl
removeModportInstance (Variable d t x a e) =
if maybeModportBinding == Nothing then
Variable d t x a e
else if makeBindingBaseExpr modportE == Nothing then
CommentDecl $ "removed modport instance " ++ x
2021-06-02 21:25:02 +02:00
else if null modportDims then
localparam (modportBaseName x) bindingBaseExpr
else
localparam (modportBaseName x) $
2021-06-02 21:25:02 +02:00
BinOp Sub bindingBaseExpr (sliceLo NonIndexed modportDim)
where
maybeModportBinding = lookup x modportBindings
Just (_, modportE) = maybeModportBinding
bindingBaseExpr = Ident $ bindingBaseName ++ x
2021-06-02 21:25:02 +02:00
modportDims = a ++ snd (typeRanges t)
[modportDim] = modportDims
removeModportInstance other = other
2019-03-07 02:30:47 +01:00
removeDeclDir :: Decl -> Decl
removeDeclDir (Variable _ t x a e) =
Variable Local t' x a e
where t' = case t of
Implicit Unspecified rs ->
IntegerVector TLogic Unspecified rs
_ -> t
removeDeclDir decl@Net{} =
traverseNetAsVar removeDeclDir decl
2019-04-23 23:12:56 +02:00
removeDeclDir other = other
-- capture the lower bound for each modport array binding
bindingBaseParams = mapMaybe makeBindingBaseParam modportBindings
makeBindingBaseParam :: ModportBinding -> Maybe Decl
makeBindingBaseParam (portName, (_, modportE)) =
fmap (localparam $ bindingBaseName ++ portName) $
makeBindingBaseExpr modportE
bindingBaseName = "_bbase_" ++ key ++ "_"
makeBindingBaseExpr :: Expr -> Maybe Expr
makeBindingBaseExpr modportE =
case modportE of
Dot (Range _ mode range) _ -> Just $ sliceLo mode range
Range _ mode range -> Just $ sliceLo mode range
Dot (Bit _ idx) _ -> Just idx
Bit _ idx -> Just idx
_ -> Nothing
localparam :: Identifier -> Expr -> Decl
localparam = Param Localparam (Implicit Unspecified [])
paramTmp = "_param_" ++ key ++ "_"
parameterBinds = map makeParameterBind instanceParams
makeParameterBind :: ParamBinding -> Decl
makeParameterBind (x, Left t) =
ParamType Localparam (paramTmp ++ x) t
makeParameterBind (x, Right e) =
Param Localparam UnknownType (paramTmp ++ x) e
overrideParam :: Decl -> Decl
overrideParam (Param Parameter t x e) =
Param Localparam t x $
case lookup x instanceParams of
Nothing -> e
Just _ -> Ident $ paramTmp ++ x
overrideParam (ParamType Parameter x t) =
ParamType Localparam x $
case lookup x instanceParams of
Nothing -> t
Just _ -> Alias (paramTmp ++ x) []
overrideParam other = other
portBindingItem :: PortBinding -> ModuleItem
2020-06-14 21:56:09 +02:00
portBindingItem (ident, expr) =
if findDeclDir ident == Input
then bind (LHSDot (inj LHSBit LHSIdent) ident) expr
else bind (toLHS expr) (Dot (inj Bit Ident) ident)
where
bind = Assign AssignOptionNone
inj bit idn = if null ranges
then idn instanceName
else bit (idn instanceName) (Ident loopVar)
declDirs = execWriter $
mapM (collectDeclsM collectDeclDir) items
collectDeclDir :: Decl -> Writer (Map.Map Identifier Direction) ()
collectDeclDir (Variable dir _ ident _ _) =
2020-06-20 22:41:13 +02:00
when (dir /= Local) $
tell $ Map.singleton ident dir
collectDeclDir net@Net{} =
collectNetAsVarM collectDeclDir net
collectDeclDir _ = return ()
findDeclDir :: Identifier -> Direction
findDeclDir ident =
case Map.lookup ident declDirs of
Nothing -> error $ "could not find decl dir of " ++ ident
++ " among " ++ show declDirs
Just dir -> dir
toLHS :: Expr -> LHS
toLHS expr =
case exprToLHS expr of
Just lhs -> lhs
Nothing -> error $ "trying to bind an " ++ inlineKind
++ " output to " ++ show expr ++ " but that can't be an LHS"
-- for instance arrays, a unique identifier to be used as a genvar
loopVar = "_arr_" ++ key
isArray = not $ null ranges
[arrayRange@(arrayLeft, arrayRight)] = ranges
-- wrap the given item in a generate loop if necessary
wrapInstance :: Identifier -> [ModuleItem] -> ModuleItem
wrapInstance blockName moduleItems =
Generate $
if not isArray then
[item]
else
[ GenModuleItem (Genvar loopVar)
, GenFor inits cond incr item
]
where
item = GenBlock blockName $ map GenModuleItem moduleItems
inits = (loopVar, arrayLeft)
cond = endianCondExpr arrayRange
(BinOp Ge (Ident loopVar) arrayRight)
(BinOp Le (Ident loopVar) arrayRight)
incr = (loopVar, AsgnOp Add, step)
step = endianCondExpr arrayRange
(UniOp UniSub $ RawNum 1) (RawNum 1)
-- used for modport array binding offset placeholders
pattern Tag :: Expr
pattern Tag = Ident "%"
modportBaseName :: Identifier -> Identifier
modportBaseName = (++) "_mbase_"
-- the dimensions of interface instance arrays are encoded as synthetic modports
-- during inlining, enabling subsequent modport bindings to implicitly use the
-- bounds of the interface instance array when the bounds are unspecified
pattern InstArrName :: Identifier
pattern InstArrName = "~instance_array_dimensions~"
pattern InstArrVal :: Expr -> Expr -> [ModportDecl]
pattern InstArrVal l r = [(Local, "l", l), (Local, "r", r)]
pattern InstArrKey :: Expr -> Expr
pattern InstArrKey expr = Dot (Bit expr (RawNum 0)) InstArrName
pattern InstArrEncoded :: Expr -> Expr -> ModuleItem
pattern InstArrEncoded l r = Modport InstArrName (InstArrVal l r)
-- encoding for normal declarations in the current module
pattern DeclVal :: [ModportDecl]
pattern DeclVal = [(Local, "~decl~", Nil)]
-- encoding for the interface type of an interface instantiation
pattern InterfaceTypeName :: Identifier
pattern InterfaceTypeName = "~interface_type~"
pattern InterfaceTypeVal :: Identifier -> [ModportDecl]
pattern InterfaceTypeVal x = [(Local, "~interface~type~", Ident x)]
pattern InterfaceTypeKey :: Expr -> Expr
pattern InterfaceTypeKey e = Dot e InterfaceTypeName
pattern InterfaceTypeEncoded :: Identifier -> ModuleItem
pattern InterfaceTypeEncoded x = Modport InterfaceTypeName (InterfaceTypeVal x)
-- determines the lower bound for the given slice
sliceLo :: PartSelectMode -> Range -> Expr
sliceLo NonIndexed (l, r) = endianCondExpr (l, r) r l
sliceLo IndexedPlus (base, _) = base
sliceLo IndexedMinus (base, len) = BinOp Add (BinOp Sub base len) (RawNum 1)
-- check for cases where an expression in an inlined part only resolves after
-- inlining, potentially hiding a design error
checkBeforeInline :: Scopes a -> Identifier -> [ModuleItem]
-> (String -> String) -> [ModuleItem]
checkBeforeInline global partName items checkErrMsg =
evalScoper $ scopeModuleItems scoper partName $ items
where
scoper = scopeModuleItem
checkDecl checkModuleItem checkGenItem checkStmt
checkDecl :: Decl -> Scoper () Decl
checkDecl decl = do
case decl of
Variable _ _ x _ _ -> insertElem x ()
Net _ _ _ _ x _ _ -> insertElem x ()
Param _ _ x _ -> insertElem x ()
ParamType _ x _ -> insertElem x ()
CommentDecl{} -> return ()
traverseDeclExprsM checkExpr decl
checkModuleItem :: ModuleItem -> Scoper () ModuleItem
checkModuleItem item@(Instance _ _ x _ _) =
insertElem x () >> traverseExprsM checkExpr item
checkModuleItem item =
traverseExprsM checkExpr item >>=
traverseLHSsM checkLHS
checkGenItem :: GenItem -> Scoper () GenItem
checkGenItem = traverseGenItemExprsM checkExpr
checkStmt :: Stmt -> Scoper () Stmt
checkStmt =
traverseStmtExprsM checkExpr >=>
traverseStmtLHSsM checkLHS
checkExpr :: Expr -> Scoper () Expr
checkExpr = embedScopes checkExprResolutionId
checkLHS :: LHS -> Scoper () LHS
checkLHS = embedScopes checkLHSResolutionId
checkLHSResolutionId :: Scopes () -> LHS -> LHS
checkLHSResolutionId local lhs = checkExprResolution local expr lhs
where expr = lhsToExpr lhs
checkExprResolutionId :: Scopes () -> Expr -> Expr
checkExprResolutionId local expr = checkExprResolution local expr expr
-- error if the given expression resolves globally but not locally
checkExprResolution :: Scopes () -> Expr -> a -> a
checkExprResolution local expr =
if exprResolves global expr && not (anyPrefixResolves local expr)
then scopedError local $ checkErrMsg $ show expr
else id
-- check if hierarchical prefix of an expr exists in the given scope
anyPrefixResolves :: Scopes () -> Expr -> Bool
anyPrefixResolves local expr =
exprResolves local expr ||
case expr of
Dot inner _ -> anyPrefixResolves local inner
Bit inner _ -> anyPrefixResolves local inner
_ -> False
-- check if expr exists in the given scope
exprResolves :: Scopes a -> Expr -> Bool
exprResolves local (Ident x) =
isJust (lookupElem local x) || isLoopVar local x
exprResolves local expr =
isJust (lookupElem local expr)