From 4de585ec0f3fc7cd0875b8fdea5d7156df5e10e9 Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Wed, 11 Sep 2019 09:27:18 +0200 Subject: [PATCH] initial parameter type conversion --- src/Convert.hs | 2 + src/Convert/ParamType.hs | 145 +++++++++++++++++++++++++++++++++++++++ sv2v.cabal | 1 + test/basic/paramtype.sv | 99 ++++++++++++++++++++++++++ test/basic/paramtype.v | 38 ++++++++++ 5 files changed, 285 insertions(+) create mode 100644 src/Convert/ParamType.hs create mode 100644 test/basic/paramtype.sv create mode 100644 test/basic/paramtype.v diff --git a/src/Convert.hs b/src/Convert.hs index ede81ef..0eab057 100644 --- a/src/Convert.hs +++ b/src/Convert.hs @@ -27,6 +27,7 @@ import qualified Convert.NamedBlock import qualified Convert.NestPI import qualified Convert.Package import qualified Convert.PackedArray +import qualified Convert.ParamType import qualified Convert.RemoveComments import qualified Convert.Return import qualified Convert.StarPort @@ -54,6 +55,7 @@ phases excludes = , Convert.KWArgs.convert , Convert.Mux.convert , Convert.PackedArray.convert + , Convert.ParamType.convert , Convert.StarPort.convert , Convert.StmtBlock.convert , Convert.Stream.convert diff --git a/src/Convert/ParamType.hs b/src/Convert/ParamType.hs new file mode 100644 index 0000000..7819cd6 --- /dev/null +++ b/src/Convert/ParamType.hs @@ -0,0 +1,145 @@ +{- sv2v + - Author: Zachary Snow + - + - Conversion for `parameter type` in module instantiations + -} + +module Convert.ParamType (convert) where + +import Control.Monad.Writer +import Data.Either (isLeft) +import Data.Maybe (isJust, isNothing, fromJust) +import qualified Data.Map.Strict as Map + +import Convert.Traverse +import Language.SystemVerilog.AST + +type MaybeTypeMap = Map.Map Identifier (Maybe Type) +type Info = Map.Map Identifier ([Identifier], MaybeTypeMap) + +type Instance = Map.Map Identifier Type +type Instances = [(Identifier, Instance)] + +convert :: [AST] -> [AST] +convert files = + concatMap (map explodeDescription) files' + where + info = execWriter $ + mapM (collectDescriptionsM collectDescriptionM) files + (files', instancesRaw) = runWriter $ mapM + (mapM $ traverseModuleItemsM $ mapInstance info) files + instances = reverse $ uniq [] instancesRaw + -- TODO: use the unique package + uniq curr [] = curr + uniq curr (x : xs) = + if elem x curr + then uniq curr xs + else uniq (x : curr) xs + + explodeDescription :: Description -> [Description] + explodeDescription (part @ (Part _ _ _ name _ _)) = + if null theseInstances + then [part] + else map (rewriteModule part) theseInstances + where theseInstances = map snd $ filter ((== name) . fst) instances + explodeDescription other = [other] + + -- TODO FIXME: Need to keep around the default instance and not perform + -- substitutions in it. + + -- substitute in a particular instance's paramter types + rewriteModule :: Description -> Instance -> Description + rewriteModule part typeMap = + Part extern kw ml m' p items' + where + Part extern kw ml m p items = part + m' = renameModule info m typeMap + items' = map rewriteDecl items + rewriteDecl :: ModuleItem -> ModuleItem + rewriteDecl (MIPackageItem (Decl (ParamType Parameter x _))) = + MIPackageItem $ Typedef (typeMap Map.! x) x + rewriteDecl other = other + -- TODO FIXME: Typedef conversion must be made to handle + -- ParamTypes! + -----items' = map (traverseDecls rewriteDecl) items + -----rewriteDecl :: Decl -> Decl + -----rewriteDecl (ParamType Parameter x _) = + ----- ParamType Localparam x (Just $ typeMap Map.! x) + -----rewriteDecl other = other + + +-- write down module parameter names and type parameters +collectDescriptionM :: Description -> Writer Info () +collectDescriptionM (part @ (Part _ _ _ name _ _)) = + tell $ Map.singleton name (paramNames, maybeTypeMap) + where + params = execWriter $ + collectModuleItemsM (collectDeclsM collectDeclM) part + paramNames = map fst params + maybeTypeMap = Map.fromList $ + map (\(x, y) -> (x, fromJust y)) $ + filter (isJust . snd) params + collectDeclM :: Decl -> Writer [(Identifier, Maybe (Maybe Type))] () + collectDeclM (Param Parameter _ x _) = tell [(x, Nothing)] + collectDeclM (ParamType Parameter x v) = tell [(x, Just v )] + collectDeclM _ = return () +collectDescriptionM _ = return () + +-- produces the default type mapping of a module, if there is one +defaultInstance :: MaybeTypeMap -> Maybe Instance +defaultInstance maybeTypeMap = + if any isNothing maybeTypeMap + then Nothing + else Just $ Map.map fromJust maybeTypeMap + +-- generate a "unique" name for a particular module type instance +renameModule :: Info -> Identifier -> Instance -> Identifier +renameModule info m inst = + if defaultInstance maybeTypeMap == Just inst + then m -- default instances keep the original module name + else m ++ "_" ++ shortHash (m, inst) + where maybeTypeMap = snd $ info Map.! m + + +mapInstance :: Info -> ModuleItem -> Writer Instances ModuleItem +mapInstance info (orig @ (Instance m bindings x r p)) = + if Map.notMember m info then + return orig + else if any (isLeft . snd) bindings' then + error $ "param type resolution left type params: " ++ show orig + ++ " converted to: " ++ show bindings' + else do + tell [(m, resolvedTypes)] + let m' = renameModule info m resolvedTypes + return $ Instance m' bindings' x r p + where + (paramNames, maybeTypeMap) = info Map.! m + -- attach names to unnamed parameters + bindingsNamed = + if all (== "") (map fst bindings) then + zip paramNames (map snd bindings) + else if any (== "") (map fst bindings) then + error $ "instance has a mix of named and unnamed params: " + ++ show orig + else bindings + -- determine the types corresponding to each type parameter + bindingsMap = Map.fromList bindingsNamed + resolvedTypes = Map.mapWithKey resolveType maybeTypeMap + resolveType :: Identifier -> Maybe Type -> Type + resolveType paramName defaultType = + case (Map.lookup paramName bindingsMap, defaultType) of + (Nothing, Just t) -> t + (Nothing, Nothing) -> + error $ "instantiation " ++ show orig ++ + " is missing a type parameter: " ++ paramName + (Just (Left t), _) -> t + (Just (Right e), _) -> + -- TODO: Some types could have been parsed as an expression + -- (i.e. aliases). Ideally we should have any such aliases + -- resolved before applying this conversion. + error $ "instantiation " ++ show orig ++ " has expr " + ++ show e ++ " for type param: " ++ paramName + -- leave only the normal expression params behind + isParamType = flip Map.member maybeTypeMap + bindings' = filter (not . isParamType . fst) bindingsNamed +mapInstance _ other = return other diff --git a/sv2v.cabal b/sv2v.cabal index cc6cdee..e1eb726 100644 --- a/sv2v.cabal +++ b/sv2v.cabal @@ -71,6 +71,7 @@ executable sv2v Convert.NestPI Convert.Package Convert.PackedArray + Convert.ParamType Convert.RemoveComments Convert.Return Convert.StarPort diff --git a/test/basic/paramtype.sv b/test/basic/paramtype.sv new file mode 100644 index 0000000..52aef7d --- /dev/null +++ b/test/basic/paramtype.sv @@ -0,0 +1,99 @@ +module m_def #( + parameter type T = logic +); + T x = 0; + initial begin + $display("m_def %b %b %d", x, x+1, $bits(T)); + end +endmodule + +module m_nodef #( + parameter type T +); + T x = 0; + initial begin + $display("m_nodef %b %b %d", x, x+1, $bits(T)); + end +endmodule + +module n_nodef #( + parameter type T, + parameter type U +); + T x = 0; + U y = 1; + initial begin + $display("n_nodef %b %b %d", x, x+1, $bits(T)); + $display("n_nodef %b %b %d", y, y+1, $bits(U)); + end +endmodule + +module n_def #( + parameter type T = logic, + parameter type U = logic +); + T x = 0; + U y = 1; + initial begin + $display("n_def %b %b %d", x, x+1, $bits(T)); + $display("n_def %b %b %d", y, y+1, $bits(U)); + end +endmodule + +module n_tdef #( + parameter type T, + parameter type U = logic +); + T x = 0; + U y = 1; + initial begin + $display("n_tdef %b %b %d", x, x+1, $bits(T)); + $display("n_tdef %b %b %d", y, y+1, $bits(U)); + end +endmodule + +// TODO Add support for parameters without default values. +module o_nodef #( + parameter a = 0, + parameter type T, + parameter type U, + parameter b = 0 +); + T x = a; + U y = b; + initial begin + $display("n_nodef a=%d %b %b %d", a, x, x+1, $bits(T)); + $display("n_nodef b=%d %b %b %d", b, y, y+1, $bits(U)); + end +endmodule + +module top; endmodule + +// Top level modules appear to be generally instantiated in lexicographic order, +// but instances within a module do not. This silliness helps produce more +// consistent output. + +module a_1; m_def x(); endmodule +module a_2; m_def #(logic [1:0]) x(); endmodule +module a_3; m_def #(.T(logic [1:0])) x(); endmodule + +module b_1; m_nodef #(logic [1:0]) x(); endmodule +module b_2; m_nodef #(.T(logic [1:0])) x(); endmodule + +module c_1; n_nodef #(logic [1:0], logic [2:0]) x(); endmodule +module c_2; n_nodef #(.T(logic [1:0]), .U(logic)) x(); endmodule +module c_3; n_nodef #(.U(logic), .T(logic [1:0])) x(); endmodule + +module d_1; n_def #(logic [1:0], logic [2:0]) x(); endmodule +module d_2; n_def #(.T(logic [1:0])) x(); endmodule +module d_3; n_def #(.U(logic [1:0])) x(); endmodule +module d_4; n_def #(.U(logic), .T(logic [1:0])) x(); endmodule + +module e_1; n_tdef #(logic [1:0], logic [2:0]) x(); endmodule +module e_2; n_tdef #(.T(logic [1:0]), .U(logic)) x(); endmodule +module e_3; n_tdef #(.U(logic), .T(logic [1:0])) x(); endmodule + +module f_1; o_nodef #(1, logic [1:0], logic [2:0], 0) x(); endmodule +module f_2; o_nodef #(.T(logic [1:0]), .U(logic), .b(1), .a(0)) x(); endmodule +module f_3; o_nodef #(0, logic [1:0], logic [2:0], 1) x(); endmodule +module f_4; o_nodef #(.T(logic [1:0]), .U(logic), .b(0), .a(1)) x(); endmodule diff --git a/test/basic/paramtype.v b/test/basic/paramtype.v new file mode 100644 index 0000000..c2d872e --- /dev/null +++ b/test/basic/paramtype.v @@ -0,0 +1,38 @@ +module top; + initial begin + // generated by running a commercial simulator + $display("m_def 0 00000000000000000000000000000001 1"); + $display("m_def 00 00000000000000000000000000000001 2"); + $display("m_def 00 00000000000000000000000000000001 2"); + $display("m_nodef 00 00000000000000000000000000000001 2"); + $display("m_nodef 00 00000000000000000000000000000001 2"); + $display("n_nodef 00 00000000000000000000000000000001 2"); + $display("n_nodef 001 00000000000000000000000000000010 3"); + $display("n_nodef 00 00000000000000000000000000000001 2"); + $display("n_nodef 1 00000000000000000000000000000010 1"); + $display("n_nodef 00 00000000000000000000000000000001 2"); + $display("n_nodef 1 00000000000000000000000000000010 1"); + $display("n_def 00 00000000000000000000000000000001 2"); + $display("n_def 001 00000000000000000000000000000010 3"); + $display("n_def 00 00000000000000000000000000000001 2"); + $display("n_def 1 00000000000000000000000000000010 1"); + $display("n_def 0 00000000000000000000000000000001 1"); + $display("n_def 01 00000000000000000000000000000010 2"); + $display("n_def 00 00000000000000000000000000000001 2"); + $display("n_def 1 00000000000000000000000000000010 1"); + $display("n_tdef 00 00000000000000000000000000000001 2"); + $display("n_tdef 001 00000000000000000000000000000010 3"); + $display("n_tdef 00 00000000000000000000000000000001 2"); + $display("n_tdef 1 00000000000000000000000000000010 1"); + $display("n_tdef 00 00000000000000000000000000000001 2"); + $display("n_tdef 1 00000000000000000000000000000010 1"); + $display("n_nodef a= 1 01 00000000000000000000000000000010 2"); + $display("n_nodef b= 0 000 00000000000000000000000000000001 3"); + $display("n_nodef a= 0 00 00000000000000000000000000000001 2"); + $display("n_nodef b= 1 1 00000000000000000000000000000010 1"); + $display("n_nodef a= 0 00 00000000000000000000000000000001 2"); + $display("n_nodef b= 1 001 00000000000000000000000000000010 3"); + $display("n_nodef a= 1 01 00000000000000000000000000000010 2"); + $display("n_nodef b= 0 0 00000000000000000000000000000001 1"); + end +endmodule