From ea56f51d03f53d2c6a1fb3e1300ba52a22b1ed20 Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Thu, 21 Jan 2021 11:48:36 -0700 Subject: [PATCH] support for parameters without defaults --- src/Convert.hs | 5 +- src/Convert/Interface.hs | 13 ---- src/Convert/ParamNoDefault.hs | 88 +++++++++++++++++++++++ src/Language/SystemVerilog/AST.hs | 14 ++++ src/Language/SystemVerilog/AST/Decl.hs | 2 +- src/Language/SystemVerilog/Parser/Parse.y | 6 +- sv2v.cabal | 1 + test/basic/no_default_param.sv | 48 +++++++++++++ test/basic/no_default_param.v | 48 +++++++++++++ test/error/localparam_no_default.sv | 4 ++ test/error/localparam_type_no_default.sv | 4 ++ test/error/parameter_no_default_1.sv | 7 ++ test/error/parameter_no_default_2.sv | 9 +++ test/error/parameter_no_default_3.sv | 9 +++ 14 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 src/Convert/ParamNoDefault.hs create mode 100644 test/basic/no_default_param.sv create mode 100644 test/basic/no_default_param.v create mode 100644 test/error/localparam_no_default.sv create mode 100644 test/error/localparam_type_no_default.sv create mode 100644 test/error/parameter_no_default_1.sv create mode 100644 test/error/parameter_no_default_2.sv create mode 100644 test/error/parameter_no_default_3.sv diff --git a/src/Convert.hs b/src/Convert.hs index fcfcf26..01aa4d9 100644 --- a/src/Convert.hs +++ b/src/Convert.hs @@ -33,6 +33,7 @@ import qualified Convert.MultiplePacked import qualified Convert.NamedBlock import qualified Convert.NestPI import qualified Convert.Package +import qualified Convert.ParamNoDefault import qualified Convert.ParamType import qualified Convert.RemoveComments import qualified Convert.SignCast @@ -109,7 +110,9 @@ run excludes = foldr (.) id $ phases excludes convert :: [Job.Exclude] -> Phase convert excludes = - convert' . Convert.NestPI.reorder + convert' + . Convert.NestPI.reorder + . Convert.ParamNoDefault.convert where convert' :: Phase convert' descriptions = diff --git a/src/Convert/Interface.hs b/src/Convert/Interface.hs index 268a23e..25c275f 100644 --- a/src/Convert/Interface.hs +++ b/src/Convert/Interface.hs @@ -588,19 +588,6 @@ pattern InstArrKey expr = Dot (Bit expr (RawNum 0)) InstArrName pattern InstArrEncoded :: Expr -> Expr -> ModuleItem pattern InstArrEncoded l r = Modport InstArrName (InstArrVal l r) -type Binding t = (Identifier, t) --- give a set of bindings explicit names -resolveBindings :: Show t => [Identifier] -> [Binding t] -> [Binding t] -resolveBindings available bindings = - zipWith resolveBinding bindings [0..] - where - resolveBinding ("", e) idx = - if idx < length available - then (available !! idx, e) - else error $ "binding " ++ show e ++ " is out of range " - ++ show available - resolveBinding other _ = other - -- given a list of module items, produces the parameter names in order parameterNames :: [ModuleItem] -> [Identifier] parameterNames = diff --git a/src/Convert/ParamNoDefault.hs b/src/Convert/ParamNoDefault.hs new file mode 100644 index 0000000..5893b28 --- /dev/null +++ b/src/Convert/ParamNoDefault.hs @@ -0,0 +1,88 @@ +{- sv2v + - Author: Zachary Snow + - + - Conversion for parameters without default values + - + - This conversion ensures that any parameters which don't have a default value + - are always given an explicit value wherever that module or interface is used. + - + - Parameters are given a fake default value if they do not have one so that the + - given source for that module can be consumed by downstream tools. This is not + - done for type parameters, as those modules are rewritten by a separate + - conversion. Localparams without defaults are expressly caught and forbidden. + -} + +module Convert.ParamNoDefault (convert) where + +import Control.Monad.Writer.Strict +import Data.List (intercalate) +import qualified Data.Map.Strict as Map + +import Convert.Traverse +import Language.SystemVerilog.AST + +type Parts = Map.Map Identifier [(Identifier, Bool)] + +convert :: [AST] -> [AST] +convert files = + map convertFile files' + where + (files', parts) = runWriter $ + mapM (traverseDescriptionsM traverseDescriptionM) files + convertFile = traverseDescriptions $ traverseModuleItems $ + traverseModuleItem parts + +traverseDescriptionM :: Description -> Writer Parts Description +traverseDescriptionM (Part attrs extern kw lifetime name ports items) = do + let (items', params) = runWriter $ mapM traverseModuleItemM items + tell $ Map.singleton name params + return $ Part attrs extern kw lifetime name ports items' +traverseDescriptionM other = return other + +traverseModuleItemM :: ModuleItem -> Writer [(Identifier, Bool)] ModuleItem +traverseModuleItemM (MIAttr attr item) = + traverseModuleItemM item >>= return . MIAttr attr +traverseModuleItemM (MIPackageItem (Decl decl)) = + traverseDeclM decl >>= return . MIPackageItem . Decl +traverseModuleItemM other = return other + +-- writes down the parameters for a part +traverseDeclM :: Decl -> Writer [(Identifier, Bool)] Decl +traverseDeclM (Param Localparam _ x Nil) = + error $ "localparam " ++ show x ++ " has no default value" +traverseDeclM (Param Parameter t x e) = do + tell [(x, e == Nil)] + return $ if e == Nil + then Param Parameter t x $ RawNum 0 + else Param Parameter t x e +traverseDeclM (ParamType Localparam x Nothing) = + error $ "localparam type " ++ show x ++ " has no default value" +traverseDeclM (ParamType Parameter x mt) = do + -- parameter types are rewritten separately, so no fake default here + tell [(x, mt == Nothing)] + return $ ParamType Parameter x mt +traverseDeclM other = return other + +-- check for instances missing values for parameters without defaults +traverseModuleItem :: Parts -> ModuleItem -> ModuleItem +traverseModuleItem parts (orig @ (Instance part params name _ _)) = + if maybePartInfo == Nothing || null missingParams + then orig + else error $ "instance " ++ show name ++ " of " ++ show part + ++ " is missing values for parameters without defaults: " + ++ (intercalate " " $ map show missingParams) + where + maybePartInfo = Map.lookup part parts + Just partInfo = maybePartInfo + paramsResolved = resolveBindings (map fst partInfo) params + paramsWithNoDefault = map fst $ filter snd partInfo + missingParams = filter (needsDefault paramsResolved) paramsWithNoDefault +traverseModuleItem _ other = other + +-- whether a given parameter is unspecified in the given parameter bindings +needsDefault :: [(Identifier, TypeOrExpr)] -> Identifier -> Bool +needsDefault instanceParams param = + case lookup param instanceParams of + Nothing -> True + Just (Right Nil) -> True + Just _ -> False diff --git a/src/Language/SystemVerilog/AST.hs b/src/Language/SystemVerilog/AST.hs index 14dc2bb..43813c1 100644 --- a/src/Language/SystemVerilog/AST.hs +++ b/src/Language/SystemVerilog/AST.hs @@ -29,6 +29,7 @@ module Language.SystemVerilog.AST , exprToLHS , lhsToExpr , shortHash + , resolveBindings ) where import Text.Printf (printf) @@ -84,3 +85,16 @@ shortHash :: (Show a) => a -> String shortHash x = take 5 $ printf "%05X" val where val = hash $ show x + +type Binding t = (Identifier, t) +-- give a set of bindings explicit names +resolveBindings :: Show t => [Identifier] -> [Binding t] -> [Binding t] +resolveBindings available bindings = + zipWith resolveBinding bindings [0..] + where + resolveBinding ("", e) idx = + if idx < length available + then (available !! idx, e) + else error $ "binding " ++ show e ++ " is out of range " + ++ show available + resolveBinding other _ = other diff --git a/src/Language/SystemVerilog/AST/Decl.hs b/src/Language/SystemVerilog/AST/Decl.hs index d84db21..677298b 100644 --- a/src/Language/SystemVerilog/AST/Decl.hs +++ b/src/Language/SystemVerilog/AST/Decl.hs @@ -28,7 +28,7 @@ data Decl instance Show Decl where showList l _ = unlines' $ map show l - show (Param s t x e) = printf "%s %s%s = %s;" (show s) (showPad t) x (show e) + show (Param s t x e) = printf "%s %s%s%s;" (show s) (showPad t) x (showAssignment e) show (ParamType s x mt) = printf "%s type %s%s;" (show s) x tStr where tStr = maybe "" ((" = " ++) . show) mt show (Variable d t x a e) = printf "%s%s%s%s%s;" (showPad d) (showPad t) x (showRanges a) (showAssignment e) diff --git a/src/Language/SystemVerilog/Parser/Parse.y b/src/Language/SystemVerilog/Parser/Parse.y index 5527362..1195b68 100644 --- a/src/Language/SystemVerilog/Parser/Parse.y +++ b/src/Language/SystemVerilog/Parser/Parse.y @@ -921,8 +921,10 @@ DeclAsgns :: { [(Identifier, Expr, [Range])] } : DeclAsgn { [$1] } | DeclAsgns "," DeclAsgn { $1 ++ [$3] } DeclAsgn :: { (Identifier, Expr, [Range]) } - : Identifier "=" Expr { ($1, $3, []) } - | Identifier DimensionsNonEmpty "=" Expr { ($1, $4, $2) } + : Identifier "=" Expr { ($1, $3, []) } + | Identifier DimensionsNonEmpty "=" Expr { ($1, $4, $2) } + | Identifier { ($1, Nil, []) } + | Identifier DimensionsNonEmpty { ($1, Nil, $2) } Range :: { Range } : "[" Expr ":" Expr "]" { ($2, $4) } diff --git a/sv2v.cabal b/sv2v.cabal index cabddd7..de74c4e 100644 --- a/sv2v.cabal +++ b/sv2v.cabal @@ -83,6 +83,7 @@ executable sv2v Convert.NamedBlock Convert.NestPI Convert.Package + Convert.ParamNoDefault Convert.ParamType Convert.RemoveComments Convert.Scoper diff --git a/test/basic/no_default_param.sv b/test/basic/no_default_param.sv new file mode 100644 index 0000000..cfaa9ce --- /dev/null +++ b/test/basic/no_default_param.sv @@ -0,0 +1,48 @@ +`define DUMP(name) initial $display(`"name W=%b $bits(X)=%0d Y=%b $bits(Z)=%0d", W, $bits(X), Y, $bits(Z)); + +interface InterfaceA #( + parameter W, + parameter type X, + parameter byte Y, + parameter type Z +); + `DUMP(InterfaceA) +endinterface + +interface InterfaceB; + parameter W; + parameter type X; + parameter byte Y; + parameter type Z; + `DUMP(InterfaceB) +endinterface + +module ModuleA #( + parameter W, + parameter type X, + parameter byte Y, + parameter type Z +); + `DUMP(ModuleA) +endmodule + +module ModuleB; + parameter W; + parameter type X; + parameter byte Y; + parameter type Z; + `DUMP(ModuleB) +endmodule + +module top; +`define PARAMS_A #(.Y(6), .X(logic [4:0]), .Z(logic [6:0]), .W(4)) +`define PARAMS_B #(0, logic, 2, logic [2:0]) + InterfaceA `PARAMS_A ia1(); + InterfaceA `PARAMS_B ia2(); + InterfaceB `PARAMS_A ib1(); + InterfaceB `PARAMS_B ib2(); + ModuleA `PARAMS_A ma1(); + ModuleA `PARAMS_B ma2(); + ModuleB `PARAMS_A mb1(); + ModuleB `PARAMS_B mb2(); +endmodule diff --git a/test/basic/no_default_param.v b/test/basic/no_default_param.v new file mode 100644 index 0000000..847b57a --- /dev/null +++ b/test/basic/no_default_param.v @@ -0,0 +1,48 @@ +`define DUMP(name) initial $display(`"name W=%b $bits(X)=%0d Y=%b $bits(Z)=%0d`", W, X, Y, Z); + +module InterfaceA #( + parameter W = 9, + parameter X = 9, + parameter [7:0] Y = 9, + parameter Z = 9 +); + `DUMP(InterfaceA) +endmodule + +module InterfaceB; + parameter W = 9; + parameter X = 9; + parameter [7:0] Y = 9; + parameter Z = 9; + `DUMP(InterfaceB) +endmodule + +module ModuleA #( + parameter W = 9, + parameter X = 9, + parameter [7:0] Y = 9, + parameter Z = 9 +); + `DUMP(ModuleA) +endmodule + +module ModuleB; + parameter W = 9; + parameter X = 9; + parameter [7:0] Y = 9; + parameter Z = 9; + `DUMP(ModuleB) +endmodule + +module top; +`define PARAMS_A #(.Y(6), .X(5), .Z(7), .W(4)) +`define PARAMS_B #(0, 1, 2, 3) + InterfaceA `PARAMS_A ia1(); + InterfaceA `PARAMS_B ia2(); + InterfaceB `PARAMS_A ib1(); + InterfaceB `PARAMS_B ib2(); + ModuleA `PARAMS_A ma1(); + ModuleA `PARAMS_B ma2(); + ModuleB `PARAMS_A mb1(); + ModuleB `PARAMS_B mb2(); +endmodule diff --git a/test/error/localparam_no_default.sv b/test/error/localparam_no_default.sv new file mode 100644 index 0000000..6f809ef --- /dev/null +++ b/test/error/localparam_no_default.sv @@ -0,0 +1,4 @@ +// pattern: localparam "X" has no default value +module top; + localparam X; +endmodule diff --git a/test/error/localparam_type_no_default.sv b/test/error/localparam_type_no_default.sv new file mode 100644 index 0000000..a8e4c73 --- /dev/null +++ b/test/error/localparam_type_no_default.sv @@ -0,0 +1,4 @@ +// pattern: localparam type "X" has no default value +module top; + localparam type X; +endmodule diff --git a/test/error/parameter_no_default_1.sv b/test/error/parameter_no_default_1.sv new file mode 100644 index 0000000..4429d72 --- /dev/null +++ b/test/error/parameter_no_default_1.sv @@ -0,0 +1,7 @@ +// pattern: instance "e" of "Example" is missing values for parameters without defaults: "X" +module Example; + parameter type X; +endmodule +module top; + Example e(); +endmodule diff --git a/test/error/parameter_no_default_2.sv b/test/error/parameter_no_default_2.sv new file mode 100644 index 0000000..af36686 --- /dev/null +++ b/test/error/parameter_no_default_2.sv @@ -0,0 +1,9 @@ +// pattern: instance "bad" of "Example" is missing values for parameters without defaults: "Y" +module Example; + parameter X = 1; + parameter Y; +endmodule +module top; + Example #(.Y(1)) good(); + Example #(1) bad(); +endmodule diff --git a/test/error/parameter_no_default_3.sv b/test/error/parameter_no_default_3.sv new file mode 100644 index 0000000..bb985f7 --- /dev/null +++ b/test/error/parameter_no_default_3.sv @@ -0,0 +1,9 @@ +// pattern: instance "bad" of "Example" is missing values for parameters without defaults: "Y" +interface Example; + parameter X = 1; + parameter Y; +endinterface +module top; + Example #(.Y(1)) good(); + Example #(.Y()) bad(); +endmodule