Skip to content

Commit 14f75bb

Browse files
committed
Hedgehog Integration (WIP)
1 parent fdf0667 commit 14f75bb

File tree

8 files changed

+337
-130
lines changed

8 files changed

+337
-130
lines changed

clash-testbench/clash-testbench.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ library
4949
base,
5050
mtl,
5151
array,
52+
lattices,
5253
hedgehog,
5354
containers,
5455
bytestring,

clash-testbench/example/Main.hs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,59 @@
11
{-# LANGUAGE RecursiveDo #-}
2+
{-# LANGUAGE DataKinds #-}
23
module Main where
34

5+
import Data.Bool (bool)
6+
7+
import Clash.Prelude (Signed)
8+
49
import Clash.Testbench
510

611
import Calculator (OPC(..))
712
import qualified Calculator (topEntity)
813

14+
import Clash.Hedgehog.Sized.Signed
15+
import Hedgehog
16+
import qualified Hedgehog.Gen as Gen
17+
import qualified Hedgehog.Range as Range
18+
19+
genIO :: Gen [(OPC (Signed 4), Maybe (Signed 4))]
20+
genIO = do
21+
-- generate 7 constants
22+
cs <- Gen.list (Range.singleton 7) (genSigned Range.constantBounded)
23+
-- generate 6 operations
24+
ops <- map (bool (ADD, (+)) (MUL, (*))) <$> Gen.list (Range.singleton 6) Gen.bool
25+
26+
let
27+
-- push the constants to the stack
28+
in1 = concatMap ((: [Push]) . Imm) cs -- inputs
29+
eo1 = concatMap ((: [Nothing]) . Just) cs -- expected outputs
30+
31+
-- calculate the results of the applied operations
32+
x : xr = reverse cs
33+
rs = [ foldl (\a (op, b) -> op a b) x $ zip (map snd ops) $ take n xr
34+
| n <- [1,2..length xr]
35+
]
36+
37+
-- apply the operations
38+
in2 = concatMap ((replicate 3 Pop <>) . pure . fst) ops -- inputs
39+
eo2 = concatMap ((replicate 3 Nothing <>) . pure . Just) rs -- expected outputs
40+
41+
return $ zip (in1 <> in2) (eo1 <> eo2)
42+
943
myTestbench
1044
:: TB ()
1145
myTestbench = mdo
12-
input <- fromList Pop [Imm 1, Push, Imm 2, Push, Pop, Pop, Pop, ADD]
46+
-- input <- fromList Pop [Imm 1, Push, Imm 2, Push, Pop, Pop, Pop, ADD]
47+
input <- matchIOGenN output genIO
1348
output <- ("topEntity" @@ Calculator.topEntity) auto auto auto input
1449
watch input
1550
watch output
1651

1752
main :: IO ()
18-
main = simulate 10 myTestbench
53+
main = simulate 38 myTestbench
1954

2055
foreign export ccall "clash_ffi_main"
2156
ffiMain :: IO ()
2257

2358
ffiMain :: IO ()
24-
ffiMain = simulateFFI 10 myTestbench
59+
ffiMain = simulateFFI 38 myTestbench

clash-testbench/example/cabal.project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
packages: . .. ../../clash-ghc ../../clash-lib ../../clash-prelude ../../clash-ffi
1+
packages: . .. ../../clash-ghc ../../clash-lib ../../clash-prelude ../../clash-ffi ../../clash-prelude-hedgehog
22

33
write-ghc-environment-files: always

clash-testbench/example/clash-testbench-example.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ common basic-config
2020
-fplugin GHC.TypeLits.KnownNat.Solver
2121
build-depends:
2222
base,
23+
hedgehog,
2324
clash-prelude,
25+
clash-prelude-hedgehog,
2426
clash-testbench,
2527
ghc-typelits-extra,
2628
ghc-typelits-knownnat,

clash-testbench/src/Clash/Testbench/Generate.hs

Lines changed: 131 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ module Clash.Testbench.Generate where
22

33
import Hedgehog
44
import Hedgehog.Gen
5+
import Control.Monad.State.Lazy (liftIO, get)
6+
import Data.IORef (newIORef, readIORef, writeIORef)
57

68
import Clash.Prelude (KnownDomain(..), BitPack(..), NFDataX)
79

@@ -10,15 +12,138 @@ import Clash.Testbench.Internal.ID
1012
import Clash.Testbench.Internal.Signal hiding (TBSignal, TBClock, TBReset, TBEnable)
1113
import Clash.Testbench.Internal.Monad
1214

15+
matchIOGen ::
16+
(NFDataX i, BitPack i, KnownDomain dom, Eq o, Show o) =>
17+
TBSignal dom o -> Gen (i, o) -> TB (TBSignal dom i)
18+
matchIOGen expectedOutput gen = do
19+
ST{..} <- get
20+
21+
vRef <- liftIO $ newIORef undefined
22+
simStepCache <- liftIO (readIORef simStepRef >>= newIORef)
23+
24+
mind SomeSignal $ IOInput
25+
{ signalId = NoID
26+
, signalCurVal = do
27+
v <- readIORef simStepRef
28+
v' <- readIORef simStepCache
29+
30+
if v == v'
31+
then readIORef vRef
32+
else do
33+
(i, o) <- sample gen
34+
signalExpect expectedOutput $ Expectation (v + 1, verify o)
35+
36+
writeIORef vRef i
37+
writeIORef simStepCache v
38+
return i
39+
, signalPrint = Nothing
40+
}
41+
where
42+
verify x y
43+
| x == y = Nothing
44+
| otherwise = Just $ "Expected " <> show x <> " but the output is " <> show y
45+
46+
47+
matchIOGenN ::
48+
(NFDataX i, BitPack i, KnownDomain dom, Eq o, Show o) =>
49+
TBSignal dom o -> Gen [(i, o)] -> TB (TBSignal dom i)
50+
matchIOGenN expectedOutput gen = do
51+
ST{..} <- get
52+
53+
vRef <- liftIO $ newIORef []
54+
simStepCache <- liftIO (readIORef simStepRef >>= newIORef)
55+
56+
mind SomeSignal $ IOInput
57+
{ signalId = NoID
58+
, signalCurVal = do
59+
v <- readIORef simStepRef
60+
v' <- readIORef simStepCache
61+
62+
if v == v'
63+
then readIORef vRef >>= \case
64+
(i, _) : _ -> return i
65+
[] -> do
66+
(i, o) : xr <- sample gen
67+
writeIORef vRef ((i, o) : xr)
68+
return i
69+
else do
70+
writeIORef simStepCache v
71+
readIORef vRef >>= \case
72+
_ : (i, o) : xr -> do
73+
writeIORef vRef ((i, o) : xr)
74+
signalExpect expectedOutput $ Expectation (v, verify o)
75+
return i
76+
_ -> do
77+
(i, o) : xr <- sample gen
78+
writeIORef vRef ((i, o) : xr)
79+
signalExpect expectedOutput $ Expectation (v, verify o)
80+
return i
81+
, signalPrint = Nothing
82+
}
83+
where
84+
verify x y
85+
| x == y = Nothing
86+
| otherwise = Just $ "Expected '" <> show x <> "' but the output is '" <> show y <> "'"
87+
88+
1389
generate ::
1490
(NFDataX a, BitPack a, KnownDomain dom) =>
15-
Gen a -> TB (TBSignal dom a)
16-
generate generator =
17-
mindSignal Generator
91+
a -> Gen a -> TB (TBSignal dom a)
92+
generate def gen = do
93+
ST{..} <- get
94+
95+
vRef <- liftIO $ newIORef def
96+
simStepCache <- liftIO (readIORef simStepRef >>= newIORef)
97+
98+
mind SomeSignal IOInput
1899
{ signalId = NoID
19-
, signalCurVal = sample generator
100+
, signalCurVal = do
101+
v <- readIORef simStepRef
102+
v' <- readIORef simStepCache
103+
104+
if v == v'
105+
then readIORef vRef
106+
else do
107+
x <- sample gen
108+
writeIORef vRef x
109+
writeIORef simStepCache v
110+
return x
20111
, signalPrint = Nothing
21-
, ..
22112
}
23113

24-
114+
generateN ::
115+
(NFDataX a, BitPack a, KnownDomain dom) =>
116+
a -> Gen [a] -> TB (TBSignal dom a)
117+
generateN def gen = do
118+
ST{..} <- get
119+
120+
vRef <- liftIO $ newIORef [def]
121+
simStepCache <- liftIO (readIORef simStepRef >>= newIORef)
122+
123+
mindSignal IOInput
124+
{ signalId = NoID
125+
, signalCurVal = do
126+
v <- readIORef simStepRef
127+
v' <- readIORef simStepCache
128+
129+
if v == v'
130+
then readIORef vRef >>= \case
131+
x : _ -> return x
132+
[] -> do
133+
x : xr <- sample gen
134+
writeIORef vRef (x : xr)
135+
return x
136+
137+
else do
138+
writeIORef simStepCache v
139+
readIORef vRef >>= \case
140+
_ : x : xr -> do
141+
writeIORef vRef (x : xr)
142+
return x
143+
_ -> do
144+
x : xr <- sample gen
145+
writeIORef vRef (x : xr)
146+
return x
147+
, signalPrint = Nothing
148+
, ..
149+
}

0 commit comments

Comments
 (0)