Skip to content

Commit cec1e7e

Browse files
authored
Merge pull request #156 from clash-lang/lucas/improve-expect-options
2 parents ca60822 + 5c40aae commit cec1e7e

File tree

8 files changed

+45
-34
lines changed

8 files changed

+45
-34
lines changed

clash-protocols/src/Protocols/Hedgehog.hs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ module Protocols.Hedgehog (
2323
genStallAck,
2424
genStallMode,
2525
genStalls,
26+
expectedEmptyCycles,
2627
) where
2728

2829
-- base
@@ -64,16 +65,6 @@ resetGen n =
6465
C.unsafeFromActiveHigh
6566
(C.fromList (replicate n True <> repeat False))
6667

67-
smallInt :: H.Range Int
68-
smallInt = Range.linear 0 10
69-
70-
genSmallInt :: H.Gen Int
71-
genSmallInt =
72-
Gen.frequency
73-
[ (90, Gen.integral smallInt)
74-
, (10, Gen.constant (Range.lowerBound 99 smallInt))
75-
]
76-
7768
{- | Attach a timeout to a property. Fails if the property does not finish in
7869
the given time. The timeout is given in milliseconds.
7970
-}
@@ -120,11 +111,12 @@ propWithModel eOpts genData model prot prop =
120111
when (eoTrace eOpts) $ liftIO $ putStr "propWithModel: dat: " >> print dat
121112

122113
-- TODO: Different 'n's for each output
123-
n <- H.forAll (Gen.integral (Range.linear 0 (eoSampleMax eOpts)))
114+
n <- H.forAll (Gen.integral (Range.linear 0 (eoStallsMax eOpts)))
124115
when (eoTrace eOpts) $ liftIO $ putStr "propWithModel: n: " >> print n
125116

126117
-- TODO: Different distributions?
127-
let genStall = genSmallInt
118+
let
119+
genStall = Gen.int (Range.linear 1 eOpts.eoConsecutiveStalls)
128120

129121
-- Generate stalls for LHS part of the protocol. The first line determines
130122
-- whether to stall or not. The second determines how many cycles to stall

clash-protocols/src/Protocols/Hedgehog/Internal.hs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@ import Clash.Prelude qualified as C
2929
import Hedgehog qualified as H
3030
import Hedgehog.Internal.Property qualified as H
3131

