2019-02-22 02:12:34 +01:00
|
|
|
{- sv2v
|
|
|
|
|
- Author: Zachary Snow <zach@zachjs.com>
|
|
|
|
|
-
|
|
|
|
|
- Conversion for flattening multi-dimensional packed arrays
|
|
|
|
|
-
|
2019-02-28 06:16:53 +01:00
|
|
|
- This removes one dimension per identifier at a time. This works fine because
|
|
|
|
|
- the conversions are repeatedly applied.
|
2019-02-22 02:12:34 +01:00
|
|
|
-
|
2019-04-09 03:28:33 +02:00
|
|
|
- We previously had a very complex conversion which used `generate` to make
|
|
|
|
|
- flattened and unflattened versions of the array as necessary. This has now
|
|
|
|
|
- been "simplified" to always flatten the array, and then rewrite all usages of
|
|
|
|
|
- the array as appropriate.
|
2019-03-01 01:48:58 +01:00
|
|
|
-
|
2019-04-09 03:28:33 +02:00
|
|
|
- Note that the ranges being combined may not be of the form [hi:lo], and need
|
2019-04-18 02:05:55 +02:00
|
|
|
- not even be the same direction! Because of this, we have to flip around the
|
|
|
|
|
- indices of certain accesses.
|
2019-02-22 02:12:34 +01:00
|
|
|
-}
|
|
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
module Convert.PackedArray (convert) where
|
2019-02-22 02:12:34 +01:00
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
import Control.Monad.State
|
2019-04-05 01:40:19 +02:00
|
|
|
import Data.Tuple (swap)
|
2019-02-22 02:12:34 +01:00
|
|
|
import qualified Data.Map.Strict as Map
|
2019-04-18 02:05:55 +02:00
|
|
|
import qualified Data.Set as Set
|
2019-02-22 02:12:34 +01:00
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
import Convert.Traverse
|
2019-02-22 02:12:34 +01:00
|
|
|
import Language.SystemVerilog.AST
|
|
|
|
|
|
2019-04-18 02:05:55 +02:00
|
|
|
type DimMap = Map.Map Identifier [Range]
|
|
|
|
|
type IdentSet = Set.Set Identifier
|
2019-03-01 01:48:58 +01:00
|
|
|
|
|
|
|
|
data Info = Info
|
|
|
|
|
{ sTypeDims :: DimMap
|
2019-04-18 02:05:55 +02:00
|
|
|
, sIdents :: IdentSet
|
2019-04-09 03:28:33 +02:00
|
|
|
} deriving Show
|
2019-02-22 02:12:34 +01:00
|
|
|
|
2019-04-18 02:05:55 +02:00
|
|
|
defaultInfo :: Info
|
|
|
|
|
defaultInfo = Info Map.empty Set.empty
|
|
|
|
|
|
2019-02-22 02:12:34 +01:00
|
|
|
convert :: AST -> AST
|
2019-02-28 06:16:53 +01:00
|
|
|
convert = traverseDescriptions convertDescription
|
2019-02-22 02:12:34 +01:00
|
|
|
|
|
|
|
|
convertDescription :: Description -> Description
|
2019-03-31 22:43:19 +02:00
|
|
|
convertDescription (description @ (Part _ _ _ _ _ _)) =
|
2019-04-18 02:05:55 +02:00
|
|
|
traverseModuleItems (convertModuleItem info) description
|
2019-02-22 02:12:34 +01:00
|
|
|
where
|
2019-04-18 02:05:55 +02:00
|
|
|
collector = collectModuleItemsM $ collectDeclsM' ExcludeTFs collectDecl
|
|
|
|
|
info = execState (collector description) defaultInfo
|
2019-03-01 01:48:58 +01:00
|
|
|
convertDescription description = description
|
2019-02-22 02:12:34 +01:00
|
|
|
|
2019-04-18 02:05:55 +02:00
|
|
|
-- collects packed-array dimension and variable existing info into the state
|
|
|
|
|
collectDecl :: Decl -> State Info ()
|
|
|
|
|
collectDecl (Variable _ t ident _ _) = do
|
|
|
|
|
Info typeDims idents <- get
|
|
|
|
|
let (_, rs) = typeRanges t
|
|
|
|
|
let typeDims' =
|
|
|
|
|
if not (isImplicit t) && length rs > 1
|
|
|
|
|
then Map.insert ident rs typeDims
|
|
|
|
|
else typeDims
|
|
|
|
|
let idents' =
|
|
|
|
|
if not (isImplicit t)
|
|
|
|
|
then
|
|
|
|
|
if Set.member ident idents
|
|
|
|
|
then error $ "unsupported complex shadowing of " ++ show ident
|
|
|
|
|
else Set.insert ident idents
|
|
|
|
|
else idents
|
|
|
|
|
put $ Info typeDims' idents'
|
|
|
|
|
where
|
|
|
|
|
isImplicit :: Type -> Bool
|
|
|
|
|
isImplicit (Implicit _ _) = True
|
|
|
|
|
isImplicit _ = False
|
2019-02-28 06:16:53 +01:00
|
|
|
collectDecl _ = return ()
|
|
|
|
|
|
2019-04-18 02:05:55 +02:00
|
|
|
-- shadows the latter info with the former
|
|
|
|
|
combineInfo :: Info -> Info -> Info
|
|
|
|
|
combineInfo local global =
|
|
|
|
|
Info typeDims idents
|
2019-03-31 22:43:19 +02:00
|
|
|
where
|
2019-04-18 02:05:55 +02:00
|
|
|
Info globalTypeDims globalIdents = global
|
|
|
|
|
Info localTypeDims localIdents = local
|
|
|
|
|
idents = Set.union globalIdents localIdents
|
|
|
|
|
typeDims = Map.union localTypeDims $
|
|
|
|
|
Map.withoutKeys globalTypeDims localIdents
|
|
|
|
|
|
|
|
|
|
-- Convert the multi-dimensional packed arrays within the given module item.
|
|
|
|
|
-- This function must ensure that function/task level shadowing is respected.
|
|
|
|
|
convertModuleItem :: Info -> ModuleItem -> ModuleItem
|
|
|
|
|
convertModuleItem globalInfo (orig @ (MIPackageItem (Function ml t x decls stmts))) =
|
|
|
|
|
rewrite info $
|
|
|
|
|
MIPackageItem $ Function ml t' x decls stmts
|
|
|
|
|
where
|
|
|
|
|
localInfo =
|
|
|
|
|
execState (collectDecl $ Variable Local t x [] Nothing) $
|
|
|
|
|
execState (collectDeclsM collectDecl orig) $
|
|
|
|
|
defaultInfo
|
|
|
|
|
info = combineInfo localInfo globalInfo
|
|
|
|
|
-- rewrite the return type of this function
|
|
|
|
|
Variable Local t' _ [] Nothing =
|
|
|
|
|
flattenDecl info $ Variable Local t x [] Nothing
|
|
|
|
|
convertModuleItem globalInfo (orig @ (MIPackageItem (Task ml x decls stmts))) =
|
|
|
|
|
rewrite info $
|
|
|
|
|
MIPackageItem $ Task ml x decls stmts
|
2019-03-31 22:43:19 +02:00
|
|
|
where
|
2019-04-18 02:05:55 +02:00
|
|
|
localInfo =
|
|
|
|
|
execState (collectDeclsM collectDecl orig) $
|
|
|
|
|
defaultInfo
|
|
|
|
|
info = combineInfo localInfo globalInfo
|
|
|
|
|
convertModuleItem info other =
|
|
|
|
|
rewrite info other
|
|
|
|
|
|
|
|
|
|
-- combine the leading two packed ranges of a declaration
|
|
|
|
|
flattenDecl :: Info -> Decl -> Decl
|
|
|
|
|
flattenDecl info (origDecl @ (Variable dir t ident a me)) =
|
2019-04-09 03:28:33 +02:00
|
|
|
if Map.notMember ident typeDims
|
|
|
|
|
then origDecl
|
|
|
|
|
else flatDecl
|
2019-02-28 06:16:53 +01:00
|
|
|
where
|
2019-04-18 02:05:55 +02:00
|
|
|
typeDims = sTypeDims info
|
2019-03-01 01:48:58 +01:00
|
|
|
(tf, rs) = typeRanges t
|
2019-04-18 02:05:55 +02:00
|
|
|
r1 : r2 : rest = rs
|
|
|
|
|
rs' = (combineRanges r1 r2) : rest
|
|
|
|
|
flatDecl = Variable dir (tf rs') ident a me
|
|
|
|
|
flattenDecl _ other = other
|
|
|
|
|
|
|
|
|
|
-- combines two ranges into one flattened range
|
|
|
|
|
combineRanges :: Range -> Range -> Range
|
|
|
|
|
combineRanges r1 r2 = r
|
2019-02-22 02:12:34 +01:00
|
|
|
where
|
2019-04-18 02:05:55 +02:00
|
|
|
rYY = combine r1 r2
|
|
|
|
|
rYN = combine r1 (swap r2)
|
|
|
|
|
rNY = combine (swap r1) r2
|
|
|
|
|
rNN = combine (swap r1) (swap r2)
|
2019-04-05 19:53:52 +02:00
|
|
|
rY = endianCondRange r2 rYY rYN
|
|
|
|
|
rN = endianCondRange r2 rNY rNN
|
2019-04-09 03:28:33 +02:00
|
|
|
r = endianCondRange r1 rY rN
|
2019-04-05 01:40:19 +02:00
|
|
|
|
2019-04-18 02:05:55 +02:00
|
|
|
combine :: Range -> Range -> Range
|
|
|
|
|
combine (s1, e1) (s2, e2) =
|
|
|
|
|
(simplify upper, simplify lower)
|
|
|
|
|
where
|
|
|
|
|
size1 = rangeSize (s1, e1)
|
|
|
|
|
size2 = rangeSize (s2, e2)
|
|
|
|
|
lower = BinOp Add e2 (BinOp Mul e1 size2)
|
|
|
|
|
upper = BinOp Add (BinOp Mul size1 size2)
|
|
|
|
|
(BinOp Sub lower (Number "1"))
|
2019-02-22 02:12:34 +01:00
|
|
|
|
2019-04-18 02:05:55 +02:00
|
|
|
-- rewrite the declarations, expressions, and lvals in a module item to remove
|
|
|
|
|
-- the packed array dimensions captured in the given info
|
|
|
|
|
rewrite :: Info -> ModuleItem -> ModuleItem
|
|
|
|
|
rewrite info =
|
|
|
|
|
traverseDecls (flattenDecl info) .
|
2019-04-09 03:28:33 +02:00
|
|
|
traverseLHSs (traverseNestedLHSs rewriteLHS ) .
|
2019-03-18 19:27:14 +01:00
|
|
|
traverseExprs (traverseNestedExprs rewriteExpr)
|
2019-02-22 02:12:34 +01:00
|
|
|
where
|
2019-04-18 02:05:55 +02:00
|
|
|
typeDims = sTypeDims info
|
2019-04-09 03:28:33 +02:00
|
|
|
|
|
|
|
|
dims :: Identifier -> (Range, Range)
|
|
|
|
|
dims x =
|
|
|
|
|
(dimInner, dimOuter)
|
|
|
|
|
where
|
2019-04-18 02:05:55 +02:00
|
|
|
dimInner : dimOuter : _ = typeDims Map.! x
|
2019-03-01 01:48:58 +01:00
|
|
|
|
2019-04-18 02:05:55 +02:00
|
|
|
-- if the given range is flipped, the result will flip around the given
|
|
|
|
|
-- indexing expression
|
2019-04-09 03:28:33 +02:00
|
|
|
orientIdx :: Range -> Expr -> Expr
|
|
|
|
|
orientIdx r e =
|
|
|
|
|
endianCondExpr r e eSwapped
|
|
|
|
|
where
|
|
|
|
|
eSwapped = BinOp Sub (snd r) (BinOp Sub e (fst r))
|
2019-02-28 23:12:37 +01:00
|
|
|
|
2019-04-18 02:05:55 +02:00
|
|
|
-- Converted idents are prefixed with an invalid character to ensure
|
|
|
|
|
-- that are not converted twice when the traversal steps downward. When
|
|
|
|
|
-- the prefixed identifier is encountered at the lowest level, it is
|
|
|
|
|
-- removed.
|
|
|
|
|
|
|
|
|
|
tag = ':'
|
|
|
|
|
|
2019-02-28 23:12:37 +01:00
|
|
|
rewriteExpr :: Expr -> Expr
|
2019-04-09 03:28:33 +02:00
|
|
|
rewriteExpr (Ident x) =
|
2019-04-18 02:05:55 +02:00
|
|
|
if head x == tag
|
2019-04-09 03:28:33 +02:00
|
|
|
then Ident $ tail x
|
|
|
|
|
else Ident x
|
|
|
|
|
rewriteExpr (orig @ (Bit (Bit (Ident x) idxInner) idxOuter)) =
|
|
|
|
|
if Map.member x typeDims
|
|
|
|
|
then Bit (Ident x') idx'
|
|
|
|
|
else orig
|
2019-03-31 22:43:19 +02:00
|
|
|
where
|
2019-04-09 03:28:33 +02:00
|
|
|
(dimInner, dimOuter) = dims x
|
2019-04-18 02:05:55 +02:00
|
|
|
x' = tag : x
|
2019-04-09 03:28:33 +02:00
|
|
|
idxInner' = orientIdx dimInner idxInner
|
|
|
|
|
idxOuter' = orientIdx dimOuter idxOuter
|
|
|
|
|
base = BinOp Mul idxInner' (rangeSize dimOuter)
|
|
|
|
|
idx' = simplify $ BinOp Add base idxOuter'
|
|
|
|
|
rewriteExpr (orig @ (Bit (Ident x) idx)) =
|
|
|
|
|
if Map.member x typeDims
|
|
|
|
|
then Range (Ident x') mode' range'
|
|
|
|
|
else orig
|
2019-03-01 01:48:58 +01:00
|
|
|
where
|
2019-04-09 03:28:33 +02:00
|
|
|
(dimInner, dimOuter) = dims x
|
2019-04-18 02:05:55 +02:00
|
|
|
x' = tag : x
|
2019-04-09 03:28:33 +02:00
|
|
|
mode' = IndexedPlus
|
|
|
|
|
idx' = orientIdx dimInner idx
|
|
|
|
|
len = rangeSize dimOuter
|
|
|
|
|
base = BinOp Add (endianCondExpr dimOuter (snd dimOuter) (fst dimOuter)) (BinOp Mul idx' len)
|
|
|
|
|
range' = (simplify base, simplify len)
|
|
|
|
|
rewriteExpr (orig @ (Range (Ident x) mode range)) =
|
|
|
|
|
if Map.member x typeDims
|
|
|
|
|
then Range (Ident x') mode' range'
|
|
|
|
|
else orig
|
|
|
|
|
where
|
|
|
|
|
(_, dimOuter) = dims x
|
2019-04-18 02:05:55 +02:00
|
|
|
x' = tag : x
|
2019-04-09 03:28:33 +02:00
|
|
|
mode' = mode
|
|
|
|
|
size = rangeSize dimOuter
|
2019-04-11 23:30:29 +02:00
|
|
|
base = endianCondExpr dimOuter (snd dimOuter) (fst dimOuter)
|
2019-04-09 03:28:33 +02:00
|
|
|
range' =
|
|
|
|
|
case mode of
|
|
|
|
|
NonIndexed ->
|
|
|
|
|
(simplify hi, simplify lo)
|
|
|
|
|
where
|
|
|
|
|
lo = BinOp Mul size (snd range)
|
|
|
|
|
hi = BinOp Sub (BinOp Add lo (BinOp Mul (rangeSize range) size)) (Number "1")
|
2019-04-11 23:30:29 +02:00
|
|
|
IndexedPlus -> (BinOp Add (BinOp Mul size (fst range)) base, BinOp Mul size (snd range))
|
|
|
|
|
IndexedMinus -> (BinOp Add (BinOp Mul size (fst range)) base, BinOp Mul size (snd range))
|
2019-04-12 00:29:30 +02:00
|
|
|
rewriteExpr (orig @ (Range (Bit (Ident x) idxInner) modeOuter rangeOuter)) =
|
|
|
|
|
if Map.member x typeDims
|
|
|
|
|
then Range (Ident x') mode' range'
|
|
|
|
|
else orig
|
|
|
|
|
where
|
|
|
|
|
(dimInner, dimOuter) = dims x
|
2019-04-18 02:05:55 +02:00
|
|
|
x' = tag : x
|
2019-04-12 00:29:30 +02:00
|
|
|
mode' = IndexedPlus
|
|
|
|
|
idxInner' = orientIdx dimInner idxInner
|
|
|
|
|
rangeOuterReverseIndexed =
|
|
|
|
|
(BinOp Add (fst rangeOuter) (BinOp Sub (snd rangeOuter)
|
|
|
|
|
(Number "1")), snd rangeOuter)
|
|
|
|
|
(baseOuter, lenOuter) =
|
|
|
|
|
case modeOuter of
|
|
|
|
|
IndexedPlus ->
|
|
|
|
|
endianCondRange dimOuter rangeOuter rangeOuterReverseIndexed
|
|
|
|
|
IndexedMinus ->
|
|
|
|
|
endianCondRange dimOuter rangeOuterReverseIndexed rangeOuter
|
|
|
|
|
NonIndexed ->
|
|
|
|
|
(endianCondExpr dimOuter (snd rangeOuter) (fst rangeOuter), rangeSize rangeOuter)
|
|
|
|
|
idxOuter' = orientIdx dimOuter baseOuter
|
|
|
|
|
start = BinOp Mul idxInner' (rangeSize dimOuter)
|
|
|
|
|
base = simplify $ BinOp Add start idxOuter'
|
|
|
|
|
len = lenOuter
|
|
|
|
|
range' = (base, len)
|
2019-02-28 23:12:37 +01:00
|
|
|
rewriteExpr other = other
|
|
|
|
|
|
2019-04-18 02:05:55 +02:00
|
|
|
-- LHSs need to be converted too. Rather than duplicating the
|
|
|
|
|
-- procedures, we turn the relevant LHSs into expressions temporarily
|
|
|
|
|
-- and use the expression conversion written above.
|
2019-03-01 00:04:34 +01:00
|
|
|
rewriteLHS :: LHS -> LHS
|
2019-04-09 03:28:33 +02:00
|
|
|
rewriteLHS (LHSIdent x) =
|
|
|
|
|
LHSIdent x'
|
|
|
|
|
where Ident x' = rewriteExpr (Ident x)
|
|
|
|
|
rewriteLHS (orig @ (LHSBit (LHSBit (LHSIdent x) idxInner) idxOuter)) =
|
|
|
|
|
if Map.member x typeDims
|
|
|
|
|
then LHSBit (LHSIdent x') idx'
|
|
|
|
|
else orig
|
|
|
|
|
where Bit (Ident x') idx' =
|
|
|
|
|
rewriteExpr (Bit (Bit (Ident x) idxInner) idxOuter)
|
|
|
|
|
rewriteLHS (orig @ (LHSBit (LHSRange (LHSIdent x) modeInner rangeInner) idxOuter)) =
|
|
|
|
|
if Map.member x typeDims
|
|
|
|
|
then LHSRange (LHSIdent x') mode' range'
|
|
|
|
|
else orig
|
|
|
|
|
where Range (Ident x') mode' range' =
|
|
|
|
|
rewriteExpr (Bit (Range (Ident x) modeInner rangeInner) idxOuter)
|
|
|
|
|
rewriteLHS (orig @ (LHSBit (LHSIdent x) idx)) =
|
|
|
|
|
if Map.member x typeDims
|
|
|
|
|
then LHSRange (LHSIdent x') mode' range'
|
|
|
|
|
else orig
|
|
|
|
|
where Range (Ident x') mode' range' = rewriteExpr (Bit (Ident x) idx)
|
|
|
|
|
rewriteLHS (orig @ (LHSRange (LHSIdent x) mode range)) =
|
|
|
|
|
if Map.member x typeDims
|
|
|
|
|
then LHSRange (LHSIdent x') mode' range'
|
|
|
|
|
else orig
|
|
|
|
|
where Range (Ident x') mode' range' =
|
|
|
|
|
rewriteExpr (Range (Ident x) mode range)
|
|
|
|
|
rewriteLHS (orig @ (LHSRange (LHSBit (LHSIdent x) idxInner) modeOuter rangeOuter)) =
|
|
|
|
|
if Map.member x typeDims
|
|
|
|
|
then LHSRange (LHSIdent x') mode' range'
|
|
|
|
|
else orig
|
|
|
|
|
where Range (Ident x') mode' range' =
|
|
|
|
|
rewriteExpr (Range (Bit (Ident x) idxInner) modeOuter rangeOuter)
|
|
|
|
|
rewriteLHS other = other
|