mirror of https://github.com/zachjs/sv2v.git
306 lines
10 KiB
Haskell
306 lines
10 KiB
Haskell
{- sv2v
|
|
- Author: Zachary Snow <zach@zachjs.com>
|
|
- Initial Verilog AST Author: Tom Hawkins <tomahawkins@gmail.com>
|
|
-
|
|
- SystemVerilog expressions
|
|
-}
|
|
|
|
module Language.SystemVerilog.AST.Expr
|
|
( Expr (..)
|
|
, Range
|
|
, TypeOrExpr
|
|
, ExprOrRange
|
|
, Args (..)
|
|
, PartSelectMode (..)
|
|
, DimsFn (..)
|
|
, DimFn (..)
|
|
, showAssignment
|
|
, showRanges
|
|
, showExprOrRange
|
|
, simplify
|
|
, rangeSize
|
|
, rangeSizeHiLo
|
|
, endianCondExpr
|
|
, endianCondRange
|
|
, dimensionsSize
|
|
, readNumber
|
|
) where
|
|
|
|
import Data.List (intercalate)
|
|
import Text.Printf (printf)
|
|
import Text.Read (readMaybe)
|
|
|
|
import Language.SystemVerilog.AST.Op
|
|
import Language.SystemVerilog.AST.ShowHelp
|
|
import {-# SOURCE #-} Language.SystemVerilog.AST.Type
|
|
|
|
type Range = (Expr, Expr)
|
|
|
|
type TypeOrExpr = Either Type Expr
|
|
type ExprOrRange = Either Expr Range
|
|
|
|
data Expr
|
|
= String String
|
|
| Number String
|
|
| Time String
|
|
| Ident Identifier
|
|
| PSIdent Identifier Identifier
|
|
| Range Expr PartSelectMode Range
|
|
| Bit Expr Expr
|
|
| Repeat Expr [Expr]
|
|
| Concat [Expr]
|
|
| Stream StreamOp Expr [Expr]
|
|
| Call Expr Args
|
|
| UniOp UniOp Expr
|
|
| BinOp BinOp Expr Expr
|
|
| Mux Expr Expr Expr
|
|
| Cast TypeOrExpr Expr
|
|
| DimsFn DimsFn TypeOrExpr
|
|
| DimFn DimFn TypeOrExpr Expr
|
|
| Dot Expr Identifier
|
|
| Pattern [(Identifier, Expr)]
|
|
| Inside Expr [ExprOrRange]
|
|
| MinTypMax Expr Expr Expr
|
|
| Nil
|
|
deriving (Eq, Ord)
|
|
|
|
instance Show Expr where
|
|
show (Nil ) = ""
|
|
show (Number str ) = str
|
|
show (Time str ) = str
|
|
show (Ident str ) = str
|
|
show (PSIdent x y ) = printf "%s::%s" x y
|
|
show (String str ) = printf "\"%s\"" str
|
|
show (Bit e b ) = printf "%s[%s]" (show e) (show b)
|
|
show (Range e m r) = printf "%s[%s%s%s]" (show e) (show $ fst r) (show m) (show $ snd r)
|
|
show (Repeat e l ) = printf "{%s {%s}}" (show e) (commas $ map show l)
|
|
show (Concat l ) = printf "{%s}" (commas $ map show l)
|
|
show (Stream o e l) = printf "{%s %s%s}" (show o) (show e) (show $ Concat l)
|
|
show (UniOp o e ) = printf "%s%s" (show o) (showUniOpPrec e)
|
|
show (BinOp o a b) = printf "%s %s %s" (showBinOpPrec a) (show o) (showBinOpPrec b)
|
|
show (Dot e n ) = printf "%s.%s" (show e) n
|
|
show (Mux c a b) = printf "(%s ? %s : %s)" (show c) (show a) (show b)
|
|
show (Call e l ) = printf "%s%s" (show e) (show l)
|
|
show (Cast tore e ) = printf "%s'(%s)" (showEither tore) (show e)
|
|
show (DimsFn f v ) = printf "%s(%s)" (show f) (showEither v)
|
|
show (DimFn f v e) = printf "%s(%s, %s)" (show f) (showEither v) (show e)
|
|
show (Inside e l ) = printf "(%s inside { %s })" (show e) (intercalate ", " strs)
|
|
where
|
|
strs = map showExprOrRange l
|
|
show (Pattern l ) =
|
|
printf "'{\n%s\n}" (indent $ intercalate ",\n" $ map showPatternItem l)
|
|
where
|
|
showPatternItem :: (Identifier, Expr) -> String
|
|
showPatternItem ("" , e) = show e
|
|
showPatternItem (':' : n, e) = showPatternItem (n, e)
|
|
showPatternItem (n , e) = printf "%s: %s" n (show e)
|
|
show (MinTypMax a b c) = printf "(%s : %s : %s)" (show a) (show b) (show c)
|
|
|
|
data Args
|
|
= Args [Maybe Expr] [(Identifier, Maybe Expr)]
|
|
deriving (Eq, Ord)
|
|
|
|
instance Show Args where
|
|
show (Args pnArgs kwArgs) = "(" ++ (commas strs) ++ ")"
|
|
where
|
|
strs = (map showPnArg pnArgs) ++ (map showKwArg kwArgs)
|
|
showPnArg = maybe "" show
|
|
showKwArg (x, me) = printf ".%s(%s)" x (showPnArg me)
|
|
|
|
data PartSelectMode
|
|
= NonIndexed
|
|
| IndexedPlus
|
|
| IndexedMinus
|
|
deriving (Eq, Ord)
|
|
|
|
instance Show PartSelectMode where
|
|
show NonIndexed = ":"
|
|
show IndexedPlus = "+:"
|
|
show IndexedMinus = "-:"
|
|
|
|
data DimsFn
|
|
= FnBits
|
|
| FnDimensions
|
|
| FnUnpackedDimensions
|
|
deriving (Eq, Ord)
|
|
|
|
data DimFn
|
|
= FnLeft
|
|
| FnRight
|
|
| FnLow
|
|
| FnHigh
|
|
| FnIncrement
|
|
| FnSize
|
|
deriving (Eq, Ord)
|
|
|
|
instance Show DimsFn where
|
|
show FnBits = "$bits"
|
|
show FnDimensions = "$dimensions"
|
|
show FnUnpackedDimensions = "$unpacked_dimensions"
|
|
|
|
instance Show DimFn where
|
|
show FnLeft = "$left"
|
|
show FnRight = "$right"
|
|
show FnLow = "$low"
|
|
show FnHigh = "$high"
|
|
show FnIncrement = "$increment"
|
|
show FnSize = "$size"
|
|
|
|
|
|
showAssignment :: Show a => Maybe a -> String
|
|
showAssignment Nothing = ""
|
|
showAssignment (Just val) = " = " ++ show val
|
|
|
|
showRanges :: [Range] -> String
|
|
showRanges [] = ""
|
|
showRanges l = " " ++ (concatMap showRange l)
|
|
|
|
showRange :: Range -> String
|
|
showRange (h, l) = printf "[%s:%s]" (show h) (show l)
|
|
|
|
showExprOrRange :: ExprOrRange -> String
|
|
showExprOrRange (Left x) = show x
|
|
showExprOrRange (Right x) = show x
|
|
|
|
clog2Help :: Int -> Int -> Int
|
|
clog2Help p n = if p >= n then 0 else 1 + clog2Help (p*2) n
|
|
clog2 :: Int -> Int
|
|
clog2 n = if n < 2 then 0 else clog2Help 1 n
|
|
|
|
readNumber :: String -> Maybe Int
|
|
readNumber n =
|
|
readMaybe n' :: Maybe Int
|
|
where
|
|
n' = case n of
|
|
'3' : '2' : '\'' : 'd' : rest -> rest
|
|
'\'' : 'd' : rest -> rest
|
|
_ -> n
|
|
|
|
showUniOpPrec :: Expr -> String
|
|
showUniOpPrec (e @ UniOp{}) = printf "(%s)" (show e)
|
|
showUniOpPrec (e @ BinOp{}) = printf "(%s)" (show e)
|
|
showUniOpPrec e = show e
|
|
|
|
showBinOpPrec :: Expr -> String
|
|
showBinOpPrec (e @ BinOp{}) = printf "(%s)" (show e)
|
|
showBinOpPrec e = show e
|
|
|
|
-- basic expression simplfication utility to help us generate nicer code in the
|
|
-- common case of ranges like `[FOO-1:0]`
|
|
simplify :: Expr -> Expr
|
|
simplify (UniOp LogNot (Number "1")) = Number "0"
|
|
simplify (UniOp LogNot (Number "0")) = Number "1"
|
|
simplify (orig @ (UniOp UniSub (Number n))) =
|
|
case readNumber n of
|
|
Nothing -> orig
|
|
Just x -> Number $ show (-x)
|
|
simplify (orig @ (Repeat (Number n) exprs)) =
|
|
case readNumber n of
|
|
Nothing -> orig
|
|
Just 0 -> Concat []
|
|
Just 1 -> Concat exprs
|
|
Just x ->
|
|
if x < 0
|
|
then error $ "negative repeat count: " ++ show orig
|
|
else orig
|
|
simplify (Concat [expr]) = expr
|
|
simplify (Concat exprs) =
|
|
Concat $ filter (/= Concat []) exprs
|
|
simplify (orig @ (Call (Ident "$clog2") (Args [Just (Number n)] []))) =
|
|
case readNumber n of
|
|
Nothing -> orig
|
|
Just x -> Number $ show $ clog2 x
|
|
simplify (Mux cc e1 e2) =
|
|
case cc' of
|
|
Number "1" -> e1'
|
|
Number "0" -> e2'
|
|
_ -> Mux cc' e1' e2'
|
|
where
|
|
cc' = simplify cc
|
|
e1' = simplify e1
|
|
e2' = simplify e2
|
|
simplify (Range e NonIndexed r) = Range e NonIndexed r
|
|
simplify (Range e _ (i, Number "0")) = Bit e i
|
|
simplify (BinOp Sub (Number n1) (BinOp Sub (Number n2) e)) =
|
|
simplify $ BinOp Add (BinOp Sub (Number n1) (Number n2)) e
|
|
simplify (BinOp Sub (Number n1) (BinOp Sub e (Number n2))) =
|
|
simplify $ BinOp Sub (BinOp Add (Number n1) (Number n2)) e
|
|
simplify (BinOp Add (BinOp Sub (Number n1) e) (Number n2)) =
|
|
case (readNumber n1, readNumber n2) of
|
|
(Just x, Just y) ->
|
|
simplify $ BinOp Sub (Number $ show (x + y)) e'
|
|
_ -> nochange
|
|
where
|
|
e' = simplify e
|
|
nochange = BinOp Add (BinOp Sub (Number n1) e') (Number n2)
|
|
simplify (BinOp op e1 e2) =
|
|
case (op, e1', e2') of
|
|
(Add, Number "0", e) -> e
|
|
(Add, e, Number "0") -> e
|
|
(Mul, _, Number "0") -> Number "0"
|
|
(Mul, Number "0", _) -> Number "0"
|
|
(Mul, e, Number "1") -> e
|
|
(Mul, Number "1", e) -> e
|
|
(Sub, e, Number "0") -> e
|
|
(Add, BinOp Sub e (Number "1"), Number "1") -> e
|
|
(Add, e, BinOp Sub (Number "0") (Number "1")) -> BinOp Sub e (Number "1")
|
|
(_ , Number a, Number b) ->
|
|
case (op, readNumber a :: Maybe Int, readNumber b :: Maybe Int) of
|
|
(Add, Just x, Just y) -> Number $ show (x + y)
|
|
(Sub, Just x, Just y) -> Number $ show (x - y)
|
|
(Mul, Just x, Just y) -> Number $ show (x * y)
|
|
(Div, Just _, Just 0) -> Number "x"
|
|
(Div, Just x, Just y) -> Number $ show (x `quot` y)
|
|
(Eq , Just x, Just y) -> bool $ x == y
|
|
(Ne , Just x, Just y) -> bool $ x /= y
|
|
(Gt , Just x, Just y) -> bool $ x > y
|
|
(Ge , Just x, Just y) -> bool $ x >= y
|
|
(Lt , Just x, Just y) -> bool $ x < y
|
|
(Le , Just x, Just y) -> bool $ x <= y
|
|
_ -> BinOp op e1' e2'
|
|
(Add, BinOp Add e (Number a), Number b) ->
|
|
case (readNumber a, readNumber b) of
|
|
(Just x, Just y) -> BinOp Add e $ Number $ show (x + y)
|
|
_ -> BinOp op e1' e2'
|
|
(Sub, e, Number "-1") -> BinOp Add e (Number "1")
|
|
_ -> BinOp op e1' e2'
|
|
where
|
|
e1' = simplify e1
|
|
e2' = simplify e2
|
|
bool True = Number "1"
|
|
bool False = Number "0"
|
|
simplify other = other
|
|
|
|
rangeSize :: Range -> Expr
|
|
rangeSize (s, e) =
|
|
endianCondExpr (s, e) a b
|
|
where
|
|
a = rangeSizeHiLo (s, e)
|
|
b = rangeSizeHiLo (e, s)
|
|
|
|
rangeSizeHiLo :: Range -> Expr
|
|
rangeSizeHiLo (hi, lo) =
|
|
simplify $ BinOp Add (BinOp Sub hi lo) (Number "1")
|
|
|
|
-- chooses one or the other expression based on the endianness of the given
|
|
-- range; [hi:lo] chooses the first expression
|
|
endianCondExpr :: Range -> Expr -> Expr -> Expr
|
|
endianCondExpr r e1 e2 = simplify $ Mux (uncurry (BinOp Ge) r) e1 e2
|
|
|
|
-- chooses one or the other range based on the endianness of the given range,
|
|
-- but in such a way that the result is itself also usable as a range even if
|
|
-- the endianness cannot be resolved during conversion, i.e. if it's dependent
|
|
-- on a parameter value; [hi:lo] chooses the first range
|
|
endianCondRange :: Range -> Range -> Range -> Range
|
|
endianCondRange r r1 r2 =
|
|
( endianCondExpr r (fst r1) (fst r2)
|
|
, endianCondExpr r (snd r1) (snd r2)
|
|
)
|
|
|
|
dimensionsSize :: [Range] -> Expr
|
|
dimensionsSize ranges =
|
|
simplify $
|
|
foldl (BinOp Mul) (Number "1") $
|
|
map rangeSize $
|
|
ranges
|