32-
{- | Resets for 30 cycles, checks for superfluous data for 50 cycles after
33-
seeing last valid data cycle, and times out after seeing 1000 consecutive
34-
empty cycles.
32+
{- | Conservative settings for `ExpectOptions`:
33+
- Reset for 30 cycles
34+
- Insert at most 10 stall moments
35+
- Every stall moment is at most 10 cycles long
36+
- Sample at most 1000 cycles
37+
- Automatically derive when to stop sampling empty samples using `expectedEmptyCycles`.
3538
-}
3639
defExpectOptions :: ExpectOptions
3740
defExpectOptions =
@@ -41,8 +44,10 @@ defExpectOptions =
4144
-- increase the time it takes to run the tests. This is because
4245
-- the test will run for at least the number of cycles specified
4346
-- in 'eoStopAfterEmpty'.
44-
eoStopAfterEmpty = 256
45-
, eoSampleMax = 256
47+
eoStopAfterEmpty = Nothing
48+
, eoSampleMax = 1000
49+
, eoStallsMax = 10
50+
, eoConsecutiveStalls = 10
4651
, eoResetCycles = 30
4752
, eoDriveEarly = True
4853
, eoTimeoutMs = Nothing
@@ -57,9 +62,10 @@ instance (TestType a, C.KnownDomain dom) => Test (Df dom a) where
5762
ExpectOptions ->
5863
[Maybe a] ->
5964
m [a]
60-
expectN Proxy (ExpectOptions{eoSampleMax, eoStopAfterEmpty}) sampled = do
61-
go eoSampleMax eoStopAfterEmpty sampled
65+
expectN Proxy eOpts sampled = do
66+
go eOpts.eoSampleMax maxEmptyCycles sampled
6267
where
68+
maxEmptyCycles = expectedEmptyCycles eOpts
6369
go :: (HasCallStack) => Int -> Int -> [Maybe a] -> m [a]
6470
go _timeout _n [] =
6571
-- This really should not happen, protocols should produce data indefinitely
@@ -69,7 +75,7 @@ instance (TestType a, C.KnownDomain dom) => Test (Df dom a) where
6975
H.failWith
7076
Nothing
7177
( "Sample limit reached after sampling "
72-
<> show eoSampleMax
78+
<> show eOpts.eoSampleMax
7379
<> " samples. "
7480
<> "Consider increasing 'eoSampleMax' in 'ExpectOptions'."
7581
)
@@ -78,7 +84,7 @@ instance (TestType a, C.KnownDomain dom) => Test (Df dom a) where
7884
pure []
7985
go sampleTimeout _emptyTimeout (Just a : as) =
8086
-- Valid sample
81-
(a :) <$> go (sampleTimeout - 1) eoStopAfterEmpty as
87+
(a :) <$> go (sampleTimeout - 1) maxEmptyCycles as
8288
go sampleTimeout emptyTimeout (Nothing : as) =
8389
-- Empty sample
8490
go sampleTimeout (emptyTimeout - 1) as

clash-protocols/src/Protocols/Hedgehog/Types.hs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module Protocols.Hedgehog.Types where
99
import Control.DeepSeq
1010

1111
import Clash.Prelude qualified as C
12+
import Data.Maybe (fromMaybe)
1213
import Data.Proxy
1314
import GHC.Stack (HasCallStack)
1415
import Protocols.Internal.Types
@@ -23,14 +24,16 @@ instance (NFData a, C.NFDataX a, C.ShowX a, C.Show a, Eq a) => TestType a
2324

2425
-- | Options for 'expectN' function. See individual fields for more information.
2526
data ExpectOptions = ExpectOptions
26-
{ eoStopAfterEmpty :: Int
27-
-- ^ Stop sampling after seeing /n/ consecutive empty samples
27+
{ eoStopAfterEmpty :: Maybe Int
28+
-- ^ Explicitly control the number of samples empty samples simulate before we stop
29+
-- the simulation. When set to `Nothing`, this is derived using `expectedEmptyCycles`.
2830
, eoSampleMax :: Int
2931
-- ^ Produce an error if the circuit produces more than /n/ valid samples. This
3032
-- is used to terminate (potentially) infinitely running circuits.
31-
--
32-
-- This number is used to generate stall information, so setting it to
33-
-- unreasonable values will result in long runtimes.
33+
, eoStallsMax :: Int
34+
-- ^ Generate at most /n/ stall moments of zero or more cycles(set by 'eoConsecutiveStalls').
35+
, eoConsecutiveStalls :: Int
36+
-- ^ Maximum number of consecutive stalls that are allowed to be inserted.
3437
, eoResetCycles :: Int
3538
-- ^ Ignore first /n/ cycles
3639
, eoDriveEarly :: Bool
@@ -42,6 +45,16 @@ data ExpectOptions = ExpectOptions
4245
-- ^ Trace data generation for debugging purposes
4346
}
4447

48+
-- | Default derivation of `eoStopAfterEmpty` when it is set to `Nothing`.
49+
expectedEmptyCycles :: ExpectOptions -> Int
50+
expectedEmptyCycles eOpts =
51+
-- +2 on `eoStallsMax` to account worst case left side stalling + right side stalling
52+
-- +1 on `eoConsecutiveStalls` to consume 1 sample after stalling
53+
-- +100 arbitrarily chosen to allow the circuit to have some internal latency.
54+
fromMaybe
55+
(eOpts.eoStallsMax * (eOpts.eoConsecutiveStalls + 1) + eOpts.eoResetCycles + 100)
56+
eOpts.eoStopAfterEmpty
57+
4558
{- | Provides a way of comparing expected data with data produced by a
4659
protocol component.
4760
-}

clash-protocols/src/Protocols/Wishbone/Standard/Hedgehog.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,6 @@ sampleUnfiltered ::
670670
Circuit (Wishbone dom mode addressWidth a) () ->
671671
[(WishboneM2S addressWidth (BitSize a `DivRU` 8) a, WishboneS2M a)]
672672
sampleUnfiltered eOpts manager subordinate =
673-
takeWhileAnyInWindow (eoStopAfterEmpty eOpts) hasBusActivity $
673+
takeWhileAnyInWindow (expectedEmptyCycles eOpts) hasBusActivity $
674674
uncurry P.zip $
675675
observeComposedWishboneCircuit manager subordinate

clash-protocols/tests/Tests/Protocols/PacketStream/Converters.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ generateDownConverterProperty ::
4242
Property
4343
generateDownConverterProperty SNat SNat =
4444
idWithModelSingleDomain
45-
defExpectOptions{eoSampleMax = 1000}
45+
defExpectOptions
4646
(genPackets 1 8 (genValidPacket defPacketOptions Gen.enumBounded (Range.linear 0 10)))
4747
(exposeClockResetEnable (upConvert . downConvert))
4848
(exposeClockResetEnable @System (downConverterC @dwOut @n @Int))

clash-protocols/tests/Tests/Protocols/PacketStream/Depacketizers.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ depacketizerPropGen ::
4444
depacketizerPropGen SNat SNat metaGen toMetaOut =
4545
idWithModelSingleDomain
4646
@System
47-
defExpectOptions{eoSampleMax = 1000, eoStopAfterEmpty = 1000}
47+
defExpectOptions
4848
(genPackets 1 4 (genValidPacket defPacketOptions metaGen (Range.linear 0 30)))
4949
(exposeClockResetEnable (depacketizerModel toMetaOut))
5050
(exposeClockResetEnable ckt)
@@ -80,7 +80,7 @@ depacketizeToDfPropGen ::
8080
depacketizeToDfPropGen SNat SNat metaGen toOut =
8181
idWithModelSingleDomain
8282
@System
83-
defExpectOptions{eoSampleMax = 1000, eoStopAfterEmpty = 1000}
83+
defExpectOptions
8484
(genPackets 1 10 (genValidPacket defPacketOptions metaGen (Range.linear 0 20)))
8585
(exposeClockResetEnable (depacketizeToDfModel toOut))
8686
(exposeClockResetEnable ckt)

clash-protocols/tests/Tests/Protocols/PacketStream/PacketFifo.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ prop_packet_fifo_small_buffer_id :: Property
5555
prop_packet_fifo_small_buffer_id =
5656
idWithModelSingleDomain
5757
@System
58-
defExpectOptions{eoStopAfterEmpty = 1000}
58+
defExpectOptions{eoStopAfterEmpty = Just 500} -- To account for empty cycles due to dropped packets
5959
(genPackets 1 10 (genValidPacket defPacketOptions Gen.enumBounded (Range.linear 0 30)))
6060
(exposeClockResetEnable (dropBigPackets d3 . dropAbortedPackets))
6161
(exposeClockResetEnable (packetFifoC @_ @1 @Int16 d3 d5 Backpressure))
@@ -78,7 +78,7 @@ prop_overFlowDrop_packetFifo_id :: Property
7878
prop_overFlowDrop_packetFifo_id =
7979
idWithModelSingleDomain
8080
@System
81-
defExpectOptions{eoStopAfterEmpty = 1000}
81+
defExpectOptions
8282
(genPackets 1 10 (genValidPacket defPacketOptions Gen.enumBounded (Range.linear 0 10)))
8383
(exposeClockResetEnable dropAbortedPackets)
8484
(exposeClockResetEnable (packetFifoC @_ @1 @Int16 d10 d10 Drop))

clash-protocols/tests/Tests/Protocols/PacketStream/Routing.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ makePropPacketArbiter ::
3939
makePropPacketArbiter SNat SNat mode =
4040
propWithModelSingleDomain
4141
@System
42-
defExpectOptions{eoSampleMax = 1000}
42+
defExpectOptions
4343
genSources
4444
(exposeClockResetEnable L.concat)
4545
(exposeClockResetEnable (packetArbiterC mode))
@@ -87,7 +87,7 @@ makePropPacketDispatcher ::
8787
Property
8888
makePropPacketDispatcher SNat fs =
8989
idWithModelSingleDomain @System
90-
defExpectOptions{eoSampleMax = 2000, eoStopAfterEmpty = 1000}
90+
defExpectOptions
9191
(genPackets 1 10 (genValidPacket defPacketOptions Gen.enumBounded (Range.linear 0 6)))
9292
(exposeClockResetEnable (model 0))
9393
(exposeClockResetEnable (packetDispatcherC fs))

0 commit comments

Comments
 (0)