diff --git a/CHANGELOG.md b/CHANGELOG.md index 325d4a2..7d483ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### New Features +* Added support for module and interface input ports with default values * Added conversion of severity system tasks and elaboration system tasks (e.g., `$info`) into `$display` tasks that include source file and scope information; pass `-E SeverityTask` to disable this new conversion diff --git a/src/Convert.hs b/src/Convert.hs index 75e11af..4d08e55 100644 --- a/src/Convert.hs +++ b/src/Convert.hs @@ -43,6 +43,7 @@ import qualified Convert.Package import qualified Convert.ParamNoDefault import qualified Convert.ParamType import qualified Convert.PortDecl +import qualified Convert.PortDefault import qualified Convert.RemoveComments import qualified Convert.ResolveBindings import qualified Convert.SeverityTask @@ -75,6 +76,7 @@ finalPhases _ = , Convert.FuncRet.convert , Convert.TFBlock.convert , Convert.Unsigned.convert + , Convert.PortDefault.convert , Convert.StringType.convert ] diff --git a/src/Convert/Interface.hs b/src/Convert/Interface.hs index 27c73d9..1d57c08 100644 --- a/src/Convert/Interface.hs +++ b/src/Convert/Interface.hs @@ -631,10 +631,14 @@ inlineInstance global ranges modportBindings items partName removeDeclDir :: Decl -> Decl removeDeclDir (Variable _ t x a e) = - Variable Local t' x a e - where t' = case t of + Variable Local t' x a e' + where + t' = case t of Implicit sg rs -> IntegerVector TLogic sg rs _ -> t + e' = if isNothing (lookup x instancePorts) + then e + else Nil removeDeclDir decl@Net{} = traverseNetAsVar removeDeclDir decl removeDeclDir other = other diff --git a/src/Convert/PortDefault.hs b/src/Convert/PortDefault.hs new file mode 100644 index 0000000..59252da --- /dev/null +++ b/src/Convert/PortDefault.hs @@ -0,0 +1,92 @@ +{- sv2v + - Author: Zachary Snow + - + - Conversion for input ports with default values + - + - The default values are permitted to depend on complex constants declared + - within the instantiated module. The relevant constants are copied into the + - site of the instantiation. + -} + +module Convert.PortDefault (convert) where + +import Control.Monad.Writer.Strict +import Data.Functor ((<&>)) +import qualified Data.Map.Strict as Map +import Data.Maybe (isNothing) + +import Convert.Traverse +import Convert.UnbasedUnsized (inlineConstants) +import Language.SystemVerilog.AST + +type Part = ([ModuleItem], [PortDefault]) +type Parts = Map.Map Identifier Part +type PortDefault = (Identifier, Decl) + +convert :: [AST] -> [AST] +convert files = + map (traverseDescriptions convertDescription) files' + where + (files', parts) = runWriter $ + mapM (traverseDescriptionsM preparePart) files + convertDescription = traverseModuleItems $ convertModuleItem parts + +-- remove and record input ports with defaults +preparePart :: Description -> Writer Parts Description +preparePart description@(Part att ext kw lif name ports items) = + if null portDefaults + then return description + else do + tell $ Map.singleton name (items, portDefaults) + return $ Part att ext kw lif name ports items' + where + (items', portDefaults) = runWriter $ + mapM (traverseNestedModuleItemsM prepareModuleItem) items +preparePart other = return other + +prepareModuleItem :: ModuleItem -> Writer [PortDefault] ModuleItem +prepareModuleItem (MIPackageItem (Decl decl)) = + prepareDecl decl <&> MIPackageItem . Decl +prepareModuleItem other = return other + +prepareDecl :: Decl -> Writer [PortDefault] Decl +prepareDecl (Variable Input t x [] e) | e /= Nil = + preparePortDefault t x e >> return (Variable Input t x [] Nil) +prepareDecl (Net Input n s t x [] e) | e /= Nil = + preparePortDefault t x e >> return (Net Input n s t x [] Nil) +prepareDecl other = return other + +preparePortDefault :: Type -> Identifier -> Expr -> Writer [PortDefault] () +preparePortDefault t x e = tell [(x, decl)] + where + decl = Param Localparam t' x e + t' = case t of + Implicit sg [] -> Implicit sg [(RawNum 0, RawNum 0)] + _ -> t + +-- add default port bindings to module instances that need them +convertModuleItem :: Parts -> ModuleItem -> ModuleItem +convertModuleItem parts (Instance moduleName params instanceName ds bindings) = + if isNothing maybePart || null neededDecls + then instanceBase bindings + else Generate $ map GenModuleItem $ + stubItems ++ [instanceBase $ neededBindings ++ bindings] + where + instanceBase = Instance moduleName params instanceName ds + maybePart = Map.lookup moduleName parts + Just (moduleItems, portDefaults) = maybePart + + -- determine which defaulted ports are unbound + (neededBindings, neededDecls) = unzip + [ ( (port, Ident $ blockName ++ '_' : port) + , MIPackageItem $ Decl decl + ) + | (port, decl) <- portDefaults + , isNothing (lookup port bindings) + ] + + -- inline and prefix the declarations used by the defaults + stubItems = inlineConstants blockName params moduleItems neededDecls + blockName = "sv2v_pd_" ++ instanceName + +convertModuleItem _ other = other diff --git a/src/Convert/UnbasedUnsized.hs b/src/Convert/UnbasedUnsized.hs index 6ca100d..39ba729 100644 --- a/src/Convert/UnbasedUnsized.hs +++ b/src/Convert/UnbasedUnsized.hs @@ -14,7 +14,10 @@ - creating hierarchical or generate-scoped references. -} -module Convert.UnbasedUnsized (convert) where +module Convert.UnbasedUnsized + ( convert + , inlineConstants + ) where import Control.Monad.Writer.Strict import Data.Either (isLeft) @@ -62,7 +65,7 @@ convertModuleItem parts (Instance moduleName params instanceName ds bindings) = -- checking whether we're ready to inline hasTypeParams = any (isLeft . snd) params - moduleIsResolved = isEntirelyResolved selectedStubItems + moduleIsResolved = isEntirelyResolved stubItems -- transform the existing bindings to reference extension declarations (bindings', extensionDeclLists) = unzip $ @@ -71,13 +74,18 @@ convertModuleItem parts (Instance moduleName params instanceName ds bindings) = -- inline the necessary portions of the module alongside the selected -- extension declarations - stubItems = - map (traverseDecls overrideParam) $ - prefixItems blockName selectedStubItems - selectedStubItems = inject rawStubItems extensionDecls - rawStubItems = createModuleStub moduleItems + stubItems = inlineConstants blockName params moduleItems extensionDecls blockName = "sv2v_uu_" ++ instanceName +convertModuleItem _ other = convertModuleItem' other + +inlineConstants :: Identifier -> [ParamBinding] -> [ModuleItem] -> [ModuleItem] + -> [ModuleItem] +inlineConstants blockName params moduleItems = + map (traverseDecls overrideParam) . + prefixItems blockName . + inject (createModuleStub moduleItems) + where -- override a parameter value in the stub overrideParam :: Decl -> Decl overrideParam (Param Parameter t x e) = @@ -89,8 +97,6 @@ convertModuleItem parts (Instance moduleName params instanceName ds bindings) = where xOrig = drop (length blockName + 1) x overrideParam decl = decl -convertModuleItem _ other = convertModuleItem' other - -- convert a port binding and produce a list of needed extension decls convertBinding :: Identifier -> PortBinding -> (PortBinding, [Decl]) convertBinding blockName (portName, expr) = diff --git a/sv2v.cabal b/sv2v.cabal index 59634e4..15f2b01 100644 --- a/sv2v.cabal +++ b/sv2v.cabal @@ -95,6 +95,7 @@ executable sv2v Convert.ParamNoDefault Convert.ParamType Convert.PortDecl + Convert.PortDefault Convert.RemoveComments Convert.ResolveBindings Convert.Scoper diff --git a/test/core/port_default.sv b/test/core/port_default.sv new file mode 100644 index 0000000..3f36c46 --- /dev/null +++ b/test/core/port_default.sv @@ -0,0 +1,51 @@ +module M(x, y); + parameter P = 0; + localparam L = 10; + function automatic integer F; + input integer inp; + return inp + L; + endfunction + input wire integer x = F(P); // defining a default here is non-standard + input wire integer y; + initial #1 $display("M %0d %0d", x, y); +endmodule + +module N #( + parameter type P, + parameter type Q = integer, + localparam L = 10 +) ( + input wire P x = $bits(P) + L, + input wire Q y = $bits(Q) + L +); + initial #1 $display("N %b %b", x, y); +endmodule + +interface I #( + parameter type P, + parameter type Q = integer, + localparam L = 10 +) ( + input wire P x = $bits(P) + L, + input wire Q y = $bits(Q) + L +); + initial #2 $display("I %b %b", x, y); +endinterface + +module top; + M m0(0, 0); + M m1(.y(1)); + M#(1) m2(.y(2)); + + N#(logic) n0(); + N#(logic) n1(1'b0); + N#(logic) n2(.y(2)); + N#(byte) n3(); + N#(byte, shortint) n4(); + + I#(logic) i0(); + I#(logic) i1(0); + I#(logic) i2(.y(2)); + I#(byte) i3(); + I#(byte, shortint) i4(); +endmodule diff --git a/test/core/port_default.v b/test/core/port_default.v new file mode 100644 index 0000000..e8081da --- /dev/null +++ b/test/core/port_default.v @@ -0,0 +1,19 @@ +module top; + initial #2 begin + $display("M %0d %0d", 0, 0); + $display("M %0d %0d", 10, 1); + $display("M %0d %0d", 11, 2); + + $display("N %b %b", 1'b1, 32'd42); + $display("N %b %b", 1'b0, 32'd42); + $display("N %b %b", 1'b1, 32'd2); + $display("N %b %b", 8'd18, 32'd42); + $display("N %b %b", 8'd18, 16'd26); + + $display("I %b %b", 1'b1, 32'd42); + $display("I %b %b", 1'b0, 32'd42); + $display("I %b %b", 1'b1, 32'd2); + $display("I %b %b", 8'd18, 32'd42); + $display("I %b %b", 8'd18, 16'd26); + end +endmodule