refactor cast conversion

- delegate cast type and sign resolution to TypeOf conversion
- internal support for injecting data declarations into statements
- fix size and sign of unbased unsized literals used in casts
- avoid generating many unnecessary explicit casts
- support casts which depend on localparams within procedures
- expression traversal correctly visits types within type casts
- fix typeof on expressions of net types
- handle additional edge cases for unsized integer array patterns
- preserve signedness of implicitly flattened unpacked integer arrays
This commit is contained in:
Zachary Snow 2021-02-03 16:12:05 -05:00
parent c656cbb977
commit dd1a9efb40
28 changed files with 647 additions and 312 deletions

View File

@ -13,6 +13,7 @@ import qualified Convert.AlwaysKW
import qualified Convert.AsgnOp
import qualified Convert.Assertion
import qualified Convert.BlockDecl
import qualified Convert.Cast
import qualified Convert.DimensionQuery
import qualified Convert.DuplicateGenvar
import qualified Convert.EmptyArgs
@ -35,9 +36,7 @@ import qualified Convert.Package
import qualified Convert.ParamNoDefault
import qualified Convert.ParamType
import qualified Convert.RemoveComments
import qualified Convert.SignCast
import qualified Convert.Simplify
import qualified Convert.SizeCast
import qualified Convert.StarPort
import qualified Convert.Stream
import qualified Convert.StringParam
@ -70,11 +69,11 @@ phases excludes =
, Convert.KWArgs.convert
, Convert.LogOp.convert
, Convert.MultiplePacked.convert
, Convert.UnbasedUnsized.convert
, Convert.Cast.convert
, Convert.TypeOf.convert
, Convert.DimensionQuery.convert
, Convert.ParamType.convert
, Convert.UnbasedUnsized.convert
, Convert.SizeCast.convert
, Convert.Simplify.convert
, Convert.Stream.convert
, Convert.Struct.convert
@ -83,7 +82,6 @@ phases excludes =
, Convert.Unique.convert
, Convert.UnpackedArray.convert
, Convert.Unsigned.convert
, Convert.SignCast.convert
, Convert.Wildcard.convert
, Convert.Enum.convert
, Convert.ForDecl.convert

214
src/Convert/Cast.hs Normal file
View File

