From fd96b8a71014e0a9ac20ff79a8591bf1a8bc5d4c Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Thu, 29 Jul 2021 19:54:20 -0400 Subject: [PATCH] unbased unsized conversion inlines module stubs - support ports with sizes which depend on constant functions - restore package item injection utility to package conversion module - additional unbased unsized conversion test coverage --- src/Convert/Package.hs | 21 ++- src/Convert/UnbasedUnsized.hs | 215 +++++++++++++++----------- test/core/unbased_unsized.sv | 13 ++ test/core/unbased_unsized.v | 13 ++ test/core/unbased_unsized_function.sv | 30 ++++ test/core/unbased_unsized_function.v | 25 +++ 6 files changed, 229 insertions(+), 88 deletions(-) create mode 100644 test/core/unbased_unsized_function.sv create mode 100644 test/core/unbased_unsized_function.v diff --git a/src/Convert/Package.hs b/src/Convert/Package.hs index ff7d0f9..8f7bd6d 100644 --- a/src/Convert/Package.hs +++ b/src/Convert/Package.hs @@ -18,7 +18,11 @@ - into modules and interfaces as needed. -} -module Convert.Package (convert) where +module Convert.Package + ( convert + , inject + , prefixItems + ) where import Control.Monad.State.Strict import Control.Monad.Writer.Strict @@ -57,6 +61,21 @@ makeLocal (Decl (Param _ t x e)) = Decl $ Param Localparam t x e makeLocal (Decl (ParamType _ x t)) = Decl $ ParamType Localparam x t makeLocal other = other +-- utility for inserting package items into a set of module items as needed +inject :: [PackageItem] -> [ModuleItem] -> [ModuleItem] +inject packageItems items = + addItems localPIs Set.empty (map addUsedPIs items) + where + localPIs = Map.fromList $ concatMap toPIElem packageItems + toPIElem :: PackageItem -> [(Identifier, PackageItem)] + toPIElem item = map (, item) (piNames item) + +-- utility for appling a prefix to all of the top level items in a module +prefixItems :: Identifier -> [ModuleItem] -> [ModuleItem] +prefixItems prefix items = + snd $ evalState (processItems "" prefix items) initialState + where initialState = ([], Map.empty, Map.empty) + -- collect packages and global package items collectPackageM :: Description -> Writer (Packages, Classes, [PackageItem]) () collectPackageM (PackageItem item) = diff --git a/src/Convert/UnbasedUnsized.hs b/src/Convert/UnbasedUnsized.hs index 4366a24..a8b0606 100644 --- a/src/Convert/UnbasedUnsized.hs +++ b/src/Convert/UnbasedUnsized.hs @@ -1,5 +1,4 @@ {-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE TupleSections #-} {- sv2v - Author: Zachary Snow - @@ -9,24 +8,27 @@ - allow sign extension. For context-determined expressions, the converted - literals are repeated to match the context-determined size. - - - As a special case, unbased, unsized literals which take on the size of a - - module's port are replaced as above, but with the size of the port being - - determined based on the parameter bindings of the instance and the definition - - of the instantiated module. + - When an unbased, unsized literal depends on the width a module port, the + - constant portions of the instantiated module are inlined alongside synthetic + - declarations matching the size of the port and filled with the desired bit. + - This allows port widths to depend on functions or parameters while avoiding + - creating hierarchical or generate-scoped references. -} module Convert.UnbasedUnsized (convert) where import Control.Monad.Writer.Strict -import Data.Maybe (mapMaybe) +import Data.Either (isLeft) +import Data.Maybe (isNothing, mapMaybe) import qualified Data.Map.Strict as Map -import Convert.ExprUtils +import Convert.Package (inject, prefixItems) import Convert.Traverse import Language.SystemVerilog.AST type Part = [ModuleItem] type Parts = Map.Map Identifier Part +type PortBit = (Identifier, Bit) data ExprContext = SelfDetermined @@ -45,86 +47,122 @@ collectPartsM (Part _ _ _ _ name _ items) = collectPartsM _ = return () convertModuleItem :: Parts -> ModuleItem -> ModuleItem -convertModuleItem parts (Instance moduleName params instanceName [] bindings) = - if Map.member moduleName parts && not (any isTypeParam moduleItems) - then convertModuleItem' $ - Instance moduleName params instanceName [] bindings' - else Instance moduleName params instanceName [] bindings +convertModuleItem parts (Instance moduleName params instanceName ds bindings) = + if null extensionDecls || isNothing maybeModuleItems then + convertModuleItem' $ instanceBase bindings + else if hasTypeParams || not moduleIsResolved then + instanceBase bindings + else + Generate $ map GenModuleItem $ + stubItems ++ [instanceBase bindings'] where - bindings' = map convertBinding bindings - moduleItems = - case Map.lookup moduleName parts of - Nothing -> error $ "could not find module: " ++ moduleName - Just partInfo -> partInfo - isTypeParam :: ModuleItem -> Bool - isTypeParam (MIPackageItem (Decl ParamType{})) = True - isTypeParam _ = False - tag = Ident "~~uub~~" - convertBinding :: PortBinding -> PortBinding - convertBinding (portName, expr) = - (portName, ) $ - traverseNestedExprs (replaceBindingExpr portName) $ - convertExpr (ContextDetermined tag) expr - replaceBindingExpr :: Identifier -> Expr -> Expr - replaceBindingExpr portName (orig @ (Repeat _ [ConvertedUU a b])) = - if orig == sizedLiteralFor tag bit - then Repeat portSize [ConvertedUU a b] - else orig - where - bit = bitForBased a b - portSize = determinePortSize portName params moduleItems - replaceBindingExpr _ other = other + instanceBase = Instance moduleName params instanceName ds + maybeModuleItems = Map.lookup moduleName parts + Just moduleItems = maybeModuleItems + + -- checking whether we're ready to inline + hasTypeParams = any (isLeft . snd) params + moduleIsResolved = isEntirelyResolved selectedStubItems + + -- transform the existing bindings to reference extension declarations + (bindings', extensionDeclLists) = unzip $ + map (convertBinding blockName) bindings + extensionDecls = map (MIPackageItem . Decl) $ concat extensionDeclLists + + -- inline the necessary portions of the module alongside the selected + -- extension declarations + stubItems = + map (traverseDecls overrideParam) $ + prefixItems blockName selectedStubItems + selectedStubItems = inject rawStubItems extensionDecls + rawStubItems = createModuleStub moduleItems + blockName = "sv2v_uu_" ++ instanceName + + -- override a parameter value in the stub + overrideParam :: Decl -> Decl + overrideParam (Param Parameter t x e) = + Param Localparam t x $ + case lookup xOrig params of + Just val -> e' + where Right e' = val + Nothing -> e + where xOrig = drop (length blockName + 1) x + overrideParam decl = decl + convertModuleItem _ other = convertModuleItem' other -determinePortSize :: Identifier -> [ParamBinding] -> [ModuleItem] -> Expr -determinePortSize portName instanceParams moduleItems = - step (reverse initialMapping) moduleItems +-- convert a port binding and produce a list of needed extension decls +convertBinding :: Identifier -> PortBinding -> (PortBinding, [Decl]) +convertBinding blockName (portName, expr) = + ((portName, exprPatched), portBits) where - initialMapping = mapMaybe createParamReplacement instanceParams - createParamReplacement :: ParamBinding -> Maybe (Identifier, Expr) - createParamReplacement (_, Left _) = Nothing - createParamReplacement (paramName, Right expr) = - Just (paramName, tagExpr expr) + exprRaw = convertExpr (ContextDetermined PortTag) expr + (exprPatched, portBits) = runWriter $ traverseNestedExprsM + (replaceBindingExpr blockName portName) exprRaw - step :: [(Identifier, Expr)] -> [ModuleItem] -> Expr - step mapping (MIPackageItem (Decl (Param _ _ x e)) : rest) = - step ((x, e) : mapping) rest - step mapping (MIPackageItem (Decl (Variable _ t x a _)) : rest) = - if x == portName - then substituteExpr (reverse mapping) size - else step mapping rest - where size = BinOp Mul (dimensionsSize a) (DimsFn FnBits $ Left t) - step mapping (MIPackageItem (Decl (Net d _ _ t x a e)) : rest) = - step mapping $ item : rest - where item = MIPackageItem $ Decl $ Variable d t x a e - step mapping (_ : rest) = step mapping rest - step _ [] = error $ "could not find size of port " ++ portName +-- identify and rewrite references to the width of the current port +replaceBindingExpr :: Identifier -> Identifier -> Expr -> Writer [Decl] Expr +replaceBindingExpr blockName portName (PortTaggedUU v k) = do + tell [extensionDecl portBit] + return $ Ident $ blockName ++ "_" ++ extensionDeclName portBit + where portBit = (portName, bitForBased v k) +replaceBindingExpr _ _ other = return other -substituteExpr :: [(Identifier, Expr)] -> Expr -> Expr -substituteExpr _ (Ident (':' : x)) = - Ident x -substituteExpr mapping (Dot (Ident x) y) = - case lookup x mapping of - Nothing -> Dot (Ident x) y - Just (Pattern items) -> - case lookup (Right $ Ident y) items of - Just item -> substituteExpr mapping item - Nothing -> Dot (substituteExpr mapping (Pattern items)) y - Just expr -> Dot (substituteExpr mapping expr) y -substituteExpr mapping (Ident x) = - case lookup x mapping of - Nothing -> Ident x - Just expr -> substituteExpr mapping expr -substituteExpr mapping expr = - traverseExprTypes typeMapper $ - traverseSinglyNestedExprs exprMapper expr +-- standardized name format for the synthetic declarations below +extensionDeclName :: PortBit -> Identifier +extensionDeclName (portName, bit) = "ext_" ++ portName ++ "_" ++ show bit + +-- synthetic declaration with the type of the port filled with the given bit +extensionDecl :: PortBit -> Decl +extensionDecl portBit@(portName, bit) = + Param Localparam t x e where - exprMapper = substituteExpr mapping - typeMapper = traverseNestedTypes $ traverseTypeExprs exprMapper + t = Alias portName [] + x = extensionDeclName portBit + e = literalFor bit -tagExpr :: Expr -> Expr -tagExpr (Ident x) = Ident (':' : x) -tagExpr expr = traverseSinglyNestedExprs tagExpr expr +-- create an all-constant stub for an instantiated module +createModuleStub :: [ModuleItem] -> [PackageItem] +createModuleStub = + mapMaybe stub + where + stub :: ModuleItem -> Maybe PackageItem + stub (MIPackageItem (Decl decl)) = fmap Decl $ stubDecl decl + stub (MIPackageItem item) = Just item + stub _ = Nothing + -- transform declarations into appropriate constants and type params + stubDecl :: Decl -> Maybe Decl + stubDecl (Variable d t x a _) = makePortType d t x a + stubDecl (Net d _ _ t x a _) = makePortType d t x a + stubDecl decl = Just decl + -- make a type parameter for each port declaration + makePortType :: Direction -> Type -> Identifier -> [Range] -> Maybe Decl + makePortType Input UnknownType x [] = Just $ ParamType Localparam x t + where t = IntegerVector TLogic Unspecified [] + makePortType Input t x [] = Just $ ParamType Localparam x t + makePortType _ _ _ _ = Nothing + +-- ensure inlining the constants doesn't produce generate-scoped exprs or +-- expression type references +isEntirelyResolved :: [ModuleItem] -> Bool +isEntirelyResolved = + not . getAny . execWriter . + mapM (collectNestedModuleItemsM collectModuleItem) + where + collectModuleItem :: ModuleItem -> Writer Any () + collectModuleItem item = + collectExprsM collectExpr item >> + collectTypesM collectType item + collectExpr :: Expr -> Writer Any () + collectExpr Dot{} = tell $ Any True + collectExpr expr = + collectExprTypesM collectType expr >> + collectSinglyNestedExprsM collectExpr expr + collectType :: Type -> Writer Any () + collectType TypeOf{} = tell $ Any True + collectType typ = + collectTypeExprsM collectExpr typ >> + collectSinglyNestedTypesM collectType typ convertModuleItem' :: ModuleItem -> ModuleItem convertModuleItem' = @@ -135,8 +173,14 @@ convertModuleItem' = literalFor :: Bit -> Expr literalFor = Number . (uncurry $ Based 1 True Binary) . bitToVK -pattern ConvertedUU :: Integer -> Integer -> Expr -pattern ConvertedUU a b = Number (Based 1 True Binary a b) +pattern PortTag :: Expr +pattern PortTag = Ident "~~uub~~" + +-- a converted literal which depends on the current port's width +pattern PortTaggedUU :: Integer -> Integer -> Expr +pattern PortTaggedUU v k <- Repeat + (DimsFn FnBits (Right PortTag)) + [Number (Based 1 True Binary v k)] bitForBased :: Integer -> Integer -> Bit bitForBased 0 0 = Bit0 @@ -169,12 +213,9 @@ convertExpr _ (Pattern items) = Pattern $ zip (map fst items) (map (convertExpr SelfDetermined . snd) items) -convertExpr _ (Call expr (Args pnArgs kwArgs)) = - Call expr $ Args pnArgs' kwArgs' - where - pnArgs' = map (convertExpr SelfDetermined) pnArgs - kwArgs' = zip (map fst kwArgs) $ - map (convertExpr SelfDetermined) $ map snd kwArgs +convertExpr _ (Call expr (Args pnArgs [])) = + Call expr $ Args pnArgs' [] + where pnArgs' = map (convertExpr SelfDetermined) pnArgs convertExpr _ (Repeat count exprs) = Repeat count $ map (convertExpr SelfDetermined) exprs convertExpr SelfDetermined (Mux cond (e1 @ UU{}) (e2 @ UU{})) = diff --git a/test/core/unbased_unsized.sv b/test/core/unbased_unsized.sv index 3f66bfd..5b15a4a 100644 --- a/test/core/unbased_unsized.sv +++ b/test/core/unbased_unsized.sv @@ -77,6 +77,12 @@ module top; `TEST_OP(32'hffffffff , ==, '1, 1'b1) `TEST_OP(33'h1ffffffff, ==, '1, 1'b1) + `TEST_OP('1, ==, 1'h1 , 1'b1) + `TEST_OP('1, ==, 2'h3 , 1'b1) + `TEST_OP('1, ==, 31'h7fffffff , 1'b1) + `TEST_OP('1, ==, 32'hffffffff , 1'b1) + `TEST_OP('1, ==, 33'h1ffffffff, 1'b1) + `TEST_OP( 1'h1 , <=, '1, 1'b1) `TEST_OP( 2'h3 , <=, '1, 1'b1) `TEST_OP(31'h7fffffff , <=, '1, 1'b1) @@ -97,6 +103,7 @@ module top; `TEST_OP(33'h1ffffffff, &, P ? '1 : '0, 33'h1ffffffff) `TEST_OP(33'h1ffffffff, &, '1 & '1, 33'h1ffffffff) + `TEST_OP('1 & '1, &, 33'h1ffffffff, 33'h1ffffffff) `TEST_OP(33'h1ffffffff, &, !P ? '1 : '0 - 1, 33'h1ffffffff) `TEST_OP(34'h3ffffffff, &, '0 - 1, 34'h3ffffffff) @@ -126,6 +133,12 @@ module top; #1 pick = 0; #1 pick = 1; end + + initial begin + $display("tern %b", A ? '1 : 'X); + $display("tern %b", A ? '1 : A); + $display("tern %b", A ? A : '1); + end endmodule module M(a, b, c, d); diff --git a/test/core/unbased_unsized.v b/test/core/unbased_unsized.v index 9a84c86..e0532c5 100644 --- a/test/core/unbased_unsized.v +++ b/test/core/unbased_unsized.v @@ -72,6 +72,12 @@ module top; `TEST_OP(32'hffffffff , ==, '1, 1'b1) `TEST_OP(33'h1ffffffff, ==, '1, 1'b1) + `TEST_OP('1, ==, 1'h1 , 1'b1) + `TEST_OP('1, ==, 2'h3 , 1'b1) + `TEST_OP('1, ==, 31'h7fffffff , 1'b1) + `TEST_OP('1, ==, 32'hffffffff , 1'b1) + `TEST_OP('1, ==, 33'h1ffffffff, 1'b1) + `TEST_OP( 1'h1 , <=, '1, 1'b1) `TEST_OP( 2'h3 , <=, '1, 1'b1) `TEST_OP(31'h7fffffff , <=, '1, 1'b1) @@ -92,6 +98,7 @@ module top; `TEST_OP(33'h1ffffffff, &, P ? '1 : '0, 33'h1ffffffff) `TEST_OP(33'h1ffffffff, &, '1 & '1, 33'h1ffffffff) + `TEST_OP('1 & '1, &, 33'h1ffffffff, 33'h1ffffffff) `TEST_OP(33'h1ffffffff, &, !P ? '1 : '0 - 1, 33'h1ffffffff) `TEST_OP(34'h3ffffffff, &, '0 - 1, 34'h3ffffffff) @@ -121,6 +128,12 @@ module top; #1 pick = 0; #1 pick = 1; end + + initial begin + $display("tern %b", 1'b1); + $display("tern %b", A ? -1 : A); + $display("tern %b", A ? A : -1); + end endmodule module M(a, b, c, d); diff --git a/test/core/unbased_unsized_function.sv b/test/core/unbased_unsized_function.sv new file mode 100644 index 0000000..bcdc343 --- /dev/null +++ b/test/core/unbased_unsized_function.sv @@ -0,0 +1,30 @@ +module mod(a, b, c, d); + parameter P = 2; + function automatic [P - 1:0] F; + input signed inp; + F = inp; + endfunction + parameter Q = 1; + if (1) begin : blk + localparam X = F(Q) + Q; + wire [X - 1:0] g; + end + wire x = blk.g; + localparam R = $bits(blk.g); + input logic [P - 1:0] a; + input wire [Q - 1:0] b; + input [R - 1:0] c; + input d; + initial #1 $display("mod P=%0d Q=%0d R=%0d a=%b b=%b c=%b d=%b", + P, Q, R, a, b, c, d); +endmodule + +module top; + parameter P = 1; + parameter Q = 2; + parameter R = 3; + mod #() m1('1, 'X, 'Z, '1); + mod #(P, Q) m2('1, '1, '1, '1); + mod #(P, R) m3('1, '1, '1, '1); + mod #(Q, R) m4('1, '1, P ? '1 : 'X, '1); +endmodule diff --git a/test/core/unbased_unsized_function.v b/test/core/unbased_unsized_function.v new file mode 100644 index 0000000..126ef78 --- /dev/null +++ b/test/core/unbased_unsized_function.v @@ -0,0 +1,25 @@ +module mod(a, b, c, d); + parameter P = 2; + function automatic [P - 1:0] F; + input signed inp; + F = inp; + endfunction + parameter Q = 1; + localparam R = F(Q) + Q; + input signed a; + input signed b; + input signed c; + input d; + initial #1 $display("mod P=%0d Q=%0d R=%0d a=%b b=%b c=%b d=%b", + P, Q, R, {P{a}}, {Q{b}}, {R{c}}, d); +endmodule + +module top; + parameter P = 1; + parameter Q = 2; + parameter R = 3; + mod #() m1(1'b1, 1'bX, 1'bZ, 1'b1); + mod #(P, Q) m2(1'b1, 1'b1, 1'b1, 1'b1); + mod #(P, R) m3(1'b1, 1'b1, 1'b1, 1'b1); + mod #(Q, R) m4(1'b1, 1'b1, 1'b1, 1'b1); +endmodule