Commit dd1a9efb by Zachary Snow

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
parent c656cbb9
......@@ -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
......
{-# 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
......@@ -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'']
......
{- 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
{-# 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
......@@ -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
......
......@@ -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
......
......@@ -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
......@@ -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 $
......
......@@ -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 ++
......
......@@ -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
......
......@@ -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
......@@ -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
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
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
`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
`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
`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;
`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
`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
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
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
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
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}};
......
......@@ -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;
......
......@@ -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
......@@ -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
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment