Skip to content

Commit 1b380fd

Browse files
committed
add timeout to driveStandard function
1 parent 6026ee9 commit 1b380fd

File tree

2 files changed

+84
-39
lines changed

2 files changed

+84
-39
lines changed

src/Protocols/Wishbone/Standard/Hedgehog.hs

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ import GHC.Stack (HasCallStack)
5454
data WishboneMasterRequest addressWidth dat
5555
= Read (BitVector addressWidth) (BitVector (BitSize dat `DivRU` 8))
5656
| Write (BitVector addressWidth) (BitVector (BitSize dat `DivRU` 8)) dat
57+
deriving (Generic)
5758

5859
deriving instance (KnownNat addressWidth, KnownNat (BitSize a), Show a) => (Show (WishboneMasterRequest addressWidth a))
60+
deriving instance (KnownNat addressWidth, KnownNat (BitSize a), ShowX a) => (ShowX (WishboneMasterRequest addressWidth a))
5961

6062
--
6163
-- Validation for (lenient) spec compliance
@@ -238,13 +240,13 @@ stallStandard stallsPerCycle =
238240
-- tell the master that the slave has no reply yet
239241
(emptyWishboneS2M :-)
240242
-- tell the slave that the cycle is over
241-
(m :-)
243+
(emptyWishboneM2S :-)
242244
(go (st - 1 : stalls) Nothing m2s s2m)
243245
-- done stalling, got a reply last second, pass through
244246
| busCycle m && strobe m && st == 0 && hasTerminateFlag s =
245247
B.bimap
246248
(s :-)
247-
(m :-)
249+
(emptyWishboneM2S :-)
248250
(go stalls Nothing m2s s2m)
249251
-- done stalling but no termination signal yet, just pass through to give the slave
250252
-- the chance to reply
@@ -284,15 +286,15 @@ driveStandard ::
284286
C.KnownNat (C.BitSize a)
285287
) =>
286288
ExpectOptions ->
287-
-- | Requests to send out
288-
[WishboneMasterRequest addressWidth a] ->
289+
-- | Requests to send out and maximum cycles a transfer is allowed to take
290+
[(WishboneMasterRequest addressWidth a, Int)] ->
289291
Circuit () (Wishbone dom 'Standard addressWidth a)
290292
driveStandard ExpectOptions {..} reqs =
291293
Circuit $
292294
((),)
293295
. C.fromList_lazy
294296
. (emptyWishboneM2S :)
295-
. go eoResetCycles reqs
297+
. go eoResetCycles 0 reqs
296298
. (\s -> C.sample_lazy s)
297299
. snd
298300
where
@@ -326,22 +328,25 @@ driveStandard ExpectOptions {..} reqs =
326328

327329
go ::
328330
Int ->
329-
[WishboneMasterRequest addressWidth a] ->
331+
Int -> -- cycles in the current transaction
332+
[(WishboneMasterRequest addressWidth a, Int)] ->
330333
[WishboneS2M a] ->
331334
[WishboneM2S addressWidth (BitSize a `DivRU` 8) a]
332-
go nResets dat ~(rep : replies)
333-
| nResets > 0 = emptyWishboneM2S : (rep `C.seqX` go (nResets - 1) dat replies)
335+
go nResets _ dat ~(rep : replies)
336+
| nResets > 0 = emptyWishboneM2S : (rep `C.seqX` go (nResets - 1) 0 dat replies)
334337
-- no more data to send
335-
go _ [] ~(rep : replies) = emptyWishboneM2S : (rep `C.seqX` go 0 [] replies)
336-
go _ (d : dat) ~(rep : replies)
338+
go _ _ [] ~(rep : replies) = emptyWishboneM2S : (rep `C.seqX` go 0 0 [] replies)
339+
go _ cycles ((d, timeOut) : dat) ~(rep : replies)
337340
-- the sent data was acknowledged, end the cycle before continuing
338-
| acknowledge rep || err rep = emptyWishboneM2S : (rep `C.seqX` go 0 dat replies)
341+
| acknowledge rep || err rep = emptyWishboneM2S : (rep `C.seqX` go 0 0 dat replies)
339342
-- end cycle, continue but send the previous request again
340-
| retry rep = emptyWishboneM2S : (rep `C.seqX` go 0 (d : dat) replies)
341-
-- not a termination signal, so keep sending the data
343+
| retry rep = emptyWishboneM2S : (rep `C.seqX` go 0 0 ((d, timeOut) : dat) replies)
344+
-- not a termination signal, the transfer timed out
345+
| cycles == timeOut = emptyWishboneM2S : (rep `C.seqX` go 0 0 dat replies)
346+
-- not a termination signal, no timeout, keep sending request
342347
| otherwise -- trace "D in-cycle wait for ACK" $
343348
=
344-
transferToSignals d : (rep `C.seqX` go 0 (d : dat) replies)
349+
transferToSignals d : (rep `C.seqX` go 0 (cycles + 1) ((d, timeOut) : dat) replies)
345350

346351
-- | Circuit which validates the wishbone communication signals between a
347352
-- master and a slave circuit.
@@ -410,7 +415,8 @@ validatorCircuitLenient =
410415
-- | Test a wishbone 'Standard' circuit against a pure model.
411416
wishbonePropWithModel ::
412417
forall dom a addressWidth st.
413-
( Eq a,
418+
( HasCallStack,
419+
Eq a,
414420
C.ShowX a,
415421
Show a,
416422
C.NFDataX a,
@@ -436,36 +442,45 @@ wishbonePropWithModel eOpts model circuit0 inputGen st = H.property $ do
436442

437443
let
438444
n = P.length input
439-
genStall = Gen.integral (Range.linear 0 10)
445+
genStall = Gen.integral (Range.linear 0 15)
446+
genTimeOut = Gen.integral (Range.linear 1 15)
440447

448+
timeOuts <- H.forAll (Gen.list (Range.singleton n) genTimeOut)
441449
stalls <- H.forAll (Gen.list (Range.singleton n) genStall)
442450

443451
let
444452
resets = 50
445-
driver = driveStandard @dom (defExpectOptions {eoResetCycles = resets}) input
453+
inputs = P.zip input timeOuts
454+
driver = driveStandard @dom (defExpectOptions {eoResetCycles = resets}) inputs |> validatorCircuit |> stallC
446455
stallC = stallStandard stalls
447-
circuit1 = stallC |> validatorCircuit |> circuit0
448-
(_, _ : s2m) = observeComposedWishboneCircuit (eoTimeout eOpts) driver circuit1
456+
circuit1 = validatorCircuit |> circuit0
457+
(m2s, s2m) = observeComposedWishboneCircuit (eoTimeout eOpts) driver circuit1
458+
459+
matchModel 0 m2s s2m st === Right ()
449460

450-
matchModel 0 s2m input st === Right ()
451461
where
462+
m2sToReq WishboneM2S{..}
463+
| not (busCycle && strobe) = Nothing
464+
| writeEnable = Just $ Write addr busSelect writeData
465+
| otherwise = Just $ Read addr busSelect
466+
452467
matchModel ::
453468
Int ->
469+
[WishboneM2S addressWidth (BitSize a `DivRU` 8) a] ->
454470
[WishboneS2M a] ->
455-
[WishboneMasterRequest addressWidth a] ->
456471
st ->
457472
Either (Int, String) ()
458-
matchModel _ [] _ _ = Right () -- so far everything is good but the sampling stopped
473+
matchModel _ [] _ _ = Right ()
459474
matchModel _ _ [] _ = Right ()
460-
matchModel cyc (s : s2m) (req : reqs0) state
461-
| not (hasTerminateFlag s) = s `C.seqX` matchModel (succ cyc) s2m (req : reqs0) state
462-
| otherwise = case model req s state of
463-
Left err -> Left (cyc, err)
464-
Right st1 -> s `C.seqX` matchModel (succ cyc) s2m reqs1 st1
465-
where
466-
reqs1
467-
| retry s = req : reqs0
468-
| otherwise = reqs0
475+
matchModel cyc (m : m2s) (s : s2m) state
476+
| Nothing <- m2sToReq m, hasTerminateFlag s =
477+
Left (cyc, "Termination flag outside of bus cycle")
478+
| Just req <- m2sToReq m, hasTerminateFlag s =
479+
case model req s state of
480+
Left err -> Left (cyc, err)
481+
Right st1 -> s `C.seqX` matchModel (succ cyc) m2s s2m st1
482+
| otherwise = s `C.seqX` matchModel (succ cyc) m2s s2m state
483+
469484

470485
observeComposedWishboneCircuit ::
471486
(KnownDomain dom) =>

tests/Tests/Protocols/Wishbone.hs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import Test.Tasty.Hedgehog (HedgehogTestLimit (HedgehogTestLimit))
2222
import Test.Tasty.Hedgehog.Extra (testProperty)
2323
import Test.Tasty.TH (testGroupGenerator)
2424
import Prelude hiding (undefined)
25+
import GHC.Stack (HasCallStack)
2526

2627
smallInt :: Range Int
2728
smallInt = Range.linear 0 10
@@ -48,7 +49,7 @@ genWishboneTransfer genA =
4849

4950
addrReadIdWb ::
5051
forall dom addressWidth.
51-
(KnownNat addressWidth) =>
52+
(HasCallStack, KnownNat addressWidth) =>
5253
Circuit (Wishbone dom 'Standard addressWidth (BitVector addressWidth)) ()
5354
addrReadIdWb = Circuit go
5455
where
@@ -64,21 +65,41 @@ addrReadIdWb = Circuit go
6465
}
6566
| otherwise = emptyWishboneS2M
6667

68+
-- | Same as 'addrReadIdWb' but responses are delayed by one cycle.
69+
addrReadIdRegisterWb ::
70+
forall dom addressWidth.
71+
(HasCallStack, KnownNat addressWidth, HiddenClockResetEnable dom) =>
72+
Circuit (Wishbone dom 'Standard addressWidth (BitVector addressWidth)) ()
73+
addrReadIdRegisterWb = Circuit go
74+
where
75+
go (m2s, ()) = (reply <$> register emptyWishboneM2S m2s <*> m2s, ())
76+
77+
reply prev cur
78+
| not (busCycle cur && strobe cur) = emptyWishboneS2M
79+
| busCycle prev && strobe prev && writeEnable prev =
80+
emptyWishboneS2M { acknowledge = True }
81+
| busCycle prev && strobe prev =
82+
(emptyWishboneS2M @(BitVector addressWidth))
83+
{ acknowledge = True,
84+
readData = addr prev
85+
}
86+
| otherwise = emptyWishboneS2M
87+
6788
addrReadIdWbModel ::
68-
(KnownNat addressWidth) =>
89+
(HasCallStack, KnownNat addressWidth) =>
6990
WishboneMasterRequest addressWidth (BitVector addressWidth) ->
7091
WishboneS2M (BitVector addressWidth) ->
7192
-- | pure state
7293
() ->
7394
Either String ()
74-
addrReadIdWbModel (Read addr _) s@WishboneS2M {..} ()
75-
| acknowledge && hasX readData == Right addr = Right ()
95+
addrReadIdWbModel req@(Read addr _) s@WishboneS2M {..} ()
96+
| acknowledge && not (hasUndefined readData) && readData == addr = Right ()
7697
| otherwise =
77-
Left $ "Read should have been acknowledged with address as DAT " <> showX s
78-
addrReadIdWbModel Write {} s@WishboneS2M {..} ()
98+
Left $ "Read should have been acknowledged with address as DAT " <> showX req <> "\n" <> showX s
99+
addrReadIdWbModel req@Write{} s@WishboneS2M {..} ()
79100
| acknowledge && hasUndefined readData = Right ()
80101
| otherwise =
81-
Left $ "Write should have been acknowledged with no DAT " <> showX s
102+
Left $ "Write should have been acknowledged with no DAT " <> showX req <> "\n" <> showX s
82103

83104
prop_addrReadIdWb_model :: Property
84105
prop_addrReadIdWb_model = withClockResetEnable clockGen resetGen enableGen $
@@ -89,6 +110,15 @@ prop_addrReadIdWb_model = withClockResetEnable clockGen resetGen enableGen $
89110
(genData $ genWishboneTransfer @10 genDefinedBitVector)
90111
()
91112

113+
prop_addrReadIdRegisterWb_model :: Property
114+
prop_addrReadIdRegisterWb_model = withClockResetEnable clockGen resetGen enableGen $
115+
wishbonePropWithModel @System
116+
defExpectOptions
117+
addrReadIdWbModel
118+
addrReadIdRegisterWb
119+
(genData $ genWishboneTransfer @10 genDefinedBitVector)
120+
()
121+
92122
--
93123
-- memory element circuit
94124
--
@@ -144,7 +174,7 @@ tests :: TestTree
144174
tests =
145175
-- TODO: Move timeout option to hedgehog for better error messages.
146176
-- TODO: Does not seem to work for combinatorial loops like @let x = x in x@??
147-
localOption (mkTimeout 12_000_000 {- 12 seconds -}) $
177+
localOption (mkTimeout 25_000_000 {- 25 seconds -}) $
148178
localOption
149179
(HedgehogTestLimit (Just 1000))
150180
$(testGroupGenerator)

0 commit comments

Comments
 (0)