@ -0,0 +1,214 @@
{-# LANGUAGE PatternSynonyms #-}
{- sv2v
- Author: Zachary Snow <zach@zachjs.com>
-
- Conversion of elaborated type casts
-
- Much of the work of elaborating various casts into explicit integer vector
- type casts happens in the TypeOf conversion, which contains the primary logic
- for resolving the type and signedness of expressions. It also removes
- redundant explicit casts to produce cleaner output.
-
- Type casts are defined as producing the result of the expression assigned to
- a variable of the given type. In the general case, this conversion generates
- a pass-through function which performs this assignment-based casting. This
- allows for casts to be used anywhere expressions are used, including within
- constant expressions.
-
- It is possible for the type in a cast to refer to localparams within a
- procedure. Without evaluating the localparam itself, a function outside of
- the procedure cannot refer to the size of the type in the cast. In these
- scenarios, the cast is instead performed by adding a temporary parameter or
- data declaration within the procedure and assigning the expression to that
- declaration to perform the cast.
-
- A few common cases of casts on number literals are fully elaborated into
- their corresponding resulting number literals to avoid excessive noise.
-}
module Convert.Cast (convert) where
import Control.Monad.Writer.Strict
import Data.List (isPrefixOf)
import Convert.ExprUtils
import Convert.Scoper
import Convert.Traverse
import Language.SystemVerilog.AST
convert :: [AST] -> [AST]
convert = map $ traverseDescriptions convertDescription
convertDescription :: Description -> Description
convertDescription description =
traverseModuleItems dropDuplicateCaster $
partScoper
traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM
description
type ST = Scoper Expr
traverseDeclM :: Decl -> ST Decl
traverseDeclM decl = do
decl' <- case decl of
Variable d t x a e -> do
enterStmt
e' <- traverseExprM e
exitStmt
details <- lookupLocalIdentM x
if isPrefixOf "sv2v_cast_" x && details /= Nothing
then return $ Variable Local t DuplicateTag [] Nil
else do
insertElem x Nil
return $ Variable d t x a e'
Param _ _ x _ ->
insertElem x Nil >> return decl
ParamType _ _ _ -> return decl
CommentDecl _ -> return decl
traverseDeclExprsM traverseExprM decl'
pattern DuplicateTag :: Identifier
pattern DuplicateTag = ":duplicate_cast_to_be_removed:"
dropDuplicateCaster :: ModuleItem -> ModuleItem
dropDuplicateCaster (MIPackageItem (Function _ _ DuplicateTag _ _)) =
Generate []
dropDuplicateCaster other = other
traverseModuleItemM :: ModuleItem -> ST ModuleItem
traverseModuleItemM (Genvar x) =
insertElem x Nil >> return (Genvar x)
traverseModuleItemM item =
traverseExprsM traverseExprM item
traverseGenItemM :: GenItem -> ST GenItem
traverseGenItemM = traverseGenItemExprsM traverseExprM
traverseStmtM :: Stmt -> ST Stmt
traverseStmtM stmt = do
enterStmt
stmt' <- traverseStmtExprsM traverseExprM stmt
exitStmt
return stmt'
traverseExprM :: Expr -> ST Expr
traverseExprM (Cast (Left (IntegerVector _ sg rs)) e) = do
e' <- traverseExprM e
convertCastM (dimensionsSize rs) e' signed
where signed = sg == Signed
traverseExprM other =
traverseSinglyNestedExprsM traverseExprM other
convertCastM :: Expr -> Expr -> Bool -> ST Expr
convertCastM (RawNum size) (RawNum val) signed =
return $ Number $ Decimal (fromIntegral size) signed val'
where val' = val `mod` (2 ^ size)
convertCastM (RawNum size) (Number (Based 1 True Binary a b)) signed =
return $ Number $ Based (fromIntegral size) signed Binary
(val * a) (val * b)
where val = (2 ^ size) - 1
convertCastM (RawNum size) (Number (UnbasedUnsized ch)) signed =
convertCastM (RawNum size) (Number num) signed
where
num = Based 1 True Binary a b
(a, b) = case ch of
'0' -> (0, 0)
'1' -> (1, 0)
'x' -> (0, 1)
'z' -> (1, 1)
_ -> error $ "unexpected unbased-unsized digit: " ++ [ch]
convertCastM size value signed = do
value' <- traverseExprM value
useFn <- embedScopes canUseCastFn size
if useFn then do
let name = castFnName size signed
details <- lookupLocalIdentM name
when (details == Nothing) $
injectItem $ castFn name size signed
return $ Call (Ident name) (Args [value'] [])
else do
name <- castDeclName 0
insertElem name Nil
useVar <- withinStmt
injectDecl $ castDecl useVar name value' size signed
return $ Ident name
-- checks if a cast size can be hoisted to a cast function
canUseCastFn :: Scopes a -> Expr -> Bool
canUseCastFn scopes size =
not (inProcedure && anyNonLocal)
where
inProcedure = withinProcedure scopes
anyNonLocal = getAny $ execWriter $
collectNestedExprsM collectNonLocalExprM size
collectNonLocalExprM :: Expr -> Writer Any ()
collectNonLocalExprM expr =
case lookupElem scopes expr of
Nothing -> return ()
Just ([_, _], _, _) -> return ()
Just (_, _, _) -> tell $ Any True
castType :: Expr -> Bool -> Type
castType size signed =
IntegerVector TLogic sg [r]
where
r = (simplify $ BinOp Sub size (RawNum 1), RawNum 0)
sg = if signed then Signed else Unspecified
castFn :: Identifier -> Expr -> Bool -> ModuleItem
castFn name size signed =
MIPackageItem $ Function Automatic t name [decl] [stmt]
where
inp = "inp"
t = castType size signed
decl = Variable Input t inp [] Nil
stmt = Asgn AsgnOpEq Nothing (LHSIdent name) (Ident inp)
castFnName :: Expr -> Bool -> String
castFnName size signed =
"sv2v_cast_" ++ sizeStr ++ suffix
where
sizeStr = case size of
Number n ->
case numberToInteger n of
Just v -> show v
_ -> shortHash size
_ -> shortHash size
suffix = if signed then "_signed" else ""
castDecl :: Bool -> Identifier -> Expr -> Expr -> Bool -> Decl
castDecl useVar name value size signed =
if useVar
then Variable Local t name [] value
else Param Localparam t name value
where t = castType size signed
castDeclName :: Int -> ST String
castDeclName counter = do
details <- lookupElemM name
if details == Nothing
then return name
else castDeclName (counter + 1)
where
name = if counter == 0
then prefix
else prefix ++ '_' : show counter
prefix = "sv2v_tmp_cast"
-- track whether procedural casts should use variables
pattern WithinStmt :: Identifier
pattern WithinStmt = ":within_stmt:"
withinStmt :: ST Bool
withinStmt = do
details <- lookupElemM WithinStmt
return $ case details of
Just (_, _, t) -> t /= Nil
Nothing -> False
enterStmt :: ST ()
enterStmt = do
inProcedure <- withinProcedureM
when inProcedure $ insertElem WithinStmt (RawNum 1)
exitStmt :: ST ()
exitStmt = do
inProcedure <- withinProcedureM
when inProcedure $ insertElem WithinStmt Nil

View File

@ -32,6 +32,7 @@ module Convert.Scoper
, partScoperT
, insertElem
, injectItem
, injectDecl
, lookupElem
, lookupElemM
, Access(..)
@ -83,7 +84,8 @@ data Scopes a = Scopes
{ sCurrent :: [Tier]
, sMapping :: Mapping a
, sProcedure :: Bool
, sInjected :: [ModuleItem]
, sInjectedItems :: [ModuleItem]
, sInjectedDecls :: [Decl]
} deriving Show
extractMapping :: Scopes a -> Map.Map Identifier a
@ -176,13 +178,25 @@ insertElem key element = do
injectItem :: Monad m => ModuleItem -> ScoperT a m ()
injectItem item =
modify' $ \s -> s { sInjected = add $ sInjected s }
where
add :: [ModuleItem] -> [ModuleItem]
add items =
if elem item items
then items
else items ++ [item]
modify' $ \s -> s { sInjectedItems = item : sInjectedItems s }
injectDecl :: Monad m => Decl -> ScoperT a m ()
injectDecl decl =
modify' $ \s -> s { sInjectedDecls = decl : sInjectedDecls s }
consumeInjectedItems :: Monad m => ScoperT a m [ModuleItem]
consumeInjectedItems = do
injected <- gets sInjectedItems
when (not $ null injected) $
modify' $ \s -> s { sInjectedItems = [] }
return $ reverse injected
consumeInjectedDecls :: Monad m => ScoperT a m [Decl]
consumeInjectedDecls = do
injected <- gets sInjectedDecls
when (not $ null injected) $
modify' $ \s -> s { sInjectedDecls = [] }
return $ reverse injected
type Replacements = Map.Map Identifier Expr
@ -305,7 +319,7 @@ runScoperT declMapper moduleItemMapper genItemMapper stmtMapper topName items =
items' <- mapM wrappedModuleItemMapper items
exitScope topName ""
return items'
initialState = Scopes [] Map.empty False []
initialState = Scopes [] Map.empty False [] []
wrappedModuleItemMapper = scopeModuleItemT
declMapper moduleItemMapper genItemMapper stmtMapper
@ -324,13 +338,28 @@ scopeModuleItemT declMapper moduleItemMapper genItemMapper stmtMapper =
fullStmtMapper :: Stmt -> ScoperT a m Stmt
fullStmtMapper (Block kw name decls stmts) = do
enterScope name ""
decls' <- mapM declMapper decls
decls' <- fmap concat $ mapM declMapper' decls
stmts' <- mapM fullStmtMapper stmts
exitScope name ""
return $ Block kw name decls' stmts'
-- TODO: Do we need to support the various procedural loops?
fullStmtMapper stmt =
stmtMapper stmt >>= traverseSinglyNestedStmtsM fullStmtMapper
fullStmtMapper stmt = do
stmt' <- stmtMapper stmt
injected <- consumeInjectedDecls
if null injected
then traverseSinglyNestedStmtsM fullStmtMapper stmt'
else fullStmtMapper $ Block Seq "" injected [stmt']
-- converts a decl and adds decls injected during conversion
declMapper' :: Decl -> ScoperT a m [Decl]
declMapper' decl = do
decl' <- declMapper decl
injected <- consumeInjectedDecls
if null injected
then return [decl']
else do
injected' <- mapM declMapper injected
return $ injected' ++ [decl']
mapTFDecls :: [Decl] -> ScoperT a m [Decl]
mapTFDecls = mapTFDecls' 0
@ -340,14 +369,14 @@ scopeModuleItemT declMapper moduleItemMapper genItemMapper stmtMapper =
mapTFDecls' idx (decl : decls) =
case argIdxDecl decl of
Nothing -> do
decl' <- declMapper decl
decl' <- declMapper' decl
decls' <- mapTFDecls' idx decls
return $ decl' : decls'
return $ decl' ++ decls'
Just declFunc -> do
_ <- declMapper $ declFunc idx
decl' <- declMapper decl
decl' <- declMapper' decl
decls' <- mapTFDecls' (idx + 1) decls
return $ decl' : decls'
return $ decl' ++ decls'
argIdxDecl :: Decl -> Maybe (Int -> Decl)
argIdxDecl (Variable d t _ a e) =
@ -369,11 +398,10 @@ scopeModuleItemT declMapper moduleItemMapper genItemMapper stmtMapper =
wrappedModuleItemMapper :: ModuleItem -> ScoperT a m ModuleItem
wrappedModuleItemMapper item = do
item' <- fullModuleItemMapper item
injected <- gets sInjected
injected <- consumeInjectedItems
if null injected
then return item'
else do
modify' $ \s -> s { sInjected = [] }
injected' <- mapM fullModuleItemMapper injected
return $ Generate $ map GenModuleItem $ injected' ++ [item']
fullModuleItemMapper :: ModuleItem -> ScoperT a m ModuleItem
@ -423,11 +451,10 @@ scopeModuleItemT declMapper moduleItemMapper genItemMapper stmtMapper =
fullGenItemMapper :: GenItem -> ScoperT a m GenItem
fullGenItemMapper genItem = do
genItem' <- genItemMapper genItem
injected <- gets sInjected
injected <- consumeInjectedItems
if null injected
then scopeGenItemMapper genItem'
else do
modify' $ \s -> s { sInjected = [] }
injected' <- mapM fullModuleItemMapper injected
genItem'' <- scopeGenItemMapper genItem'
let genItems = map GenModuleItem injected' ++ [genItem'']

View File

@ -1,29 +0,0 @@
{- sv2v
- Author: Zachary Snow <zach@zachjs.com>
-
- Conversion for `signed` and `unsigned` type casts.
-
- SystemVerilog has `signed'(foo)` and `unsigned'(foo)` as syntactic sugar for
- the `$signed` and `$unsigned` system functions present in Verilog-2005. This
- conversion elaborates these casts.
-}
module Convert.SignCast (convert) where
import Convert.Traverse
import Language.SystemVerilog.AST
convert :: [AST] -> [AST]
convert =
map $
traverseDescriptions $
traverseModuleItems $
traverseExprs $
traverseNestedExprs convertExpr
convertExpr :: Expr -> Expr
convertExpr (Cast (Left (Implicit Signed [])) e) =
Call (Ident "$signed") (Args [e] [])
convertExpr (Cast (Left (Implicit Unsigned [])) e) =
Call (Ident "$unsigned") (Args [e] [])
convertExpr other = other

View File

@ -1,224 +0,0 @@
{-# LANGUAGE PatternSynonyms #-}
{- sv2v
- Author: Zachary Snow <zach@zachjs.com>
-
- Conversion of size casts on non-constant expressions.
-}
module Convert.SizeCast (convert) where
import Control.Monad.Writer.Strict
import Data.List (isPrefixOf)
import Convert.ExprUtils
import Convert.Scoper
import Convert.Traverse
import Language.SystemVerilog.AST
convert :: [AST] -> [AST]
convert = map $ traverseDescriptions convertDescription
convertDescription :: Description -> Description
convertDescription =
traverseModuleItems dropDuplicateCaster . partScoper
traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM
traverseDeclM :: Decl -> Scoper Type Decl
traverseDeclM decl = do
decl' <- case decl of
Variable _ t x _ _ -> do
details <- lookupLocalIdentM x
if isPrefixOf "sv2v_cast_" x && details /= Nothing
then return $ Variable Local t DuplicateTag [] Nil
else insertElem x t >> return decl
Param _ t x _ -> do
inProcedure <- withinProcedureM
when (not inProcedure) $ insertElem x t
return decl
ParamType _ _ _ -> return decl
CommentDecl _ -> return decl
traverseDeclExprsM traverseExprM decl'
pattern DuplicateTag :: Identifier
pattern DuplicateTag = ":duplicate_cast_to_be_removed:"
dropDuplicateCaster :: ModuleItem -> ModuleItem
dropDuplicateCaster (MIPackageItem (Function _ _ DuplicateTag _ _)) =
Generate []
dropDuplicateCaster other = other
traverseModuleItemM :: ModuleItem -> Scoper Type ModuleItem
traverseModuleItemM (Genvar x) =
insertElem x (IntegerAtom TInteger Unspecified) >> return (Genvar x)
traverseModuleItemM item =
traverseExprsM traverseExprM item
traverseGenItemM :: GenItem -> Scoper Type GenItem
traverseGenItemM = traverseGenItemExprsM traverseExprM
traverseStmtM :: Stmt -> Scoper Type Stmt
traverseStmtM = traverseStmtExprsM traverseExprM
traverseExprM :: Expr -> Scoper Type Expr
traverseExprM =
traverseNestedExprsM convertExprM
where
convertExprM :: Expr -> Scoper Type Expr
convertExprM (Cast (Right (Number s)) (Number n)) =
case n of
UnbasedUnsized{} -> fallback
Decimal (-32) True val ->
num $ Decimal (fromIntegral size) False val'
where
Just size = numberToInteger s
val' = val `mod` (2 ^ size)
Decimal size signed val ->
if sizesMatch
then num $ Decimal (abs size) signed val
else fallback
Based size signed base vals knds ->
if sizesMatch
then num $ Based (abs size) signed base vals knds
else fallback
where
sizesMatch = numberToInteger s == Just (numberBitLength n)
fallback = convertCastM (Number s) (Number n)
num = return . Number
convertExprM (Cast (Right (Ident x)) e) = do
details <- lookupElemM x
-- can't convert this cast yet because x could be a typename
if details == Nothing
then return $ Cast (Right $ Ident x) e
else convertCastM (Ident x) e
convertExprM (Cast (Right s) e) =
if isSimpleExpr s
then convertCastM s e
else return $ Cast (Right s) e
convertExprM (Cast (Left (IntegerVector _ Signed rs)) e) =
convertCastWithSigningM (dimensionsSize rs) e Signed
convertExprM (Cast (Left (IntegerVector _ _ rs)) e) =
convertExprM $ Cast (Right $ dimensionsSize rs) e
convertExprM other = return other
convertCastM :: Expr -> Expr -> Scoper Type Expr
convertCastM (RawNum n) (Number (Based 1 True Binary a b)) =
return $ Number $ Based (fromIntegral n) True Binary
(extend a) (extend b)
where
extend 0 = 0
extend 1 = (2 ^ n) - 1
extend _ = error "not possible"
convertCastM (RawNum n) (Number (UnbasedUnsized ch)) =
return $ Number $ Based (fromIntegral n) False Binary
(extend a) (extend b)
where
(a, b) = case ch of
'0' -> (0, 0)
'1' -> (1, 0)
'x' -> (0, 1)
'z' -> (1, 1)
_ -> error $ "unexpected unbased-unsized digit: " ++ [ch]
extend :: Integer -> Integer
extend 0 = 0
extend 1 = (2 ^ n) - 1
extend _ = error "not possible"
convertCastM s e = do
signing <- embedScopes exprSigning e
case signing of
Just sg -> convertCastWithSigningM s e sg
_ -> return $ Cast (Right s) e
convertCastWithSigningM :: Expr -> Expr -> Signing -> Scoper Type Expr
convertCastWithSigningM (RawNum size) (RawNum val) Signed =
return $ Number $ Decimal (fromIntegral size) True val'
where val' = val `mod` (2 ^ size)
convertCastWithSigningM s e sg = do
details <- lookupLocalIdentM $ castFnName s sg
when (details == Nothing) $ injectItem $ MIPackageItem $ castFn s sg
let f = castFnName s sg
let args = Args [e] []
return $ Call (Ident f) args
isSimpleExpr :: Expr -> Bool
isSimpleExpr =
null . execWriter . collectNestedExprsM collectUnresolvedExprM
where
collectUnresolvedExprM :: Expr -> Writer [Expr] ()
collectUnresolvedExprM (expr @ PSIdent{}) = tell [expr]
collectUnresolvedExprM (expr @ CSIdent{}) = tell [expr]
collectUnresolvedExprM (expr @ DimsFn{}) = tell [expr]
collectUnresolvedExprM (expr @ DimFn {}) = tell [expr]
collectUnresolvedExprM _ = return ()
castFn :: Expr -> Signing -> PackageItem
castFn e sg =
Function Automatic t fnName [decl] [Return $ Ident inp]
where
inp = "inp"
r = (simplify $ BinOp Sub e (RawNum 1), RawNum 0)
t = IntegerVector TLogic sg [r]
fnName = castFnName e sg
decl = Variable Input t inp [] Nil
castFnName :: Expr -> Signing -> String
castFnName e sg =
if sg == Unspecified
then init name
else name
where
sizeStr = case e of
Number n ->
case numberToInteger n of
Just v -> show v
_ -> shortHash e
_ -> shortHash e
name = "sv2v_cast_" ++ sizeStr ++ "_" ++ show sg
exprSigning :: Scopes Type -> Expr -> Maybe Signing
exprSigning scopes (BinOp op e1 e2) =
combiner sg1 sg2
where
sg1 = exprSigning scopes e1
sg2 = exprSigning scopes e2
combiner = case op of
BitAnd -> combineSigning
BitXor -> combineSigning
BitXnor -> combineSigning
BitOr -> combineSigning
Mul -> combineSigning
Div -> combineSigning
Add -> combineSigning
Sub -> combineSigning
Mod -> curry fst
Pow -> curry fst
ShiftAL -> curry fst
ShiftAR -> curry fst
_ -> \_ _ -> Just Unspecified
exprSigning _ (Number n) =
Just $ if numberIsSigned n
then Signed
else Unsigned
exprSigning scopes expr =
case lookupElem scopes expr of
Just (_, _, t) -> typeSigning t
Nothing -> Just Unspecified
combineSigning :: Maybe Signing -> Maybe Signing -> Maybe Signing
combineSigning Nothing _ = Nothing
combineSigning _ Nothing = Nothing
combineSigning (Just Unspecified) _ = Just Unspecified
combineSigning _ (Just Unspecified) = Just Unspecified
combineSigning (Just Unsigned) _ = Just Unsigned
combineSigning _ (Just Unsigned) = Just Unsigned
combineSigning (Just Signed) (Just Signed) = Just Signed
typeSigning :: Type -> Maybe Signing
typeSigning (Net _ sg _) = Just sg
typeSigning (Implicit sg _) = Just sg
typeSigning (IntegerVector _ sg _) = Just sg
typeSigning (IntegerAtom t sg ) =
Just $ case (sg, t) of
(Unspecified, TTime) -> Unsigned
(Unspecified, _ ) -> Signed
(_ , _ ) -> sg
typeSigning _ = Nothing

View File

@ -281,7 +281,9 @@ convertExpr (t @ IntegerVector{}) (Concat exprs) =
t' = dropInnerTypeRange t
isUnsizedNumber :: Expr -> Bool
isUnsizedNumber (Number n) = not $ numberIsSized n
isUnsizedNumber (UniOp UniSub e) = isUnsizedNumber e
isUnsizedNumber (UniOp _ e) = isUnsizedNumber e
isUnsizedNumber (BinOp _ e1 e2) =
isUnsizedNumber e1 || isUnsizedNumber e2
isUnsizedNumber _ = False
-- TODO: This is really a conversion for using default patterns to

View File

@ -460,12 +460,10 @@ traverseSinglyNestedExprsM exprMapper = em
e2' <- exprMapper e2
e3' <- exprMapper e3
return $ Mux e1' e2' e3'
em (Cast (Left t) e) =
exprMapper e >>= return . Cast (Left t)
em (Cast (Right e1) e2) = do
e1' <- exprMapper e1
e2' <- exprMapper e2
return $ Cast (Right e1') e2'
em (Cast tore e) = do
tore' <- typeOrExprMapper tore
e' <- exprMapper e
return $ Cast tore' e'
em (DimsFn f tore) =
typeOrExprMapper tore >>= return . DimsFn f
em (DimFn f tore e) = do
@ -834,8 +832,8 @@ traverseExprTypesM mapper = exprMapper
typeOrExprMapper (Right e) = return $ Right e
typeOrExprMapper (Left t) =
mapper t >>= return . Left
exprMapper (Cast (Left t) e) =
mapper t >>= \t' -> return $ Cast (Left t') e
exprMapper (Cast tore e) =
typeOrExprMapper tore >>= return . flip Cast e
exprMapper (DimsFn f tore) =
typeOrExprMapper tore >>= return . DimsFn f
exprMapper (DimFn f tore e) = do

View File

@ -11,6 +11,13 @@
- concatenation conversions, defer the resolution of type information to this
- conversion pass by producing nodes with the `type` operator during
- elaboration.
-
- This conversion also elaborates sign and size casts to their primitive types.
- Sign casts take on the size of the underlying expression. Size casts take on
- the sign of the underlying expression. This conversion incorporates this
- elaboration as the canonical source for type information. It also enables the
- removal of unnecessary casts often resulting from struct literals or casts in
- the source intended to appease certain lint rules.
-}
module Convert.TypeOf (convert) where
@ -18,7 +25,7 @@ module Convert.TypeOf (convert) where
import Data.Tuple (swap)
import qualified Data.Map.Strict as Map
import Convert.ExprUtils (endianCondRange, simplify)
import Convert.ExprUtils (dimensionsSize, endianCondRange, simplify)
import Convert.Scoper
import Convert.Traverse
import Language.SystemVerilog.AST
@ -34,8 +41,8 @@ pattern UnitType = IntegerVector TLogic Unspecified []
-- insert the given declaration into the scope, and convert an TypeOfs within
traverseDeclM :: Decl -> Scoper Type Decl
traverseDeclM decl = do
item <- traverseModuleItemM (MIPackageItem $ Decl decl)
let MIPackageItem (Decl decl') = item
decl' <- traverseDeclExprsM traverseExprM decl
>>= traverseDeclTypesM traverseTypeM
case decl' of
Variable _ (Implicit sg rs) ident a _ ->
-- implicit types, which are commonly found in function return
@ -66,7 +73,8 @@ traverseModuleItemM (Genvar x) = do
insertElem x $ IntegerAtom TInteger Unspecified
return $ Genvar x
traverseModuleItemM item =
traverseTypesM (traverseNestedTypesM traverseTypeM) item
traverseNodesM traverseExprM return traverseTypeM traverseLHSM return item
where traverseLHSM = traverseLHSExprsM traverseExprM
-- convert TypeOf in a GenItem
traverseGenItemM :: GenItem -> Scoper Type GenItem
@ -76,14 +84,57 @@ traverseGenItemM = traverseGenItemExprsM traverseExprM
traverseStmtM :: Stmt -> Scoper Type Stmt
traverseStmtM = traverseStmtExprsM traverseExprM
-- convert TypeOf in a Expr
-- convert TypeOf in an Expr
traverseExprM :: Expr -> Scoper Type Expr
traverseExprM = traverseNestedExprsM $ traverseExprTypesM $
traverseNestedTypesM traverseTypeM
traverseExprM (Cast (Left (Implicit sg [])) expr) =
-- `signed'(foo)` and `unsigned'(foo)` are syntactic sugar for the `$signed`
-- and `$unsigned` system functions present in Verilog-2005
traverseExprM $ Call (Ident fn) $ Args [expr] []
where fn = if sg == Signed then "$signed" else "$unsigned"
traverseExprM (Cast (Left t) (Number (UnbasedUnsized ch))) =
-- defer until this expression becomes explicit
return $ Cast (Left t) (Number (UnbasedUnsized ch))
traverseExprM (Cast (Left (t @ (IntegerAtom TInteger _))) expr) =
-- convert to cast to an integer vector type
traverseExprM $ Cast (Left t') expr
where
(tf, []) = typeRanges t
t' = tf [(RawNum 1, RawNum 1)]
traverseExprM (Cast (Left t1) expr) = do
expr' <- traverseExprM expr
t1' <- traverseTypeM t1
t2 <- typeof expr'
if typeCastUnneeded t1' t2
then traverseExprM $ makeExplicit expr'
else return $ Cast (Left t1') expr'
traverseExprM (Cast (Right (Ident x)) expr) = do
expr' <- traverseExprM expr
details <- lookupElemM x
if details == Nothing
then return $ Cast (Left $ Alias x []) expr'
else elaborateSizeCast (Ident x) expr'
traverseExprM (Cast (Right size) expr) = do
expr' <- traverseExprM expr
elaborateSizeCast size expr'
traverseExprM other =
traverseExprTypesM traverseTypeM other
>>= traverseSinglyNestedExprsM traverseExprM
-- carry forward the signedness of the expression when cast to the given size
elaborateSizeCast :: Expr -> Expr -> Scoper Type Expr
elaborateSizeCast size value = do
t <- typeof value
case typeSignedness t of
Unspecified -> return $ Cast (Right size) value
sg -> traverseExprM $ Cast (Left $ typeOfSize sg size) value
-- convert TypeOf in a Type
traverseTypeM :: Type -> Scoper Type Type
traverseTypeM (TypeOf expr) = typeof expr
traverseTypeM other = return other
traverseTypeM (TypeOf expr) =
traverseExprM expr >>= typeof
traverseTypeM other =
traverseTypeExprsM traverseExprM other
>>= traverseSinglyNestedTypesM traverseTypeM
-- attempts to find the given (potentially hierarchical or generate-scoped)
-- expression in the available scope information
@ -92,11 +143,15 @@ lookupTypeOf expr = do
details <- lookupElemM expr
case details of
Nothing -> return $ TypeOf expr
Just (_, replacements, typ) ->
Just (_, replacements, typ) -> do
let typ' = toVarType typ
return $ if Map.null replacements
then typ
else rewriteType replacements typ
then typ'
else rewriteType replacements typ'
where
toVarType :: Type -> Type
toVarType (Net _ sg rs) = IntegerVector TLogic sg rs
toVarType other = other
rewriteType :: Replacements -> Type -> Type
rewriteType replacements = traverseNestedTypes $ traverseTypeExprs $
traverseNestedExprs (replace replacements)
@ -155,6 +210,7 @@ typeof (orig @ (Dot e x)) = do
case lookup x $ map swap fields of
Just typ -> typ
Nothing -> TypeOf orig
typeof (Cast (Left t) _) = traverseTypeM t
typeof (UniOp op expr) = typeofUniOp op expr
typeof (BinOp op a b) = typeofBinOp op a b
typeof (Mux _ a b) = largerSizeType a b
@ -190,7 +246,6 @@ typeSignednessOverride fallback sg t =
IntegerVector base _ rs -> IntegerVector base sg rs
IntegerAtom base _ -> IntegerAtom base sg
Net base _ rs -> Net base sg rs
Implicit _ rs -> Implicit sg rs
_ -> fallback
-- type of a unary operator expression
@ -262,7 +317,6 @@ binopSignedness Signed Signed = Signed
-- returns the signedness of the given type, if possible
typeSignedness :: Type -> Signing
typeSignedness (Net _ sg _) = signednessFallback Unsigned sg
typeSignedness (Implicit sg _) = signednessFallback Unsigned sg
typeSignedness (IntegerVector _ sg _) = signednessFallback Unsigned sg
typeSignedness (IntegerAtom t sg ) = signednessFallback fallback sg
where fallback = if t == TTime then Unsigned else Signed
@ -295,7 +349,7 @@ largerSizeOf a b =
typeOfSize :: Signing -> Expr -> Type
typeOfSize sg size =
IntegerVector TLogic sg [(hi, RawNum 0)]
where hi = BinOp Sub size (RawNum 1)
where hi = simplify $ BinOp Sub size (RawNum 1)
-- combines a type with unpacked ranges
injectRanges :: Type -> [Range] -> Type
@ -321,3 +375,32 @@ replaceRange r (IntegerAtom TInteger sg) =
replaceRange r t =
tf (r : rs)
where (tf, _ : rs) = typeRanges t
-- checks for a cast type which already trivially matches the expression type
typeCastUnneeded :: Type -> Type -> Bool
typeCastUnneeded t1 t2 =
sg1 == sg2 && sz1 == sz2 && sz1 /= Nothing && sz2 /= Nothing
where
sg1 = typeSignedness t1
sg2 = typeSignedness t2
sz1 = typeSize t1
sz2 = typeSize t2
typeSize :: Type -> Maybe Expr
typeSize (Net _ _ rs) = Just $ dimensionsSize rs
typeSize (IntegerVector _ _ rs) = Just $ dimensionsSize rs
typeSize (t @ IntegerAtom{}) =
typeSize $ tf [(RawNum 1, RawNum 1)]
where (tf, []) = typeRanges t
typeSize _ = Nothing
-- explicitly sizes top level numbers used in arithmetic expressions
makeExplicit :: Expr -> Expr
makeExplicit (Number (Decimal size signed values)) =
Number $ Decimal (abs size) signed values
makeExplicit (Number (Based size base signed values kinds)) =
Number $ Based (abs size) base signed values kinds
makeExplicit (BinOp op e1 e2) =
BinOp op (makeExplicit e1) (makeExplicit e2)
makeExplicit (UniOp op e) =
UniOp op $ makeExplicit e
makeExplicit other = other

View File

@ -168,7 +168,7 @@ baseSize Hex = 16
-- get the number of bits in a number
numberBitLength :: Number -> Integer
numberBitLength UnbasedUnsized{} = 32
numberBitLength UnbasedUnsized{} = 1
numberBitLength (Decimal size _ _) = fromIntegral $ abs size
numberBitLength (Based size _ _ _ _) =
fromIntegral $

View File

@ -121,7 +121,8 @@ nullRange t [] = t
nullRange t [(RawNum 0, RawNum 0)] = t
nullRange (IntegerAtom TInteger sg) rs =
-- integer arrays are allowed in SystemVerilog but not in Verilog
IntegerVector TBit sg (rs ++ [(RawNum 31, RawNum 0)])
IntegerVector TBit sg' (rs ++ [(RawNum 31, RawNum 0)])
where sg' = if sg == Unsigned then Unsigned else Signed
nullRange t rs1 =
if t == t'
then error $ "non-vector type " ++ show t ++

View File

@ -62,6 +62,7 @@ executable sv2v
Convert.AsgnOp
Convert.Assertion
Convert.BlockDecl
Convert.Cast
Convert.DimensionQuery
Convert.DuplicateGenvar
Convert.EmptyArgs
@ -86,9 +87,7 @@ executable sv2v
Convert.ParamType
Convert.RemoveComments
Convert.Scoper
Convert.SignCast
Convert.Simplify
Convert.SizeCast
Convert.StarPort
Convert.Stream
Convert.StringParam

View File

@ -52,4 +52,19 @@ module top;
$display("%b", W'(j));
end
typedef integer T1;
typedef integer signed T2;
typedef integer unsigned T3;
initial begin
$display("T1 %0d", T1'(1'sb1));
$display("T2 %0d", T2'(1'sb1));
$display("T3 %0d", T3'(1'sb1));
$display("T1 %0d", T1'(32'sd1));
$display("T2 %0d", T2'(32'sd1));
$display("T3 %0d", T3'(32'sd1));
$display("T1 %0d", T1'(32'd1));
$display("T2 %0d", T2'(32'd1));
$display("T3 %0d", T3'(32'd1));
end
endmodule

