diff --git a/src/Convert/Interface.hs b/src/Convert/Interface.hs index 439b863..d529711 100644 --- a/src/Convert/Interface.hs +++ b/src/Convert/Interface.hs @@ -7,6 +7,7 @@ module Convert.Interface (convert) where +import Data.List (intercalate, (\\)) import Data.Maybe (isJust, isNothing, mapMaybe) import Control.Monad.Writer.Strict import qualified Data.Map.Strict as Map @@ -57,7 +58,7 @@ convertDescription :: PartInfos -> Description -> Description convertDescription _ (Part _ _ Interface _ name _ _) = PackageItem $ Decl $ CommentDecl $ "removed interface: " ++ name convertDescription parts (Part attrs extern Module lifetime name ports items) = - if null $ extractModportInstances $ PartInfo Module ports items then + if null $ extractModportInstances name $ PartInfo Module ports items then Part attrs extern Module lifetime name ports items' else PackageItem $ Decl $ CommentDecl $ @@ -99,12 +100,12 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = else do -- inline instantiation of a module let modportBindings = getModportBindings modports - if length modportInstances /= length modportBindings - then - error $ "instance " ++ instanceName ++ " of " ++ part - ++ " has interface ports " - ++ showKeys modportInstances ++ ", but only " - ++ showKeys modportBindings ++ " are connected" + let unconnected = map fst modportInstances \\ + map fst modportBindings + if not (null unconnected) + then scopedErrorM $ "instance " ++ instanceName ++ " of " + ++ part ++ " has unconnected interface ports: " + ++ intercalate ", " unconnected else scoper $ Generate $ map GenModuleItem $ inlineInstance modports rs modportBindings partItems part instanceName paramBindings portBindings @@ -115,12 +116,11 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = Just partInfo = maybePartInfo PartInfo partKind _ partItems = partInfo - modportInstances = extractModportInstances partInfo + modportInstances = extractModportInstances part partInfo getModportBindings modports = mapMaybe (inferModportBinding modports modportInstances) $ map (second $ addImpliedSlice modports) portBindings second f = \(a, b) -> (a, f b) - showKeys = show . map fst traverseModuleItemM other = return other @@ -195,8 +195,8 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = -- given entire interface, but just bound to a modport foundModport $ Dot expr modportName else if modportInstance /= Nothing then - error $ "could not resolve modport binding " ++ show expr - ++ " for port " ++ portName ++ " of type " + scopedError modports $ "could not resolve modport binding " + ++ show expr ++ " for port " ++ portName ++ " of type " ++ showModportType interfaceName modportName else Nothing @@ -208,14 +208,15 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = (interfaceName, modportName) = case modportInstance of Just x -> x - Nothing -> error $ "can't deduce modport for interface " - ++ show expr ++ " bound to port " ++ portName + Nothing -> scopedError modports $ + "can't deduce modport for interface " ++ show expr + ++ " bound to port " ++ portName foundModport modportE = if (null interfaceName || bInterfaceName == interfaceName) && (null modportName || bModportName == modportName) then Just (instanceE, qualifyModport modportE) - else error msg + else scopedError modports msg where bModportName = case modportE of @@ -246,8 +247,8 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = Nothing -> case lookupIntfElem modports (Dot e "") of Just (accesses, _, _) -> init accesses - Nothing -> - error $ "could not find modport " ++ show e + Nothing -> scopedError modports $ + "could not find modport " ++ show e showModportType :: Identifier -> Identifier -> String showModportType "" "" = "generic interface" @@ -271,23 +272,26 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = e' = replaceInExpr replacements e -- association list of modport instances in the given module body - extractModportInstances :: PartInfo -> ModportInstances - extractModportInstances partInfo = - execWriter $ mapM (collectDeclsM collectDecl) (pItems partInfo) + extractModportInstances :: Identifier -> PartInfo -> ModportInstances + extractModportInstances part partInfo = + execWriter $ runScoperT $ scopeModuleItems collector part decls where - collectDecl :: Decl -> Writer ModportInstances () - collectDecl (Variable _ t x _ _) = + collector = scopeModuleItem checkDecl return return return + decls = filter isDecl $ pItems partInfo + checkDecl :: Decl -> ScoperT () (Writer ModportInstances) Decl + checkDecl decl@(Variable _ t x _ _) = if maybeInfo == Nothing then - return () + return decl else if elem x (pPorts partInfo) then - tell [(x, info)] + tell [(x, info)] >> return decl else - error $ "Modport not in port list: " ++ show (t, x) + scopedErrorM $ + "Modport not in port list: " ++ show t ++ " " ++ x ++ ". Is this an interface missing a port list?" where maybeInfo = extractModportInfo t Just info = maybeInfo - collectDecl _ = return () + checkDecl decl = return decl extractModportInfo :: Type -> Maybe (Identifier, Identifier) extractModportInfo (InterfaceT "" "" _) = Just ("", "") @@ -309,6 +313,10 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = convertDescription _ other = other +isDecl :: ModuleItem -> Bool +isDecl (MIPackageItem Decl{}) = True +isDecl _ = False + -- produce the implicit modport decls for an interface bundle impliedModport :: [ModuleItem] -> [ModportDecl] impliedModport = @@ -525,8 +533,8 @@ inlineInstance global ranges modportBindings items partName checkExprResolution local expr = if not (exprResolves local expr) && exprResolves global expr then - error $ "inlining instance \"" ++ instanceName ++ "\" of " - ++ inlineKind ++ " \"" ++ partName + scopedError local $ "inlining instance \"" ++ instanceName + ++ "\" of " ++ inlineKind ++ " \"" ++ partName ++ "\" would make expression \"" ++ show expr ++ "\" used in \"" ++ instanceName ++ "\" resolvable when it wasn't previously" diff --git a/test/error/interface_bad_expr.sv b/test/error/interface_bad_expr.sv index 581fe83..d6e94a8 100644 --- a/test/error/interface_bad_expr.sv +++ b/test/error/interface_bad_expr.sv @@ -1,4 +1,5 @@ // pattern: inlining instance "intf" of interface "Interface" would make expression "x" used in "intf" resolvable when it wasn't previously +// location: interface_bad_expr.sv:4:5 interface Interface; assign x = 1; endinterface diff --git a/test/error/interface_bad_expr_genvar.sv b/test/error/interface_bad_expr_genvar.sv index 8b8f54f..82f3ce4 100644 --- a/test/error/interface_bad_expr_genvar.sv +++ b/test/error/interface_bad_expr_genvar.sv @@ -1,4 +1,5 @@ // pattern: inlining instance "intf" of interface "Interface" would make expression "x" used in "intf" resolvable when it wasn't previously +// location: interface_bad_expr_genvar.sv:4:5 interface Interface; assign x = 1; endinterface diff --git a/test/error/interface_mismatch_1.sv b/test/error/interface_mismatch_1.sv index ef2e10c..a77b0ce 100644 --- a/test/error/interface_mismatch_1.sv +++ b/test/error/interface_mismatch_1.sv @@ -1,4 +1,5 @@ // pattern: port intf has type Interface1, but the binding intf2 has type Interface2 +// location: interface_mismatch_1.sv:9:5 `include "interface_mismatch.svh" module Module(intf); Interface1 intf; diff --git a/test/error/interface_mismatch_2.sv b/test/error/interface_mismatch_2.sv index 12eac7a..712886f 100644 --- a/test/error/interface_mismatch_2.sv +++ b/test/error/interface_mismatch_2.sv @@ -1,4 +1,5 @@ // pattern: port intf has type Interface1, but the binding intf2\.ModportA has type Interface2\.ModportA +// location: interface_mismatch_2.sv:9:5 `include "interface_mismatch.svh" module Module(intf); Interface1 intf; diff --git a/test/error/interface_mismatch_3.sv b/test/error/interface_mismatch_3.sv index 55f4240..a93e16f 100644 --- a/test/error/interface_mismatch_3.sv +++ b/test/error/interface_mismatch_3.sv @@ -1,4 +1,5 @@ // pattern: port intf has type Interface1\.ModportB, but the binding intf1\.ModportA has type Interface1\.ModportA +// location: interface_mismatch_3.sv:9:5 `include "interface_mismatch.svh" module Module(intf); Interface1.ModportB intf; diff --git a/test/error/interface_mismatch_4.sv b/test/error/interface_mismatch_4.sv index 6a02fc8..bfac735 100644 --- a/test/error/interface_mismatch_4.sv +++ b/test/error/interface_mismatch_4.sv @@ -1,4 +1,5 @@ // pattern: could not resolve modport binding intf1\.ModportC for port intf of type Interface1\.ModportB +// location: interface_mismatch_4.sv:9:5 `include "interface_mismatch.svh" module Module(intf); Interface1.ModportB intf; diff --git a/test/error/interface_mismatch_5.sv b/test/error/interface_mismatch_5.sv index a38e18e..cf15819 100644 --- a/test/error/interface_mismatch_5.sv +++ b/test/error/interface_mismatch_5.sv @@ -1,4 +1,5 @@ // pattern: could not resolve modport binding intf1\.ModportC for port intf of type Interface1\.ModportC +// location: interface_mismatch_5.sv:9:5 `include "interface_mismatch.svh" module Module(intf); Interface1.ModportC intf; diff --git a/test/error/interface_mismatch_6.sv b/test/error/interface_mismatch_6.sv index ecdd702..bd3d2cc 100644 --- a/test/error/interface_mismatch_6.sv +++ b/test/error/interface_mismatch_6.sv @@ -1,4 +1,5 @@ // pattern: could not find modport intf1\.ModportC +// location: interface_mismatch_6.sv:9:5 `include "interface_mismatch.svh" module Module(intf); Interface1.ModportC intf; diff --git a/test/error/interface_modport_missing.sv b/test/error/interface_modport_missing.sv index c1ba561..511264c 100644 --- a/test/error/interface_modport_missing.sv +++ b/test/error/interface_modport_missing.sv @@ -1,13 +1,13 @@ +// pattern: can't deduce modport for interface j bound to port j, within scope top +// location: interface_modport_missing.sv:14:5 interface Interface; logic x; endinterface - module Module(i, j); Interface i; logic j; assign i.x = j.x; endmodule - module top; Interface i(); Interface j(); diff --git a/test/error/interface_modport_unlisted.sv b/test/error/interface_modport_unlisted.sv index 9371607..129e74e 100644 --- a/test/error/interface_modport_unlisted.sv +++ b/test/error/interface_modport_unlisted.sv @@ -1,7 +1,8 @@ +// pattern: Modport not in port list: Interface i\. Is this an interface missing a port list\? +// location: interface_modport_unlisted.sv:7:5 interface Interface; logic x; endinterface - module Module; Interface i; endmodule diff --git a/test/error/interface_unbound_modport.sv b/test/error/interface_unbound_modport.sv index 7b507a5..74a73d4 100644 --- a/test/error/interface_unbound_modport.sv +++ b/test/error/interface_unbound_modport.sv @@ -1,11 +1,11 @@ +// pattern: instance m of Module has unconnected interface ports: i +// location: interface_unbound_modport.sv:11:5 interface Interface; logic x; endinterface - module Module(i); Interface i; endmodule - module top; Interface i(); Module m(); diff --git a/test/error/interface_unbound_modports.sv b/test/error/interface_unbound_modports.sv new file mode 100644 index 0000000..0b836c6 --- /dev/null +++ b/test/error/interface_unbound_modports.sv @@ -0,0 +1,14 @@ +// pattern: instance m of Module has unconnected interface ports: i, k +// location: interface_unbound_modports.sv:13:5 +interface Interface; + logic x; +endinterface +module Module(i, j, k); + Interface i; + Interface j; + Interface k; +endmodule +module top; + Interface j(); + Module m(.j); +endmodule