clash-protocols-0.1: tests/Tests/Protocols/PacketStream/Routing.hs
{-# LANGUAGE NoImplicitPrelude #-}
module Tests.Protocols.PacketStream.Routing (
tests,
) where
import Clash.Prelude
import Hedgehog hiding (Parallel)
import Hedgehog.Gen qualified as Gen
import Hedgehog.Range qualified as Range
import Test.Tasty
import Test.Tasty.Hedgehog (HedgehogTestLimit (HedgehogTestLimit))
import Test.Tasty.Hedgehog.Extra (testProperty)
import Test.Tasty.TH (testGroupGenerator)
import Protocols.Df qualified as Df
import Protocols.Experimental.Hedgehog
import Protocols.Experimental.PacketStream
import Protocols.PacketStream.Hedgehog
import Data.List qualified as L
{- |
Tests a packet arbiter for any data width and number of sources. In particular,
tests that packets from all sources are sent out unmodified in the same order
they were in in the source streams.
-}
makePropPacketArbiter ::
forall sources dataWidth.
(1 <= sources) =>
(1 <= dataWidth) =>
SNat sources ->
SNat dataWidth ->
Df.CollectMode ->
Property
makePropPacketArbiter SNat SNat mode =
propWithModelSingleDomain
@System
defExpectOptions
genSources
(exposeClockResetEnable L.concat)
(exposeClockResetEnable (packetArbiterC mode))
(\xs ys -> partitionPackets xs === partitionPackets ys)
where
(minPackets, maxPackets) = case mode of
-- NoSkip mode needs the same amount of packets generated for each
-- source. Otherwise, starvation happens and the test won't end.
Df.NoSkip -> (5, 5)
_ -> (1, 10)
genSources = mapM setMeta (indicesI @sources)
setMeta j = do
pkts <-
genPackets
@dataWidth
minPackets
maxPackets
(genValidPacket defPacketOptions (pure ()) (Range.linear 0 10))
pure $ L.map (\pkt -> pkt{_meta = j}) pkts
partitionPackets packets =
L.sortOn getMeta
$ L.groupBy (\a b -> _meta a == _meta b)
<$> chunkByPacket packets
getMeta ((pkt : _) : _) = _meta pkt
getMeta _ = error "makePropPacketArbiter: empty partition"
{- |
Generic test function for the packet dispatcher, testing for all data widths,
dispatch functions, and some meta types.
-}
makePropPacketDispatcher ::
forall sinks dataWidth meta.
(KnownNat sinks) =>
(1 <= sinks) =>
(1 <= dataWidth) =>
(TestType meta) =>
(Bounded meta) =>
(Enum meta) =>
(BitPack meta) =>
SNat dataWidth ->
-- | Dispatch function
Vec sinks (meta -> Bool) ->
Property
makePropPacketDispatcher SNat fs =
idWithModelSingleDomain @System
defExpectOptions
(genPackets 1 10 (genValidPacket defPacketOptions Gen.enumBounded (Range.linear 0 6)))
(exposeClockResetEnable (model 0))
(exposeClockResetEnable (packetDispatcherC fs))
where
model ::
Index sinks ->
[PacketStreamM2S dataWidth meta] ->
Vec sinks [PacketStreamM2S dataWidth meta]
model _ [] = pure []
model i (y : ys)
| (fs !! i) (_meta y) =
let next = model 0 ys
in replace i (y : (next !! i)) next
| i < maxBound = model (i + 1) (y : ys)
| otherwise = model 0 ys
-- | Tests the @NoSkip@ packet arbiter with one source; essentially an id test.
prop_packet_arbiter_noskip_id :: Property
prop_packet_arbiter_noskip_id = makePropPacketArbiter d1 d2 Df.NoSkip
-- | Tests the @Skip@ packet arbiter with one source; essentially an id test.
prop_packet_arbiter_skip_id :: Property
prop_packet_arbiter_skip_id = makePropPacketArbiter d1 d2 Df.Skip
-- | Tests the @Parallel@ packet arbiter with one source; essentially an id test.
prop_packet_arbiter_parallel_id :: Property
prop_packet_arbiter_parallel_id = makePropPacketArbiter d1 d2 Df.Parallel
-- | Tests the @NoSkip@ arbiter with five sources.
prop_packet_arbiter_noskip :: Property
prop_packet_arbiter_noskip = makePropPacketArbiter d5 d2 Df.NoSkip
-- | Tests the @Skip@ arbiter with five sources.
prop_packet_arbiter_skip :: Property
prop_packet_arbiter_skip = makePropPacketArbiter d5 d2 Df.Skip
-- | Tests the @Parallel@ arbiter with five sources.
prop_packet_arbiter_parallel :: Property
prop_packet_arbiter_parallel = makePropPacketArbiter d5 d2 Df.Parallel
{- |
Tests that the packet dispatcher works correctly with one sink that accepts
all packets; essentially an id test.
-}
prop_packet_dispatcher_id :: Property
prop_packet_dispatcher_id =
makePropPacketDispatcher
d4
((const True :: Int -> Bool) :> Nil)
{- |
Tests the packet dispatcher for a data width of four bytes and three
overlapping but incomplete dispatch functions, effectively testing whether
the circuit sends input to the first allowed output channel and drops input
if there are none.
-}
prop_packet_dispatcher :: Property
prop_packet_dispatcher = makePropPacketDispatcher d4 fs
where
fs :: Vec 3 (Index 4 -> Bool)
fs =
(>= 3)
:> (>= 2)
:> (>= 1)
:> Nil
tests :: TestTree
tests =
localOption (mkTimeout 20_000_000 {- 20 seconds -})
$ localOption
(HedgehogTestLimit (Just 500))
$(testGroupGenerator)