From 7ea5b60d0b30deda0417bc2b7e23c65ea8a215c8 Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Sat, 21 Dec 2019 19:31:56 -0500 Subject: [PATCH] support for inside case statements --- src/Convert/Inside.hs | 45 ++++++++++++++-- src/Language/SystemVerilog/AST/Expr.hs | 8 +-- src/Language/SystemVerilog/AST/Stmt.hs | 8 ++- src/Language/SystemVerilog/Parser/Parse.y | 62 +++++++++++++++-------- test/basic/inside_expr.sv | 14 +++++ test/basic/inside_expr.v | 15 ++++++ 6 files changed, 121 insertions(+), 31 deletions(-) diff --git a/src/Convert/Inside.hs b/src/Convert/Inside.hs index ba40160..410c6f9 100644 --- a/src/Convert/Inside.hs +++ b/src/Convert/Inside.hs @@ -1,14 +1,18 @@ {- sv2v - Author: Zachary Snow - - - Conversion for `inside` expressions + - Conversion for `inside` expressions and cases - - The expressions are compared to each candidate using the wildcard comparison - operator. Note that if expression has any Xs or Zs that are not wildcarded in - the candidate, the results is `1'bx`. As required by the specification, the - result of each comparison is combined using an OR reduction. - + - `case ... inside` statements are converted to an equivalent if-else cascade. + - - TODO: Add support for array value ranges. + - TODO: This conversion may cause an expression with side effects to be + - evaluated more than once. -} module Convert.Inside (convert) where @@ -16,13 +20,20 @@ module Convert.Inside (convert) where import Convert.Traverse import Language.SystemVerilog.AST +import Data.Maybe (fromMaybe) + convert :: [AST] -> [AST] -convert = - map $ - traverseDescriptions $ traverseModuleItems $ - traverseExprs $ traverseNestedExprs convertExpr +convert = map $ traverseDescriptions $ traverseModuleItems convertModuleItem + +convertModuleItem :: ModuleItem -> ModuleItem +convertModuleItem item = + traverseExprs (traverseNestedExprs convertExpr) $ + traverseStmts convertStmt $ + item convertExpr :: Expr -> Expr +convertExpr (Inside Nil valueRanges) = + Inside Nil valueRanges convertExpr (Inside expr valueRanges) = if length checks == 1 then head checks @@ -44,3 +55,27 @@ convertExpr (Inside expr valueRanges) = (BinOp Le lo expr) (BinOp Ge hi expr) convertExpr other = other + +convertStmt :: Stmt -> Stmt +convertStmt (Case u kw expr items) = + if not $ any isSpecialInside exprs then + Case u kw expr items + else if kw /= CaseN then + error $ "cannot use inside with " ++ show kw + else + foldr ($) defaultStmt $ + map (uncurry $ If NoCheck) $ + zip comps stmts + where + exprs = map fst items + itemsNonDefault = filter (not . null . fst) items + isSpecialInside :: [Expr] -> Bool + isSpecialInside [Inside Nil _] = True + isSpecialInside _ = False + makeComp :: [Expr] -> Expr + makeComp [Inside Nil ovr] = Inside expr ovr + makeComp _ = error "internal invariant violated" + comps = map (makeComp . fst) itemsNonDefault + stmts = map snd itemsNonDefault + defaultStmt = fromMaybe Null (lookup [] items) +convertStmt other = other diff --git a/src/Language/SystemVerilog/AST/Expr.hs b/src/Language/SystemVerilog/AST/Expr.hs index e0f7436..7ebd74c 100644 --- a/src/Language/SystemVerilog/AST/Expr.hs +++ b/src/Language/SystemVerilog/AST/Expr.hs @@ -16,6 +16,7 @@ module Language.SystemVerilog.AST.Expr , DimFn (..) , showAssignment , showRanges + , showExprOrRange , simplify , rangeSize , endianCondExpr @@ -85,9 +86,6 @@ instance Show Expr where show (Inside e l ) = printf "(%s inside { %s })" (show e) (intercalate ", " strs) where strs = map showExprOrRange l - showExprOrRange :: ExprOrRange -> String - showExprOrRange (Left x) = show x - showExprOrRange (Right x) = show x show (Pattern l ) = printf "'{\n%s\n}" (indent $ intercalate ",\n" $ map showPatternItem l) where @@ -159,6 +157,10 @@ showRanges l = " " ++ (concatMap showRange l) showRange :: Range -> String showRange (h, l) = printf "[%s:%s]" (show h) (show l) +showExprOrRange :: ExprOrRange -> String +showExprOrRange (Left x) = show x +showExprOrRange (Right x) = show x + clog2Help :: Int -> Int -> Int clog2Help p n = if p >= n then 0 else 1 + clog2Help (p*2) n clog2 :: Int -> Int diff --git a/src/Language/SystemVerilog/AST/Stmt.hs b/src/Language/SystemVerilog/AST/Stmt.hs index 12b8ed9..7586c34 100644 --- a/src/Language/SystemVerilog/AST/Stmt.hs +++ b/src/Language/SystemVerilog/AST/Stmt.hs @@ -28,7 +28,7 @@ import Text.Printf (printf) import Language.SystemVerilog.AST.ShowHelp (commas, indent, unlines', showPad) import Language.SystemVerilog.AST.Attr (Attr) import Language.SystemVerilog.AST.Decl (Decl) -import Language.SystemVerilog.AST.Expr (Expr, Args(..)) +import Language.SystemVerilog.AST.Expr (Expr(Inside, Nil), Args(..), showExprOrRange) import Language.SystemVerilog.AST.LHS (LHS) import Language.SystemVerilog.AST.Op (AsgnOp(AsgnOpEq)) import Language.SystemVerilog.AST.Type (Identifier) @@ -132,7 +132,11 @@ showShortBranch stmt = showBranch stmt showCase :: Case -> String showCase (a, b) = printf "%s:%s" exprStr (showShortBranch b) - where exprStr = if null a then "default" else commas $ map show a + where + exprStr = case a of + [] -> "default" + [Inside Nil c] -> commas $ map showExprOrRange c + _ -> commas $ map show a data CaseKW = CaseN diff --git a/src/Language/SystemVerilog/Parser/Parse.y b/src/Language/SystemVerilog/Parser/Parse.y index e783906..b50e0cd 100644 --- a/src/Language/SystemVerilog/Parser/Parse.y +++ b/src/Language/SystemVerilog/Parser/Parse.y @@ -1046,17 +1046,13 @@ CaseKW :: { CaseKW } | "casez" { CaseZ } Cases :: { [Case] } - : {- empty -} { [] } - | Case Cases { $1 : $2 } - | CaseDefault CasesNoDefault { ([], $1) : $2 } -CasesNoDefault :: { [Case] } - : {- empty -} { [] } - | CasesNoDefault Case { $1 ++ [$2] } - -Case :: { Case } - : Exprs ":" Stmt { ($1, $3) } -CaseDefault :: { Stmt } - : "default" opt(":") Stmt { $3 } + : opt("inside") InsideCases { validateCases $1 $2 } +InsideCases :: { [([ExprOrRange], Stmt)] } + : InsideCase { [$1] } + | InsideCases InsideCase { $1 ++ [$2] } +InsideCase :: { ([ExprOrRange], Stmt) } + : OpenRangeList ":" Stmt { ($1, $3) } + | "default" opt(":") Stmt { ([], $3) } Number :: { String } : number { tokenString $1 } @@ -1223,17 +1219,11 @@ GenBlock :: { (Identifier, [GenItem]) } : "begin" StrTag GenItems "end" StrTag { (combineTags $2 $5, $3) } GenCases :: { [GenCase] } - : {- empty -} { [] } - | GenCase GenCases { $1 : $2 } - | GenCaseDefault GenCasesNoDefault { ([], $1) : $2 } -GenCasesNoDefault :: { [GenCase] } - : {- empty -} { [] } - | GenCasesNoDefault GenCase { $1 ++ [$2] } - + : GenCase { [$1] } + | GenCases GenCase { validateGenCases $ $1 ++ [$2] } GenCase :: { GenCase } - : Exprs ":" GenItemOrNull { ($1, $3) } -GenCaseDefault :: { GenItem } - : "default" opt(":") GenItemOrNull { $3 } + : Exprs ":" GenItemOrNull { ($1, $3) } + | "default" opt(":") GenItemOrNull { ([], $3) } GenvarInitialization :: { (Bool, Identifier, Expr) } : "genvar" Identifier "=" Expr { (True , $2, $4) } @@ -1335,4 +1325,34 @@ fieldDecl t (x, rs2) = (tf $ rs2 ++ rs1, x) where (tf, rs1) = typeRanges t +validateCases :: Maybe Token -> [([ExprOrRange], Stmt)] -> [Case] +validateCases Nothing items = + if length (filter null exprs) <= 1 + then zip exprs' stmts + else error $ "multiple default cases: " ++ show items + where + (exprs, stmts) = unzip items + exprs' = map (map unwrap) exprs + unwrap (Left expr) = expr + unwrap (Right range) = + error $ "illegal use of a range (" ++ show range + ++ ") in a non-inside case" +validateCases (Just _) items = + if length (filter null sets) <= 1 + then zip sets' stmts + else error $ "multiple default cases: " ++ show items + where + (sets, stmts) = unzip items + sets' = map unwrap sets + unwrap [] = [] + unwrap ls = [Inside Nil ls] + +validateGenCases :: [GenCase] -> [GenCase] +validateGenCases items = + if length (filter null exprs) <= 1 + then items + else error $ "multiple default generate cases: " ++ show items + where + (exprs, _) = unzip items + } diff --git a/test/basic/inside_expr.sv b/test/basic/inside_expr.sv index c383886..f250dc7 100644 --- a/test/basic/inside_expr.sv +++ b/test/basic/inside_expr.sv @@ -46,4 +46,18 @@ module top; $display("test2(%02d) = %b", i, test2(i)); end + function integer test3; + input integer inp; + case (inp) inside + [16:23]: return 1; + [32:47]: return 2; + default: return 0; + 0, [60:61], 4: return 3; + endcase + endfunction + initial begin + for (integer i = 0; i < 64; ++i) + $display("test3(%02d) = %b", i, test3(i)); + end + endmodule diff --git a/test/basic/inside_expr.v b/test/basic/inside_expr.v index 969260b..39fbc51 100644 --- a/test/basic/inside_expr.v +++ b/test/basic/inside_expr.v @@ -51,4 +51,19 @@ module top; $display("test2(%02d) = %b", i, test2(i)); end + function [0:31] test3; + input integer inp; + begin + if (16 <= inp && inp <= 23) test3 = 1; + else if (32 <= inp && inp <= 47) test3 = 2; + else if (inp == 0 || (60 <= inp && inp <= 61) || inp == 4) test3 = 3; + else test3 = 0; + end + endfunction + initial begin : block3 + integer i; + for (i = 0; i < 64; ++i) + $display("test3(%02d) = %b", i, test3(i)); + end + endmodule