packages feed

sv2v-0.0.9: src/Convert/ParamNoDefault.hs

{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 -
 - Conversion for parameters without default values
 -
 - This conversion ensures that any parameters which don't have a default value
 - are always given an explicit value wherever that module or interface is used.
 -
 - Parameters are given a fake default value if they do not have one so that the
 - given source for that module can be consumed by downstream tools. This is not
 - done for type parameters, as those modules are rewritten by a separate
 - conversion. Localparams without defaults are expressly caught and forbidden.
 -}

module Convert.ParamNoDefault (convert) where

import Control.Monad.Writer.Strict
import Data.List (intercalate)
import qualified Data.Map.Strict as Map

import Convert.Traverse
import Language.SystemVerilog.AST

type Parts = Map.Map Identifier [(Identifier, Bool)]

convert :: [AST] -> [AST]
convert files =
    map convertFile files'
    where
        (files', parts) = runWriter $
            mapM (traverseDescriptionsM traverseDescriptionM) files
        convertFile = traverseDescriptions $ traverseModuleItems $
            traverseModuleItem parts

traverseDescriptionM :: Description -> Writer Parts Description
traverseDescriptionM (Part attrs extern kw lifetime name ports items) = do
    let (items', params) = runWriter $ mapM traverseModuleItemM items
    tell $ Map.singleton name params
    return $ Part attrs extern kw lifetime name ports items'
traverseDescriptionM other = return other

traverseModuleItemM :: ModuleItem -> Writer [(Identifier, Bool)] ModuleItem
traverseModuleItemM (MIAttr attr item) =
    traverseModuleItemM item >>= return . MIAttr attr
traverseModuleItemM (MIPackageItem (Decl decl)) =
    traverseDeclM decl >>= return . MIPackageItem . Decl
traverseModuleItemM other = return other

-- writes down the parameters for a part
traverseDeclM :: Decl -> Writer [(Identifier, Bool)] Decl
traverseDeclM (Param Localparam _ x Nil) =
    error $ "localparam " ++ show x ++ " has no default value"
traverseDeclM (Param Parameter t x e) = do
    tell [(x, e == Nil)]
    return $ if e == Nil
        then Param Parameter t x $ RawNum 0
        else Param Parameter t x e
traverseDeclM (ParamType Localparam x UnknownType) =
    error $ "localparam type " ++ show x ++ " has no default value"
traverseDeclM (ParamType Parameter x t) = do
    -- parameter types are rewritten separately, so no fake default here
    tell [(x, t == UnknownType)]
    return $ ParamType Parameter x t
traverseDeclM other = return other

-- check for instances missing values for parameters without defaults
traverseModuleItem :: Parts -> ModuleItem -> ModuleItem
traverseModuleItem parts orig@(Instance part params name _ _) =
    if maybePartInfo == Nothing || null missingParams
        then orig
        else error $ "instance " ++ show name ++ " of " ++ show part
                ++ " is missing values for parameters without defaults: "
                ++ (intercalate " " $ map show missingParams)
    where
        maybePartInfo = Map.lookup part parts
        Just partInfo = maybePartInfo
        paramsWithNoDefault = map fst $ filter snd partInfo
        missingParams = filter (needsDefault params) paramsWithNoDefault
traverseModuleItem _ other = other

-- whether a given parameter is unspecified in the given parameter bindings
needsDefault :: [(Identifier, TypeOrExpr)] -> Identifier -> Bool
needsDefault instanceParams param =
    case lookup param instanceParams of
        Nothing -> True
        Just (Right Nil) -> True
        Just _ -> False