diff --git a/CHANGELOG.md b/CHANGELOG.md index 518375b..7dcf156 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,12 +15,15 @@ * Fixed an issue that prevented parsing tasks and functions with `inout` ports * Fixed conflicting genvar names when inlining interfaces and modules that use them; all genvars are now given a design-wide unique name +* Fixed failure to resolve typenames suffixed with dimensions in contexts + permitting both types and expressions, e.g., `$bits(T[W-1:0])` * Fixed errant constant folding of shadowed non-trivial localparams * Fixed certain non-ANSI style port declarations being incorrectly reported as incompatible ### Other Enhancements +* Added error checking for unresolved typenames * Added constant folding for `||` and `&&` ## v0.0.11 diff --git a/src/Convert/Typedef.hs b/src/Convert/Typedef.hs index 261aea9..6cc0427 100644 --- a/src/Convert/Typedef.hs +++ b/src/Convert/Typedef.hs @@ -1,7 +1,7 @@ {- sv2v - Author: Zachary Snow - - - Conversion for `typedef` + - Conversion for `typedef` and `localparam type` - - Aliased types can appear in all data declarations, including modules, blocks, - and function parameters. They are also found in type cast expressions. @@ -20,22 +20,44 @@ convert = map $ traverseDescriptions $ evalScoper . scopeModule scoper where scoper = scopeModuleItem traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM -traverseTypeOrExprM :: TypeOrExpr -> Scoper Type TypeOrExpr -traverseTypeOrExprM (Left (TypeOf (Ident x))) = do - details <- lookupElemM x - return $ case details of - Nothing -> Left $ TypeOf $ Ident x - Just (_, _, UnknownType) -> Left $ TypeOf $ Ident x - Just (_, _, typ) -> Left typ -traverseTypeOrExprM (Right (Ident x)) = do - details <- lookupElemM x - return $ case details of - Nothing -> Right $ Ident x - Just (_, _, UnknownType) -> Right $ Ident x - Just (_, _, typ) -> Left typ -traverseTypeOrExprM other = return other +type SC = Scoper IdentKind -traverseExprM :: Expr -> Scoper Type Expr +data IdentKind + = Type Type -- resolved typename + | Pending -- unresolved type parameter + | NonType String -- anything else + +traverseTypeOrExprM :: TypeOrExpr -> SC TypeOrExpr +traverseTypeOrExprM tore + | Left (TypeOf expr) <- tore = possibleTypeName tore expr + | Right expr <- tore = possibleTypeName tore expr + | otherwise = return tore + +possibleTypeName :: TypeOrExpr -> Expr -> SC TypeOrExpr +possibleTypeName orig expr + | Just (x, rs1) <- maybeTypeName = do + details <- lookupElemM x + return $ case details of + Just (_, _, Type typ) -> + Left $ tf $ rs1 ++ rs2 + where (tf, rs2) = typeRanges typ + Just (_, _, Pending) -> + Left $ Alias x rs1 + _ -> orig + | otherwise = return orig + where maybeTypeName = exprToTypeName [] expr + +-- aliases in type-or-expr contexts are parsed as expressions +exprToTypeName :: [Range] -> Expr -> Maybe (Identifier, [Range]) +exprToTypeName rs (Ident x) = Just (x, rs) +exprToTypeName rs (Bit expr idx) = + exprToTypeName (r : rs) expr + where r = (RawNum 0, BinOp Sub idx (RawNum 1)) +exprToTypeName rs (Range expr NonIndexed r) = do + exprToTypeName (r : rs) expr +exprToTypeName _ _ = Nothing + +traverseExprM :: Expr -> SC Expr traverseExprM (Cast v e) = do v' <- traverseTypeOrExprM v traverseExprM' $ Cast v' e @@ -51,68 +73,82 @@ traverseExprM (Pattern items) = do traverseExprM' $ Pattern $ zip names exprs traverseExprM other = traverseExprM' other -traverseExprM' :: Expr -> Scoper Type Expr +traverseExprM' :: Expr -> SC Expr traverseExprM' = traverseSinglyNestedExprsM traverseExprM >=> traverseExprTypesM traverseTypeM -traverseModuleItemM :: ModuleItem -> Scoper Type ModuleItem +traverseModuleItemM :: ModuleItem -> SC ModuleItem traverseModuleItemM (Instance m params x rs p) = do let mapParam (i, v) = traverseTypeOrExprM v >>= \v' -> return (i, v') params' <- mapM mapParam params traverseModuleItemM' $ Instance m params' x rs p traverseModuleItemM item = traverseModuleItemM' item -traverseModuleItemM' :: ModuleItem -> Scoper Type ModuleItem +traverseModuleItemM' :: ModuleItem -> SC ModuleItem traverseModuleItemM' = traverseNodesM traverseExprM return traverseTypeM traverseLHSM return where traverseLHSM = traverseLHSExprsM traverseExprM -traverseGenItemM :: GenItem -> Scoper Type GenItem +traverseGenItemM :: GenItem -> SC GenItem traverseGenItemM = traverseGenItemExprsM traverseExprM -traverseDeclM :: Decl -> Scoper Type Decl +traverseDeclM :: Decl -> SC Decl traverseDeclM decl = do decl' <- traverseDeclNodesM traverseTypeM traverseExprM decl case decl' of - Variable{} -> return decl' - Net{} -> return decl' + Variable _ _ x _ _ -> insertElem x (NonType "var") >> return decl' + Net _ _ _ _ x _ _ -> insertElem x (NonType "net") >> return decl' Param s (UnpackedType t rs1) x e -> do - insertElem x UnknownType + insertElem x (NonType $ show s) let (tf, rs2) = typeRanges t let t' = tf $ rs1 ++ rs2 return $ Param s t' x e - Param _ _ x _ -> - insertElem x UnknownType >> return decl' + Param s _ x _ -> + insertElem x (NonType $ show s) >> return decl' ParamType Localparam x t -> do - traverseTypeM t >>= scopeType >>= insertElem x + traverseTypeM t >>= scopeType >>= insertElem x . Type return $ case t of Enum{} -> ParamType Localparam tmpX t _ -> CommentDecl $ "removed localparam type " ++ x where tmpX = "_sv2v_keep_enum_for_params" - ParamType{} -> return decl' + ParamType Parameter x _ -> + insertElem x Pending >> return decl' CommentDecl{} -> return decl' -traverseStmtM :: Stmt -> Scoper Type Stmt +traverseStmtM :: Stmt -> SC Stmt traverseStmtM = traverseStmtExprsM traverseExprM -traverseTypeM :: Type -> Scoper Type Type +traverseTypeM :: Type -> SC Type traverseTypeM (Alias st rs1) = do details <- lookupElemM st rs1' <- mapM traverseRangeM rs1 - return $ case details of - Nothing -> Alias st rs1' - Just (_, _, UnknownType) -> Alias st rs1' - Just (_, _, typ) -> tf $ rs1' ++ rs2 + case details of + Just (_, _, Type typ) -> + return $ tf $ rs1' ++ rs2 where (tf, rs2) = typeRanges typ + Just (_, _, Pending) -> + return $ Alias st rs1' + Just (_, _, NonType kind) -> + scopedErrorM $ "expected typename, but found " ++ kind + ++ " identifier " ++ show st + Nothing -> + scopedErrorM $ "couldn't resolve typename " ++ show st traverseTypeM (TypedefRef expr) = do details <- lookupElemM expr - return $ case details of - Nothing -> TypedefRef expr - Just (_, _, typ) -> typ + case details of + Just (_, _, Type typ) -> return typ + Just (_, _, Pending) -> + error "TypdefRef invariant violated! Please file an issue." + Just (_, _, NonType kind) -> + scopedErrorM $ "expected interface-based typename, but found " + ++ kind ++ " " ++ show expr + -- This can occur when the interface conversion is delayed due to + -- multi-dimension instances. + Nothing -> return $ TypedefRef expr traverseTypeM other = traverseSinglyNestedTypesM traverseTypeM other >>= traverseTypeExprsM traverseExprM -traverseRangeM :: Range -> Scoper Type Range +traverseRangeM :: Range -> SC Range traverseRangeM = mapBothM traverseExprM diff --git a/test/core/typename_deep.sv b/test/core/typename_deep.sv new file mode 100644 index 0000000..f0f86ba --- /dev/null +++ b/test/core/typename_deep.sv @@ -0,0 +1,6 @@ +`define TEST_INNER(expr, size) \ + initial $display(`"expr %0d %0d %0d`", \ + $bits(expr), $bits(type(expr)), size); +localparam type T = logic; +localparam type U = logic [2:1]; +`include "typename_deep.svh" diff --git a/test/core/typename_deep.svh b/test/core/typename_deep.svh new file mode 100644 index 0000000..a7db615 --- /dev/null +++ b/test/core/typename_deep.svh @@ -0,0 +1,14 @@ +`define TEST(expr, size) \ + `TEST_INNER(expr, size) \ + `TEST_INNER(expr[2], size * 2) \ + `TEST_INNER(expr[2][3], size * 6) \ + `TEST_INNER(expr[3:7], size * 5) \ + `TEST_INNER(expr[$bits(T)], size) \ + `TEST_INNER(expr[$bits(U)], size * 2) \ + `TEST_INNER(expr[$bits(T[$bits(U)])], size * 2) \ + `TEST_INNER(expr[$bits(U[$bits(U)])], size * 4) + +module top; + `TEST(T, 1) + `TEST(U, 2) +endmodule diff --git a/test/core/typename_deep.v b/test/core/typename_deep.v new file mode 100644 index 0000000..745a5d9 --- /dev/null +++ b/test/core/typename_deep.v @@ -0,0 +1,4 @@ +`define TEST_INNER(expr, size) \ + initial $display(`"expr %0d %0d %0d`", \ + size, size, size); +`include "typename_deep.svh" diff --git a/test/error/typedef_missing.sv b/test/error/typedef_missing.sv new file mode 100644 index 0000000..dc5fc19 --- /dev/null +++ b/test/error/typedef_missing.sv @@ -0,0 +1,5 @@ +// pattern: couldn't resolve typename "T" +// location: typedef_missing.sv:4:5 +module top; + T x; +endmodule diff --git a/test/error/typedef_not_type_localparam.sv b/test/error/typedef_not_type_localparam.sv new file mode 100644 index 0000000..eea7e18 --- /dev/null +++ b/test/error/typedef_not_type_localparam.sv @@ -0,0 +1,6 @@ +// pattern: expected typename, but found localparam identifier "L" +// location: typedef_not_type_localparam.sv:5:5 +module top; + localparam L = 0; + L x; +endmodule diff --git a/test/error/typedef_not_type_net.sv b/test/error/typedef_not_type_net.sv new file mode 100644 index 0000000..09dcc5b --- /dev/null +++ b/test/error/typedef_not_type_net.sv @@ -0,0 +1,6 @@ +// pattern: expected typename, but found net identifier "w" +// location: typedef_not_type_net.sv:5:5 +module top; + wire w; + w x; +endmodule diff --git a/test/error/typedef_not_type_var.sv b/test/error/typedef_not_type_var.sv new file mode 100644 index 0000000..2c8f3bf --- /dev/null +++ b/test/error/typedef_not_type_var.sv @@ -0,0 +1,6 @@ +// pattern: expected typename, but found var identifier "v" +// location: typedef_not_type_var.sv:5:5 +module top; + var v; + v x; +endmodule diff --git a/test/error/typedef_ref_not_type.sv b/test/error/typedef_ref_not_type.sv new file mode 100644 index 0000000..7266065 --- /dev/null +++ b/test/error/typedef_ref_not_type.sv @@ -0,0 +1,9 @@ +// pattern: expected interface-based typename, but found net x.y +// location: typedef_ref_not_type.sv:7:5 +module top; + if (1) begin : x + wire y; + end + typedef x.y T; + T x; +endmodule