{-|
Copyright  :  (C) 2013-2016, University of Twente,
                  2017     , Google Inc.
                  2019     , Myrtle Software Ltd
License    :  BSD2 (see the file LICENSE)
Maintainer :  Christiaan Baaij <christiaan.baaij@gmail.com>
-}

{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE ScopedTypeVariables #-}

{-# LANGUAGE Unsafe #-}

{-# OPTIONS_HADDOCK show-extensions #-}

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

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

import GHC.TypeLits                       (KnownNat)

import qualified Clash.Explicit.Testbench as E
import           Clash.Signal
  (HiddenClock, HiddenReset, HiddenClockResetEnable, Signal,
  DomainResetKind, ResetKind(Asynchronous), 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
>>> import Clash.Prelude
>>> 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''.
--
--
-- __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 msg :: String
msg actual :: Signal dom a
actual expected :: Signal dom a
expected ret :: 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 #-}

-- |
--
-- Example:
--
-- @
-- testInput
--   :: HiddenClockResetEnable dom
--   => 'Signal' dom Int
-- testInput = 'stimuliGenerator' $('Clash.Sized.Vector.listToVecTH' [(1::Int),3..21])
-- @
--
-- >>> sampleN 13 testInput
-- [1,3,5,7,9,11,13,15,17,19,21,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 #-}

-- |
--
-- 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 12 (expectedOutput (fromList ([0..10] List.++ [10,10,10])))
-- <BLANKLINE>
-- cycle(system10000): 0, outputVerifier'
-- expected value: 70, not equal to actual value: 0
-- [False
-- cycle(system10000): 1, outputVerifier'
-- expected value: 99, not equal to actual value: 1
-- ,False,False,False,False,False
-- cycle(system10000): 6, outputVerifier'
-- expected value: 7, not equal to actual value: 6
-- ,False
-- cycle(system10000): 7, outputVerifier'
-- expected value: 8, not equal to actual value: 7
-- ,False
-- cycle(system10000): 8, outputVerifier'
-- expected value: 9, not equal to actual value: 8
-- ,False
-- cycle(system10000): 9, outputVerifier'
-- expected value: 10, not equal to actual value: 9
-- ,False,True,True]
--
-- If your working with 'BitVector's containing don't care bits you should use 'outputVerifierBitVector''.
outputVerifier'
  :: ( KnownNat l
     , Eq a
     , ShowX a
     , DomainResetKind dom ~ 'Asynchronous
     , HiddenClock dom
     , HiddenReset dom  )
  => 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, DomainResetKind dom ~ 'Asynchronous,
 Eq a, ShowX a) =>
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 it's expected values.
outputVerifierBitVector'
  :: ( KnownNat l
     , KnownNat n
     , DomainResetKind dom ~ 'Asynchronous
     , HiddenClock dom
     , HiddenReset dom  )
  => 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,
 DomainResetKind dom ~ 'Asynchronous) =>
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 #-}