From 9d7f917608d135abdd4fc80a8f41fe3264c6b78a Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Wed, 30 Jun 2021 14:24:35 -0400 Subject: [PATCH] handle naming and scoping of unnamed generate blocks --- src/Convert.hs | 2 + src/Convert/Interface.hs | 22 ++-- src/Convert/Scoper.hs | 4 +- src/Convert/UnnamedGenBlock.hs | 121 ++++++++++++++++++++++ src/Language/SystemVerilog/Parser/Parse.y | 5 +- sv2v.cabal | 1 + test/basic/genblk_implicit.sv | 7 ++ test/basic/unnamed_genblk.sv | 55 ++++++++++ test/basic/unnamed_genblk_cascade.sv | 45 ++++++++ test/core/unnamed_genblk_zeroes.sv | 37 +++++++ test/core/unnamed_genblk_zeroes.v | 2 + 11 files changed, 289 insertions(+), 12 deletions(-) create mode 100644 src/Convert/UnnamedGenBlock.hs create mode 100644 test/basic/genblk_implicit.sv create mode 100644 test/basic/unnamed_genblk.sv create mode 100644 test/basic/unnamed_genblk_cascade.sv create mode 100644 test/core/unnamed_genblk_zeroes.sv create mode 100644 test/core/unnamed_genblk_zeroes.v diff --git a/src/Convert.hs b/src/Convert.hs index 3ae7d92..786fda1 100644 --- a/src/Convert.hs +++ b/src/Convert.hs @@ -47,6 +47,7 @@ import qualified Convert.Typedef import qualified Convert.TypeOf import qualified Convert.UnbasedUnsized import qualified Convert.Unique +import qualified Convert.UnnamedGenBlock import qualified Convert.UnpackedArray import qualified Convert.Unsigned import qualified Convert.Wildcard @@ -105,6 +106,7 @@ initialPhases selectExclude = , Convert.Package.convert , Convert.ParamNoDefault.convert , Convert.ResolveBindings.convert + , Convert.UnnamedGenBlock.convert ] convert :: [Job.Exclude] -> Phase diff --git a/src/Convert/Interface.hs b/src/Convert/Interface.hs index b629f14..bad6aa2 100644 --- a/src/Convert/Interface.hs +++ b/src/Convert/Interface.hs @@ -341,7 +341,7 @@ inlineInstance global ranges modportBindings items partName comment : map (MIPackageItem . Decl) bindingBaseParams ++ map (MIPackageItem . Decl) parameterBinds ++ - (wrapInstance $ GenBlock instanceName $ map GenModuleItem items') + wrapInstance instanceName items' : portBindings where items' = evalScoper @@ -367,11 +367,20 @@ inlineInstance global ranges modportBindings items partName comment = MIPackageItem $ Decl $ CommentDecl $ "expanded " ++ inlineKind ++ " instance: " ++ instanceName - portBindings = mapMaybe portBindingItem $ + portBindings = + wrapPortBindings $ + map portBindingItem $ + filter ((/= Nil) . snd) $ filter notSubstituted instancePorts notSubstituted :: PortBinding -> Bool notSubstituted (portName, _) = lookup portName modportBindings == Nothing + wrapPortBindings :: [ModuleItem] -> [ModuleItem] + wrapPortBindings = + if isArray + then (\x -> [x]) . wrapInstance blockName + else id + where blockName = instanceName ++ "_port_bindings" rewriteItem :: ModuleItem -> ModuleItem rewriteItem = @@ -573,10 +582,8 @@ inlineInstance global ranges modportBindings items partName ++ " expected type, found expr: " ++ show e' overrideParam other = other - portBindingItem :: PortBinding -> Maybe ModuleItem - portBindingItem (_, Nil) = Nothing + portBindingItem :: PortBinding -> ModuleItem portBindingItem (ident, expr) = - Just $ wrapInstance $ GenModuleItem $ if findDeclDir ident == Input then bind (LHSDot (inj LHSBit LHSIdent) ident) expr else bind (toLHS expr) (Dot (inj Bit Ident) ident) @@ -614,8 +621,8 @@ inlineInstance global ranges modportBindings items partName [arrayRange @ (arrayLeft, arrayRight)] = ranges -- wrap the given item in a generate loop if necessary - wrapInstance :: GenItem -> ModuleItem - wrapInstance item = + wrapInstance :: Identifier -> [ModuleItem] -> ModuleItem + wrapInstance blockName moduleItems = Generate $ if not isArray then [item] @@ -624,6 +631,7 @@ inlineInstance global ranges modportBindings items partName , GenFor inits cond incr item ] where + item = GenBlock blockName $ map GenModuleItem moduleItems inits = (loopVar, arrayLeft) cond = endianCondExpr arrayRange (BinOp Ge (Ident loopVar) arrayRight) diff --git a/src/Convert/Scoper.hs b/src/Convert/Scoper.hs index 5881234..929738e 100644 --- a/src/Convert/Scoper.hs +++ b/src/Convert/Scoper.hs @@ -512,8 +512,6 @@ scopeModuleItemT declMapper moduleItemMapper genItemMapper stmtMapper = fullModuleItemMapper item >>= return . MIAttr attr fullModuleItemMapper item = moduleItemMapper item - -- TODO: This doesn't yet support implicit naming of generate blocks as - -- blocks as described in Section 27.6. fullGenItemMapper :: GenItem -> ScoperT a m GenItem fullGenItemMapper genItem = do genItem' <- genItemMapper genItem @@ -524,7 +522,7 @@ scopeModuleItemT declMapper moduleItemMapper genItemMapper stmtMapper = injected' <- mapM fullModuleItemMapper injected genItem'' <- scopeGenItemMapper genItem' let genItems = map GenModuleItem injected' ++ [genItem''] - return $ GenBlock "" genItems + return $ GenBlock "" genItems -- flattened during traversal scopeGenItemMapper :: GenItem -> ScoperT a m GenItem scopeGenItemMapper (GenFor (index, a) b c genItem) = do genItem' <- scopeGenItemBranchMapper index genItem diff --git a/src/Convert/UnnamedGenBlock.hs b/src/Convert/UnnamedGenBlock.hs new file mode 100644 index 0000000..36b753e --- /dev/null +++ b/src/Convert/UnnamedGenBlock.hs @@ -0,0 +1,121 @@ +{- sv2v + - Author: Zachary Snow + - + - Labels any unnamed generate blocks, per IEEE 1800-2017 Section 27.6 + - + - This transformation is performed before any others, and is only performed + - once. The AST traversal utilities are not used here to avoid the automatic + - elaboration they perform. + -} + +module Convert.UnnamedGenBlock (convert) where + +import Control.Monad.State.Strict +import Data.List (isPrefixOf) + +import Language.SystemVerilog.AST + +convert :: [AST] -> [AST] +convert = map $ map traverseDescription + +traverseDescription :: Description -> Description +traverseDescription (Part attrs extern kw lifetime name ports items) = + Part attrs extern kw lifetime name ports $ + evalState (mapM traverseModuleItemM items) initialState +traverseDescription other = other + +type S = State Info +type Info = ([Identifier], Int) + +initialState :: Info +initialState = ([], 1) + +traverseModuleItemM :: ModuleItem -> S ModuleItem +traverseModuleItemM (item @ (Genvar x)) = declaration x item +traverseModuleItemM (item @ (NInputGate _ _ x _ _)) = declaration x item +traverseModuleItemM (item @ (NOutputGate _ _ x _ _)) = declaration x item +traverseModuleItemM (item @ (Instance _ _ x _ _)) = declaration x item +traverseModuleItemM (MIPackageItem (Decl decl)) = + traverseDeclM decl >>= return . MIPackageItem . Decl +traverseModuleItemM (MIAttr attr item) = + traverseModuleItemM item >>= return . MIAttr attr +traverseModuleItemM (Generate items) = + mapM traverseGenItemM items >>= return . Generate +traverseModuleItemM item = return item + +-- add a declaration to the conflict list +traverseDeclM :: Decl -> S Decl +traverseDeclM decl = + case decl of + Variable _ _ x _ _ -> declaration x decl + Param _ _ x _ -> declaration x decl + ParamType _ x _ -> declaration x decl + CommentDecl{} -> return decl + +-- label the generate blocks within an individual generate item which is already +-- in a list of generate items (top level or generate block) +traverseGenItemM :: GenItem -> S GenItem +traverseGenItemM (item @ GenIf{}) = do + item' <- labelGenElse item + incrCount >> return item' +traverseGenItemM (item @ GenBlock{}) = do + item' <- labelBlock item + incrCount >> return item' +traverseGenItemM (GenFor a b c item) = do + item' <- labelBlock item + incrCount >> return (GenFor a b c item') +traverseGenItemM (GenCase expr cases) = do + let (exprs, items) = unzip cases + items' <- mapM labelBlock items + let cases' = zip exprs items' + incrCount >> return (GenCase expr cases') +traverseGenItemM (GenModuleItem item) = + traverseModuleItemM item >>= return . GenModuleItem +traverseGenItemM GenNull = return GenNull + +-- increment the counter each time a generate construct is encountered +incrCount :: S () +incrCount = modify' $ \(idents, count) -> (idents, count + 1) + +genblk :: Identifier +genblk = "genblk" + +-- adds the given identifier to the list of possible identifier conflicts, if +-- necessary, and then returns the second argument as a shorthand courtesy +declaration :: Identifier -> a -> S a +declaration x a = do + when (genblk `isPrefixOf` x) $ do + let ident = drop (length genblk) x + modify' $ \(idents, count) -> (ident : idents, count) + return a + +-- generate a locally unique gen block name +makeBlockName :: S Identifier +makeBlockName = do + (idents, count) <- get + let uniqueSuffix = prependZeroes idents (show count) + return $ genblk ++ uniqueSuffix + +-- prepend zeroes until the string isn't in the list +prependZeroes :: [String] -> String -> String +prependZeroes xs x | notElem x xs = x +prependZeroes xs x = prependZeroes xs ('0' : x) + +-- if the item is a generate conditional item, give its `then` block and any +-- direct `else if` blocks the same name +labelGenElse :: GenItem -> S GenItem +labelGenElse (GenIf cond thenItem elseItem) = do + thenItem' <- labelBlock thenItem + elseItem' <- labelGenElse elseItem + return $ GenIf cond thenItem' elseItem' +labelGenElse other = labelBlock other + +-- transform the given item into a named generate block +labelBlock :: GenItem -> S GenItem +labelBlock (GenBlock "" items) = + makeBlockName >>= labelBlock . flip GenBlock items +labelBlock (GenBlock x items) = + return $ GenBlock x $ + evalState (mapM traverseGenItemM items) initialState +labelBlock GenNull = return GenNull +labelBlock item = labelBlock $ GenBlock "" [item] diff --git a/src/Language/SystemVerilog/Parser/Parse.y b/src/Language/SystemVerilog/Parser/Parse.y index 98edf7c..7b51761 100644 --- a/src/Language/SystemVerilog/Parser/Parse.y +++ b/src/Language/SystemVerilog/Parser/Parse.y @@ -1352,6 +1352,7 @@ GenItems :: { [GenItem] } GenItem :: { GenItem } : GenBlock { uncurry GenBlock $1 } | NonGenerateModuleItem { genItemsToGenItem $ map GenModuleItem $1 } + | "generate" GenItems "endgenerate" { genItemsToGenItem $2 } | ConditionalGenerateConstruct { $1 } | LoopGenerateConstruct { $1 } ConditionalGenerateConstruct :: { GenItem } @@ -1375,7 +1376,7 @@ GenCase :: { GenCase } | "default" opt(":") GenItemOrNull { ([], $3) } GenvarInitialization :: { Expr -> (Identifier, AsgnOp, Expr) -> GenItem -> GenItem } - : "genvar" Identifier "=" Expr { \a b c -> GenBlock "" [GenModuleItem (Genvar $2), GenFor ($2, $4) a b c] } + : "genvar" Identifier "=" Expr { \a b c -> genItemsToGenItem [GenModuleItem (Genvar $2), GenFor ($2, $4) a b c] } | Identifier "=" Expr { GenFor ($1, $3) } GenvarIteration :: { (Identifier, AsgnOp, Expr) } @@ -1480,7 +1481,7 @@ parseError a = case a of genItemsToGenItem :: [GenItem] -> GenItem genItemsToGenItem [x] = x -genItemsToGenItem xs = GenBlock "" xs +genItemsToGenItem xs = GenModuleItem $ Generate xs combineDeclsAndStmts :: ([Decl], [Stmt]) -> ([Decl], [Stmt]) -> ParseState ([Decl], [Stmt]) diff --git a/sv2v.cabal b/sv2v.cabal index 7c49dbb..0c2274a 100644 --- a/sv2v.cabal +++ b/sv2v.cabal @@ -99,6 +99,7 @@ executable sv2v Convert.TypeOf Convert.UnbasedUnsized Convert.Unique + Convert.UnnamedGenBlock Convert.UnpackedArray Convert.Unsigned Convert.Wildcard diff --git a/test/basic/genblk_implicit.sv b/test/basic/genblk_implicit.sv new file mode 100644 index 0000000..2153968 --- /dev/null +++ b/test/basic/genblk_implicit.sv @@ -0,0 +1,7 @@ +module top; + if (1) begin + // should not be visible in a top-level VCD + wire x; + assign x = 1; + end +endmodule diff --git a/test/basic/unnamed_genblk.sv b/test/basic/unnamed_genblk.sv new file mode 100644 index 0000000..b5add5f --- /dev/null +++ b/test/basic/unnamed_genblk.sv @@ -0,0 +1,55 @@ +// This test was adapted from Section 27.6 of IEEE 1800-2017 + +module mod; + initial $dumpvars(0, mod); + // needed because of steveicarus/iverilog#528 + `ifdef __ICARUS__ + `define BEGIN(name) begin : name + `define END end + `else + `define BEGIN(name) + `define END + `endif + + parameter genblk2 = 0; + genvar i; + + // The following generate block is implicitly named genblk1 + + if (genblk2) `BEGIN(genblk1) logic a; `END // mod.genblk1.a + else `BEGIN(genblk1) logic b; `END // mod.genblk1.b + + // The following generate block is implicitly named genblk02 + // as genblk2 is already a declared identifier + + if (genblk2) `BEGIN(genblk02) logic a; `END // mod.genblk02.a + else `BEGIN(genblk02) logic b; `END // mod.genblk02.b + + // The following generate block would have been named genblk3 + // but is explicitly named g1 + + for (i = 0; i < 1; i = i + 1) begin : g1 // block name + // The following generate block is implicitly named genblk1 + // as the first nested scope inside g1 + if (1) `BEGIN(genblk1) logic a; `END // mod.g1[0].genblk1.a + end + + // The following generate block is implicitly named genblk4 since + // it belongs to the fourth generate construct in scope "mod". + // The previous generate block would have been + // named genblk3 if it had not been explicitly named g1 + + for (i = 0; i < 1; i = i + 1) `BEGIN(genblk4) + // The following generate block is implicitly named genblk1 + // as the first nested generate block in genblk4 + if (1) `BEGIN(genblk1) logic a; `END // mod.genblk4[0].genblk1.a + `END + + // The following generate block is implicitly named genblk5 + if (1) `BEGIN(genblk5) logic a; `END // mod.genblk5.a +endmodule + +module top; + mod #(0) m0(); + mod #(1) m1(); +endmodule diff --git a/test/basic/unnamed_genblk_cascade.sv b/test/basic/unnamed_genblk_cascade.sv new file mode 100644 index 0000000..67b178c --- /dev/null +++ b/test/basic/unnamed_genblk_cascade.sv @@ -0,0 +1,45 @@ +module example; + parameter P = 0; + +// needed because of steveicarus/iverilog#528 +`ifdef __ICARUS__ + `define BEGIN begin : `BLK + `define END end +`else + `define BEGIN + `define END +`endif + +`define BLK genblk1 + if (P == 1) `BEGIN integer w = 1; `END + else if (P == 2) `BEGIN integer x = 2; `END + else if (P == 3) `BEGIN integer y = 3; `END + else `BEGIN integer z = 9; `END + +`undef BLK +`define BLK genblk2 + case (P) + 1 : `BEGIN integer w = 1; `END + 2 : `BEGIN integer x = 2; `END + 3 : `BEGIN integer y = 3; `END + default: `BEGIN integer z = 9; `END + endcase + +`undef BLK +`define BLK genblk3 + if (1) `BEGIN wire a = 1; `END +endmodule + +module top; +`define TEST(i, v) \ + example #(i) e``i(); \ + initial #i begin \ + $display(`"e``i.genblk1.v: %0d`", e``i.genblk1.v); \ + $display(`"e``i.genblk2.v: %0d`", e``i.genblk2.v); \ + $display(`"e``i.genblk3.a: %0d`", e``i.genblk3.a); \ + end + `TEST(1, w) + `TEST(2, x) + `TEST(3, y) + `TEST(4, z) +endmodule diff --git a/test/core/unnamed_genblk_zeroes.sv b/test/core/unnamed_genblk_zeroes.sv new file mode 100644 index 0000000..c03f3f8 --- /dev/null +++ b/test/core/unnamed_genblk_zeroes.sv @@ -0,0 +1,37 @@ +module example; + initial $display("example"); +endmodule + +module top; + wire genblk1; + wire genblk33; + (* foo = 1 *) wire genblk01; + genvar genblk001; + example genblk0001(); + + wire x, y; + wire o1, o2, o3; + and genblk00001(o1, x, y); + not genblk000001(o2, o3, o1); + + parameter genblk0000001 = 1; +`ifndef REF + typedef logic genblk00000001; +`endif + +`define BLK genblk000000001 + + if (1) begin +`ifdef REF + : `BLK + reg +`else + genblk00000001 +`endif + x = genblk0000001; + end + initial begin + `BLK.x = 1; + $display("%b", `BLK.x); + end +endmodule diff --git a/test/core/unnamed_genblk_zeroes.v b/test/core/unnamed_genblk_zeroes.v new file mode 100644 index 0000000..6b07598 --- /dev/null +++ b/test/core/unnamed_genblk_zeroes.v @@ -0,0 +1,2 @@ +`define REF +`include "unnamed_genblk_zeroes.sv"