aboutsummaryrefslogtreecommitdiffstats
path: root/src/Test/VeriFuzz/Verilog/Mutate.hs
blob: ab9f0ac80b64cb0b2fd2a1099838c1e7b4742894 (plain)
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
{-|
Module      : Test.VeriFuzz.Verilog.Mutation
Description : Functions to mutate the Verilog AST.
Copyright   : (c) 2018-2019, Yann Herklotz Grave
License     : BSD-3
Maintainer  : ymherklotz [at] gmail [dot] com
Stability   : experimental
Portability : POSIX

Functions to mutate the Verilog AST from "Test.VeriFuzz.Verilog.AST" to generate
more random patterns, such as nesting wires instead of creating new ones.
-}

module Test.VeriFuzz.Verilog.Mutate where

import           Control.Lens
import           Data.Maybe                    (catMaybes, fromMaybe)
import           Test.VeriFuzz.Internal.Gen
import           Test.VeriFuzz.Internal.Shared
import           Test.VeriFuzz.Verilog.AST
import           Test.VeriFuzz.Verilog.CodeGen

-- | Return if the 'Identifier' is in a 'ModDecl'.
inPort :: Identifier -> ModDecl -> Bool
inPort id mod = inInput
  where
    inInput = any (\a -> a ^. portName == id) $ mod ^. modInPorts ++ mod ^. modOutPorts

-- | Find the last assignment of a specific wire/reg to an expression, and
-- returns that expression.
findAssign :: Identifier -> [ModItem] -> Maybe Expr
findAssign id items =
  safe last . catMaybes $ isAssign <$> items
  where
    isAssign (ModCA (ContAssign val expr))
      | val == id = Just expr
      | otherwise = Nothing
    isAssign _ = Nothing

-- | Transforms an expression by replacing an Identifier with an
-- expression. This is used inside 'transformOf' and 'traverseExpr' to replace
-- the 'Identifier' recursively.
idTrans :: Identifier -> Expr -> Expr -> Expr
idTrans i expr (Id id)
  | id == i = expr
  | otherwise = Id id
idTrans _ _ e = e

-- | Replaces the identifier recursively in an expression.
replace :: Identifier -> Expr -> Expr -> Expr
replace = (transformOf traverseExpr .) . idTrans

-- | Nest expressions for a specific 'Identifier'. If the 'Identifier' is not found,
-- the AST is not changed.
--
-- This could be improved by instead of only using the last assignment to the
-- wire that one finds, to use the assignment to the wire before the current
-- expression. This would require a different approach though.
nestId :: Identifier -> ModDecl -> ModDecl
nestId id mod
  | not $ inPort id mod =
      let expr = fromMaybe def . findAssign id $ mod ^. moduleItems
      in mod & get %~ replace id expr
  | otherwise = mod
  where
    get = moduleItems . traverse . _ModCA . contAssignExpr
    def = Id id

-- | Replaces an identifier by a expression in all the module declaration.
nestSource :: Identifier -> VerilogSrc -> VerilogSrc
nestSource id src =
  src & getVerilogSrc . traverse . getDescription %~ nestId id

-- | Nest variables in the format @w[0-9]*@ up to a certain number.
nestUpTo :: Int -> VerilogSrc -> VerilogSrc
nestUpTo i src =
  foldl (flip nestSource) src $ Identifier . fromNode <$> [1..i]

-- $setup
-- >>> let mod = (ModDecl (Identifier "m") [Port Wire 5 (Identifier "y")] [Port Wire 5 "x"] [])
-- >>> let main = (ModDecl "main" [] [] [])

-- | Add a Module Instantiation using 'ModInst' from the first module passed to
-- it to the body of the second module. It first has to make all the inputs into
-- @reg@.
--
-- >>> instantiateMod mod main
-- module main;
-- wire [4:0] y;
-- reg [4:0] x;
-- m m1(y, x);
-- endmodule
-- <BLANKLINE>
instantiateMod :: ModDecl -> ModDecl -> ModDecl
instantiateMod mod main =
  main & moduleItems %~ ((out ++ regIn ++ [inst])++)
  where
    out = Decl Nothing <$> mod ^. modOutPorts
    regIn = Decl Nothing <$> (mod ^. modInPorts & traverse . portType .~ Reg False)
    inst = ModInst (mod ^. moduleId) (mod ^. moduleId <> (Identifier . showT $ count+1)) conns
    count = length . filter (==mod ^. moduleId) $ main ^.. moduleItems . traverse . modInstId
    conns = ModConn . Id <$>
      (mod ^.. modOutPorts . traverse . portName) ++ (mod ^.. modInPorts . traverse . portName)

-- | Initialise all the inputs and outputs to a module.
--
-- >>> initMod mod
-- module m(y, x);
-- output wire [4:0] y;
-- input wire [4:0] x;
-- endmodule
-- <BLANKLINE>
initMod :: ModDecl -> ModDecl
initMod mod = mod & moduleItems %~ ((out ++ inp)++)
  where
    out = Decl (Just PortOut) <$> (mod ^. modOutPorts)
    inp = Decl (Just PortIn) <$> (mod ^. modInPorts)