2019-03-06 06:51:09 +01:00
|
|
|
{- sv2v
|
|
|
|
|
- Author: Zachary Snow <zach@zachjs.com>
|
|
|
|
|
-
|
|
|
|
|
- Conversion for `packed struct`
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
module Convert.Struct (convert) where
|
|
|
|
|
|
|
|
|
|
import Data.Maybe (isJust)
|
|
|
|
|
import Data.List (sortOn)
|
|
|
|
|
import Data.Tuple (swap)
|
|
|
|
|
import Control.Monad.Writer
|
|
|
|
|
import qualified Data.Map.Strict as Map
|
|
|
|
|
|
|
|
|
|
import Convert.Traverse
|
|
|
|
|
import Language.SystemVerilog.AST
|
|
|
|
|
|
|
|
|
|
type TypeFunc = [Range] -> Type
|
|
|
|
|
type StructInfo = (Type, Map.Map Identifier (Range, Expr))
|
|
|
|
|
type Structs = Map.Map TypeFunc StructInfo
|
|
|
|
|
type Types = Map.Map Identifier Type
|
|
|
|
|
|
|
|
|
|
convert :: AST -> AST
|
|
|
|
|
convert = traverseDescriptions convertDescription
|
|
|
|
|
|
|
|
|
|
convertDescription :: Description -> Description
|
|
|
|
|
convertDescription description =
|
|
|
|
|
traverseModuleItems (traverseTypes $ convertType structs) $
|
|
|
|
|
traverseModuleItems (traverseAsgns $ convertAsgn structs types) $
|
|
|
|
|
description
|
|
|
|
|
where
|
|
|
|
|
structs = execWriter $ collectModuleItemsM
|
|
|
|
|
(collectTypesM collectType) description
|
|
|
|
|
typesA = execWriter $ collectModuleItemsM
|
|
|
|
|
(collectDeclsM collectDecl) description
|
|
|
|
|
typesB = execWriter $ collectModuleItemsM
|
|
|
|
|
collectFunction description
|
|
|
|
|
types = Map.union typesA typesB
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- write down unstructured versions of a packed struct type
|
|
|
|
|
collectType :: Type -> Writer Structs ()
|
2019-03-22 21:57:13 +01:00
|
|
|
collectType (Struct (Packed sg) fields _) = do
|
|
|
|
|
-- TODO: How should we combine the structs Signing with that of the types it
|
|
|
|
|
-- contains?
|
2019-03-06 06:51:09 +01:00
|
|
|
if canUnstructure
|
|
|
|
|
then tell $ Map.singleton
|
2019-03-22 21:57:13 +01:00
|
|
|
(Struct (Packed sg) fields)
|
2019-03-06 06:51:09 +01:00
|
|
|
(unstructType, unstructFields)
|
|
|
|
|
else return ()
|
|
|
|
|
where
|
|
|
|
|
zero = Number "0"
|
|
|
|
|
typeRange :: Type -> Range
|
|
|
|
|
typeRange t =
|
|
|
|
|
if null ranges then (zero, zero) else head ranges
|
|
|
|
|
where ranges = snd $ typeRanges t
|
|
|
|
|
|
|
|
|
|
-- extract info about the fields
|
|
|
|
|
fieldTypes = map fst fields
|
|
|
|
|
fieldRanges = map typeRange fieldTypes
|
|
|
|
|
fieldSizes = map rangeSize fieldRanges
|
|
|
|
|
|
|
|
|
|
-- layout the fields into the unstructured type; note that `scanr` is
|
|
|
|
|
-- used here because SystemVerilog structs are laid out backwards
|
|
|
|
|
fieldLos = map simplify $ tail $ scanr (BinOp Add) (Number "0") fieldSizes
|
|
|
|
|
fieldHis = map simplify $ init $ scanr (BinOp Add) (Number "-1") fieldSizes
|
|
|
|
|
|
|
|
|
|
-- create the mapping structure for the unstructured fields
|
|
|
|
|
unstructOffsets = map simplify $ map snd fieldRanges
|
|
|
|
|
unstructRanges = zip fieldHis fieldLos
|
|
|
|
|
keys = map snd fields
|
|
|
|
|
vals = zip unstructRanges unstructOffsets
|
|
|
|
|
unstructFields = Map.fromList $ zip keys vals
|
|
|
|
|
|
|
|
|
|
-- create the unstructured type
|
|
|
|
|
tf = fst $ typeRanges $ head fieldTypes
|
|
|
|
|
structSize = foldl1 (BinOp Add) fieldSizes
|
|
|
|
|
packedRange = (simplify $ BinOp Sub structSize (Number "1"), zero)
|
|
|
|
|
unstructType = tf [packedRange]
|
|
|
|
|
|
|
|
|
|
-- TODO: For now, we only convert packed structs which contain fields
|
|
|
|
|
-- with all the same base type. We might be able to get away with
|
|
|
|
|
-- converting everything to a Logic type. This should work in cases of
|
|
|
|
|
-- mixed `wire`/`logic` or `reg`/`logic`.
|
|
|
|
|
fieldClasses = map (show . fst . typeRanges) fieldTypes
|
2019-03-30 07:33:31 +01:00
|
|
|
isComplex :: Type -> Bool
|
|
|
|
|
isComplex (Struct _ _ _ ) = True
|
|
|
|
|
isComplex (Enum _ _ _ ) = True
|
|
|
|
|
isComplex (Alias _ _) = True
|
|
|
|
|
isComplex _ = False
|
|
|
|
|
canUnstructure =
|
|
|
|
|
all (head fieldClasses ==) (map show fieldClasses) &&
|
|
|
|
|
not (any isComplex fieldTypes)
|
2019-03-06 06:51:09 +01:00
|
|
|
|
|
|
|
|
collectType _ = return ()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- convert a struct type to its unstructured equivalent
|
|
|
|
|
convertType :: Structs -> Type -> Type
|
|
|
|
|
convertType structs t1 =
|
|
|
|
|
case Map.lookup tf1 structs of
|
|
|
|
|
Nothing -> t1
|
|
|
|
|
Just (t2, _) -> tf2 (rs2 ++ rs1)
|
|
|
|
|
where (tf2, rs2) = typeRanges t2
|
|
|
|
|
where (tf1, rs1) = typeRanges t1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- write down the type a declarations
|
|
|
|
|
collectDecl :: Decl -> Writer Types ()
|
2019-03-22 21:57:13 +01:00
|
|
|
collectDecl (Variable _ (Implicit _ []) _ _ _) = return ()
|
2019-03-06 06:51:09 +01:00
|
|
|
collectDecl (Variable _ t x a _) =
|
|
|
|
|
-- We add the unpacked dimensions to the type so that our type traversal can
|
|
|
|
|
-- correctly match-off the dimensions whenever we see a `Bit` or `Range`
|
|
|
|
|
-- expression.
|
|
|
|
|
tell $ Map.singleton x (tf $ rs ++ a)
|
|
|
|
|
where (tf, rs) = typeRanges t
|
|
|
|
|
collectDecl (Parameter t x _) = tell $ Map.singleton x t
|
|
|
|
|
collectDecl (Localparam t x _) = tell $ Map.singleton x t
|
|
|
|
|
|
|
|
|
|
-- write down the return type of a function
|
|
|
|
|
collectFunction :: ModuleItem -> Writer Types ()
|
2019-03-07 19:19:31 +01:00
|
|
|
collectFunction (MIPackageItem (Function _ t f _ _)) = tell $ Map.singleton f t
|
2019-03-06 06:51:09 +01:00
|
|
|
collectFunction _ = return ()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
convertAsgn :: Structs -> Types -> (LHS, Expr) -> (LHS, Expr)
|
|
|
|
|
convertAsgn structs types (lhs, expr) =
|
|
|
|
|
(lhs', expr')
|
|
|
|
|
where
|
|
|
|
|
(typ, lhs') = convertLHS lhs
|
|
|
|
|
expr' = snd $ convertSubExpr $ convertExpr typ expr
|
|
|
|
|
|
|
|
|
|
-- converting LHSs by looking at the innermost types first
|
|
|
|
|
convertLHS :: LHS -> (Type, LHS)
|
|
|
|
|
convertLHS (LHSIdent x) =
|
|
|
|
|
case Map.lookup x types of
|
2019-03-22 21:57:13 +01:00
|
|
|
Nothing -> (Implicit Unspecified [], LHSIdent x)
|
2019-03-06 06:51:09 +01:00
|
|
|
Just t -> (t, LHSIdent x)
|
|
|
|
|
convertLHS (LHSBit l e) =
|
2019-03-08 23:17:30 +01:00
|
|
|
if null rs
|
2019-03-22 21:57:13 +01:00
|
|
|
then (Implicit Unspecified [], LHSBit l' e)
|
2019-03-08 23:17:30 +01:00
|
|
|
else (tf $ tail rs, LHSBit l' e)
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(t, l') = convertLHS l
|
|
|
|
|
(tf, rs) = typeRanges t
|
|
|
|
|
convertLHS (LHSRange l r ) =
|
2019-03-08 23:17:30 +01:00
|
|
|
if null rs
|
2019-03-22 21:57:13 +01:00
|
|
|
then (Implicit Unspecified [], LHSRange l' r)
|
2019-03-08 23:17:30 +01:00
|
|
|
else (tf rs', LHSRange l' r)
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(t, l') = convertLHS l
|
|
|
|
|
(tf, rs) = typeRanges t
|
|
|
|
|
rs' = r : tail rs
|
|
|
|
|
convertLHS (LHSDot l x ) =
|
|
|
|
|
case t of
|
2019-03-22 21:57:13 +01:00
|
|
|
InterfaceT _ _ _ -> (Implicit Unspecified [], LHSDot l' x)
|
2019-03-06 06:51:09 +01:00
|
|
|
Struct _ _ _ -> case Map.lookup structTf structs of
|
|
|
|
|
Nothing -> (fieldType, LHSDot l' x)
|
|
|
|
|
Just (structT, m) -> (tf [tr], LHSRange l' r)
|
|
|
|
|
where
|
|
|
|
|
(tf, _) = typeRanges structT
|
|
|
|
|
(r @ (hi, lo), base) = m Map.! x
|
|
|
|
|
hi' = BinOp Add base $ BinOp Sub hi lo
|
|
|
|
|
lo' = base
|
|
|
|
|
tr = (simplify hi', simplify lo')
|
2019-03-22 21:57:13 +01:00
|
|
|
Implicit sg _ -> (Implicit sg [], LHSDot l' x)
|
2019-03-07 03:55:27 +01:00
|
|
|
_ -> error $ "convertLHS encountered dot for bad type: " ++ show (t, l, x)
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(t, l') = convertLHS l
|
|
|
|
|
Struct p fields [] = t
|
|
|
|
|
structTf = Struct p fields
|
|
|
|
|
fieldType = lookupFieldType fields x
|
|
|
|
|
convertLHS (LHSConcat lhss) =
|
2019-03-22 21:57:13 +01:00
|
|
|
(Implicit Unspecified [], LHSConcat $ map (snd . convertLHS) lhss)
|
2019-03-06 06:51:09 +01:00
|
|
|
|
|
|
|
|
-- try expression conversion by looking at the *outermost* type first
|
|
|
|
|
convertExpr :: Type -> Expr -> Expr
|
2019-03-30 07:33:31 +01:00
|
|
|
convertExpr (Struct _ fields []) (Pattern [(Just "default", e)]) =
|
|
|
|
|
Concat $ take (length fields) (repeat e)
|
2019-03-22 21:57:13 +01:00
|
|
|
convertExpr (Struct (Packed sg) fields []) (Pattern items) =
|
2019-03-06 06:51:09 +01:00
|
|
|
if Map.notMember structTf structs
|
|
|
|
|
then Pattern items''
|
|
|
|
|
else Concat exprs
|
|
|
|
|
where
|
|
|
|
|
subMap = \(Just ident, subExpr) ->
|
|
|
|
|
(Just ident, convertExpr (lookupFieldType fields ident) subExpr)
|
2019-03-22 21:57:13 +01:00
|
|
|
structTf = Struct (Packed sg) fields
|
2019-03-06 06:51:09 +01:00
|
|
|
items' =
|
|
|
|
|
-- if the pattern does not use identifiers, use the
|
|
|
|
|
-- identifiers from the struct type definition in order
|
|
|
|
|
if not (all (isJust . fst) items)
|
|
|
|
|
then zip (map (Just. snd) fields) (map snd items)
|
|
|
|
|
else items
|
|
|
|
|
items'' = map subMap items'
|
|
|
|
|
fieldRange = \(Just x, _) -> lookupUnstructRange structTf x
|
|
|
|
|
exprs = map snd $ reverse $ sortOn fieldRange items''
|
|
|
|
|
convertExpr _ other = other
|
|
|
|
|
|
|
|
|
|
-- try expression conversion by looking at the *innermost* type first
|
|
|
|
|
convertSubExpr :: Expr -> (Type, Expr)
|
|
|
|
|
convertSubExpr (Ident x) =
|
|
|
|
|
case Map.lookup x types of
|
2019-03-22 21:57:13 +01:00
|
|
|
Nothing -> (Implicit Unspecified [], Ident x)
|
2019-03-06 06:51:09 +01:00
|
|
|
Just t -> (t, Ident x)
|
2019-03-23 00:24:45 +01:00
|
|
|
convertSubExpr (Dot e x) =
|
2019-03-08 02:03:35 +01:00
|
|
|
case subExprType of
|
|
|
|
|
Struct _ _ _ ->
|
|
|
|
|
if Map.notMember structTf structs
|
2019-03-23 00:24:45 +01:00
|
|
|
then (fieldType, Dot e' x)
|
2019-03-08 02:03:35 +01:00
|
|
|
else (fieldType, Range e' r)
|
2019-03-23 00:24:45 +01:00
|
|
|
_ -> (Implicit Unspecified [], Dot e' x)
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(subExprType, e') = convertSubExpr e
|
|
|
|
|
Struct p fields [] = subExprType
|
|
|
|
|
structTf = Struct p fields
|
|
|
|
|
fieldType = lookupFieldType fields x
|
|
|
|
|
r = lookupUnstructRange structTf x
|
|
|
|
|
convertSubExpr (Range eOuter (rOuter @ (hiO, loO))) =
|
|
|
|
|
-- VCS doesn't allow ranges to be cascaded, so we need to combine
|
|
|
|
|
-- nested Ranges into a single range. My understanding of the
|
|
|
|
|
-- semantics are that a range return a new, zero-indexed sub-range.
|
|
|
|
|
case eOuter' of
|
|
|
|
|
Range eInner (hiI, loI) ->
|
|
|
|
|
(t, Range eInner (simplify hi, simplify lo))
|
|
|
|
|
where
|
|
|
|
|
hi = BinOp Add (BinOp Sub hiI loI) hiO
|
|
|
|
|
lo = BinOp Add loI loO
|
|
|
|
|
_ -> (t, Range eOuter' rOuter)
|
|
|
|
|
where (t, eOuter') = convertSubExpr eOuter
|
|
|
|
|
convertSubExpr (Concat exprs) =
|
2019-03-22 21:57:13 +01:00
|
|
|
(Implicit Unspecified [], Concat $ map (snd . convertSubExpr) exprs)
|
2019-03-06 06:51:09 +01:00
|
|
|
convertSubExpr (BinOp op e1 e2) =
|
2019-03-22 21:57:13 +01:00
|
|
|
(Implicit Unspecified [], BinOp op e1' e2')
|
2019-03-06 06:51:09 +01:00
|
|
|
where
|
|
|
|
|
(_, e1') = convertSubExpr e1
|
|
|
|
|
(_, e2') = convertSubExpr e2
|
|
|
|
|
-- TODO: There are other expression cases that we probably need to
|
|
|
|
|
-- recurse into. That said, it's not clear to me how much we really
|
|
|
|
|
-- expect to see things like concatenated packed structs, for example.
|
2019-03-22 21:57:13 +01:00
|
|
|
convertSubExpr other = (Implicit Unspecified [], other)
|
2019-03-06 06:51:09 +01:00
|
|
|
|
|
|
|
|
-- lookup the range of a field in its unstructured type
|
|
|
|
|
lookupUnstructRange :: TypeFunc -> Identifier -> Range
|
|
|
|
|
lookupUnstructRange structTf fieldName =
|
|
|
|
|
fieldRangeMap Map.! fieldName
|
|
|
|
|
where fieldRangeMap = Map.map fst $ snd $ structs Map.! structTf
|
|
|
|
|
|
|
|
|
|
-- lookup the type of a field in the given field list
|
|
|
|
|
lookupFieldType :: [(Type, Identifier)] -> Identifier -> Type
|
|
|
|
|
lookupFieldType fields fieldName = fieldMap Map.! fieldName
|
|
|
|
|
where fieldMap = Map.fromList $ map swap fields
|