diff --git a/CHANGELOG.md b/CHANGELOG.md index b09695a..26e775a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### New Features * Added support for attributes in unary, binary, and ternary expressions +* Added support for shadowing interface names with local typenames ### Bug Fixes diff --git a/src/Convert.hs b/src/Convert.hs index db70419..61fcd75 100644 --- a/src/Convert.hs +++ b/src/Convert.hs @@ -116,6 +116,7 @@ initialPhases tops selectExclude = , Convert.FuncRoutine.convert , selectExclude Job.Assert Convert.Assertion.convert , selectExclude Job.Always Convert.AlwaysKW.convert + , Convert.Interface.disambiguate , Convert.Package.convert , Convert.StructConst.convert , Convert.PortDecl.convert diff --git a/src/Convert/Interface.hs b/src/Convert/Interface.hs index 9b8153c..51f1b56 100644 --- a/src/Convert/Interface.hs +++ b/src/Convert/Interface.hs @@ -5,7 +5,7 @@ - Conversion for interfaces -} -module Convert.Interface (convert) where +module Convert.Interface (convert, disambiguate) where import Data.List (intercalate, (\\)) import Data.Maybe (isJust, isNothing, mapMaybe) @@ -38,11 +38,6 @@ convert tops files = (map . convertDescription tops) files where - -- we can only collect/map non-extern interfaces and modules - collectPart :: Description -> Writer PartInfos () - collectPart (Part _ False kw _ name ports items) = - tell $ Map.singleton name $ PartInfo kw ports items - collectPart _ = return () -- multidimensional instances need to be flattened before this -- conversion can proceed needsFlattening = @@ -55,6 +50,67 @@ convert tops files = checkItem (Instance _ _ _ rs _) = when (length rs > 1) $ tell $ Any True checkItem _ = return () +-- we can only collect/map non-extern interfaces and modules +collectPart :: Description -> Writer PartInfos () +collectPart (Part _ False kw _ name ports items) = + tell $ Map.singleton name $ PartInfo kw ports items +collectPart _ = return () + +-- disambiguate typenames from interface names +disambiguate :: [AST] -> [AST] +disambiguate = traverseFiles + (collectDescriptionsM collectPart) + (map . disambiguateDescription) + +-- disambiguate any typenames within a description +disambiguateDescription :: PartInfos -> Description -> Description +disambiguateDescription parts (Part att ext kw lif name ports items) = + Part att ext kw lif name ports $ map traverseModuleItem items + where + typeNames = getTypeNames items + + traverseModuleItem :: ModuleItem -> ModuleItem + traverseModuleItem (MIAttr attr item) = + MIAttr attr $ traverseModuleItem item + traverseModuleItem (MIPackageItem (Decl (Variable d t x a e))) = + MIPackageItem $ Decl $ Variable d (traverseType t) x a e + traverseModuleItem other = other + + traverseType :: Type -> Type + traverseType (Alias interfaceName rs) = + if isInterface interfaceName && not (elem interfaceName typeNames) + then InterfaceT interfaceName "" rs + else Alias interfaceName rs + traverseType orig@(InterfaceT interfaceName _ _) = + if null interfaceName || isInterface interfaceName + then orig + else error $ "declaration type " ++ show orig ++ " appears to " + ++ "refer to an interface that isn't defined" + traverseType other = other + + isInterface :: Identifier -> Bool + isInterface partName = + fmap pKind (Map.lookup partName parts) == Just Interface + +disambiguateDescription _ other = other + +-- get all of the typenames declared anywhere in the top-level module items +getTypeNames :: [ModuleItem] -> [Identifier] +getTypeNames (MIAttr _ item : rest) = getTypeNames $ item : rest +getTypeNames (Generate genItems : rest) = + getTypeNames $ genModuleItems genItems ++ rest +getTypeNames (MIPackageItem (Decl (ParamType _ name _)) : rest) = + name : getTypeNames rest +getTypeNames (_ : rest) = getTypeNames rest +getTypeNames [] = [] + +-- get the top-level (i.e., un-scoped) module items within a generate block +genModuleItems :: [GenItem] -> [ModuleItem] +genModuleItems (GenModuleItem item : rest) = + item : genModuleItems rest +genModuleItems (_ : rest) = genModuleItems rest +genModuleItems [] = [] + topInterfaceError :: String -> String -> a topInterfaceError name issue = error $ "Specified top module " ++ name ++ " " ++ issue ++ ". Please " ++ @@ -307,7 +363,6 @@ convertDescription tops parts (Part att ext Module lif name ports items) = else if elem x (pPorts partInfo) then tell [(x, info)] >> return decl else - -- TODO: This does not handle shadowed typenames. scopedErrorM $ "Modport not in port list: " ++ show t ++ " " ++ x ++ ". Is this an interface missing a port list?" @@ -317,23 +372,10 @@ convertDescription tops parts (Part att ext Module lif name ports items) = checkDecl decl = return decl extractModportInfo :: Type -> Maybe (Identifier, Identifier) - extractModportInfo (InterfaceT "" "" _) = Just ("", "") extractModportInfo (InterfaceT interfaceName modportName _) = - if isInterface interfaceName - then Just (interfaceName, modportName) - else Nothing - extractModportInfo (Alias interfaceName _) = - if isInterface interfaceName - then Just (interfaceName, "") - else Nothing + Just (interfaceName, modportName) extractModportInfo _ = Nothing - isInterface :: Identifier -> Bool - isInterface partName = - case Map.lookup partName parts of - Nothing -> False - Just info -> pKind info == Interface - convertDescription _ _ other = other isDecl :: ModuleItem -> Bool diff --git a/src/Language/SystemVerilog/AST/Type.hs b/src/Language/SystemVerilog/AST/Type.hs index 68b9b10..fe6bf7f 100644 --- a/src/Language/SystemVerilog/AST/Type.hs +++ b/src/Language/SystemVerilog/AST/Type.hs @@ -62,7 +62,8 @@ instance Show Type where show (IntegerVector kw sg rs) = printf "%s%s%s" (show kw) (showPadBefore sg) (showRanges rs) show (IntegerAtom kw sg ) = printf "%s%s" (show kw) (showPadBefore sg) show (NonInteger kw ) = printf "%s" (show kw) - show (InterfaceT "" "" rs) = printf "interface%s" ( showRanges rs) + show (InterfaceT "" "" rs) = printf "interface%s" (showRanges rs) + show (InterfaceT xx "" rs) = printf "%s%s" xx (showRanges rs) show (InterfaceT xx yy rs) = printf "%s.%s%s" xx yy (showRanges rs) show (Enum t vals r) = printf "enum %s{%s}%s" tStr (commas $ map showVal vals) (showRanges r) where diff --git a/test/core/interface_name_shadow.sv b/test/core/interface_name_shadow.sv index a8d4c87..d5ce5bf 100644 --- a/test/core/interface_name_shadow.sv +++ b/test/core/interface_name_shadow.sv @@ -1,6 +1,12 @@ interface I; logic [3:0] x; endinterface +module A(I i); + initial $display("A %b", i.x); +endmodule +module B #(localparam type I = logic [3:0]) (I i); + initial $display("B %b", i); +endmodule module top; I i(); if (1) begin : blk @@ -9,4 +15,7 @@ module top; assign i = 0; end initial $display("%b %b", i.x, blk.i); + A a(i); + B b(i.x); + assign i.x = 1; endmodule diff --git a/test/core/interface_name_shadow.v b/test/core/interface_name_shadow.v index 9b9c256..256d7b0 100644 --- a/test/core/interface_name_shadow.v +++ b/test/core/interface_name_shadow.v @@ -1,3 +1,9 @@ +module A(input wire [3:0] i); + initial $display("A %b", i); +endmodule +module B(input wire [3:0] i); + initial $display("B %b", i); +endmodule module top; generate if (1) begin : i @@ -9,4 +15,7 @@ module top; end endgenerate initial $display("%b %b", i.x, blk.i); + A a(i.x); + B b(i.x); + assign i.x = 1; endmodule diff --git a/test/error/interface_unknown.sv b/test/error/interface_unknown.sv new file mode 100644 index 0000000..df484f5 --- /dev/null +++ b/test/error/interface_unknown.sv @@ -0,0 +1,3 @@ +// pattern: declaration type I.J appears to refer to an interface that isn't defined +module mod(I.J K); +endmodule