packages feed

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

{-# LANGUAGE NoImplicitPrelude #-}

module Tests.Protocols.PacketStream.PacketFifo (
  tests,
) where

import Clash.Prelude

import Data.Int (Int16)
import Data.List qualified as L

import Hedgehog
import Hedgehog.Gen qualified as Gen
import Hedgehog.Range qualified as Range

import Prelude qualified as P

import Test.Tasty
import Test.Tasty.Hedgehog (HedgehogTestLimit (HedgehogTestLimit))
import Test.Tasty.Hedgehog.Extra (testProperty)
import Test.Tasty.TH (testGroupGenerator)

import Protocols.Experimental.Hedgehog
import Protocols.Experimental.PacketStream
import Protocols.Experimental.Simulate
import Protocols.PacketStream.Hedgehog

-- | Drops packets that consist of more than 2^n transfers.
dropBigPackets ::
  SNat n ->
  [PacketStreamM2S dataWidth meta] ->
  [PacketStreamM2S dataWidth meta]
dropBigPackets n packets =
  L.concat
    $ L.filter
      (\p -> L.length p < 2 P.^ snatToInteger n)
      (chunkByPacket packets)

-- | Test for id and proper dropping of aborted packets.
prop_packet_fifo_id :: Property
prop_packet_fifo_id =
  idWithModelSingleDomain
    @System
    defExpectOptions
    (genPackets 1 10 (genValidPacket defPacketOptions Gen.enumBounded (Range.linear 0 10)))
    (exposeClockResetEnable dropAbortedPackets)
    (exposeClockResetEnable (packetFifoC @_ @1 @Int16 d10 d10 Backpressure))

{- |
Ensure that backpressure because of a full content RAM and dropping of packets
that are too big to fit in the FIFO is tested.
-}
prop_packet_fifo_small_buffer_id :: Property
prop_packet_fifo_small_buffer_id =
  idWithModelSingleDomain
    @System
    defExpectOptions{eoStopAfterEmpty = Just 500} -- To account for empty cycles due to dropped packets
    (genPackets 1 10 (genValidPacket defPacketOptions Gen.enumBounded (Range.linear 0 30)))
    (exposeClockResetEnable (dropBigPackets d3 . dropAbortedPackets))
    (exposeClockResetEnable (packetFifoC @_ @1 @Int16 d3 d5 Backpressure))

{- |
Test for id using a small meta buffer to ensure backpressure using
the meta buffer is tested.
-}
prop_packet_fifo_small_meta_buffer_id :: Property
prop_packet_fifo_small_meta_buffer_id =
  idWithModelSingleDomain
    @System
    defExpectOptions
    (genPackets 1 30 (genValidPacket defPacketOptions Gen.enumBounded (Range.linear 0 4)))
    (exposeClockResetEnable dropAbortedPackets)
    (exposeClockResetEnable (packetFifoC @_ @1 @Int16 d10 d2 Backpressure))

-- | test for id and proper dropping of aborted packets
prop_overFlowDrop_packetFifo_id :: Property
prop_overFlowDrop_packetFifo_id =
  idWithModelSingleDomain
    @System
    defExpectOptions
    (genPackets 1 10 (genValidPacket defPacketOptions Gen.enumBounded (Range.linear 0 10)))
    (exposeClockResetEnable dropAbortedPackets)
    (exposeClockResetEnable (packetFifoC @_ @1 @Int16 d10 d10 Drop))

-- | test for proper dropping when full
prop_overFlowDrop_packetFifo_drop :: Property
prop_overFlowDrop_packetFifo_drop =
  propWithModelSingleDomain
    @System
    defExpectOptions
    -- make sure the timeout is long as the packetFifo can be quiet for a while while dropping
    (liftA3 (\a b c -> a L.++ b L.++ c) genSmall genBig genSmall)
    (exposeClockResetEnable id)
    (exposeClockResetEnable (packetFifoC @_ @4 @Int16 d3 d5 Drop))
    (\xs ys -> diff ys L.isSubsequenceOf xs)
 where
  genSmall =
    genValidPacket defPacketOptions{poAbortMode = NoAbort} Gen.enumBounded (Range.linear 0 3)
  genBig =
    genValidPacket
      defPacketOptions{poAbortMode = NoAbort}
      Gen.enumBounded
      (Range.linear 9 9)

-- | test to check if there are no gaps inside of packets
prop_packetFifo_no_gaps :: Property
prop_packetFifo_no_gaps = property $ do
  let maxInputSize = 50
      ckt =
        exposeClockResetEnable
          (packetFifoC d12 d12 Backpressure)
          systemClockGen
          resetGen
          enableGen
      gen =
        genPackets
          1
          10
          (genValidPacket defPacketOptions{poAbortMode = NoAbort} Gen.enumBounded (Range.linear 0 10))

  packets :: [PacketStreamM2S 4 Int16] <- forAll gen

  let packetSize = 2 P.^ snatToInteger d12
      cfg = SimulationConfig 1 (2 * packetSize) False
      cktResult = simulateC ckt cfg (Just <$> packets)

  assert $ noGaps $ L.take (5 * maxInputSize) cktResult
 where
  noGaps :: [Maybe (PacketStreamM2S 4 Int16)] -> Bool
  noGaps (Just (PacketStreamM2S{_last = Nothing}) : Nothing : _) = False
  noGaps (_ : xs) = noGaps xs
  noGaps _ = True

tests :: TestTree
tests =
  localOption (mkTimeout 20_000_000 {- 20 seconds -})
    $ localOption
      (HedgehogTestLimit (Just 500))
      $(testGroupGenerator)