{-# LANGUAGE MagicHash #-}

module CLaSH.Signal
  ( -- * Implicitly clocked synchronous signal
    Signal
    -- * Basic circuit functions
  , signal
  , register
  , regEn
  , mux
    -- * Boolean connectives
  , (.&&.), (.||.), not1
    -- * Product/Signal isomorphism
  , Bundle
  , Unbundled'
  , bundle'
  , unbundle'
    -- * Simulation functions (not synthesisable)
  , simulate
  , simulateB
    -- * List \<-\> Signal conversion (not synthesisable)
  , sample
  , sampleN
  , fromList
    -- * Type classes
    -- ** 'Eq'-like
  , (.==.), (./=.)
    -- ** 'Ord'-like
  , compare1, (.<.), (.<=.), (.>=.), (.>.)
    -- ** 'Enum'-like
  , fromEnum1
    -- ** 'Rational'-like
  , toRational1
    -- ** 'Integral'-like
  , toInteger1
    -- ** 'Bits'-like
  , testBit1
  , popCount1
  , shift1
  , rotate1
  , setBit1
  , clearBit1
  , shiftL1
  , unsafeShiftL1
  , shiftR1
  , unsafeShiftR1
  , rotateL1
  , rotateR1
  )
where

import CLaSH.Signal.Internal  (CSignal, register#, regEn#, signal#, (.==.), (./=.),
                               compare1, (.<.), (.<=.), (.>=.), (.>.), fromEnum1,
                               toRational1, toInteger1, testBit1, popCount1,
                               shift1, rotate1, setBit1, clearBit1, shiftL1,
                               unsafeShiftL1, shiftR1, unsafeShiftR1, rotateL1,
                               rotateR1, (.||.), (.&&.), not1, mux)
import CLaSH.Signal.Explicit  (SystemClock, cfromList, csample, csampleN,
                               systemClock)
import CLaSH.Signal.Bundle    (Bundle (..), Unbundled)

-- * Implicitly clocked synchronous signal

-- | Signal synchronised to the \"system\" clock, which has a period of 1000.
type Signal a = CSignal SystemClock a

-- * Basic circuit functions

{-# INLINE signal #-}
-- | Create a constant 'Signal' from a combinational value
--
-- >>> sample (signal 4)
-- [4, 4, 4, 4, ...
signal :: a -> Signal a
signal = signal#

{-# INLINE register #-}
-- | 'register' @i s@ delays the values in 'Signal' @s@ for one cycle, and sets
-- the value at time 0 to @i@
--
-- >>> sampleN 3 (register 8 (fromList [1,2,3,4]))
-- [8,1,2]
register :: a -> Signal a -> Signal a
register = register# systemClock

{-# INLINE regEn #-}
-- | Version of 'register' that only updates its content when its second argument
-- is asserted. So given:
--
-- @
-- oscillate = register False ('not1' oscillate)
-- count     = regEn 0 oscillate (count + 1)
-- @
--
-- We get:
--
-- >>> sampleN 8 oscillate
-- [False,True,False,True,False,True,False,True]
-- >>> sampleN 8 count
-- [0,0,1,1,2,2,3,3]
regEn :: a -> Signal Bool -> Signal a -> Signal a
regEn = regEn# systemClock

-- * Product/Signal isomorphism

-- | Isomorphism between a 'Signal' of a product type (e.g. a tuple) and a
-- product type of 'Signal's.
type Unbundled' a = Unbundled SystemClock a

{-# INLINE unbundle' #-}
-- | Example:
--
-- > unbundle' :: Signal (a,b) -> (Signal a, Signal b)
--
-- However:
--
-- > unbundle' :: Signal Bit -> Signal Bit
unbundle' :: Bundle a => Signal a -> Unbundled' a
unbundle' = unbundle systemClock

{-# INLINE bundle' #-}
-- | Example:
--
-- > bundle' :: (Signal a, Signal b) -> Signal (a,b)
--
-- However:
--
-- > bundle' :: Signal Bit -> Signal Bit
bundle' :: Bundle a => Unbundled' a -> Signal a
bundle' = bundle systemClock

-- * Simulation functions (not synthesisable)

-- | Simulate a (@'Signal' a -> 'Signal' b@) function given a list of samples of
-- type @a@
--
-- >>> simulate (register 8) [1, 2, 3, ...
-- [8, 1, 2, 3, ...
--
-- __NB__: This function is not synthesisable
simulate :: (Signal a -> Signal b) -> [a] -> [b]
simulate f = sample . f . fromList

-- | Simulate a (@'Bundled' a -> 'Bundled' b@) function given a list of samples
-- of type @a@
--
-- >>> simulateB (wrap . register (8,8) . unwrap) [(1,1), (2,2), (3,3), ...
-- [(8,8), (1,1), (2,2), (3,3), ...
--
-- __NB__: This function is not synthesisable
simulateB :: (Bundle a, Bundle b) => (Unbundled' a -> Unbundled' b) -> [a] -> [b]
simulateB f = simulate (bundle' . f . unbundle')

-- * List \<-\> Signal conversion (not synthesisable)

-- | Get an infinite list of samples from a 'Signal'
--
-- The elements in the list correspond to the values of the 'Signal' at
-- consecutive clock cycles
--
-- > sample s == [s0, s1, s2, s3, ...
--
-- __NB__: This function is not synthesisable
sample :: Signal a -> [a]
sample = csample

-- | Get a list of @n@ samples from a 'Signal'
--
-- The elements in the list correspond to the values of the 'Signal' at
-- consecutive clock cycles
--
-- > sampleN 3 s == [s0, s1, s2]
--
-- __NB__: This function is not synthesisable
sampleN :: Int -> Signal a -> [a]
sampleN = csampleN

-- | Create a 'Signal' from a list
--
-- Every element in the list will correspond to a value of the signal for one
-- clock cycle.
--
-- >>> sampleN 2 (fromList [1,2,3,4,5])
-- [1,2]
--
-- __NB__: This function is not synthesisable
fromList :: [a] -> Signal a
fromList = cfromList