1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
{-|
Module : VeriFuzz.Gen
Description : Various useful generators.
Copyright : (c) 2019, Yann Herklotz Grave
License : GPL-3
Maintainer : ymherklotz [at] gmail [dot] com
Stability : experimental
Portability : POSIX
Various useful generators.
-}
{-# LANGUAGE TemplateHaskell #-}
module VeriFuzz.Gen
( -- * Generation methods
procedural
, fromGraph
, randomMod
)
where
import Control.Lens hiding (Context)
import Control.Monad (replicateM)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader hiding (local)
import Control.Monad.Trans.State.Lazy
import Data.Foldable (fold)
import qualified Data.Text as T
import Test.QuickCheck (Gen)
import qualified Test.QuickCheck as QC
import VeriFuzz.AST
import VeriFuzz.ASTGen
import VeriFuzz.Config
import VeriFuzz.Internal
import VeriFuzz.Mutate
import VeriFuzz.Random
newtype Context = Context { _variables :: [Port]
-- , _modules :: [ModDecl]
}
makeLenses ''Context
type StateGen = StateT Context (ReaderT Config Gen)
toId :: Int -> Identifier
toId = Identifier . ("w" <>) . T.pack . show
toPort :: Identifier -> Gen Port
toPort ident = do
i <- abs <$> QC.arbitrary
return $ wire i ident
sumSize :: [Port] -> Int
sumSize ps = sum $ ps ^.. traverse . portSize
random :: [Identifier] -> (Expr -> ContAssign) -> Gen ModItem
random ctx fun = do
expr <- QC.sized (exprWithContext ctx)
return . ModCA $ fun expr
--randomAssigns :: [Identifier] -> [Gen ModItem]
--randomAssigns ids = random ids . ContAssign <$> ids
randomOrdAssigns :: [Identifier] -> [Identifier] -> [Gen ModItem]
randomOrdAssigns inp ids = snd $ foldr generate (inp, []) ids
where generate cid (i, o) = (cid : i, random i (ContAssign cid) : o)
randomMod :: Int -> Int -> Gen ModDecl
randomMod inps total = do
x <- sequence $ randomOrdAssigns start end
ident <- sequence $ toPort <$> ids
let inputs_ = take inps ident
let other = drop inps ident
let y = ModCA . ContAssign "y" . fold $ Id <$> drop inps ids
let yport = [wire (sumSize other) "y"]
return . declareMod other . ModDecl "test_module" yport inputs_ $ x ++ [y]
where
ids = toId <$> [1 .. total]
end = drop inps ids
start = take inps ids
fromGraph :: Gen ModDecl
fromGraph = do
gr <- rDupsCirc <$> QC.resize 100 randomCircuit
return
$ initMod
. head
$ nestUpTo 5 (generateAST gr)
^.. getVerilogSrc
. traverse
. getDescription
gen :: Gen a -> StateGen a
gen = lift . lift
contAssign :: StateGen ContAssign
contAssign = do
name <- gen QC.arbitrary
size <- gen positiveArb
signed <- gen QC.arbitrary
variables %= (Port Wire signed size name :)
ContAssign name <$> scopedExpr
scopedExpr :: StateGen Expr
scopedExpr = do
context <- get
gen
. QC.sized
. exprWithContext
$ context
^.. variables
. traverse
. portName
lvalFromPort :: Port -> LVal
lvalFromPort (Port _ _ _ i) = RegId i
scopedReg :: StateGen LVal
scopedReg = do
context <- get
gen
. QC.elements
. fmap lvalFromPort
. filter (\p -> p ^. portType == Reg)
$ context
^. variables
probability :: Config -> Probability
probability c = c ^. configProbability
askProbability :: StateGen Probability
askProbability = lift $ asks probability
assignment :: StateGen Assign
assignment = do
lval <- scopedReg
Assign lval Nothing <$> scopedExpr
statement :: StateGen Statement
statement = do
prob <- askProbability
as <- assignment
gen $ QC.frequency
[ (prob ^. probBlock , return $ BlockAssign as)
, (prob ^. probNonBlock, return $ NonBlockAssign as)
]
modItem :: StateGen ModItem
modItem = do
prob <- askProbability
amount <- gen positiveArb
stat <- fold <$> replicateM amount statement
event <- gen QC.arbitrary
modCA <- ModCA <$> contAssign
gen $ QC.frequency
[ (prob ^. probAssign, return modCA)
, ( prob ^. probAlways
, return $ Always (EventCtrl event (Just stat))
)
]
ports :: StateGen [Port]
ports = do
portList <- gen $ QC.listOf1 QC.arbitrary
variables %= (<> portList)
return portList
moduleDef :: Bool -> StateGen ModDecl
moduleDef top = do
name <- if top then return "top" else gen QC.arbitrary
portList <- ports
amount <- gen positiveArb
mi <- replicateM amount modItem
context <- get
let local = filter (`notElem` portList) $ context ^. variables
let size = sum $ local ^.. traverse . portSize
let yport = Port Wire False size "y"
return . declareMod local . ModDecl name [yport] portList $ combineAssigns
yport
mi
procedural :: Config -> Gen VerilogSrc
procedural config =
VerilogSrc
. (: [])
. Description
<$> QC.resize num (runReaderT (evalStateT (moduleDef True) context) config)
where context = Context []
num = config ^. configProperty . propSize
|