From 87ea9de853f093dc96571cdc34e34ca2a6f18e1f Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Wed, 10 Feb 2021 12:43:59 -0500 Subject: [PATCH] substitute constants from type information across scopes --- src/Convert.hs | 2 + src/Convert/HierConst.hs | 113 +++++++++++++++++++++++++++++++++++++ src/Convert/Interface.hs | 7 --- src/Convert/Scoper.hs | 44 +++++++++++++++ src/Convert/TypeOf.hs | 76 ++++++++++++++----------- sv2v.cabal | 1 + test/basic/bits_scope.sv | 54 ++++++++++++++++++ test/basic/bits_scope.v | 48 ++++++++++++++++ test/basic/typeof_scope.sv | 72 +++++++++++++++++++++++ test/basic/typeof_scope.v | 2 + 10 files changed, 378 insertions(+), 41 deletions(-) create mode 100644 src/Convert/HierConst.hs create mode 100644 test/basic/bits_scope.sv create mode 100644 test/basic/bits_scope.v create mode 100644 test/basic/typeof_scope.sv create mode 100644 test/basic/typeof_scope.v diff --git a/src/Convert.hs b/src/Convert.hs index 41d59ee..b02225e 100644 --- a/src/Convert.hs +++ b/src/Convert.hs @@ -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 diff --git a/src/Convert/HierConst.hs b/src/Convert/HierConst.hs new file mode 100644 index 0000000..f48bb72 --- /dev/null +++ b/src/Convert/HierConst.hs @@ -0,0 +1,113 @@ +{- sv2v + - Author: Zachary Snow + - + - 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 diff --git a/src/Convert/Interface.hs b/src/Convert/Interface.hs index 7151a6f..430c045 100644 --- a/src/Convert/Interface.hs +++ b/src/Convert/Interface.hs @@ -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 diff --git a/src/Convert/Scoper.hs b/src/Convert/Scoper.hs index e9066ef..7713ee5 100644 --- a/src/Convert/Scoper.hs +++ b/src/Convert/Scoper.hs @@ -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 diff --git a/src/Convert/TypeOf.hs b/src/Convert/TypeOf.hs index a07e5fb..a73b38f 100644 --- a/src/Convert/TypeOf.hs +++ b/src/Convert/TypeOf.hs @@ -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 diff --git a/sv2v.cabal b/sv2v.cabal index 6d29888..3cbc829 100644 --- a/sv2v.cabal +++ b/sv2v.cabal @@ -72,6 +72,7 @@ executable sv2v Convert.Foreach Convert.FuncRet Convert.FuncRoutine + Convert.HierConst Convert.ImplicitNet Convert.Inside Convert.Interface diff --git a/test/basic/bits_scope.sv b/test/basic/bits_scope.sv new file mode 100644 index 0000000..95dd5ef --- /dev/null +++ b/test/basic/bits_scope.sv @@ -0,0 +1,54 @@ +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 diff --git a/test/basic/bits_scope.v b/test/basic/bits_scope.v new file mode 100644 index 0000000..e543f17 --- /dev/null +++ b/test/basic/bits_scope.v @@ -0,0 +1,48 @@ +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 diff --git a/test/basic/typeof_scope.sv b/test/basic/typeof_scope.sv new file mode 100644 index 0000000..fe96397 --- /dev/null +++ b/test/basic/typeof_scope.sv @@ -0,0 +1,72 @@ +`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 diff --git a/test/basic/typeof_scope.v b/test/basic/typeof_scope.v new file mode 100644 index 0000000..9ff8a48 --- /dev/null +++ b/test/basic/typeof_scope.v @@ -0,0 +1,2 @@ +`define REF 1 +`include "typeof_scope.sv"