Commit 87ea9de8 by Zachary Snow

substitute constants from type information across scopes

parent 6d2cdf1d
......@@ -22,6 +22,7 @@ import qualified Convert.ForDecl
import qualified Convert.Foreach
import qualified Convert.FuncRet
import qualified Convert.FuncRoutine
import qualified Convert.HierConst
import qualified Convert.ImplicitNet
import qualified Convert.Inside
import qualified Convert.Interface
......@@ -74,6 +75,7 @@ mainPhases selectExclude =
, Convert.MultiplePacked.convert
, Convert.UnbasedUnsized.convert
, Convert.Cast.convert
, Convert.HierConst.convert
, Convert.TypeOf.convert
, Convert.DimensionQuery.convert
, Convert.ParamType.convert
......
{- sv2v
- Author: Zachary Snow <zach@zachjs.com>
-
- Elaborate hierarchical references to constants
-
- [System]Verilog does not allow hierarchical identifiers as constant
- primaries. However, the resolution of type information across scopes can
- create such hierarchical references. This conversion performs substitution
- for any hierarchical references to parameters or localparams, regardless of
- whether or not they occur within what should be a constant expression.
-
- If an identifier refers to a parameter which has been shadowed locally, the
- conversion creates a localparam alias of the parameter at the top level scope
- and refers to the parameter using that alias instead.
-
- TODO: Support resolution of hierarchical references to constant functions
- TODO: Some other conversions still blindly substitute type information
-}
module Convert.HierConst (convert) where
import Data.Either (fromLeft)
import qualified Data.Map.Strict as Map
import Convert.Scoper
import Convert.Traverse
import Language.SystemVerilog.AST
convert :: [AST] -> [AST]
convert = map $ traverseDescriptions convertDescription
convertDescription :: Description -> Description
convertDescription (Part attrs extern kw lifetime name ports items) =
Part attrs extern kw lifetime name ports $
if null shadowedParams
then items'
else map expand items'
where
(items', mapping) = runScoper traverseDeclM
(traverseExprsM traverseExprM)
(traverseGenItemExprsM traverseExprM)
(traverseStmtExprsM traverseExprM)
name items
shadowedParams = Map.keys $ Map.filter (fromLeft False) $
extractMapping mapping
expand = traverseNestedModuleItems $ expandParam shadowedParams
convertDescription description = description
expandParam :: [Identifier] -> ModuleItem -> ModuleItem
expandParam shadowed (MIPackageItem (Decl (param @ (Param Parameter _ x _)))) =
if elem x shadowed
then Generate $ map (GenModuleItem . wrap) [param, extra]
else wrap param
where
wrap = MIPackageItem . Decl
extra = Param Localparam UnknownType (prefix x) (Ident x)
expandParam _ item = item
prefix :: Identifier -> Identifier
prefix = (++) "_sv2v_disambiguate_"
type ST = Scoper (Either Bool Expr)
traverseDeclM :: Decl -> ST Decl
traverseDeclM decl = do
case decl of
Param Parameter _ x _ ->
insertElem x (Left False)
Param Localparam UnknownType x e ->
scopeExpr e >>= insertElem x . Right
Param Localparam (Implicit sg rs) x e ->
scopeExpr (Cast (Left t) e) >>= insertElem x . Right
where t = IntegerVector TLogic sg rs
Param Localparam t x e ->
scopeExpr (Cast (Left t) e) >>= insertElem x . Right
_ -> return ()
traverseDeclExprsM traverseExprM decl
-- rewrite an expression so that any constant identifiers it contains
-- unambiguously refer refer to currently visible constant declarations so it
-- can be substituted elsewhere
scopeExpr :: Expr -> ST Expr
scopeExpr expr = do
expr' <- traverseSinglyNestedExprsM scopeExpr expr
details <- lookupElemM expr'
case details of
Just (accesses, _, _) -> return $ accessesToExpr accesses
_ -> return expr'
-- substitute hierarchical references to constants
traverseExprM :: Expr -> ST Expr
traverseExprM (expr @ (Dot _ x)) = do
expr' <- traverseSinglyNestedExprsM traverseExprM expr
detailsE <- lookupElemM expr'
detailsX <- lookupElemM x
case (detailsE, detailsX) of
(Just ([_, _], _, Left{}), Just ([_, _], _, Left{})) ->
return $ Ident x
(Just (accesses @ [Access _ Nil, _], _, Left False), _) -> do
insertElem accesses (Left True)
return $ Ident $ prefix x
(Just ([Access _ Nil, _], _, Left True), _) ->
return $ Ident $ prefix x
(Just (aE, replacements, Right value), Just (aX, _, _)) ->
if aE == aX && Map.null replacements
then return $ Ident x
else traverseSinglyNestedExprsM traverseExprM $
replaceInExpr replacements value
(Just (_, replacements, Right value), Nothing) ->
traverseSinglyNestedExprsM traverseExprM $
replaceInExpr replacements value
_ -> traverseSinglyNestedExprsM traverseExprM expr
traverseExprM expr = traverseSinglyNestedExprsM traverseExprM expr
......@@ -214,13 +214,6 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) =
Nothing -> accessesToExpr $ init accesses
where Just (accesses, _, _) =
lookupElem modports (Dot e "")
accessesToExpr :: [Access] -> Expr
accessesToExpr accesses =
foldl accessToExpr (Ident topName) rest
where Access topName Nil : rest = accesses
accessToExpr :: Expr -> Access -> Expr
accessToExpr e (Access x Nil) = Dot e x
accessToExpr e (Access x i) = Bit (Dot e x) i
-- expand a modport binding into a series of expression substitutions
genSubstitutions :: Scopes [ModportDecl] -> Expr -> Expr -> Expr
......
......@@ -27,9 +27,13 @@ module Convert.Scoper
, ScoperT
, evalScoper
, evalScoperT
, runScoper
, runScoperT
, partScoper
, partScoperT
, accessesToExpr
, replaceInType
, replaceInExpr
, insertElem
, injectItem
, injectDecl
......@@ -154,6 +158,34 @@ exprToAccesses (Dot e x) = do
Just $ accesses ++ [Access x Nil]
exprToAccesses _ = Nothing
accessesToExpr :: [Access] -> Expr
accessesToExpr accesses =
foldl accessToExpr (Ident topName) rest
where Access topName Nil : rest = accesses
accessToExpr :: Expr -> Access -> Expr
accessToExpr e (Access x Nil) = Dot e x
accessToExpr e (Access x i) = Bit (Dot e x) i
replaceInType :: Replacements -> Type -> Type
replaceInType replacements =
if Map.null replacements
then id
else traverseNestedTypes $ traverseTypeExprs $
replaceInExpr' replacements
replaceInExpr :: Replacements -> Expr -> Expr
replaceInExpr replacements =
if Map.null replacements
then id
else replaceInExpr' replacements
replaceInExpr' :: Replacements -> Expr -> Expr
replaceInExpr' replacements (Ident x) =
Map.findWithDefault (Ident x) x replacements
replaceInExpr' replacements other =
traverseSinglyNestedExprs (replaceInExpr replacements) other
class ScopePath k where
toTiers :: Scopes a -> k -> [Tier]
......@@ -301,6 +333,18 @@ evalScoperT declMapper moduleItemMapper genItemMapper stmtMapper topName items =
topName items
return items'
runScoper
:: MapperM (Scoper a) Decl
-> MapperM (Scoper a) ModuleItem
-> MapperM (Scoper a) GenItem
-> MapperM (Scoper a) Stmt
-> Identifier
-> [ModuleItem]
-> ([ModuleItem], Scopes a)
runScoper declMapper moduleItemMapper genItemMapper stmtMapper topName items =
runIdentity $ runScoperT
declMapper moduleItemMapper genItemMapper stmtMapper topName items
runScoperT
:: forall a m. Monad m
=> MapperM (ScoperT a m) Decl
......
......@@ -23,7 +23,6 @@
module Convert.TypeOf (convert) where
import Data.Tuple (swap)
import qualified Data.Map.Strict as Map
import Convert.ExprUtils (dimensionsSize, endianCondRange, simplify)
import Convert.Scoper
......@@ -38,8 +37,10 @@ convert = map $ traverseDescriptions $ partScoper
pattern UnitType :: Type
pattern UnitType = IntegerVector TLogic Unspecified []
type ST = Scoper (Type, Bool)
-- insert the given declaration into the scope, and convert an TypeOfs within
traverseDeclM :: Decl -> Scoper Type Decl
traverseDeclM :: Decl -> ST Decl
traverseDeclM decl = do
decl' <- traverseDeclExprsM traverseExprM decl
>>= traverseDeclTypesM traverseTypeM
......@@ -47,45 +48,61 @@ traverseDeclM decl = do
Variable _ (Implicit sg rs) ident a _ ->
-- implicit types, which are commonly found in function return
-- types, are recast as logics to avoid outputting bare ranges
insertElem ident t' >> return decl'
insertType ident t' >> return decl'
where t' = injectRanges (IntegerVector TLogic sg rs) a
Variable d t ident a e -> do
let t' = injectRanges t a
insertElem ident t'
insertType ident t'
return $ case t' of
UnpackedType t'' a' -> Variable d t'' ident a' e
_ -> Variable d t' ident [] e
Param _ UnknownType ident String{} ->
insertElem ident UnknownType >> return decl'
insertType ident UnknownType >> return decl'
Param _ UnknownType ident e ->
typeof e >>= insertElem ident >> return decl'
typeof e >>= insertType ident >> return decl'
Param _ (Implicit sg rs) ident _ ->
insertElem ident t' >> return decl'
insertType ident t' >> return decl'
where t' = IntegerVector TLogic sg rs
Param _ t ident _ ->
insertElem ident t >> return decl'
insertType ident t >> return decl'
ParamType{} -> return decl'
CommentDecl{} -> return decl'
-- rewrite and store a non-genvar data declaration's type information
insertType :: Identifier -> Type -> ST ()
insertType ident typ = do
typ' <- traverseNestedTypesM (traverseTypeExprsM scopeExpr) typ
insertElem ident (typ', False)
-- rewrite an expression so that any identifiers it contains unambiguously refer
-- refer to currently visible declarations so it can be substituted elsewhere
scopeExpr :: Expr -> ST Expr
scopeExpr expr = do
expr' <- traverseSinglyNestedExprsM scopeExpr expr
details <- lookupElemM expr'
case details of
Just (accesses, _, (_, False)) -> return $ accessesToExpr accesses
_ -> return expr'
-- convert TypeOf in a ModuleItem
traverseModuleItemM :: ModuleItem -> Scoper Type ModuleItem
traverseModuleItemM (Genvar x) = do
insertElem x $ IntegerAtom TInteger Unspecified
return $ Genvar x
traverseModuleItemM :: ModuleItem -> ST ModuleItem
traverseModuleItemM (Genvar x) =
insertElem x (t, True) >> return (Genvar x)
where t = IntegerAtom TInteger Unspecified
traverseModuleItemM item =
traverseNodesM traverseExprM return traverseTypeM traverseLHSM return item
where traverseLHSM = traverseLHSExprsM traverseExprM
-- convert TypeOf in a GenItem
traverseGenItemM :: GenItem -> Scoper Type GenItem
traverseGenItemM :: GenItem -> ST GenItem
traverseGenItemM = traverseGenItemExprsM traverseExprM
-- convert TypeOf in a Stmt
traverseStmtM :: Stmt -> Scoper Type Stmt
traverseStmtM :: Stmt -> ST Stmt
traverseStmtM = traverseStmtExprsM traverseExprM
-- convert TypeOf in an Expr
traverseExprM :: Expr -> Scoper Type Expr
traverseExprM :: Expr -> ST Expr
traverseExprM (Cast (Left (Implicit sg [])) expr) =
-- `signed'(foo)` and `unsigned'(foo)` are syntactic sugar for the `$signed`
-- and `$unsigned` system functions present in Verilog-2005
......@@ -122,7 +139,7 @@ traverseExprM other =
>>= traverseSinglyNestedExprsM traverseExprM
-- carry forward the signedness of the expression when cast to the given size
elaborateSizeCast :: Expr -> Expr -> Scoper Type Expr
elaborateSizeCast :: Expr -> Expr -> ST Expr
elaborateSizeCast size value = do
t <- typeof value
case typeSignedness t of
......@@ -130,7 +147,7 @@ elaborateSizeCast size value = do
sg -> traverseExprM $ Cast (Left $ typeOfSize sg size) value
-- convert TypeOf in a Type
traverseTypeM :: Type -> Scoper Type Type
traverseTypeM :: Type -> ST Type
traverseTypeM (TypeOf expr) =
traverseExprM expr >>= typeof
traverseTypeM other =
......@@ -139,31 +156,22 @@ traverseTypeM other =
-- attempts to find the given (potentially hierarchical or generate-scoped)
-- expression in the available scope information
lookupTypeOf :: Expr -> Scoper Type Type
lookupTypeOf :: Expr -> ST Type
lookupTypeOf expr = do
details <- lookupElemM expr
case details of
Nothing -> return $ TypeOf expr
Just (_, replacements, typ) -> do
Just (_, replacements, (typ, _)) -> do
let typ' = toVarType typ
return $ if Map.null replacements
then typ'
else rewriteType replacements typ'
return $ replaceInType replacements typ'
where
toVarType :: Type -> Type
toVarType (Net _ sg rs) = IntegerVector TLogic sg rs
toVarType other = other
rewriteType :: Replacements -> Type -> Type
rewriteType replacements = traverseNestedTypes $ traverseTypeExprs $
traverseNestedExprs (replace replacements)
replace :: Replacements -> Expr -> Expr
replace replacements (Ident x) =
Map.findWithDefault (Ident x) x replacements
replace _ other = other
-- determines the type of an expression based on the available scope information
-- according the semantics defined in IEEE 1800-2017, especially Section 11.6
typeof :: Expr -> Scoper Type Type
typeof :: Expr -> ST Type
typeof (Number n) =
return $ IntegerVector TLogic sg [r]
where
......@@ -233,7 +241,7 @@ unescapedLength ('\\' : _ : rest) = 1 + unescapedLength rest
unescapedLength (_ : rest) = 1 + unescapedLength rest
-- type of a standard (non-member) function call
typeofCall :: String -> Args -> Scoper Type Type
typeofCall :: String -> Args -> ST Type
typeofCall "$unsigned" (Args [e] []) = return $ typeOfSize Unsigned $ sizeof e
typeofCall "$signed" (Args [e] []) = return $ typeOfSize Signed $ sizeof e
typeofCall "$clog2" (Args [_] []) =
......@@ -250,7 +258,7 @@ typeSignednessOverride fallback sg t =
_ -> fallback
-- type of a unary operator expression
typeofUniOp :: UniOp -> Expr -> Scoper Type Type
typeofUniOp :: UniOp -> Expr -> ST Type
typeofUniOp UniAdd e = typeof e
typeofUniOp UniSub e = typeof e
typeofUniOp BitNot e = typeof e
......@@ -259,7 +267,7 @@ typeofUniOp _ _ =
return UnitType
-- type of a binary operator expression (Section 11.6.1)
typeofBinOp :: BinOp -> Expr -> Expr -> Scoper Type Type
typeofBinOp :: BinOp -> Expr -> Expr -> ST Type
typeofBinOp op a b =
case op of
LogAnd -> unitType
......@@ -293,7 +301,7 @@ typeofBinOp op a b =
where unitType = return UnitType
-- produces a type large enough to hold either expression
largerSizeType :: Expr -> Expr -> Scoper Type Type
largerSizeType :: Expr -> Expr -> ST Type
largerSizeType a (Number (Based 1 _ _ _ _)) = typeof a
largerSizeType a b = do
t <- typeof a
......
......@@ -72,6 +72,7 @@ executable sv2v
Convert.Foreach
Convert.FuncRet
Convert.FuncRoutine
Convert.HierConst
Convert.ImplicitNet
Convert.Inside
Convert.Interface
......
module top;
parameter WIDTH = 1;
if (1) begin : a
localparam tmp = WIDTH * 2;
if (1) begin : c
localparam WIDTH = tmp * 3;
reg [WIDTH-1:0] x;
end
end
if (1) begin : b
localparam tmp = WIDTH * 5;
if (1) begin : d
localparam WIDTH = tmp * 7;
reg [WIDTH-1:0] x;
end
end
reg [$bits(a.c.x):0] a_c_x;
reg [$bits(b.d.x):0] b_d_x;
if (1) begin : e
localparam tmp = WIDTH * 11;
if (1) begin : f
localparam WIDTH = tmp * 13;
reg [WIDTH-1:0] x;
reg [$bits(a.c.x):0] a_c_x;
reg [$bits(b.d.x):0] b_d_x;
initial begin
a_c_x = 1;
b_d_x = 1;
$display("B a.c.x %b", a.c.x);
$display("B a_c_x %b", a_c_x);
$display("B b.d.x %b", b.d.x);
$display("B b_d_x %b", b_d_x);
end
end
end
reg [$bits(e.f.x):0] e_f_x;
reg [$bits(e.f.a_c_x):0] e_f_a_c_x;
reg [$bits(e.f.b_d_x):0] e_f_b_d_x;
initial begin
e_f_x = 1'sb1;
e_f_a_c_x = 1'sbx;
e_f_b_d_x = 1'sbz;
$display("A a.c.x %b", a.c.x);
$display("A a_c_x %b", a_c_x);
$display("A b.d.x %b", b.d.x);
$display("A b_d_x %b", b_d_x);
$display("A e.f.x %b", e.f.x);
$display("A e_f_x %b", e_f_x);
$display("A e.f.a_c_x %b", e.f.a_c_x);
$display("A e_f_a_c_x %b", e_f_a_c_x);
$display("A e.f.b_d_x %b", e.f.b_d_x);
$display("A e_f_b_d_x %b", e_f_b_d_x);
end
endmodule
module top;
parameter WIDTH = 1;
if (1) begin : a
if (1) begin : c
reg [WIDTH*2*3-1:0] x;
end
end
if (1) begin : b
if (1) begin : d
reg [WIDTH*5*7-1:0] x;
end
end
reg [WIDTH*2*3:0] a_c_x;
reg [WIDTH*5*7:0] b_d_x;
if (1) begin : e
if (1) begin : f
reg [WIDTH*11*13-1:0] x;
reg [WIDTH*2*3:0] a_c_x;
reg [WIDTH*5*7:0] b_d_x;
initial begin
a_c_x = 1;
b_d_x = 1;
$display("B a.c.x %b", a.c.x);
$display("B a_c_x %b", a_c_x);
$display("B b.d.x %b", b.d.x);
$display("B b_d_x %b", b_d_x);
end
end
end
reg [WIDTH*11*13:0] e_f_x;
reg [WIDTH*2*3+1:0] e_f_a_c_x;
reg [WIDTH*5*7+1:0] e_f_b_d_x;
initial begin
e_f_x = 1'sb1;
e_f_a_c_x = 1'sbx;
e_f_b_d_x = 1'sbz;
$display("A a.c.x %b", a.c.x);
$display("A a_c_x %b", a_c_x);
$display("A b.d.x %b", b.d.x);
$display("A b_d_x %b", b_d_x);
$display("A e.f.x %b", e.f.x);
$display("A e_f_x %b", e_f_x);
$display("A e.f.a_c_x %b", e.f.a_c_x);
$display("A e_f_a_c_x %b", e_f_a_c_x);
$display("A e.f.b_d_x %b", e.f.b_d_x);
$display("A e_f_b_d_x %b", e_f_b_d_x);
end
endmodule
`define TYPEOF(x) wire [$bits(x) - 1:0]
// The `REF` sections of this test are workarounds for steveicarus/iverilog#483
module top;
genvar i;
if (1) begin : blk
for (i = 0; i < 3; i = i + 1) begin : prev
localparam V = i * 2;
localparam W = V;
wire [W:0] x;
end
for (i = 0; i < 2; i = i + 1) begin : loop
`TYPEOF(prev[i+1].x) x;
if (1) begin : a
localparam j = i - 3;
if (1) begin : b
localparam i = j + 2;
`TYPEOF(prev[i+2].x) x;
if (1) begin : c
localparam j = i - 4;
if (1) begin : d
localparam i = j + 7;
localparam z = i - 1;
`TYPEOF(prev[z].x) x;
if (1) begin : e
localparam i = 0;
`ifdef REF
localparam j = 3;
`else
localparam j = $bits(blk.loop[i].a.b.c.d.x);
`endif
wire [j-1:0] y;
end
end
end
end
end
end
end
`ifdef REF
wire [1*2:0] a;
wire [2*2:0] b;
wire [1*2:0] c;
wire [2*2:0] d;
wire [1*2:0] e;
wire [2*2:0] f;
wire [1*2:0] g;
wire [1*2:0] h;
`else
`TYPEOF(blk.loop[0].x) a;
`TYPEOF(blk.loop[1].x) b;
`TYPEOF(blk.loop[0].a.b.x) c;
`TYPEOF(blk.loop[1].a.b.x) d;
`TYPEOF(blk.loop[0].a.b.c.d.x) e;
`TYPEOF(blk.loop[1].a.b.c.d.x) f;
`TYPEOF(blk.loop[0].a.b.c.d.e.y) g;
`TYPEOF(blk.loop[1].a.b.c.d.e.y) h;
`endif
`define DUMP(x) assign x = 1; initial $display(`"x: %b (%0d bits)`", x, $bits(x));
`DUMP(a) `DUMP(b) `DUMP(c) `DUMP(d) `DUMP(e) `DUMP(f) `DUMP(g) `DUMP(h)
`DUMP(blk.loop[0].x)
`DUMP(blk.loop[1].x)
`DUMP(blk.loop[0].a.b.x)
`DUMP(blk.loop[1].a.b.x)
`DUMP(blk.loop[0].a.b.c.d.x)
`DUMP(blk.loop[1].a.b.c.d.x)
`DUMP(blk.loop[0].a.b.c.d.e.y)
`DUMP(blk.loop[1].a.b.c.d.e.y)
endmodule
`define REF 1
`include "typeof_scope.sv"
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