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

We simulate DDR signal by using 'Signal's which have exactly half the period
(or double the speed) of our normal 'Signal's.

The primitives in this module can be used to produce or consume DDR signals.

DDR signals are not meant to be used internally in a design,
but only to communicate with the outside world.

In some cases hardware specific DDR IN registers can be inferred by synthesis
tools from these generic primitives. But to be sure your design will synthesize
to dedicated hardware resources use the functions from "Clash.Intel.DDR"
or "Clash.Xilinx.DDR".
-}

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

module Clash.Explicit.DDR
  ( ddrIn
  , ddrOut
    -- * Internal
  , ddrIn#
  , ddrOut#
  )
where

import GHC.Stack (HasCallStack, withFrozenCallStack)

import Clash.Annotations.Primitive    (hasBlackBox)
import Clash.Explicit.Prelude
import Clash.Signal.Internal

{- $setup
>>> :set -XNoImplicitPrelude -XTypeFamilies -XFlexibleInstances
>>> import Clash.Explicit.Prelude
>>> import Clash.Explicit.DDR
>>> :{
instance KnownDomain "Fast" where
  type KnownConf "Fast" = 'DomainConfiguration "Fast" 5000 'Rising 'Asynchronous 'Defined 'ActiveHigh
  knownDomain = SDomainConfiguration SSymbol SNat SRising SAsynchronous SDefined SActiveHigh
:}

-}

