Commit 59b416f9 by Zachary Snow

isolate interface name resolution checks

parent 6e8659a5
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
* Fixed signed `struct` fields being converted to unsigned expressions when * Fixed signed `struct` fields being converted to unsigned expressions when
accessed directly accessed directly
* Fixed conversion of casts using structs containing multi-dimensional fields * Fixed conversion of casts using structs containing multi-dimensional fields
* Fixed incorrect name resolution conflicts raised during interface inlining
## v0.0.9 ## v0.0.9
......
...@@ -346,8 +346,10 @@ inlineInstance global ranges modportBindings items partName ...@@ -346,8 +346,10 @@ inlineInstance global ranges modportBindings items partName
items' = evalScoper $ scopeModuleItems scoper partName $ items' = evalScoper $ scopeModuleItems scoper partName $
map (traverseNestedModuleItems rewriteItem) $ map (traverseNestedModuleItems rewriteItem) $
if null modportBindings if null modportBindings
then items ++ [typeModport, dimensionModport, bundleModport] then itemsChecked ++ infoModports
else items else itemsChecked
itemsChecked = checkBeforeInline global partName items checkErrMsg
infoModports = [typeModport, dimensionModport, bundleModport]
scoper = scopeModuleItem scoper = scopeModuleItem
traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM
...@@ -430,8 +432,8 @@ inlineInstance global ranges modportBindings items partName ...@@ -430,8 +432,8 @@ inlineInstance global ranges modportBindings items partName
-- LHSs are replaced using simple substitutions -- LHSs are replaced using simple substitutions
traverseLHSM :: LHS -> Scoper () LHS traverseLHSM :: LHS -> Scoper () LHS
traverseLHSM = traverseLHSM =
embedScopes tagLHS >=> fmap replaceLHS .
embedScopes replaceLHS embedScopes tagLHS
tagLHS :: Scopes () -> LHS -> LHS tagLHS :: Scopes () -> LHS -> LHS
tagLHS scopes lhs tagLHS scopes lhs
| lookupElem scopes lhs /= Nothing = | lookupElem scopes lhs /= Nothing =
...@@ -446,25 +448,24 @@ inlineInstance global ranges modportBindings items partName ...@@ -446,25 +448,24 @@ inlineInstance global ranges modportBindings items partName
then LHSDot scopedInstanceLHS y then LHSDot scopedInstanceLHS y
else LHSDot (LHSIdent x) y else LHSDot (LHSIdent x) y
renamePartLHS lhs = traverseSinglyNestedLHSs renamePartLHS lhs renamePartLHS lhs = traverseSinglyNestedLHSs renamePartLHS lhs
replaceLHS :: Scopes () -> LHS -> LHS replaceLHS :: LHS -> LHS
replaceLHS _ (LHSDot lhs "@") = lhs replaceLHS (LHSDot lhs "@") = lhs
replaceLHS local (LHSDot (LHSBit lhs elt) field) = replaceLHS (LHSDot (LHSBit lhs elt) field) =
case lookup (LHSDot (LHSBit lhs Tag) field) lhsReplacements of case lookup (LHSDot (LHSBit lhs Tag) field) lhsReplacements of
Just resolved -> replaceLHSArrTag elt resolved Just resolved -> replaceLHSArrTag elt resolved
Nothing -> LHSDot (replaceLHS local $ LHSBit lhs elt) field Nothing -> LHSDot (replaceLHS $ LHSBit lhs elt) field
replaceLHS local lhs = replaceLHS lhs =
case lookup lhs lhsReplacements of case lookup lhs lhsReplacements of
Just lhs' -> lhs' Just lhs' -> lhs'
Nothing -> checkExprResolution local (lhsToExpr lhs) $ Nothing -> traverseSinglyNestedLHSs replaceLHS lhs
traverseSinglyNestedLHSs (replaceLHS local) lhs
replaceLHSArrTag :: Expr -> LHS -> LHS replaceLHSArrTag :: Expr -> LHS -> LHS
replaceLHSArrTag = replaceLHSArrTag =
traverseNestedLHSs . (traverseLHSExprs . replaceArrTag) traverseNestedLHSs . (traverseLHSExprs . replaceArrTag)
-- top-level expressions may be modports bound to other modports -- top-level expressions may be modports bound to other modports
traverseExprM :: Expr -> Scoper () Expr traverseExprM :: Expr -> Scoper () Expr
traverseExprM = traverseExprM =
embedScopes tagExpr >=> fmap replaceExpr .
embedScopes replaceExpr embedScopes tagExpr
tagExpr :: Scopes () -> Expr -> Expr tagExpr :: Scopes () -> Expr -> Expr
tagExpr scopes expr tagExpr scopes expr
| lookupElem scopes expr /= Nothing = | lookupElem scopes expr /= Nothing =
...@@ -479,38 +480,35 @@ inlineInstance global ranges modportBindings items partName ...@@ -479,38 +480,35 @@ inlineInstance global ranges modportBindings items partName
then Dot scopedInstanceExpr y then Dot scopedInstanceExpr y
else Dot (Ident x) y else Dot (Ident x) y
renamePartExpr expr = visitExprsStep renamePartExpr expr renamePartExpr expr = visitExprsStep renamePartExpr expr
replaceExpr :: Scopes () -> Expr -> Expr replaceExpr :: Expr -> Expr
replaceExpr _ (Dot expr "@") = expr replaceExpr (Dot expr "@") = expr
replaceExpr local (Ident x) = replaceExpr (Ident x) =
case lookup x modportBindings of case lookup x modportBindings of
Just (_, m) -> m Just (_, m) -> m
Nothing -> checkExprResolution local (Ident x) (Ident x) Nothing -> Ident x
replaceExpr local expr = replaceExpr expr =
replaceExpr' local expr replaceExpr' expr
replaceExpr' :: Scopes () -> Expr -> Expr replaceExpr' :: Expr -> Expr
replaceExpr' _ (Dot expr "@") = expr replaceExpr' (Dot expr "@") = expr
replaceExpr' local (Dot (Bit expr elt) field) = replaceExpr' (Dot (Bit expr elt) field) =
case lookup (Dot (Bit expr Tag) field) exprReplacements of case lookup (Dot (Bit expr Tag) field) exprReplacements of
Just resolved -> replaceArrTag (replaceExpr' local elt) resolved Just resolved -> replaceArrTag (replaceExpr' elt) resolved
Nothing -> Dot (replaceExpr' local $ Bit expr elt) field Nothing -> Dot (replaceExpr' $ Bit expr elt) field
replaceExpr' local (Bit expr elt) = replaceExpr' (Bit expr elt) =
case lookup (Bit expr Tag) exprReplacements of case lookup (Bit expr Tag) exprReplacements of
Just resolved -> replaceArrTag (replaceExpr' local elt) resolved Just resolved -> replaceArrTag (replaceExpr' elt) resolved
Nothing -> Bit (replaceExpr' local expr) (replaceExpr' local elt) Nothing -> Bit (replaceExpr' expr) (replaceExpr' elt)
replaceExpr' local expr@(Dot Ident{} _) = replaceExpr' expr@(Dot Ident{} _) =
case lookup expr exprReplacements of case lookup expr exprReplacements of
Just expr' -> expr' Just expr' -> expr'
Nothing -> checkExprResolution local expr $ Nothing -> visitExprsStep replaceExprAny expr
visitExprsStep (replaceExprAny local) expr replaceExpr' (Ident x) = Ident x
replaceExpr' local (Ident x) = replaceExpr' expr = replaceExprAny expr
checkExprResolution local (Ident x) (Ident x) replaceExprAny :: Expr -> Expr
replaceExpr' local expr = replaceExprAny local expr replaceExprAny expr =
replaceExprAny :: Scopes () -> Expr -> Expr
replaceExprAny local expr =
case lookup expr exprReplacements of case lookup expr exprReplacements of
Just expr' -> expr' Just expr' -> expr'
Nothing -> checkExprResolution local expr $ Nothing -> visitExprsStep replaceExpr' expr
visitExprsStep (replaceExpr' local) expr
replaceArrTag :: Expr -> Expr -> Expr replaceArrTag :: Expr -> Expr -> Expr
replaceArrTag replacement Tag = replacement replaceArrTag replacement Tag = replacement
replaceArrTag replacement expr = replaceArrTag replacement expr =
...@@ -529,22 +527,11 @@ inlineInstance global ranges modportBindings items partName ...@@ -529,22 +527,11 @@ inlineInstance global ranges modportBindings items partName
. traverseExprTypes (traverseNestedTypes typeMapper) . traverseExprTypes (traverseNestedTypes typeMapper)
where typeMapper = traverseTypeExprs exprMapper where typeMapper = traverseTypeExprs exprMapper
checkExprResolution :: Scopes () -> Expr -> a -> a checkErrMsg :: String -> String
checkExprResolution local expr = checkErrMsg exprStr = "inlining instance \"" ++ instanceName
if not (exprResolves local expr) && exprResolves global expr ++ "\" of " ++ inlineKind ++ " \"" ++ partName
then ++ "\" would make expression \"" ++ exprStr ++ "\" used in \""
scopedError local $ "inlining instance \"" ++ instanceName ++ instanceName ++ "\" resolvable when it wasn't previously"
++ "\" of " ++ inlineKind ++ " \"" ++ partName
++ "\" would make expression \"" ++ show expr
++ "\" used in \"" ++ instanceName
++ "\" resolvable when it wasn't previously"
else id
exprResolves :: Scopes a -> Expr -> Bool
exprResolves local (Ident x) =
isJust (lookupElem local x) || isLoopVar local x
exprResolves local expr =
isJust (lookupElem local expr)
-- unambiguous reference to the current instance -- unambiguous reference to the current instance
scopedInstanceRaw = accessesToExpr $ localAccesses global instanceName scopedInstanceRaw = accessesToExpr $ localAccesses global instanceName
...@@ -723,3 +710,74 @@ sliceLo :: PartSelectMode -> Range -> Expr ...@@ -723,3 +710,74 @@ sliceLo :: PartSelectMode -> Range -> Expr
sliceLo NonIndexed (l, r) = endianCondExpr (l, r) r l sliceLo NonIndexed (l, r) = endianCondExpr (l, r) r l
sliceLo IndexedPlus (base, _) = base sliceLo IndexedPlus (base, _) = base
sliceLo IndexedMinus (base, len) = BinOp Add (BinOp Sub base len) (RawNum 1) 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)
`define TEST(loc, mod, expr) \
initial begin \
$display(`"loc mod.expr %b`", mod.expr); \
$display(`"loc loc.mod.expr %b`", loc.mod.expr); \
begin /* exciting shadowing */ \
localparam i = 1'bx; \
localparam j = 1'bz; \
$display(`"loc loc.mod.expr %b`", loc.mod.expr); \
end \
end
`define TEST_FUNC(loc, mod, expr) `TEST(loc, mod, expr())
`define TEST_TASK(loc, mod, expr) \
initial begin \
$display(`"loc mod.expr():`"); \
mod.expr(); \
$display(`"loc loc.mod.expr():`"); \
loc.mod.expr(); \
end
interface Intf;
parameter [3:0] P = 1;
localparam [3:0] L = 2;
wire [3:0] w;
assign w = 3;
modport D(input .x(w + 8'b1));
function automatic [3:0] F;
return -1;
endfunction
task T;
$display("T called");
endtask
if (1) begin : blk
parameter [3:0] P = 4;
localparam [3:0] L = 6;
reg [3:0] w;
initial w = 7;
function automatic [3:0] F;
return 8;
endfunction
task T;
$display("blk.T called");
endtask
end
endinterface
module ModAi(Intf i );
`TEST(ModAi, i, P)
`TEST(ModAi, i, L)
`TEST(ModAi, i, w)
`TEST_FUNC(ModAi, i, F)
`TEST_TASK(ModAi, i, T)
`TEST(ModAi, i, blk.P)
`TEST(ModAi, i, blk.L)
`TEST(ModAi, i, blk.w)
`TEST_FUNC(ModAi, i, blk.F)
`TEST_TASK(ModAi, i, blk.T)
endmodule
module ModAj(Intf j);
`TEST(ModAj, j, P)
`TEST(ModAj, j, L)
`TEST(ModAj, j, w)
`TEST_FUNC(ModAj, j, F)
`TEST_TASK(ModAj, j, T)
`TEST(ModAj, j, blk.P)
`TEST(ModAj, j, blk.L)
`TEST(ModAj, j, blk.w)
`TEST_FUNC(ModAj, j, blk.F)
`TEST_TASK(ModAj, j, blk.T)
endmodule
module ModBi(Intf.D i);
`TEST(ModBi, i, P)
`TEST(ModBi, i, L)
`TEST(ModBi, i, x)
endmodule
module ModBj(Intf.D j);
`TEST(ModBj, j, P)
`TEST(ModBj, j, L)
`TEST(ModBj, j, x)
endmodule
module top;
Intf i();
ModAi ai(i);
ModAj aj(i);
ModBi bi(i);
ModBj bj(i);
`TEST(top, i, P)
`TEST(top, i, L)
`TEST(top, i, w)
`TEST_FUNC(top, i, F)
`TEST_TASK(top, i, T)
`TEST(top, i, blk.P)
`TEST(top, i, blk.L)
`TEST(top, i, blk.w)
`TEST_FUNC(top, i, blk.F)
`TEST_TASK(top, i, blk.T)
endmodule
`define TEST(loc, mod, expr) \
initial begin \
$display(`"loc mod.expr %b`", i.expr); \
repeat (2) \
$display(`"loc loc.mod.expr %b`", top.i.expr); \
end
`define TEST_FUNC(loc, mod, expr) \
initial begin \
$display(`"loc mod.expr() %b`", i.expr(0)); \
repeat (2) \
$display(`"loc loc.mod.expr() %b`", top.i.expr(0)); \
end
`define TEST_TASK(loc, mod, expr) \
initial begin \
$display(`"loc mod.expr():`"); \
i.expr(); \
$display(`"loc loc.mod.expr():`"); \
top.i.expr(); \
end
module top;
if (1) begin : i
localparam [3:0] P = 1;
localparam [3:0] L = 2;
wire [3:0] w;
assign w = 3;
wire [7:0] x;
assign x = w + 8'b1;
function automatic [3:0] F;
input unused;
F = -1;
endfunction
task T;
$display("T called");
endtask
if (1) begin : blk
localparam [3:0] P = 4;
localparam [3:0] L = 6;
reg [3:0] w;
initial w = 7;
function automatic [3:0] F;
input unused;
F = 8;
endfunction
task T;
$display("blk.T called");
endtask
end
end
`TEST(ModAi, i, P)
`TEST(ModAi, i, L)
`TEST(ModAi, i, w)
`TEST_FUNC(ModAi, i, F)
`TEST_TASK(ModAi, i, T)
`TEST(ModAi, i, blk.P)
`TEST(ModAi, i, blk.L)
`TEST(ModAi, i, blk.w)
`TEST_FUNC(ModAi, i, blk.F)
`TEST_TASK(ModAi, i, blk.T)
`TEST(ModAj, j, P)
`TEST(ModAj, j, L)
`TEST(ModAj, j, w)
`TEST_FUNC(ModAj, j, F)
`TEST_TASK(ModAj, j, T)
`TEST(ModAj, j, blk.P)
`TEST(ModAj, j, blk.L)
`TEST(ModAj, j, blk.w)
`TEST_FUNC(ModAj, j, blk.F)
`TEST_TASK(ModAj, j, blk.T)
`TEST(ModBi, i, P)
`TEST(ModBi, i, L)
`TEST(ModBi, i, x)
`TEST(ModBj, j, P)
`TEST(ModBj, j, L)
`TEST(ModBj, j, x)
`TEST(top, i, P)
`TEST(top, i, L)
`TEST(top, i, w)
`TEST_FUNC(top, i, F)
`TEST_TASK(top, i, T)
`TEST(top, i, blk.P)
`TEST(top, i, blk.L)
`TEST(top, i, blk.w)
`TEST_FUNC(top, i, blk.F)
`TEST_TASK(top, i, blk.T)
endmodule
// pattern: inlining instance "mod" of module "Module" would make expression "a\[0\]\.x" used in "mod" resolvable when it wasn't previously
// location: interface_bad_expr_arr.sv:10:5
interface InterfaceA;
logic x;
endinterface
interface InterfaceB;
logic x;
endinterface
module Module(InterfaceB b);
assign a[0].x = 1;
endmodule
module top;
InterfaceA a[1]();
InterfaceB b();
Module mod(b);
endmodule
// pattern: inlining instance "mod" of module "Module" would make expression "x" used in "mod" resolvable when it wasn't previously
// location: interface_bad_expr_module.sv:6:5
interface Interface;
endinterface
module Module(Interface i);
assign x = 1;
endmodule
module top;
wire x;
Interface intf();
Module mod(intf);
endmodule
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment