{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE MagicHash           #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE TypeOperators       #-}
module Clash.Explicit.DDR
  ( ddrIn
  , ddrOut
    
  , ddrIn#
  , ddrOut#
  )
where
import GHC.Stack (HasCallStack, withFrozenCallStack)
import Clash.Explicit.Prelude
import Clash.Signal.Internal
ddrIn
  :: ( HasCallStack
     , fast ~ 'Dom n pFast
     , slow ~ 'Dom n (2*pFast))
  => Clock slow gated
  
  -> Reset slow synchronous
  
  -> (a, a, a)
  
  -> Signal fast a
  
  -> Signal slow (a,a)
  
ddrIn clk rst (i0,i1,i2) = withFrozenCallStack $ ddrIn# clk rst i0 i1 i2
ddrIn#
  :: forall a slow fast n pFast gated synchronous
   . ( HasCallStack
     , fast ~ 'Dom n pFast
     , slow ~ 'Dom n (2*pFast))
  => Clock slow gated
  -> Reset slow synchronous
  -> a
  -> a
  -> a
  -> Signal fast a
  -> Signal slow (a,a)
ddrIn# (Clock {}) (Sync rst) i0 i1 i2 =
  go ((errorX "ddrIn: initial value 0 undefined")
     ,(errorX "ddrIn: initial value 1 undefined")
     ,(errorX "ddrIn: initial value 2 undefined"))
     rst
  where
    go :: (a,a,a) -> Signal slow Bool -> Signal fast a -> Signal slow (a,a)
    go (o0,o1,o2) rt@(~(r :- rs)) as@(~(x0 :- x1 :- xs)) =
      let (o0',o1',o2') = if r then (i0,i1,i2) else (o2,x0,x1)
      in o0 `seqX` o1 `seqX` (o0,o1) :- (rt `seq` as `seq` go (o0',o1',o2') rs xs)
ddrIn# (Clock {}) (Async rst) i0 i1 i2 =
  go ((errorX "ddrIn: initial value 0 undefined")
     ,(errorX "ddrIn: initial value 1 undefined")
     ,(errorX "ddrIn: initial value 2 undefined"))
     rst
  where
    go :: (a,a,a) -> Signal slow Bool -> Signal fast a -> Signal slow (a,a)
    go (o0,o1,o2) ~(r :- rs) as@(~(x0 :- x1 :- xs)) =
      let (o0',o1',o2') = if r then (i0,i1,i2) else (o0,o1,o2)
      in o0' `seqX` o1' `seqX`(o0',o1') :- (as `seq` go (o2',x0,x1) rs xs)
ddrIn# (GatedClock _ _ ena) (Sync rst) i0 i1 i2 =
  go ((errorX "ddrIn: initial value 0 undefined")
     ,(errorX "ddrIn: initial value 1 undefined")
     ,(errorX "ddrIn: initial value 2 undefined"))
     rst
     ena
  where
    go :: (a,a,a) -> Signal slow Bool -> Signal slow Bool -> Signal fast a -> Signal slow (a,a)
    go (o0,o1,o2) rt@(~(r :- rs)) ~(e :- es) as@(~(x0 :- x1 :- xs)) =
      let (o0',o1',o2') = if r then (i0,i1,i2) else (o2,x0,x1)
      in o0 `seqX` o1 `seqX` (o0,o1)
           :- (rt `seq` as `seq` if e then go (o0',o1',o2') rs es xs
                                      else go (o0 ,o1 ,o2)    rs es xs)
ddrIn# (GatedClock _ _ ena) (Async rst) i0 i1 i2 =
  go ((errorX "ddrIn: initial value 0 undefined")
     ,(errorX "ddrIn: initial value 1 undefined")
     ,(errorX "ddrIn: initial value 2 undefined"))
     rst
     ena
  where
    go :: (a,a,a) -> Signal slow Bool -> Signal slow Bool -> Signal fast a -> Signal slow (a,a)
    go (o0,o1,o2) ~(r :- rs) ~(e :- es) as@(~(x0 :- x1 :- xs)) =
      let (o0',o1',o2') = if r then (i0,i1,i2) else (o0,o1,o2)
      in o0' `seqX` o1' `seqX` (o0',o1')
           :- (as `seq` if e then go (o2',x0 ,x1)   rs es xs
                             else go (o0',o1',o2') rs es xs)
{-# NOINLINE ddrIn# #-}
ddrOut :: ( HasCallStack
          , fast ~ 'Dom n pFast
          , slow ~ 'Dom n (2*pFast))
       => Clock slow gated            
       -> Reset slow synchronous      
       -> a                           
       -> Signal slow (a,a)           
       -> Signal fast a               
ddrOut clk rst i0 = uncurry (withFrozenCallStack $ ddrOut# clk rst i0) . unbundle
ddrOut# :: ( HasCallStack
           , fast ~ 'Dom n pFast
           , slow ~ 'Dom n (2*pFast))
        => Clock slow gated
        -> Reset slow synchronous
        -> a
        -> Signal slow a
        -> Signal slow a
        -> Signal fast a
ddrOut# clk rst i0 xs ys =
    
    
    
    
    
    let (_ :- out) = zipSig xs' ys' in out
  where
    xs' = register clk rst i0 xs
    ys' = register clk rst i0 ys
    zipSig (a :- as) (b :- bs) = a :- b :- zipSig as bs
{-# NOINLINE ddrOut# #-}