Enum.hs 5.85 KB
Newer Older
1 2 3 4 5
{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 -
 - Conversion for `enum`
 -
6 7 8
 - This conversion replaces the enum items with localparams declared within any
 - modules in which that enum type appears. This is not necessarily foolproof,
 - as some tools do allow the use of an enum item even if the actual enum type
9
 - does not appear in that description. The localparams are explicitly sized to
10
 - match the size of the converted enum type. This conversion includes only enum
11
 - items which are actually used within a given description.
12 13 14 15 16 17 18 19
 -
 - SystemVerilog allows for enums to have any number of the items' values
 - specified or unspecified. If the first one is unspecified, it is 0. All other
 - values take on the value of the previous item, plus 1.
 -
 - It is an error for multiple items of the same enum to take on the same value,
 - whether implicitly or explicitly. We catch try to catch "obvious" instances
 - of conflicts.
20 21 22 23 24
 -}

module Convert.Enum (convert) where

import Control.Monad.Writer
25
import Data.List (elemIndices, partition, sortOn)
26 27 28 29 30
import qualified Data.Set as Set

import Convert.Traverse
import Language.SystemVerilog.AST

31
type EnumInfo = (Maybe Range, [(Identifier, Maybe Expr)])
32
type Enums = Set.Set EnumInfo
33
type Idents = Set.Set Identifier
34
type EnumItem = ((Maybe Range, Identifier), Expr)
35

36 37
convert :: [AST] -> [AST]
convert = map $ traverseDescriptions convertDescription
38 39

defaultType :: Type
40
defaultType = IntegerVector TLogic Unspecified [(Number "31", Number "0")]
41 42

convertDescription :: Description -> Description
43 44
convertDescription (description @ Part{}) =
    Part attrs extern kw lifetime name ports (enumItems ++ items)
45
    where
46
        -- replace and collect the enum types in this description
47
        (Part attrs extern kw lifetime name ports items, enumPairs) =
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
            convertDescription' description
        -- convert the collected enums into their corresponding localparams
        enumItems = map MIPackageItem $ map toItem $ sortOn snd $ convergeUsage items enumPairs
convertDescription (description @ (Package _ _ _)) =
    Package ml name (items ++ enumItems)
    where
        -- replace and collect the enum types in this description
        (Package ml name items, enumPairs) =
            convertDescription' description
        -- convert the collected enums into their corresponding localparams
        enumItems = map toItem $ sortOn snd $ enumPairs
convertDescription other = other

-- replace and collect the enum types in a description
convertDescription' :: Description -> (Description, [EnumItem])
convertDescription' description =
    (description', enumPairs)
    where
        -- replace and collect the enum types in this description
        (description', enums) =
68 69
            runWriter $
            traverseModuleItemsM (traverseTypesM traverseType) $
70
            traverseModuleItems (traverseExprs $ traverseNestedExprs traverseExpr) $
71
            description
72
        -- convert the collected enums into their corresponding localparams
73
        enumPairs = concatMap enumVals $ Set.toList enums
74

75 76 77 78 79 80 81 82 83
-- add only the enums actually used in the given items
convergeUsage :: [ModuleItem] -> [EnumItem] -> [EnumItem]
convergeUsage items enums =
    if null usedEnums
        then []
        else usedEnums ++ convergeUsage (enumItems ++ items) unusedEnums
    where
        -- determine which of the enum items are actually used here
        (usedEnums, unusedEnums) = partition isUsed enums
84
        enumItems = map MIPackageItem $ map toItem usedEnums
85 86 87 88 89 90 91
        isUsed ((_, x), _) = Set.member x usedIdents
        usedIdents = execWriter $
            mapM (collectExprsM $ collectNestedExprsM collectIdent) $ items
        collectIdent :: Expr -> Writer Idents ()
        collectIdent (Ident x) = tell $ Set.singleton x
        collectIdent _ = return ()

92
toItem :: EnumItem -> PackageItem
93
toItem ((mr, x), v) =
94
    Decl $ Param Localparam itemType x v'
95
    where
96 97
        v' = if mr == Nothing
                then simplify v
98
                else sizedExpr x (rangeSize r) (simplify v)
99 100 101 102 103 104 105
        rs = maybe [] (\a -> [a]) mr
        r = defaultRange mr
        itemType = Implicit Unspecified rs

defaultRange :: Maybe Range -> Range
defaultRange Nothing = (Number "0", Number "0")
defaultRange (Just r) = r
106

107 108 109 110
toBaseType :: Maybe Type -> Type
toBaseType Nothing = defaultType
toBaseType (Just (Implicit _ rs)) =
    fst (typeRanges defaultType) rs
111
toBaseType (Just t @ (Alias _ _ _)) = t
112 113 114 115 116 117
toBaseType (Just t) =
    if null rs
        then tf [(Number "0", Number "0")]
        else t
    where (tf, rs) = typeRanges t

118 119
-- replace, but write down, enum types
traverseType :: Type -> Writer Enums Type
120 121
traverseType (Enum t v rs) = do
    let baseType = toBaseType t
122 123 124 125 126 127 128 129
    let (tf, rl) = typeRanges baseType
    mr <- return $ case rl of
        [] -> Nothing
        [r] -> Just r
        _ -> error $ "unexpected multi-dim enum type: "
                    ++ show (Enum t v rs)
    () <- tell $ Set.singleton (fmap simplifyRange mr, v)
    return $ tf (rl ++ rs)
130 131
traverseType other = return other

Zachary Snow committed
132 133 134
simplifyRange :: Range -> Range
simplifyRange (a, b) = (simplify a, simplify b)

135 136 137
-- drop any enum type casts in favor of implicit conversion from the
-- converted type
traverseExpr :: Expr -> Expr
138
traverseExpr (Cast (Left (IntegerVector _ _ _)) e) = e
139 140 141
traverseExpr (Cast (Left (Enum _ _ _)) e) = e
traverseExpr other = other

142
enumVals :: EnumInfo -> [EnumItem]
143
enumVals (mr, l) =
144 145 146
    -- check for obviously duplicate values
    if noDuplicates
        then res
147 148
        else error $ "enum conversion has duplicate vals: "
                ++ show (zip keys vals)
149
    where
150 151
        keys = map fst l
        vals = tail $ scanl step (Number "-1") (map snd l)
152
        res = zip (zip (repeat mr) keys) vals
153
        noDuplicates = all (null . tail . flip elemIndices vals) vals
154 155 156
        step :: Expr -> Maybe Expr -> Expr
        step _ (Just expr) = expr
        step expr Nothing =
157
            simplify $ BinOp Add expr (Number "1")