View File

@ -61,4 +61,16 @@ module top;
$display("%b", j_extended);
end
initial begin
$display("T1 %0d", -1);
$display("T2 %0d", -1);
$display("T3 %0d", 32'hFFFF_FFFF);
$display("T1 %0d", 1);
$display("T2 %0d", 1);
$display("T3 %0d", 1);
$display("T1 %0d", 1);
$display("T2 %0d", 1);
$display("T3 %0d", 1);
end
endmodule

15
test/basic/cast_nest.sv Normal file
View File

@ -0,0 +1,15 @@
module top;
reg signed x;
initial x = 1;
parameter ONE = 1;
initial begin
localparam A = ONE * 1;
localparam B = ONE * 2;
localparam C = ONE * 3;
localparam D = ONE * 4;
localparam E = ONE * 5;
$display("%b", 5'(4'(3'(2'(1'(x))))));
$display("%b", E'(D'(C'(B'(A'(x))))));
$display("%b", E'(D'(C'(B'(A'(E'(D'(C'(B'(A'(x)))))))))));
end
endmodule

12
test/basic/cast_nest.v Normal file
View File

@ -0,0 +1,12 @@
module top;
reg signed x;
initial x = 1;
parameter ONE = 1;
initial begin : blk
reg signed [4:0] y;
y = x;
$display("%b", y);
$display("%b", y);
$display("%b", y);
end
endmodule

View File

@ -0,0 +1,25 @@
`define TEST_CAST(expr, prefix, typ) \
initial begin \
localparam type T = type(prefix``typ); \
logic [63:0] x; \
T y; \
x = T'(expr); \
y = expr; \
r``typ = expr; \
tmp = r``typ; \
$display(`"%b => prefix``typ %b %b %b`", expr, T'(expr), x, y); \
end
module top;
wire foo;
type(foo) bar;
initial bar = 1;
`include "cast_nettype.vh"
`TEST('1)
`TEST('x)
`TEST(1)
`TEST(2)
`TEST(-1)
endmodule

20
test/basic/cast_nettype.v Normal file
View File

@ -0,0 +1,20 @@
`define TEST_CAST(expr, prefix, typ) \
initial begin \
r``typ = expr; \
tmp = r``typ; \
$display(`"%b => prefix``typ %b %b %b`", expr, r``typ, tmp, r``typ); \
end
module top;
wire foo;
reg bar;
initial bar = 1;
`include "cast_nettype.vh"
`TEST(1'sb1)
`TEST(1'sbx)
`TEST(1)
`TEST(2)
`TEST(-1)
endmodule

View File

@ -0,0 +1,31 @@
`define TEST(expr) \
`TEST_CAST(expr, w, t1) \
`TEST_CAST(expr, w, t2) \
`TEST_CAST(expr, w, t3) \
`TEST_CAST(expr, w, s1) \
`TEST_CAST(expr, w, s2) \
`TEST_CAST(expr, w, s3) \
`TEST_CAST(expr, r, t1) \
`TEST_CAST(expr, r, t2) \
`TEST_CAST(expr, r, t3) \
`TEST_CAST(expr, r, s1) \
`TEST_CAST(expr, r, s2) \
`TEST_CAST(expr, r, s3)
wire wt1;
wire signed wt2;
wire unsigned wt3;
wire [1:0] ws1;
wire signed [1:0] ws2;
wire unsigned [1:0] ws3;
reg rt1;
reg signed rt2;
reg unsigned rt3;
reg [1:0] rs1;
reg signed [1:0] rs2;
reg unsigned [1:0] rs3;
reg [63:0] tmp;

View File

@ -0,0 +1,31 @@
`define EXPR $unsigned(WIDTH'(ONES))
`define TEST(size) \
localparam WIDTH = ONE * size; \
localparam x = $unsigned(WIDTH'(ONES)); \
integer y, z; \
localparam type T = logic [WIDTH-1:0]; \
y = T'(ones); \
z = $unsigned(WIDTH'(ones)); \
$display(`"size: %b %b %b %b`", x, y, z, $unsigned(WIDTH'(ones)));
module top;
parameter ONE = 1;
parameter signed [0:0] ONES = 1'sb1;
logic signed [0:0] ones;
initial ones = 1'sb1;
task t;
`TEST(6)
endtask
function f;
input integer unused;
`TEST(7)
endfunction
initial t;
initial begin
integer a;
a = f(0);
end
initial begin
`TEST(8)
end
endmodule

View File

@ -0,0 +1,32 @@
`define TEST(size) \
localparam WIDTH = ONE * size; \
localparam [WIDTH-1:0] short = ONES; \
integer long; \
long = short; \
$display(`"size: %b %b %b %b`", short, long, long, short);
module top;
parameter ONE = 1;
parameter signed [0:0] ONES = 1'sb1;
reg signed [0:0] ones;
initial ones = 1'sb1;
task t;
begin : blk1
`TEST(6)
end
endtask
function f;
input integer unused;
begin : blk2
`TEST(7)
end
endfunction
initial t;
initial begin : blk3
integer a;
a = f(0);
end
initial begin : blk4
`TEST(8)
end
endmodule

View File

@ -0,0 +1,14 @@
module top;
typedef struct packed {
logic x, y;
} S;
typedef struct packed {
S x, y;
} T;
T t;
initial begin
t = 1'sb1;
$display("%b", t);
$display("%b", 5'(t));
end
endmodule

View File

@ -0,0 +1,10 @@
module top;
reg [3:0] t;
initial begin : blk
reg [4:0] x;
t = 1'sb1;
x = t;
$display("%b", t);
$display("%b", x);
end
endmodule

View File

@ -1,11 +1,7 @@
module top;
wire b;
wire [1:0] a;
function automatic val;
input inp;
val = inp;
endfunction
assign b = val(1);
assign a = {2 {val(1)}};
assign b = 1'b1;
assign a = {2 {1'b1}};
initial #1 $display("%b %b", a, b);
endmodule

View File

@ -1,4 +1,5 @@
module top;
parameter ONE = 1;
localparam integer A [4] = { 1, 2, 3, 4 };
localparam byte B [4] = { 1, 2, 3, 4 };
localparam bit C [4] = { 1, 2, 3, 4 };
@ -8,6 +9,12 @@ module top;
localparam integer G [4] = '{ 1, 2, 3, 4 };
localparam byte H [4] = '{ 1, 2, 3, 4 };
localparam bit I [4] = '{ 1, 2, 3, 4 };
localparam integer J [4] = { ONE * '0, ONE * '1, 5 * ONE, ONE * 6 };
localparam byte K [4] = { ONE * '0, ONE * '1, 5 * ONE, ONE * 6 };
localparam bit L [4] = { '0, ONE * '1, 5 * ONE, ONE * 6 };
localparam integer unsigned M [4] = { ONE * '0, ONE * '1, 5 * ONE, ONE * 6 };
localparam byte unsigned N [4] = { ONE * '0, ONE * '1, 5 * ONE, ONE * 6 };
localparam bit unsigned O [4] = { '0, ONE * '1, 5 * ONE, ONE * 6 };
initial begin
`define PRINT(X) \
$display("%b %2d %2d", {X[0], X[1], X[2], X[3]}, $bits(X), $bits(X[0]));
@ -20,6 +27,12 @@ module top;
`PRINT(G);
`PRINT(H);
`PRINT(I);
`PRINT(J);
`PRINT(K);
`PRINT(L);
`PRINT(M);
`PRINT(N);
`PRINT(O);
end
localparam [1:0][0:1] P = '{'{default:'d1}, '{default:'d2}};

View File

@ -8,6 +8,12 @@ module top;
localparam [0:127] G = { 32'h1, 32'h2, 32'h3, 32'h4 };
localparam [0:31] H = { 8'h1, 8'h2, 8'h3, 8'h4 };
localparam [0:3] I = { 1'h1, 1'h0, 1'h1, 1'h0 };
localparam [0:127] J = { 32'h0, 32'hFFFFFFFF, 32'h5, 32'h6 };
localparam [0:31] K = { 8'h0, 8'hFF, 8'h5, 8'h6 };
localparam [0:3] L = { 1'h0, 1'h1, 1'h1, 1'h0 };
localparam [0:127] M = { 32'h0, 32'hFFFFFFFF, 32'h5, 32'h6 };
localparam [0:31] N = { 8'h0, 8'hFF, 8'h5, 8'h6 };
localparam [0:3] O = { 1'h0, 1'h1, 1'h1, 1'h0 };
initial begin
$display("%b %2d %2d", A, $bits(A), 32);
$display("%b %2d %2d", B, $bits(B), 8);
@ -18,6 +24,12 @@ module top;
$display("%b %2d %2d", G, $bits(G), 32);
$display("%b %2d %2d", H, $bits(H), 8);
$display("%b %2d %2d", I, $bits(I), 1);
$display("%b %2d %2d", J, $bits(J), 32);
$display("%b %2d %2d", K, $bits(K), 8);
$display("%b %2d %2d", L, $bits(L), 1);
$display("%b %2d %2d", M, $bits(M), 32);
$display("%b %2d %2d", N, $bits(N), 8);
$display("%b %2d %2d", O, $bits(O), 1);
end
localparam [3:0] P = 4'b1100;

View File

@ -3,6 +3,8 @@ module top;
logic [2:0] test;
logic [3:0] foo;
logic [3:0] bar;
integer x;
reg [7:0] y;
initial begin
test = BW'(0);
@ -11,5 +13,15 @@ module top;
$display(foo);
bar = $bits(bar)'('1);
$display(bar);
x = 1'('1); $display("%b %0d", x, x);
y = 1'('1); $display("%b %0d", y, y);
x = 2'('0); $display("%b %0d", x, x);
y = 2'('0); $display("%b %0d", y, y);
x = 2'('1); $display("%b %0d", x, x);
y = 2'('1); $display("%b %0d", y, y);
x = 2'('x); $display("%b %0d", x, x);
y = 2'('x); $display("%b %0d", y, y);
x = 2'('z); $display("%b %0d", x, x);
y = 2'('z); $display("%b %0d", y, y);
end
endmodule

View File

@ -2,6 +2,8 @@ module top;
reg [2:0] test;
reg [3:0] foo;
reg [3:0] bar;
integer x;
reg [7:0] y;
initial begin
test = 0;
@ -10,5 +12,15 @@ module top;
$display(foo);
bar = 4'b1111;
$display(bar);
x = 1'b1; $display("%b %0d", x, x);
y = 1'b1; $display("%b %0d", y, y);
x = 2'b00; $display("%b %0d", x, x);
y = 2'b00; $display("%b %0d", y, y);
x = 2'b11; $display("%b %0d", x, x);
y = 2'b11; $display("%b %0d", y, y);
x = 2'bxx; $display("%b %0d", x, x);
y = 2'bxx; $display("%b %0d", y, y);
x = 2'bzz; $display("%b %0d", x, x);
y = 2'bzz; $display("%b %0d", y, y);
end
endmodule

View File

@ -192,4 +192,8 @@ module top;
`ASSERT_UNSIGNED(arr[5:0])
`ASSERT_UNSIGNED(arr[1+:2])
`ASSERT_UNSIGNED(arr[1-:2])
`ASSERT_UNSIGNED(integer_signed[0])
`ASSERT_UNSIGNED(integer_signed[1])
`ASSERT_UNSIGNED(integer_unsigned[0])
`ASSERT_UNSIGNED(integer_unsigned[1])
endmodule