-- | DDR input primitive
--
-- Consumes a DDR input signal and produces a regular signal containing a pair
-- of values.
--
-- >>> printX $ sampleN 5 $ ddrIn systemClockGen systemResetGen enableGen (-1,-2,-3) (fromList [0..10] :: Signal "Fast" Int)
-- [(-1,-2),(-1,-2),(-3,2),(3,4),(5,6)]
ddrIn
  :: ( HasCallStack
     , NFDataX a
     , KnownConfiguration fast ('DomainConfiguration fast fPeriod edge reset init polarity)
     , KnownConfiguration slow ('DomainConfiguration slow (2*fPeriod) edge reset init polarity) )
  => Clock slow
  -- ^ clock
  -> Reset slow
  -- ^ reset
  -> Enable slow
  -> (a, a, a)
  -- ^ reset values
  -> Signal fast a
  -- ^ DDR input signal
  -> Signal slow (a, a)
  -- ^ normal speed output pairs
ddrIn :: Clock slow
-> Reset slow
-> Enable slow
-> (a, a, a)
-> Signal fast a
-> Signal slow (a, a)
ddrIn Clock slow
clk Reset slow
rst Enable slow
en (a
i0,a
i1,a
i2) =
  (HasCallStack => Signal fast a -> Signal slow (a, a))
-> Signal fast a -> Signal slow (a, a)
forall a. HasCallStack => (HasCallStack => a) -> a
withFrozenCallStack ((HasCallStack => Signal fast a -> Signal slow (a, a))
 -> Signal fast a -> Signal slow (a, a))
-> (HasCallStack => Signal fast a -> Signal slow (a, a))
-> Signal fast a
-> Signal slow (a, a)
forall a b. (a -> b) -> a -> b
$ Clock slow
-> Reset slow
-> Enable slow
-> a
-> a
-> a
-> Signal fast a
-> Signal slow (a, a)
forall a (slow :: Domain) (fast :: Domain) (fPeriod :: Nat)
       (polarity :: ResetPolarity) (edge :: ActiveEdge)
       (reset :: ResetKind) (init :: InitBehavior).
(HasCallStack, NFDataX a,
 KnownConfiguration
   fast ('DomainConfiguration fast fPeriod edge reset init polarity),
 KnownConfiguration
   slow
   ('DomainConfiguration
      slow (2 * fPeriod) edge reset init polarity)) =>
Clock slow
-> Reset slow
-> Enable slow
-> a
-> a
-> a
-> Signal fast a
-> Signal slow (a, a)
ddrIn# Clock slow
clk Reset slow
rst Enable slow
en a
i0 a
i1 a
i2


-- For details about all the seq's en seqX's
-- see the [Note: register strictness annotations] in Clash.Signal.Internal
ddrIn#
  :: forall a slow fast fPeriod polarity edge reset init
   . ( HasCallStack
     , NFDataX a
     , KnownConfiguration fast ('DomainConfiguration fast fPeriod edge reset init polarity)
     , KnownConfiguration slow ('DomainConfiguration slow (2*fPeriod) edge reset init polarity) )
  => Clock slow
  -> Reset slow
  -> Enable slow
  -> a
  -> a
  -> a
  -> Signal fast a
  -> Signal slow (a,a)
ddrIn# :: Clock slow
-> Reset slow
-> Enable slow
-> a
-> a
-> a
-> Signal fast a
-> Signal slow (a, a)
ddrIn# (Clock SSymbol slow
_) (Reset slow -> Signal slow Bool
forall (dom :: Domain).
KnownDomain dom =>
Reset dom -> Signal dom Bool
unsafeToHighPolarity -> Signal slow Bool
hRst) (Enable slow -> Signal slow Bool
forall (dom :: Domain). Enable dom -> Signal dom Bool
fromEnable -> Signal slow Bool
ena) a
i0 a
i1 a
i2 =
  case forall (dom :: Domain) (sync :: ResetKind).
(KnownDomain dom, DomainResetKind dom ~ sync) =>
SResetKind sync
forall (sync :: ResetKind).
(KnownDomain fast, DomainResetKind fast ~ sync) =>
SResetKind sync
resetKind @fast of
    SResetKind reset
SAsynchronous ->
      (a, a, a)
-> Signal slow Bool
-> Signal slow Bool
-> Signal fast a
-> Signal slow (a, a)
goAsync
        ( String -> a
forall a. (NFDataX a, HasCallStack) => String -> a
deepErrorX String
"ddrIn: initial value 0 undefined"
        , String -> a
forall a. (NFDataX a, HasCallStack) => String -> a
deepErrorX String
"ddrIn: initial value 1 undefined"
        , String -> a
forall a. (NFDataX a, HasCallStack) => String -> a
deepErrorX String
"ddrIn: initial value 2 undefined" )
        Signal slow Bool
hRst
        Signal slow Bool
ena
    SResetKind reset
SSynchronous ->
      (a, a, a)
-> Signal slow Bool
-> Signal slow Bool
-> Signal fast a
-> Signal slow (a, a)
goSync
        ( String -> a
forall a. (NFDataX a, HasCallStack) => String -> a
deepErrorX String
"ddrIn: initial value 0 undefined"
        , String -> a
forall a. (NFDataX a, HasCallStack) => String -> a
deepErrorX String
"ddrIn: initial value 1 undefined"
        , String -> a
forall a. (NFDataX a, HasCallStack) => String -> a
deepErrorX String
"ddrIn: initial value 2 undefined" )
        Signal slow Bool
hRst
        Signal slow Bool
ena
  where
    goSync
      :: (a, a, a)
      -> Signal slow Bool
      -> Signal slow Bool
      -> Signal fast a
      -> Signal slow (a,a)
    goSync :: (a, a, a)
-> Signal slow Bool
-> Signal slow Bool
-> Signal fast a
-> Signal slow (a, a)
goSync (a
o0,a
o1,a
o2) rt :: Signal slow Bool
rt@(~(Bool
r :- Signal slow Bool
rs)) ~(Bool
e :- Signal slow Bool
es) as :: Signal fast a
as@(~(a
x0 :- a
x1 :- Signal fast a
xs)) =
      let (a
o0',a
o1',a
o2') = if Bool
r then (a
i0,a
i1,a
i2) else (a
o2,a
x0,a
x1)
      in a
o0 a -> Signal slow (a, a) -> Signal slow (a, a)
forall a b. a -> b -> b
`seqX` a
o1 a -> Signal slow (a, a) -> Signal slow (a, a)
forall a b. a -> b -> b
`seqX` (a
o0,a
o1)
           (a, a) -> Signal slow (a, a) -> Signal slow (a, a)
forall (dom :: Domain) a. a -> Signal dom a -> Signal dom a
:- (Signal slow Bool
rt Signal slow Bool -> Signal slow (a, a) -> Signal slow (a, a)
`seq` Signal fast a
as Signal fast a -> Signal slow (a, a) -> Signal slow (a, a)
`seq` if Bool
e then (a, a, a)
-> Signal slow Bool
-> Signal slow Bool
-> Signal fast a
-> Signal slow (a, a)
goSync (a
o0',a
o1',a
o2') Signal slow Bool
rs Signal slow Bool
es Signal fast a
xs
                                      else (a, a, a)
-> Signal slow Bool
-> Signal slow Bool
-> Signal fast a
-> Signal slow (a, a)
goSync (a
o0 ,a
o1 ,a
o2)  Signal slow Bool
rs Signal slow Bool
es Signal fast a
xs)

    goAsync
      :: (a, a, a)
      -> Signal slow Bool
      -> Signal slow Bool
      -> Signal fast a
      -> Signal slow (a, a)
    goAsync :: (a, a, a)
-> Signal slow Bool
-> Signal slow Bool
-> Signal fast a
-> Signal slow (a, a)
goAsync (a
o0,a
o1,a
o2) ~(Bool
r :- Signal slow Bool
rs) ~(Bool
e :- Signal slow Bool
es) as :: Signal fast a
as@(~(a
x0 :- a
x1 :- Signal fast a
xs)) =
      let (a
o0',a
o1',a
o2',a
o3',a
o4') = if Bool
r then (a
i0,a
i1,a
i0,a
i1,a
i2) else (a
o0,a
o1,a
o2,a
x0,a
x1)
      in a
o0' a -> Signal slow (a, a) -> Signal slow (a, a)
forall a b. a -> b -> b
`seqX` a
o1' a -> Signal slow (a, a) -> Signal slow (a, a)
forall a b. a -> b -> b
`seqX` (a
o0',a
o1')
           (a, a) -> Signal slow (a, a) -> Signal slow (a, a)
forall (dom :: Domain) a. a -> Signal dom a -> Signal dom a
:- (Signal fast a
as Signal fast a -> Signal slow (a, a) -> Signal slow (a, a)
`seq` if Bool
e then (a, a, a)
-> Signal slow Bool
-> Signal slow Bool
-> Signal fast a
-> Signal slow (a, a)
goAsync (a
o2',a
o3',a
o4') Signal slow Bool
rs Signal slow Bool
es Signal fast a
xs
                             else (a, a, a)
-> Signal slow Bool
-> Signal slow Bool
-> Signal fast a
-> Signal slow (a, a)
goAsync (a
o0',a
o1',a
o2') Signal slow Bool
rs Signal slow Bool
es Signal fast a
xs)

{-# NOINLINE ddrIn# #-}
{-# ANN ddrIn# hasBlackBox #-}

-- | DDR output primitive
--
-- Produces a DDR output signal from a normal signal of pairs of input.
--
-- >>> sampleN 7 (ddrOut systemClockGen systemResetGen enableGen (-1) (fromList [(0,1),(2,3),(4,5)]) :: Signal "Fast" Int)
-- [-1,-1,-1,2,3,4,5]
ddrOut
  :: ( HasCallStack
     , NFDataX a
     , KnownConfiguration fast ('DomainConfiguration fast fPeriod edge reset init polarity)
     , KnownConfiguration slow ('DomainConfiguration slow (2*fPeriod) edge reset init polarity) )
  => Clock slow
  -> Reset slow
  -> Enable slow
  -> a
  -- ^ reset value
  -> Signal slow (a, a)
  -- ^ Normal speed input pairs
  -> Signal fast a
  -- ^ DDR output signal
ddrOut :: Clock slow
-> Reset slow
-> Enable slow
-> a
-> Signal slow (a, a)
-> Signal fast a
ddrOut Clock slow
clk Reset slow
rst Enable slow
en a
i0 =
  (Signal slow a -> Signal slow a -> Signal fast a)
-> (Signal slow a, Signal slow a) -> Signal fast a
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ((HasCallStack => Signal slow a -> Signal slow a -> Signal fast a)
-> Signal slow a -> Signal slow a -> Signal fast a
forall a. HasCallStack => (HasCallStack => a) -> a
withFrozenCallStack ((HasCallStack => Signal slow a -> Signal slow a -> Signal fast a)
 -> Signal slow a -> Signal slow a -> Signal fast a)
-> (HasCallStack =>
    Signal slow a -> Signal slow a -> Signal fast a)
-> Signal slow a
-> Signal slow a
-> Signal fast a
forall a b. (a -> b) -> a -> b
$ Clock slow
-> Reset slow
-> Enable slow
-> a
-> Signal slow a
-> Signal slow a
-> Signal fast a
forall a (fast :: Domain) (fPeriod :: Nat) (edge :: ActiveEdge)
       (reset :: ResetKind) (init :: InitBehavior)
       (polarity :: ResetPolarity) (slow :: Domain).
(HasCallStack, NFDataX a,
 KnownConfiguration
   fast ('DomainConfiguration fast fPeriod edge reset init polarity),
 KnownConfiguration
   slow
   ('DomainConfiguration
      slow (2 * fPeriod) edge reset init polarity)) =>
Clock slow
-> Reset slow
-> Enable slow
-> a
-> Signal slow a
-> Signal slow a
-> Signal fast a
ddrOut# Clock slow
clk Reset slow
rst Enable slow
en a
i0) ((Signal slow a, Signal slow a) -> Signal fast a)
-> (Signal slow (a, a) -> (Signal slow a, Signal slow a))
-> Signal slow (a, a)
-> Signal fast a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Signal slow (a, a) -> (Signal slow a, Signal slow a)
forall a (dom :: Domain).
Bundle a =>
Signal dom a -> Unbundled dom a
unbundle


ddrOut#
  :: ( HasCallStack
     , NFDataX a
     , KnownConfiguration fast ('DomainConfiguration fast fPeriod edge reset init polarity)
     , KnownConfiguration slow ('DomainConfiguration slow (2*fPeriod) edge reset init polarity) )
  => Clock slow
  -> Reset slow
  -> Enable slow
  -> a
  -> Signal slow a
  -> Signal slow a
  -> Signal fast a
ddrOut# :: Clock slow
-> Reset slow
-> Enable slow
-> a
-> Signal slow a
-> Signal slow a
-> Signal fast a
ddrOut# Clock slow
clk Reset slow
rst Enable slow
en a
i0 Signal slow a
xs Signal slow a
ys =
    -- We only observe one reset value, because when the mux switches on the
    -- next clock level, the second register will already be outputting its
    -- first input.
    --
    -- That is why we drop the first value of the stream.
    let (a
_ :- Signal fast a
out) = Signal slow a -> Signal slow a -> Signal fast a
forall (dom :: Domain) a (dom :: Domain) (dom :: Domain).
Signal dom a -> Signal dom a -> Signal dom a
zipSig Signal slow a
xs' Signal slow a
ys' in Signal fast a
out
  where
    xs' :: Signal slow a
xs' = Clock slow
-> Reset slow
-> Enable slow
-> a
-> a
-> Signal slow a
-> Signal slow a
forall (dom :: Domain) a.
(KnownDomain dom, NFDataX a) =>
Clock dom
-> Reset dom
-> Enable dom
-> a
-> a
-> Signal dom a
-> Signal dom a
register# Clock slow
clk Reset slow
rst Enable slow
en (String -> a
forall a. HasCallStack => String -> a
errorX String
"ddrOut: unreachable error") a
i0 Signal slow a
xs
    ys' :: Signal slow a
ys' = Clock slow
-> Reset slow
-> Enable slow
-> a
-> a
-> Signal slow a
-> Signal slow a
forall (dom :: Domain) a.
(KnownDomain dom, NFDataX a) =>
Clock dom
-> Reset dom
-> Enable dom
-> a
-> a
-> Signal dom a
-> Signal dom a
register# Clock slow
clk Reset slow
rst Enable slow
en (String -> a
forall a. (NFDataX a, HasCallStack) => String -> a
deepErrorX String
"ddrOut: initial value undefined") a
i0 Signal slow a
ys
    zipSig :: Signal dom a -> Signal dom a -> Signal dom a
zipSig (a
a :- Signal dom a
as) (a
b :- Signal dom a
bs) = a
a a -> Signal dom a -> Signal dom a
forall (dom :: Domain) a. a -> Signal dom a -> Signal dom a
:- a
b a -> Signal dom a -> Signal dom a
forall (dom :: Domain) a. a -> Signal dom a -> Signal dom a
:- Signal dom a -> Signal dom a -> Signal dom a
zipSig Signal dom a
as Signal dom a
bs
{-# NOINLINE ddrOut# #-}
{-# ANN ddrOut# hasBlackBox #-}