    {-|
    Copyright  :  (C) 2013-2016, University of Twente,
                      2017     , Google Inc.
                      2019     , Myrtle Software Ltd,
                      2021-2023, QBayLogic B.V.
    License    :  BSD2 (see the file LICENSE)
    Maintainer :  QBayLogic B.V. <devops@qbaylogic.com>
    -}

    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE TypeFamilies #-}

    {-# LANGUAGE Unsafe #-}

    {-# OPTIONS_HADDOCK show-extensions #-}

    module Clash.Prelude.Testbench
      ( -- * Testbench functions for circuits
        assert
      , assertBitVector
      , ignoreFor
      , outputVerifier'
      , outputVerifierBitVector'
      , stimuliGenerator

      , E.tbClockGen
      , E.tbEnableGen
      , E.tbSystemClockGen
      , E.clockToDiffClock
      )
    where

    import GHC.TypeLits                       (KnownNat, type (<=))

    import qualified Clash.Explicit.Testbench as E
    import           Clash.Signal
      (HiddenClock, HiddenReset, HiddenClockResetEnable, Signal,
      hideClock, hideReset, hideClockResetEnable)
    import Clash.Promoted.Nat                 (SNat)
    import Clash.Sized.BitVector              (BitVector)
    import Clash.Sized.Vector                 (Vec)
    import Clash.XException                   (ShowX)

    {- $setup
    >>> :set -XTemplateHaskell -XDataKinds -XTypeApplications
    >>> :m -Clash.Explicit.Prelude
    >>> :m -Clash.Explicit.Prelude.Safe
    >>> :m -Clash.Explicit.Testbench
    >>> import Clash.Prelude
    >>> import Clash.Prelude.Testbench
    >>> let testInput = stimuliGenerator $(listToVecTH [(1::Int),3..21])
    >>> let expectedOutput = outputVerifier' $(listToVecTH ([70,99,2,3,4,5,7,8,9,10]::[Int]))
    -}

    -- | Compares the first two 'Signal's for equality and logs a warning when they
    -- are not equal. The second 'Signal' is considered the expected value. This
    -- function simply returns the third 'Signal' unaltered as its result. This
    -- function is used by 'outputVerifier''.
    --
    -- === Usage in @clashi@ #assert-clashi#
    --
    -- __NB__: When simulating a component that uses 'assert' in @clashi@, usually,
    -- the warnings are only logged the first time the component is simulated.
    -- Issuing @:reload@ in @clashi@ will discard the cached result of the
    -- computation, and warnings will once again be emitted.
    --
    -- __NB__: This function /can/ be used in synthesizable designs.
    assert
      :: (Eq a, ShowX a, HiddenClock dom , HiddenReset dom )
      => String
      -- ^ Additional message
      -> Signal dom a
      -- ^ Checked value
      -> Signal dom a
      -- ^ Expected value
      -> Signal dom b
      -- ^ Return value
      -> Signal dom b
    assert :: forall a (dom :: Domain) b.
    (Eq a, ShowX a, HiddenClock dom, HiddenReset dom) =>
    String
    -> Signal dom a -> Signal dom a -> Signal dom b -> Signal dom b
    assert String
    msg Signal dom a
    actual Signal dom a
    expected Signal dom b
    ret =
      (Reset dom
     -> String
     -> Signal dom a
     -> Signal dom a
     -> Signal dom b
     -> Signal dom b)
    -> String
    -> Signal dom a
    -> Signal dom a
    -> Signal dom b
    -> Signal dom b
    forall (dom :: Domain) r. HiddenReset dom => (Reset dom -> r) -> r
    hideReset ((Clock dom
     -> Reset dom
     -> String
     -> Signal dom a
     -> Signal dom a
     -> Signal dom b
     -> Signal dom b)
    -> Reset dom
    -> String
    -> Signal dom a
    -> Signal dom a
    -> Signal dom b
    -> Signal dom b
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock Clock dom
    -> Reset dom
    -> String
    -> Signal dom a
    -> Signal dom a
    -> Signal dom b
    -> Signal dom b
    forall (dom :: Domain) a b.
    (KnownDomain dom, Eq a, ShowX a) =>
    Clock dom
    -> Reset dom
    -> String
    -> Signal dom a
    -> Signal dom a
    -> Signal dom b
    -> Signal dom b
    E.assert) String
    msg Signal dom a
    actual Signal dom a
    expected Signal dom b
    ret
    {-# INLINE assert #-}

    -- | The same as 'assert', but can handle don't care bits in its expected value.
    assertBitVector
      :: (KnownNat n, HiddenClock dom , HiddenReset dom )
      => String
      -- ^ Additional message
      -> Signal dom (BitVector n)
      -- ^ Checked value
      -> Signal dom (BitVector n)
      -- ^ Expected value
      -> Signal dom b
      -- ^ Return value
      -> Signal dom b
    assertBitVector :: forall (n :: Nat) (dom :: Domain) b.
    (KnownNat n, HiddenClock dom, HiddenReset dom) =>
    String
    -> Signal dom (BitVector n)
    -> Signal dom (BitVector n)
    -> Signal dom b
    -> Signal dom b
    assertBitVector String
    msg Signal dom (BitVector n)
    actual Signal dom (BitVector n)
    expected Signal dom b
    ret =
      (Reset dom
     -> String
     -> Signal dom (BitVector n)
     -> Signal dom (BitVector n)
     -> Signal dom b
     -> Signal dom b)
    -> String
    -> Signal dom (BitVector n)
    -> Signal dom (BitVector n)
    -> Signal dom b
    -> Signal dom b
    forall (dom :: Domain) r. HiddenReset dom => (Reset dom -> r) -> r
    hideReset ((Clock dom
     -> Reset dom
     -> String
     -> Signal dom (BitVector n)
     -> Signal dom (BitVector n)
     -> Signal dom b
     -> Signal dom b)
    -> Reset dom
    -> String
    -> Signal dom (BitVector n)
    -> Signal dom (BitVector n)
    -> Signal dom b
    -> Signal dom b
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock Clock dom
    -> Reset dom
    -> String
    -> Signal dom (BitVector n)
    -> Signal dom (BitVector n)
    -> Signal dom b
    -> Signal dom b
    forall (dom :: Domain) (n :: Nat) b.
    (KnownDomain dom, KnownNat n) =>
    Clock dom
    -> Reset dom
    -> String
    -> Signal dom (BitVector n)
    -> Signal dom (BitVector n)
    -> Signal dom b
    -> Signal dom b
    E.assertBitVector) String
    msg Signal dom (BitVector n)
    actual Signal dom (BitVector n)
    expected Signal dom b
    ret
    {-# INLINE assertBitVector #-}

    -- |
    --
    -- Example:
    --
    -- @
    -- testInput
    --   :: HiddenClockResetEnable dom
    --   => 'Signal' dom Int
    -- testInput = 'stimuliGenerator' $('Clash.Sized.Vector.listToVecTH' [(1::Int),3..21])
    -- @
    --
    -- >>> sampleN @System 13 testInput
    -- [1,1,3,5,7,9,11,13,15,17,19,21,21]
    stimuliGenerator
      :: ( KnownNat l
         , HiddenClock dom
         , HiddenReset dom  )
      => Vec l a
      -- ^ Samples to generate
      -> Signal dom a
      -- ^ Signal of given samples
    stimuliGenerator :: forall (l :: Nat) (dom :: Domain) a.
    (KnownNat l, HiddenClock dom, HiddenReset dom) =>
    Vec l a -> Signal dom a
    stimuliGenerator = (Reset dom -> Vec l a -> Signal dom a) -> Vec l a -> Signal dom a
    forall (dom :: Domain) r. HiddenReset dom => (Reset dom -> r) -> r
    hideReset ((Clock dom -> Reset dom -> Vec l a -> Signal dom a)
    -> Reset dom -> Vec l a -> Signal dom a
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock Clock dom -> Reset dom -> Vec l a -> Signal dom a
    forall (l :: Nat) (dom :: Domain) a.
    (KnownNat l, KnownDomain dom) =>
    Clock dom -> Reset dom -> Vec l a -> Signal dom a
    E.stimuliGenerator)
    {-# INLINE stimuliGenerator #-}

    -- | Compare a signal (coming from a circuit) to a vector of samples. If a
    -- sample from the signal is not equal to the corresponding sample in the
    -- vector, print to stderr and continue testing. This function is
    -- synthesizable in the sense that HDL simulators will run it.
    --
    -- __NB__: This function uses 'assert'. When simulating this function in
    -- @clashi@, read the [note](#assert-clashi).
    --
    -- Example:
    --
    -- @
    -- expectedOutput
    --   :: HiddenClockResetEnable dom
    --   -> 'Signal' dom Int -> 'Signal' dom Bool
    -- expectedOutput = 'outputVerifier'' $('Clash.Sized.Vector.listToVecTH' ([70,99,2,3,4,5,7,8,9,10]::[Int]))
    -- @
    --
    -- >>> import qualified Data.List as List
    -- >>> sampleN @System 12 (expectedOutput (fromList (0:[0..10] List.++ [10,10,10])))
    -- <BLANKLINE>
    -- cycle(<Clock: System>): 0, outputVerifier
    -- expected value: 70, not equal to actual value: 0
    -- [False
    -- cycle(<Clock: System>): 1, outputVerifier
    -- expected value: 70, not equal to actual value: 0
    -- ,False
    -- cycle(<Clock: System>): 2, outputVerifier
    -- expected value: 99, not equal to actual value: 1
    -- ,False,False,False,False,False
    -- cycle(<Clock: System>): 7, outputVerifier
    -- expected value: 7, not equal to actual value: 6
    -- ,False
    -- cycle(<Clock: System>): 8, outputVerifier
    -- expected value: 8, not equal to actual value: 7
    -- ,False
    -- cycle(<Clock: System>): 9, outputVerifier
    -- expected value: 9, not equal to actual value: 8
    -- ,False
    -- cycle(<Clock: System>): 10, outputVerifier
    -- expected value: 10, not equal to actual value: 9
    -- ,False,True]
    --
    -- If you're working with 'BitVector's containing don't care bits you should use 'outputVerifierBitVector''.
    outputVerifier'
      :: ( KnownNat l
         , Eq a
         , ShowX a
         , HiddenClock dom
         , HiddenReset dom
         , 1 <= l
         )
      => Vec l a
      -- ^ Samples to compare with
      -> Signal dom a
      -- ^ Signal to verify
      -> Signal dom Bool
      -- ^ Indicator that all samples are verified
    outputVerifier' :: forall (l :: Nat) a (dom :: Domain).
    (KnownNat l, Eq a, ShowX a, HiddenClock dom, HiddenReset dom,
     1 <= l) =>
    Vec l a -> Signal dom a -> Signal dom Bool
    outputVerifier' = (Reset dom -> Vec l a -> Signal dom a -> Signal dom Bool)
    -> Vec l a -> Signal dom a -> Signal dom Bool
    forall (dom :: Domain) r. HiddenReset dom => (Reset dom -> r) -> r
    hideReset ((Clock dom
     -> Reset dom -> Vec l a -> Signal dom a -> Signal dom Bool)
    -> Reset dom -> Vec l a -> Signal dom a -> Signal dom Bool
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock Clock dom
    -> Reset dom -> Vec l a -> Signal dom a -> Signal dom Bool
    forall (l :: Nat) a (dom :: Domain).
    (KnownNat l, KnownDomain dom, Eq a, ShowX a, 1 <= l) =>
    Clock dom
    -> Reset dom -> Vec l a -> Signal dom a -> Signal dom Bool
    E.outputVerifier')
    {-# INLINE outputVerifier' #-}

    -- | Same as 'outputVerifier'',
    -- but can handle don't care bits in its expected values.
    outputVerifierBitVector'
      :: ( KnownNat l
         , KnownNat n
         , HiddenClock dom
         , HiddenReset dom
         , 1 <= l
         )
      => Vec l (BitVector n)
      -- ^ Samples to compare with
      -> Signal dom (BitVector n)
      -- ^ Signal to verify
      -> Signal dom Bool
      -- ^ Indicator that all samples are verified
    outputVerifierBitVector' :: forall (l :: Nat) (n :: Nat) (dom :: Domain).
    (KnownNat l, KnownNat n, HiddenClock dom, HiddenReset dom,
     1 <= l) =>
    Vec l (BitVector n) -> Signal dom (BitVector n) -> Signal dom Bool
    outputVerifierBitVector' = (Reset dom
     -> Vec l (BitVector n)
     -> Signal dom (BitVector n)
     -> Signal dom Bool)
    -> Vec l (BitVector n)
    -> Signal dom (BitVector n)
    -> Signal dom Bool
    forall (dom :: Domain) r. HiddenReset dom => (Reset dom -> r) -> r
    hideReset ((Clock dom
     -> Reset dom
     -> Vec l (BitVector n)
     -> Signal dom (BitVector n)
     -> Signal dom Bool)
    -> Reset dom
    -> Vec l (BitVector n)
    -> Signal dom (BitVector n)
    -> Signal dom Bool
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock Clock dom
    -> Reset dom
    -> Vec l (BitVector n)
    -> Signal dom (BitVector n)
    -> Signal dom Bool
    forall (l :: Nat) (n :: Nat) (dom :: Domain).
    (KnownNat l, KnownNat n, KnownDomain dom, 1 <= l) =>
    Clock dom
    -> Reset dom
    -> Vec l (BitVector n)
    -> Signal dom (BitVector n)
    -> Signal dom Bool
    E.outputVerifierBitVector')
    {-# INLINE outputVerifierBitVector' #-}

    -- | Ignore signal for a number of cycles, while outputting a static value.
    ignoreFor
      :: HiddenClockResetEnable dom
      => SNat n
      -- ^ Number of cycles to ignore incoming signal
      -> a
      -- ^ Value function produces when ignoring signal
      -> Signal dom a
      -- ^ Incoming signal
      -> Signal dom a
      -- ^ Either a passthrough of the incoming signal, or the static value
      -- provided as the second argument.
    ignoreFor :: forall (dom :: Domain) (n :: Nat) a.
    HiddenClockResetEnable dom =>
    SNat n -> a -> Signal dom a -> Signal dom a
    ignoreFor = (KnownDomain dom =>
     Clock dom
     -> Reset dom
     -> Enable dom
     -> SNat n
     -> a
     -> Signal dom a
     -> Signal dom a)
    -> SNat n -> a -> Signal dom a -> Signal dom a
    forall (dom :: Domain) r.
    HiddenClockResetEnable dom =>
    (KnownDomain dom => Clock dom -> Reset dom -> Enable dom -> r) -> r
    hideClockResetEnable Clock dom
    -> Reset dom
    -> Enable dom
    -> SNat n
    -> a
    -> Signal dom a
    -> Signal dom a
    KnownDomain dom =>
    Clock dom
    -> Reset dom
    -> Enable dom
    -> SNat n
    -> a
    -> Signal dom a
    -> Signal dom a
    forall (dom :: Domain) (n :: Nat) a.
    KnownDomain dom =>
    Clock dom
    -> Reset dom
    -> Enable dom
    -> SNat n
    -> a
    -> Signal dom a
    -> Signal dom a
    E.ignoreFor
    {-# INLINE ignoreFor #-}