{-|
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 :: 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 :: 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 :: 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' :: 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' :: 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 :: 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 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 #-}