{-| Module : VeriFuzz.Config Description : Configuration file format and parser. Copyright : (c) 2019, Yann Herklotz License : GPL-3 Maintainer : yann [at] yannherklotz [dot] com Stability : experimental Portability : POSIX TOML Configuration file format and parser. -} {-# LANGUAGE TemplateHaskell #-} module VeriFuzz.Config ( -- * TOML Configuration -- $conf Config(..) , defaultConfig -- ** Probabilities , Probability(..) -- *** Expression , ProbExpr(..) -- *** Module Item , ProbModItem(..) -- *** Statement , ProbStatement(..) -- ** ConfProperty , ConfProperty(..) -- ** Simulator Description , SimDescription(..) -- ** Synthesiser Description , SynthDescription(..) -- * Useful Lenses , fromXST , fromYosys , fromVivado , fromQuartus , configProbability , configProperty , configSimulators , configSynthesisers , probModItem , probStmnt , probExpr , probExprNum , probExprId , probExprRangeSelect , probExprUnOp , probExprBinOp , probExprCond , probExprConcat , probExprStr , probExprSigned , probExprUnsigned , probModItemAssign , probModItemSeqAlways , probModItemCombAlways , probModItemInst , probStmntBlock , probStmntNonBlock , probStmntCond , probStmntFor , propSampleSize , propSampleMethod , propSize , propSeed , propStmntDepth , propModDepth , propMaxModules , propCombine , propDeterminism , propNonDeterminism , parseConfigFile , parseConfig , encodeConfig , encodeConfigFile , versionInfo ) where import Control.Applicative (Alternative) import Control.Lens hiding ((.=)) import Data.List.NonEmpty (NonEmpty (..)) import Data.Maybe (fromMaybe) import Data.Text (Text, pack) import qualified Data.Text.IO as T import Data.Version (showVersion) import Development.GitRev import Hedgehog.Internal.Seed (Seed) import Paths_verifuzz (version) import Shelly (toTextIgnore) import Toml (TomlCodec, (.=)) import qualified Toml import VeriFuzz.Sim.Quartus import VeriFuzz.Sim.Vivado import VeriFuzz.Sim.XST import VeriFuzz.Sim.Yosys -- $conf -- -- VeriFuzz supports a TOML configuration file that can be passed using the @-c@ -- flag or using the 'parseConfig' and 'encodeConfig' functions. The -- configuration can then be manipulated using the lenses that are also provided -- in this module. -- -- The configuration file can be used to tweak the random Verilog generation by -- passing different probabilities to each of the syntax nodes in the AST. It -- can also be used to specify which simulators to fuzz with which options. A -- seed for the run can also be set, to replay a previous run using the same -- exact generation. A default value is associated with each key in the -- configuration file, which means that only the options that need overriding -- can be added to the configuration. The defaults can be observed in -- 'defaultConfig' or when running @verifuzz config@. -- -- == Configuration Sections -- -- There are four main configuration sections in the TOML file: -- -- [@probability@] The @probability@ section defines the probabilities at -- every node in the AST. -- -- [@property@] Controls different properties of the generation, such as -- adding a seed or the depth of the statements. -- -- [@simulator@] This is an array of tables containing descriptions of the -- different simulators that should be used. It currently only supports -- . -- -- [@synthesiser@] This is also an array of tables containing descriptions of -- the different synthesisers that should be used. The synthesisers that are -- currently supported are: -- -- - -- - -- - -- - -- | Probability of different expressions nodes. data ProbExpr = ProbExpr { _probExprNum :: {-# UNPACK #-} !Int -- ^ Probability of generation a number like -- @4'ha@. This should never be set to 0, as it is used -- as a fallback in case there are no viable -- identifiers, such as none being in scope. , _probExprId :: {-# UNPACK #-} !Int -- ^ Probability of generating an identifier that is in -- scope and of the right type. , _probExprRangeSelect :: {-# UNPACK #-} !Int -- ^ Probability of generating a range selection from a port. , _probExprUnOp :: {-# UNPACK #-} !Int -- ^ Probability of generating a unary operator. , _probExprBinOp :: {-# UNPACK #-} !Int -- ^ Probability of generation a binary operator. , _probExprCond :: {-# UNPACK #-} !Int -- ^ probability of generating a conditional ternary -- operator. , _probExprConcat :: {-# UNPACK #-} !Int -- ^ Probability of generating a concatenation. , _probExprStr :: {-# UNPACK #-} !Int -- ^ Probability of generating a string. This is not -- fully supported therefore currently cannot be set. , _probExprSigned :: {-# UNPACK #-} !Int -- ^ Probability of generating a signed function -- @$signed(...)@. , _probExprUnsigned :: {-# UNPACK #-} !Int -- ^ Probability of generating an unsigned function -- @$unsigned(...)@. } deriving (Eq, Show) -- | Probability of generating different nodes inside a module declaration. data ProbModItem = ProbModItem { _probModItemAssign :: {-# UNPACK #-} !Int -- ^ Probability of generating an @assign@. , _probModItemSeqAlways :: {-# UNPACK #-} !Int -- ^ Probability of generating a sequential @always@ block. , _probModItemCombAlways :: {-# UNPACK #-} !Int -- ^ Probability of generating an combinational @always@ block. , _probModItemInst :: {-# UNPACK #-} !Int -- ^ Probability of generating a module -- instantiation. } deriving (Eq, Show) data ProbStatement = ProbStatement { _probStmntBlock :: {-# UNPACK #-} !Int , _probStmntNonBlock :: {-# UNPACK #-} !Int , _probStmntCond :: {-# UNPACK #-} !Int , _probStmntFor :: {-# UNPACK #-} !Int } deriving (Eq, Show) data Probability = Probability { _probModItem :: {-# UNPACK #-} !ProbModItem , _probStmnt :: {-# UNPACK #-} !ProbStatement , _probExpr :: {-# UNPACK #-} !ProbExpr } deriving (Eq, Show) data ConfProperty = ConfProperty { _propSize :: {-# UNPACK #-} !Int -- ^ The size of the generated Verilog. , _propSeed :: !(Maybe Seed) -- ^ A possible seed that could be used to -- generate the same Verilog. , _propStmntDepth :: {-# UNPACK #-} !Int -- ^ The maximum statement depth that should be -- reached. , _propModDepth :: {-# UNPACK #-} !Int -- ^ The maximium module depth that should be -- reached. , _propMaxModules :: {-# UNPACK #-} !Int -- ^ The maximum number of modules that are -- allowed to be created at each level. , _propSampleMethod :: !Text -- ^ The sampling method that should be used to -- generate specific distributions of random -- programs. , _propSampleSize :: {-# UNPACK #-} !Int -- ^ The number of samples to take for the -- sampling method. , _propCombine :: !Bool -- ^ If the output should be combined into one -- bit or not. , _propNonDeterminism :: {-# UNPACK #-} !Int -- ^ The frequency at which nondeterminism -- should be generated. , _propDeterminism :: {-# UNPACK #-} !Int -- ^ The frequency at which determinism should -- be generated. } deriving (Eq, Show) data Info = Info { _infoCommit :: !Text , _infoVersion :: !Text } deriving (Eq, Show) data SimDescription = SimDescription { simName :: {-# UNPACK #-} !Text } deriving (Eq, Show) data SynthDescription = SynthDescription { synthName :: {-# UNPACK #-} !Text , synthBin :: Maybe Text , synthDesc :: Maybe Text , synthOut :: Maybe Text } deriving (Eq, Show) data Config = Config { _configInfo :: Info , _configProbability :: {-# UNPACK #-} !Probability , _configProperty :: {-# UNPACK #-} !ConfProperty , _configSimulators :: [SimDescription] , _configSynthesisers :: [SynthDescription] } deriving (Eq, Show) $(makeLenses ''ProbExpr) $(makeLenses ''ProbModItem) $(makeLenses ''ProbStatement) $(makeLenses ''Probability) $(makeLenses ''ConfProperty) $(makeLenses ''Info) $(makeLenses ''Config) defaultValue :: (Alternative r, Applicative w) => b -> Toml.Codec r w a b -> Toml.Codec r w a b defaultValue x = Toml.dimap Just (fromMaybe x) . Toml.dioptional fromXST :: XST -> SynthDescription fromXST (XST a b c) = SynthDescription "xst" (toTextIgnore <$> a) (Just b) (Just $ toTextIgnore c) fromYosys :: Yosys -> SynthDescription fromYosys (Yosys a b c) = SynthDescription "yosys" (toTextIgnore <$> a) (Just b) (Just $ toTextIgnore c) fromVivado :: Vivado -> SynthDescription fromVivado (Vivado a b c) = SynthDescription "vivado" (toTextIgnore <$> a) (Just b) (Just $ toTextIgnore c) fromQuartus :: Quartus -> SynthDescription fromQuartus (Quartus a b c) = SynthDescription "quartus" (toTextIgnore <$> a) (Just b) (Just $ toTextIgnore c) defaultConfig :: Config defaultConfig = Config (Info (pack $(gitHash)) (pack $ showVersion version)) (Probability defModItem defStmnt defExpr) defProperty [] [fromYosys defaultYosys, fromVivado defaultVivado] where defModItem = ProbModItem { _probModItemAssign = 5 , _probModItemSeqAlways = 1 , _probModItemCombAlways = 0 , _probModItemInst = 1 } defStmnt = ProbStatement { _probStmntBlock = 0 , _probStmntNonBlock = 3 , _probStmntCond = 1 , _probStmntFor = 0 } defExpr = ProbExpr { _probExprNum = 1 , _probExprId = 5 , _probExprRangeSelect = 5 , _probExprUnOp = 5 , _probExprBinOp = 5 , _probExprCond = 5 , _probExprConcat = 3 , _probExprStr = 0 , _probExprSigned = 5 , _probExprUnsigned = 5 } defProperty = ConfProperty { _propSize = 20 , _propSeed = Nothing , _propStmntDepth = 3 , _propModDepth = 2 , _propMaxModules = 5 , _propSampleMethod = "random" , _propSampleSize = 10 , _propCombine = False , _propNonDeterminism = 0 , _propDeterminism = 1 } twoKey :: Toml.Piece -> Toml.Piece -> Toml.Key twoKey a b = Toml.Key (a :| [b]) int :: Toml.Piece -> Toml.Piece -> TomlCodec Int int a = Toml.int . twoKey a exprCodec :: TomlCodec ProbExpr exprCodec = ProbExpr <$> defaultValue (defProb probExprNum) (intE "number") .= _probExprNum <*> defaultValue (defProb probExprId) (intE "variable") .= _probExprId <*> defaultValue (defProb probExprRangeSelect) (intE "rangeselect") .= _probExprRangeSelect <*> defaultValue (defProb probExprUnOp) (intE "unary") .= _probExprUnOp <*> defaultValue (defProb probExprBinOp) (intE "binary") .= _probExprBinOp <*> defaultValue (defProb probExprCond) (intE "ternary") .= _probExprCond <*> defaultValue (defProb probExprConcat) (intE "concatenation") .= _probExprConcat <*> defaultValue (defProb probExprStr) (intE "string") .= _probExprStr <*> defaultValue (defProb probExprSigned) (intE "signed") .= _probExprSigned <*> defaultValue (defProb probExprUnsigned) (intE "unsigned") .= _probExprUnsigned where defProb i = defaultConfig ^. configProbability . probExpr . i intE = int "expr" stmntCodec :: TomlCodec ProbStatement stmntCodec = ProbStatement <$> defaultValue (defProb probStmntBlock) (intS "blocking") .= _probStmntBlock <*> defaultValue (defProb probStmntNonBlock) (intS "nonblocking") .= _probStmntNonBlock <*> defaultValue (defProb probStmntCond) (intS "conditional") .= _probStmntCond <*> defaultValue (defProb probStmntFor) (intS "forloop") .= _probStmntFor where defProb i = defaultConfig ^. configProbability . probStmnt . i intS = int "statement" modItemCodec :: TomlCodec ProbModItem modItemCodec = ProbModItem <$> defaultValue (defProb probModItemAssign) (intM "assign") .= _probModItemAssign <*> defaultValue (defProb probModItemSeqAlways) (intM "sequential") .= _probModItemSeqAlways <*> defaultValue (defProb probModItemCombAlways) (intM "combinational") .= _probModItemCombAlways <*> defaultValue (defProb probModItemInst) (intM "instantiation") .= _probModItemInst where defProb i = defaultConfig ^. configProbability . probModItem . i intM = int "moditem" probCodec :: TomlCodec Probability probCodec = Probability <$> defaultValue (defProb probModItem) modItemCodec .= _probModItem <*> defaultValue (defProb probStmnt) stmntCodec .= _probStmnt <*> defaultValue (defProb probExpr) exprCodec .= _probExpr where defProb i = defaultConfig ^. configProbability . i propCodec :: TomlCodec ConfProperty propCodec = ConfProperty <$> defaultValue (defProp propSize) (Toml.int "size") .= _propSize <*> Toml.dioptional (Toml.read "seed") .= _propSeed <*> defaultValue (defProp propStmntDepth) (int "statement" "depth") .= _propStmntDepth <*> defaultValue (defProp propModDepth) (int "module" "depth") .= _propModDepth <*> defaultValue (defProp propMaxModules) (int "module" "max") .= _propMaxModules <*> defaultValue (defProp propSampleMethod) (Toml.text (twoKey "sample" "method")) .= _propSampleMethod <*> defaultValue (defProp propSampleSize) (int "sample" "size") .= _propSampleSize <*> defaultValue (defProp propCombine) (Toml.bool (twoKey "output" "combine")) .= _propCombine <*> defaultValue (defProp propNonDeterminism) (Toml.int "nondeterminism") .= _propNonDeterminism <*> defaultValue (defProp propDeterminism) (Toml.int "determinism") .= _propDeterminism where defProp i = defaultConfig ^. configProperty . i simulator :: TomlCodec SimDescription simulator = Toml.textBy pprint parseIcarus "name" where parseIcarus i@"icarus" = Right $ SimDescription i parseIcarus s = Left $ "Could not match '" <> s <> "' with a simulator." pprint (SimDescription a) = a synthesiser :: TomlCodec SynthDescription synthesiser = SynthDescription <$> Toml.text "name" .= synthName <*> Toml.dioptional (Toml.text "bin") .= synthBin <*> Toml.dioptional (Toml.text "description") .= synthDesc <*> Toml.dioptional (Toml.text "output") .= synthOut infoCodec :: TomlCodec Info infoCodec = Info <$> defaultValue (defaultConfig ^. configInfo . infoCommit) (Toml.text "commit") .= _infoCommit <*> defaultValue (defaultConfig ^. configInfo . infoVersion) (Toml.text "version") .= _infoVersion configCodec :: TomlCodec Config configCodec = Config <$> defaultValue (defaultConfig ^. configInfo) (Toml.table infoCodec "info") .= _configInfo <*> defaultValue (defaultConfig ^. configProbability) (Toml.table probCodec "probability") .= _configProbability <*> defaultValue (defaultConfig ^. configProperty) (Toml.table propCodec "property") .= _configProperty <*> defaultValue (defaultConfig ^. configSimulators) (Toml.list simulator "simulator") .= _configSimulators <*> defaultValue (defaultConfig ^. configSynthesisers) (Toml.list synthesiser "synthesiser") .= _configSynthesisers parseConfigFile :: FilePath -> IO Config parseConfigFile = Toml.decodeFile configCodec parseConfig :: Text -> Config parseConfig t = case Toml.decode configCodec t of Right c -> c Left Toml.TrivialError -> error "Trivial error while parsing Toml config" Left (Toml.KeyNotFound k) -> error $ "Key " ++ show k ++ " not found" Left (Toml.TableNotFound k) -> error $ "Table " ++ show k ++ " not found" Left (Toml.TypeMismatch k _ _) -> error $ "Type mismatch with key " ++ show k Left _ -> error "Config file parse error" encodeConfig :: Config -> Text encodeConfig = Toml.encode configCodec encodeConfigFile :: FilePath -> Config -> IO () encodeConfigFile f = T.writeFile f . encodeConfig versionInfo :: String versionInfo = "VeriFuzz " <> showVersion version <> " (" <> $(gitCommitDate) <> " " <> $(gitHash) <> ")"