2021-02-03 22:12:05 +01:00
|
|
|
{-# 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)
|
2021-07-24 22:41:31 +02:00
|
|
|
import Data.Maybe (isJust)
|
2021-02-03 22:12:05 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2021-07-24 22:41:31 +02:00
|
|
|
type SC = Scoper ()
|
2021-02-03 22:12:05 +01:00
|
|
|
|
2021-07-24 22:41:31 +02:00
|
|
|
traverseDeclM :: Decl -> SC Decl
|
2021-02-03 22:12:05 +01:00
|
|
|
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
|
2021-07-24 22:41:31 +02:00
|
|
|
insertElem x ()
|
2021-02-03 22:12:05 +01:00
|
|
|
return $ Variable d t x a e'
|
2021-07-02 23:59:21 +02:00
|
|
|
Net d n s t x a e -> do
|
|
|
|
|
enterStmt
|
|
|
|
|
e' <- traverseExprM e
|
|
|
|
|
exitStmt
|
2021-07-24 22:41:31 +02:00
|
|
|
insertElem x ()
|
2021-07-02 23:59:21 +02:00
|
|
|
return $ Net d n s t x a e'
|
2021-02-03 22:12:05 +01:00
|
|
|
Param _ _ x _ ->
|
2021-07-24 22:41:31 +02:00
|
|
|
insertElem x () >> return decl
|
2021-02-03 22:12:05 +01:00
|
|
|
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
|
|
|
|
|
|
2021-07-24 22:41:31 +02:00
|
|
|
traverseModuleItemM :: ModuleItem -> SC ModuleItem
|
2021-02-03 22:12:05 +01:00
|
|
|
traverseModuleItemM (Genvar x) =
|
2021-07-24 22:41:31 +02:00
|
|
|
insertElem x () >> return (Genvar x)
|
2021-02-03 22:12:05 +01:00
|
|
|
traverseModuleItemM item =
|
|
|
|
|
traverseExprsM traverseExprM item
|
|
|
|
|
|
2021-07-24 22:41:31 +02:00
|
|
|
traverseGenItemM :: GenItem -> SC GenItem
|
2021-02-03 22:12:05 +01:00
|
|
|
traverseGenItemM = traverseGenItemExprsM traverseExprM
|
|
|
|
|
|
2021-07-24 22:41:31 +02:00
|
|
|
traverseStmtM :: Stmt -> SC Stmt
|
2021-02-03 22:12:05 +01:00
|
|
|
traverseStmtM stmt = do
|
|
|
|
|
enterStmt
|
|
|
|
|
stmt' <- traverseStmtExprsM traverseExprM stmt
|
|
|
|
|
exitStmt
|
|
|
|
|
return stmt'
|
|
|
|
|
|
2021-07-24 22:41:31 +02:00
|
|
|
traverseExprM :: Expr -> SC Expr
|
2021-02-07 19:40:16 +01:00
|
|
|
traverseExprM (Cast (Left (IntegerVector _ sg rs)) value) = do
|
|
|
|
|
value' <- traverseExprM value
|
|
|
|
|
size' <- traverseExprM size
|
|
|
|
|
convertCastM size' value' signed
|
|
|
|
|
where
|
|
|
|
|
signed = sg == Signed
|
|
|
|
|
size = dimensionsSize rs
|
2021-02-03 22:12:05 +01:00
|
|
|
traverseExprM other =
|
|
|
|
|
traverseSinglyNestedExprsM traverseExprM other
|
|
|
|
|
|
2021-07-24 22:41:31 +02:00
|
|
|
convertCastM :: Expr -> Expr -> Bool -> SC Expr
|
2021-07-10 18:38:55 +02:00
|
|
|
convertCastM (Number size) (Number value) signed =
|
|
|
|
|
return $ Number $
|
|
|
|
|
case numberToInteger size of
|
|
|
|
|
Just size' -> numberCast signed (fromIntegral size') value
|
|
|
|
|
Nothing -> error $ "size cast width " ++ show size
|
|
|
|
|
++ " is not an integer"
|
2021-02-03 22:12:05 +01:00
|
|
|
convertCastM size value signed = do
|
|
|
|
|
value' <- traverseExprM value
|
2021-07-18 04:56:14 +02:00
|
|
|
sizeUsesLocalVars <- embedScopes usesLocalVars size
|
|
|
|
|
inProcedure <- withinProcedureM
|
|
|
|
|
if not sizeUsesLocalVars || not inProcedure then do
|
2021-02-03 22:12:05 +01:00
|
|
|
let name = castFnName size signed
|
2021-07-18 04:56:14 +02:00
|
|
|
let item = castFn name size signed
|
|
|
|
|
if sizeUsesLocalVars
|
|
|
|
|
then do
|
|
|
|
|
details <- lookupLocalIdentM name
|
|
|
|
|
when (details == Nothing) (injectItem item)
|
|
|
|
|
else do
|
|
|
|
|
details <- lookupElemM name
|
|
|
|
|
when (details == Nothing) (injectTopItem item)
|
2021-02-03 22:12:05 +01:00
|
|
|
return $ Call (Ident name) (Args [value'] [])
|
|
|
|
|
else do
|
|
|
|
|
name <- castDeclName 0
|
2021-07-24 22:41:31 +02:00
|
|
|
insertElem name ()
|
2021-02-03 22:12:05 +01:00
|
|
|
useVar <- withinStmt
|
|
|
|
|
injectDecl $ castDecl useVar name value' size signed
|
|
|
|
|
return $ Ident name
|
|
|
|
|
|
2021-07-18 04:56:14 +02:00
|
|
|
-- checks if a cast size references any vars not defined at the top level scope
|
|
|
|
|
usesLocalVars :: Scopes a -> Expr -> Bool
|
|
|
|
|
usesLocalVars scopes =
|
|
|
|
|
getAny . execWriter . collectNestedExprsM collectLocalVarsM
|
2021-02-03 22:12:05 +01:00
|
|
|
where
|
2021-07-18 04:56:14 +02:00
|
|
|
collectLocalVarsM :: Expr -> Writer Any ()
|
|
|
|
|
collectLocalVarsM expr@(Ident x) =
|
|
|
|
|
if isLoopVar scopes x
|
|
|
|
|
then tell $ Any True
|
|
|
|
|
else resolve expr
|
|
|
|
|
collectLocalVarsM expr = resolve expr
|
|
|
|
|
resolve :: Expr -> Writer Any ()
|
|
|
|
|
resolve expr =
|
2021-02-03 22:12:05 +01:00
|
|
|
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
|
2021-07-09 16:25:18 +02:00
|
|
|
_ -> error $ "size cast width " ++ show n
|
|
|
|
|
++ " is not an integer"
|
2021-02-03 22:12:05 +01:00
|
|
|
_ -> 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
|
|
|
|
|
|
2021-07-24 22:41:31 +02:00
|
|
|
castDeclName :: Int -> SC String
|
2021-02-03 22:12:05 +01:00
|
|
|
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
|
2021-07-24 22:41:31 +02:00
|
|
|
withinStmtKey :: Identifier
|
|
|
|
|
withinStmtKey = ":within_stmt:"
|
|
|
|
|
withinStmt :: SC Bool
|
|
|
|
|
withinStmt = fmap isJust $ lookupElemM withinStmtKey
|
|
|
|
|
enterStmt :: SC ()
|
2021-02-03 22:12:05 +01:00
|
|
|
enterStmt = do
|
|
|
|
|
inProcedure <- withinProcedureM
|
2021-07-24 22:41:31 +02:00
|
|
|
when inProcedure $ insertElem withinStmtKey ()
|
|
|
|
|
exitStmt :: SC ()
|
2021-02-03 22:12:05 +01:00
|
|
|
exitStmt = do
|
|
|
|
|
inProcedure <- withinProcedureM
|
2021-07-24 22:41:31 +02:00
|
|
|
when inProcedure $ removeElem withinStmtKey
|