{- sv2v - Author: Zachary Snow <zach@zachjs.com> - - Conversion for `enum` - - This conversion replaces the enum items with localparams. The localparams are - explicitly sized to match the size of the converted enum type. For packages - and enums used in the global scope, these localparams are inserted in place. - For enums used within a module or interface, the localparams are injected as - needed using a nesting procedure from the package conversion. - - 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. -} module Convert.Enum (convert) where import Control.Monad.Writer.Strict import Data.List (elemIndices) import qualified Data.Set as Set import Convert.ExprUtils import Convert.Package (inject) import Convert.Traverse import Language.SystemVerilog.AST type EnumInfo = (Type, [(Identifier, Expr)]) type Enums = Set.Set EnumInfo convert :: [AST] -> [AST] convert = map $ concatMap convertDescription convertDescription :: Description -> [Description] convertDescription (Package ml name items) = [Package ml name $ concatMap convertPackageItem items] convertDescription (description @ Part{}) = [Part attrs extern kw lifetime name ports items'] where items' = inject enumItems items -- only keep what's used Part attrs extern kw lifetime name ports items = description' (description', enumItems) = convertDescription' description convertDescription (PackageItem item) = map PackageItem $ convertPackageItem item -- explode a package item with its corresponding enum items convertPackageItem :: PackageItem -> [PackageItem] convertPackageItem item = do item' : enumItems where (PackageItem item', enumItems) = convertDescription' $ PackageItem item -- replace and collect the enum types in a description convertDescription' :: Description -> (Description, [PackageItem]) convertDescription' description = (description', enumItems) where -- replace and collect the enum types in this description (description', enums) = runWriter $ traverseModuleItemsM traverseModuleItemM description traverseModuleItemM = traverseTypesM $ traverseNestedTypesM traverseType -- convert the collected enums into their corresponding localparams enumItems = concatMap makeEnumItems $ Set.toList enums -- replace, but write down, enum types traverseType :: Type -> Writer Enums Type traverseType (Enum (t @ Alias{}) v rs) = return $ Enum t v rs -- not ready traverseType (Enum (t @ PSAlias{}) v rs) = return $ Enum t v rs -- not ready traverseType (Enum (t @ CSAlias{}) v rs) = return $ Enum t v rs -- not ready traverseType (Enum (Implicit sg rl) v rs) = traverseType $ Enum t' v rs where -- default to a 32 bit logic t' = IntegerVector TLogic sg rl' rl' = if null rl then [(RawNum 31, RawNum 0)] else rl traverseType (Enum t v rs) = do let (tf, rl) = typeRanges t rlParam <- case rl of [ ] -> return [(RawNum 0, RawNum 0)] [_] -> return rl _ -> error $ "unexpected multi-dim enum type: " ++ show (Enum t v rs) tell $ Set.singleton (tf rlParam, v) -- type of localparams return $ tf (rl ++ rs) -- type of variables traverseType other = return other makeEnumItems :: EnumInfo -> [PackageItem] makeEnumItems (itemType, l) = -- check for obviously duplicate values if noDuplicates then zipWith toPackageItem keys vals else error $ "enum conversion has duplicate vals: " ++ show (zip keys vals) where keys = map fst l vals = tail $ scanl step (UniOp UniSub $ RawNum 1) (map snd l) noDuplicates = all (null . tail . flip elemIndices vals) vals step :: Expr -> Expr -> Expr step expr Nil = simplify $ BinOp Add expr (RawNum 1) step _ expr = expr toPackageItem :: Identifier -> Expr -> PackageItem toPackageItem x v = Decl $ Param Localparam itemType x v' where v' = simplify v