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-03-01 01:48:58 +01:00
|
|
|
- Packed arrays can be used in any of the following ways: A) as a whole,
|
|
|
|
|
- including as a port; B) with an index (`foo[0]`); or C) with a range
|
|
|
|
|
- (`foo[10:0]`). The rules for this conversion are:
|
|
|
|
|
- 1. If used with an index, then we must have an unflattened/unpacked
|
|
|
|
|
- version of that array after the conversion, so that we may get at the
|
|
|
|
|
- packed sub-arrays.
|
|
|
|
|
- 2. If used as a whole or with a range, then we must have a flattened
|
|
|
|
|
- version of that array after the conversion, so that we may get at a
|
|
|
|
|
- contiguous sequence of elements.
|
|
|
|
|
- 3. If both 1 and 2 apply, then we will make a fancy generate block to
|
|
|
|
|
- derive one from the other. The derivation direction is decided based on
|
|
|
|
|
- which version, if any, is exposed directly as a port.
|
|
|
|
|
-
|
2019-02-28 06:16:53 +01:00
|
|
|
- TODO: This assumes that the first range index is the upper bound. We could
|
2019-03-01 00:04:34 +01:00
|
|
|
- probably get around this with some cleverness in the generate block. I don't
|
|
|
|
|
- think it's urgent to have support for "backwards" ranges.
|
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
|
|
|
|
|
import Data.List (partition)
|
2019-03-01 01:48:58 +01:00
|
|
|
import qualified Data.Set as Set
|
2019-02-22 02:12:34 +01:00
|
|
|
import qualified Data.Map.Strict as Map
|
|
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
import Convert.Traverse
|
2019-02-22 02:12:34 +01:00
|
|
|
import Language.SystemVerilog.AST
|
|
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
type DirMap = Map.Map Identifier Direction
|
2019-02-22 02:12:34 +01:00
|
|
|
type DimMap = Map.Map Identifier (Type, Range)
|
2019-03-01 01:48:58 +01:00
|
|
|
type IdentSet = Set.Set Identifier
|
|
|
|
|
|
|
|
|
|
data Info = Info
|
|
|
|
|
{ sTypeDims :: DimMap
|
|
|
|
|
, sPortDirs :: DirMap
|
|
|
|
|
, sIdxUses :: IdentSet
|
|
|
|
|
, sSeqUses :: IdentSet }
|
|
|
|
|
deriving Show
|
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-04 08:58:00 +01:00
|
|
|
convertDescription (description @ (Part _ _ ports _)) =
|
2019-02-28 06:16:53 +01:00
|
|
|
hoistPortDecls $
|
2019-03-01 01:48:58 +01:00
|
|
|
traverseModuleItems (flattenModuleItem info . rewriteModuleItem info) description
|
2019-02-22 02:12:34 +01:00
|
|
|
where
|
2019-03-01 01:48:58 +01:00
|
|
|
-- collect all possible information info our Info structure
|
|
|
|
|
rawInfo =
|
|
|
|
|
execState (collectModuleItemsM (collectLHSsM collectLHS) description) $
|
|
|
|
|
execState (collectModuleItemsM (collectExprsM collectExpr) description) $
|
|
|
|
|
execState (collectModuleItemsM collectDecl description) $
|
|
|
|
|
(Info Map.empty Map.empty Set.empty (Set.fromList ports))
|
|
|
|
|
relevantIdents = Map.keysSet $ sTypeDims rawInfo
|
|
|
|
|
-- restrict the sets/maps to only contain keys which need transformation
|
|
|
|
|
info = rawInfo
|
|
|
|
|
{ sPortDirs = Map.restrictKeys (sPortDirs rawInfo) relevantIdents
|
|
|
|
|
, sIdxUses = Set.intersection (sIdxUses rawInfo) relevantIdents
|
|
|
|
|
, sSeqUses = Set.intersection (sSeqUses rawInfo) relevantIdents }
|
|
|
|
|
convertDescription description = description
|
2019-02-22 02:12:34 +01:00
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
-- collects port direction and packed-array dimension info into the state
|
2019-03-01 01:48:58 +01:00
|
|
|
collectDecl :: ModuleItem -> State Info ()
|
2019-02-28 06:16:53 +01:00
|
|
|
collectDecl (MIDecl (Variable dir t ident _ _)) = do
|
2019-03-01 01:48:58 +01:00
|
|
|
let (tf, rs) = typeRanges t
|
2019-03-01 00:04:34 +01:00
|
|
|
if not (typeIsImplicit t) && length rs > 1
|
2019-03-01 01:48:58 +01:00
|
|
|
then
|
|
|
|
|
let dets = (tf $ tail rs, head rs) in
|
|
|
|
|
modify $ \s -> s { sTypeDims = Map.insert ident dets (sTypeDims s) }
|
2019-02-28 06:16:53 +01:00
|
|
|
else return ()
|
|
|
|
|
if dir /= Local
|
2019-03-01 01:48:58 +01:00
|
|
|
then modify $ \s -> s { sPortDirs = Map.insert ident dir (sPortDirs s) }
|
2019-02-28 06:16:53 +01:00
|
|
|
else return ()
|
|
|
|
|
collectDecl _ = return ()
|
|
|
|
|
|
2019-03-01 01:48:58 +01:00
|
|
|
-- collectors for identifier usage information
|
|
|
|
|
recordSeqUsage :: Identifier -> State Info ()
|
|
|
|
|
recordSeqUsage i = modify $ \s -> s { sSeqUses = Set.insert i $ sSeqUses s }
|
|
|
|
|
recordIdxUsage :: Identifier -> State Info ()
|
|
|
|
|
recordIdxUsage i = modify $ \s -> s { sIdxUses = Set.insert i $ sIdxUses s }
|
|
|
|
|
collectExpr :: Expr -> State Info ()
|
2019-03-18 19:27:14 +01:00
|
|
|
collectExpr (Ident i) = recordSeqUsage i
|
|
|
|
|
collectExpr other = collectNestedExprsM collectNestedExpr other
|
|
|
|
|
collectNestedExpr :: Expr -> State Info ()
|
|
|
|
|
collectNestedExpr (Range (Ident i) _) = recordSeqUsage i
|
|
|
|
|
collectNestedExpr (Bit (Ident i) _) = recordIdxUsage i
|
|
|
|
|
collectNestedExpr _ = return ()
|
2019-03-01 01:48:58 +01:00
|
|
|
collectLHS :: LHS -> State Info ()
|
2019-03-18 19:27:14 +01:00
|
|
|
collectLHS (LHSIdent i) = recordSeqUsage i
|
|
|
|
|
collectLHS other = collectNestedLHSsM collectNestedLHS other
|
|
|
|
|
collectNestedLHS :: LHS -> State Info ()
|
|
|
|
|
collectNestedLHS (LHSRange (LHSIdent i) _) = recordSeqUsage i
|
|
|
|
|
collectNestedLHS (LHSBit (LHSIdent i) _) = recordIdxUsage i
|
|
|
|
|
collectNestedLHS _ = return ()
|
2019-03-01 01:48:58 +01:00
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
-- VCS doesn't like port declarations inside of `generate` blocks, so we hoist
|
|
|
|
|
-- them out with this function. This obviously isn't ideal, but it's a
|
|
|
|
|
-- relatively straightforward transformation, and testing in VCS is important.
|
|
|
|
|
hoistPortDecls :: Description -> Description
|
2019-03-04 08:58:00 +01:00
|
|
|
hoistPortDecls (Part kw name ports items) =
|
|
|
|
|
Part kw name ports (concat $ map explode items)
|
2019-02-22 23:21:16 +01:00
|
|
|
where
|
2019-02-28 06:16:53 +01:00
|
|
|
explode :: ModuleItem -> [ModuleItem]
|
|
|
|
|
explode (Generate genItems) =
|
2019-03-07 03:55:27 +01:00
|
|
|
if null rest
|
|
|
|
|
then portDecls
|
|
|
|
|
else portDecls ++ [Generate rest]
|
2019-02-28 06:16:53 +01:00
|
|
|
where
|
|
|
|
|
(wrappedPortDecls, rest) = partition isPortDecl genItems
|
|
|
|
|
portDecls = map (\(GenModuleItem item) -> item) wrappedPortDecls
|
|
|
|
|
isPortDecl :: GenItem -> Bool
|
|
|
|
|
isPortDecl (GenModuleItem (MIDecl (Variable dir _ _ _ _))) =
|
|
|
|
|
dir /= Local
|
|
|
|
|
isPortDecl _ = False
|
|
|
|
|
explode other = [other]
|
|
|
|
|
hoistPortDecls other = other
|
2019-02-22 23:21:16 +01:00
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
-- rewrite a module item if it contains a declaration to flatten
|
2019-03-01 01:48:58 +01:00
|
|
|
flattenModuleItem :: Info -> ModuleItem -> ModuleItem
|
|
|
|
|
flattenModuleItem info (origDecl @ (MIDecl (Variable dir t ident a me))) =
|
|
|
|
|
-- if it doesn't need any mapping, then skip it
|
|
|
|
|
if Map.notMember ident typeDims then origDecl
|
|
|
|
|
-- if it is never used as a sequence (whole or range), then move the packed
|
|
|
|
|
-- dimension to the unpacked side
|
|
|
|
|
else if Set.notMember ident seqUses then flipDecl
|
|
|
|
|
-- if it is used as a sequence, but never indexed-into (sub-array), then
|
|
|
|
|
-- flatten (combine) the ranges, leaving them packed
|
|
|
|
|
else if Set.notMember ident duoUses then flatDecl
|
|
|
|
|
-- if it is both used as a sequence and is indexed-into
|
2019-02-28 06:16:53 +01:00
|
|
|
else
|
2019-03-01 01:48:58 +01:00
|
|
|
-- if this is not the fully-typed declaration of this item, then flatten
|
|
|
|
|
-- it, but don't make the `generate` block this time to avoid duplicates
|
|
|
|
|
if typeIsImplicit t then flatDecl
|
|
|
|
|
-- otherwise, flatten it, and also create an unflattened copy
|
|
|
|
|
else Generate $ (GenModuleItem flatDecl) : genItems
|
2019-02-28 06:16:53 +01:00
|
|
|
where
|
2019-03-01 01:48:58 +01:00
|
|
|
Info typeDims portDirs idxUses seqUses = info
|
|
|
|
|
duoUses = Set.intersection idxUses seqUses
|
|
|
|
|
writeToFlatVariant = Map.lookup ident portDirs == Just Output
|
|
|
|
|
genItems = unflattener writeToFlatVariant ident (typeDims Map.! ident)
|
|
|
|
|
(tf, rs) = typeRanges t
|
|
|
|
|
flipDecl = MIDecl $ Variable dir (tf $ tail rs) ident (a ++ [head rs]) me
|
|
|
|
|
flatDecl = MIDecl $ Variable dir (tf $ flattenRanges rs) ident a me
|
2019-02-28 06:16:53 +01:00
|
|
|
flattenModuleItem _ other = other
|
|
|
|
|
|
2019-03-01 01:48:58 +01:00
|
|
|
-- produces `generate` items for creating an unflattened copy of the given
|
|
|
|
|
-- flattened, packed array
|
2019-02-28 06:16:53 +01:00
|
|
|
unflattener :: Bool -> Identifier -> (Type, Range) -> [GenItem]
|
2019-03-01 01:48:58 +01:00
|
|
|
unflattener writeToFlatVariant arr (t, (majorHi, majorLo)) =
|
2019-03-07 19:19:31 +01:00
|
|
|
[ GenModuleItem $ MIPackageItem $ Comment $ "sv2v packed-array-flatten unflattener for " ++ arr
|
2019-02-28 06:16:53 +01:00
|
|
|
, GenModuleItem $ MIDecl $ Variable Local t arrUnflat [(majorHi, majorLo)] Nothing
|
|
|
|
|
, GenModuleItem $ Genvar index
|
2019-02-24 09:06:40 +01:00
|
|
|
, GenModuleItem $ MIDecl $ Variable Local IntegerT (arrUnflat ++ "_repeater_index") [] Nothing
|
2019-02-22 02:12:34 +01:00
|
|
|
, GenFor
|
2019-02-22 23:21:16 +01:00
|
|
|
(index, majorLo)
|
|
|
|
|
(BinOp Le (Ident index) majorHi)
|
2019-03-05 00:25:14 +01:00
|
|
|
(index, AsgnOp Add, Number "1")
|
2019-03-08 22:03:29 +01:00
|
|
|
(Just $ prefix "unflatten")
|
2019-02-22 02:12:34 +01:00
|
|
|
[ localparam startBit
|
2019-02-22 23:21:16 +01:00
|
|
|
(simplify $ BinOp Add majorLo
|
|
|
|
|
(BinOp Mul (Ident index) size))
|
2019-02-22 02:12:34 +01:00
|
|
|
, GenModuleItem $ (uncurry Assign) $
|
2019-03-01 01:48:58 +01:00
|
|
|
if not writeToFlatVariant
|
2019-03-04 21:46:12 +01:00
|
|
|
then (LHSBit (LHSIdent arrUnflat) $ Ident index, Range (Ident arr) origRange)
|
|
|
|
|
else (LHSRange (LHSIdent arr) origRange, Bit (Ident arrUnflat) (Ident index))
|
2019-02-22 02:12:34 +01:00
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
where
|
2019-02-22 23:21:16 +01:00
|
|
|
startBit = prefix "_tmp_start"
|
2019-02-22 02:12:34 +01:00
|
|
|
arrUnflat = prefix arr
|
2019-02-22 23:21:16 +01:00
|
|
|
index = prefix "_tmp_index"
|
2019-03-01 01:48:58 +01:00
|
|
|
(minorHi, minorLo) = head $ snd $ typeRanges t
|
2019-03-06 06:51:09 +01:00
|
|
|
size = rangeSize (minorHi, minorLo)
|
2019-02-22 02:12:34 +01:00
|
|
|
localparam :: Identifier -> Expr -> GenItem
|
2019-02-24 09:06:40 +01:00
|
|
|
localparam x v = GenModuleItem $ MIDecl $ Localparam (Implicit []) x v
|
2019-02-22 02:12:34 +01:00
|
|
|
origRange = ( (BinOp Add (Ident startBit)
|
2019-02-22 23:21:16 +01:00
|
|
|
(BinOp Sub size (Number "1")))
|
2019-02-22 02:12:34 +01:00
|
|
|
, Ident startBit )
|
|
|
|
|
|
2019-03-01 00:04:34 +01:00
|
|
|
typeIsImplicit :: Type -> Bool
|
|
|
|
|
typeIsImplicit (Implicit _) = True
|
|
|
|
|
typeIsImplicit _ = False
|
|
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
-- prefix a string with a namespace of sorts
|
2019-02-22 02:12:34 +01:00
|
|
|
prefix :: Identifier -> Identifier
|
2019-02-22 23:21:16 +01:00
|
|
|
prefix ident = "_sv2v_" ++ ident
|
2019-02-22 02:12:34 +01:00
|
|
|
|
2019-02-28 06:16:53 +01:00
|
|
|
-- combines (flattens) the bottom two ranges in the given list of ranges
|
2019-02-22 02:12:34 +01:00
|
|
|
flattenRanges :: [Range] -> [Range]
|
|
|
|
|
flattenRanges rs =
|
|
|
|
|
if length rs >= 2
|
|
|
|
|
then rs'
|
|
|
|
|
else error $ "flattenRanges on too small list: " ++ (show rs)
|
|
|
|
|
where
|
|
|
|
|
(s1, e1) = head rs
|
|
|
|
|
(s2, e2) = head $ tail rs
|
2019-03-06 06:51:09 +01:00
|
|
|
size1 = rangeSize (s1, e1)
|
|
|
|
|
size2 = rangeSize (s2, e2)
|
2019-02-22 02:12:34 +01:00
|
|
|
upper = BinOp Add (BinOp Mul size1 size2) (BinOp Sub e1 (Number "1"))
|
2019-02-22 23:21:16 +01:00
|
|
|
r' = (simplify upper, e1)
|
2019-02-22 02:12:34 +01:00
|
|
|
rs' = (tail $ tail rs) ++ [r']
|
|
|
|
|
|
2019-03-01 01:48:58 +01:00
|
|
|
rewriteModuleItem :: Info -> ModuleItem -> ModuleItem
|
|
|
|
|
rewriteModuleItem info =
|
2019-02-28 23:12:37 +01:00
|
|
|
traverseStmts rewriteStmt .
|
2019-03-18 19:27:14 +01:00
|
|
|
traverseExprs (traverseNestedExprs rewriteExpr)
|
2019-02-22 02:12:34 +01:00
|
|
|
where
|
2019-03-07 05:25:02 +01:00
|
|
|
Info typeDims _ idxUses seqUses = info
|
2019-03-01 01:48:58 +01:00
|
|
|
duoUses = Set.intersection idxUses seqUses
|
|
|
|
|
|
|
|
|
|
rewriteIdent :: Bool -> Identifier -> Identifier
|
2019-03-07 05:25:02 +01:00
|
|
|
rewriteIdent isSeqUsage x =
|
|
|
|
|
if Set.member x duoUses
|
|
|
|
|
then
|
|
|
|
|
-- if an array is used both ways, then the original name is
|
|
|
|
|
-- the flattened version
|
|
|
|
|
if isSeqUsage
|
|
|
|
|
then x
|
|
|
|
|
else prefix x
|
2019-03-01 01:48:58 +01:00
|
|
|
else x
|
2019-03-07 05:25:02 +01:00
|
|
|
rewriteSeqIdent = rewriteIdent True
|
|
|
|
|
rewriteIdxIdent = rewriteIdent False
|
2019-02-28 23:12:37 +01:00
|
|
|
|
|
|
|
|
rewriteExpr :: Expr -> Expr
|
2019-03-07 05:25:02 +01:00
|
|
|
rewriteExpr (Ident i) = Ident $ rewriteSeqIdent i
|
|
|
|
|
rewriteExpr (Bit (Ident i) e) = Bit (Ident $ rewriteIdxIdent i) e
|
2019-03-04 21:46:12 +01:00
|
|
|
rewriteExpr (Range (Ident i) (r @ (s, e))) =
|
2019-03-01 01:48:58 +01:00
|
|
|
if Map.member i typeDims
|
2019-03-04 21:46:12 +01:00
|
|
|
then Range (Ident i) r'
|
|
|
|
|
else Range (Ident i) r
|
2019-03-01 01:48:58 +01:00
|
|
|
where
|
|
|
|
|
(a, b) = head $ snd $ typeRanges $ fst $ typeDims Map.! i
|
2019-03-06 06:51:09 +01:00
|
|
|
size = rangeSize (a, b)
|
2019-03-01 01:48:58 +01:00
|
|
|
s' = BinOp Sub (BinOp Mul size (BinOp Add s (Number "1"))) (Number "1")
|
|
|
|
|
e' = BinOp Mul size e
|
|
|
|
|
r' = (simplify s', simplify e')
|
2019-02-28 23:12:37 +01:00
|
|
|
rewriteExpr other = other
|
|
|
|
|
|
2019-03-01 00:04:34 +01:00
|
|
|
rewriteLHS :: LHS -> LHS
|
2019-03-07 05:25:02 +01:00
|
|
|
rewriteLHS (LHSIdent x ) = LHSIdent (rewriteSeqIdent x)
|
|
|
|
|
rewriteLHS (LHSBit (LHSIdent x) e) =
|
|
|
|
|
LHSBit (LHSIdent $ rewriteIdxIdent x) e
|
2019-03-04 21:16:53 +01:00
|
|
|
rewriteLHS (LHSBit l e) = LHSBit (rewriteLHS l) e
|
|
|
|
|
rewriteLHS (LHSRange l r) = LHSRange (rewriteLHS l) r
|
|
|
|
|
rewriteLHS (LHSDot l x) = LHSDot (rewriteLHS l) x
|
2019-03-01 00:04:34 +01:00
|
|
|
rewriteLHS (LHSConcat ls) = LHSConcat $ map rewriteLHS ls
|
|
|
|
|
|
2019-02-28 23:12:37 +01:00
|
|
|
rewriteStmt :: Stmt -> Stmt
|
2019-03-07 21:56:03 +01:00
|
|
|
rewriteStmt (AsgnBlk op lhs expr) = convertAssignment (AsgnBlk op) lhs expr
|
|
|
|
|
rewriteStmt (Asgn lhs expr) = convertAssignment Asgn lhs expr
|
2019-02-28 23:12:37 +01:00
|
|
|
rewriteStmt other = other
|
2019-02-22 02:12:34 +01:00
|
|
|
convertAssignment :: (LHS -> Expr -> Stmt) -> LHS -> Expr -> Stmt
|
2019-03-04 21:16:53 +01:00
|
|
|
convertAssignment constructor (lhs @ (LHSIdent ident)) (expr @ (Repeat _ exprs)) =
|
2019-03-01 01:48:58 +01:00
|
|
|
if Map.member ident typeDims
|
|
|
|
|
then For inir chkr incr assign
|
|
|
|
|
else constructor (rewriteLHS lhs) expr
|
|
|
|
|
where
|
|
|
|
|
(_, (a, b)) = typeDims Map.! ident
|
|
|
|
|
index = prefix $ ident ++ "_repeater_index"
|
|
|
|
|
assign = constructor
|
2019-03-04 21:16:53 +01:00
|
|
|
(LHSBit (LHSIdent $ prefix ident) (Ident index))
|
2019-03-01 01:48:58 +01:00
|
|
|
(Concat exprs)
|
|
|
|
|
inir = (index, b)
|
|
|
|
|
chkr = BinOp Le (Ident index) a
|
|
|
|
|
incr = (index, BinOp Add (Ident index) (Number "1"))
|
2019-02-22 02:12:34 +01:00
|
|
|
convertAssignment constructor lhs expr =
|
2019-03-01 00:04:34 +01:00
|
|
|
constructor (rewriteLHS lhs) expr
|