    {-|
    Copyright  :  (C) 2017, Google Inc
                      2019, Myrtle Software Ltd
                      2025, QBayLogic B.V.
    License    :  BSD2 (see the file LICENSE)
    Maintainer :  QBayLogic B.V. <devops@qbaylogic.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
    >>> :{
    type Fast = ("Fast" :: Domain)
    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.
    --
    -- Data is clocked in on both edges of the clock signal. We can discern the
    -- /active edge/ of the clock and the /other edge/. When the domain has the
    -- rising edge as the active edge (which is the most common), this means that
    -- the /rising/ edge is the /active/ edge and the /falling/ edge is the /other/
    -- edge.
    --
    -- Of the output pair @(o0, o1)@, @o0@ is the data clocked in on the /other/
    -- edge and @o1@ is the data clocked in on the /active/ edge, and @o0@ comes
    -- before @o1@ in time. With a domain where the rising edge is the active edge,
    -- this means @o0@ is clocked in on the falling clock edge and @o1@ is clocked
    -- in on the rising clock edge. For a domain with the falling edge as the active
    -- edge, this is the other way around, but @o0@ still comes before @o1@ in time.
    --
    -- >>> 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
      -> Reset slow
      -> Enable slow
      -> (a, a, a)
      -- ^ Reset values
      -> Signal fast a
      -- ^ DDR input signal
      -> Signal slow (a, a)
      -- ^ Normal speed output pair @(o0, o1)@
    ddrIn :: 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, 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# :: 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 SSymbol slow
    _ Maybe (Signal slow Femtoseconds)
    Nothing) (Reset slow -> Signal slow Bool
    forall (dom :: Domain).
    KnownDomain dom =>
    Reset dom -> Signal dom Bool
    unsafeToActiveHigh -> 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
    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)
    forall a b. a -> b -> b
    `seq` Signal fast a
    as Signal fast a -> Signal slow (a, a) -> Signal slow (a, a)
    forall a b. a -> b -> b
    `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)
    forall a b. a -> b -> b
    `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)

    ddrIn# Clock slow
    _ Reset slow
    _ Enable slow
    _ a
    _ a
    _ a
    _ =
      String -> Signal fast a -> Signal slow (a, a)
    forall a. HasCallStack => String -> a
    error String
    "ddrIn#: dynamic clocks not supported"
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE ddrIn# #-}
    {-# ANN ddrIn# hasBlackBox #-}

    -- | DDR output primitive
    --
    -- Produces a DDR output signal from a normal signal of pairs of input.
    --
    -- Data is clocked out on both edges of the clock signal. We can discern the
    -- /active edge/ of the clock and the /other edge/. When the domain has the
    -- rising edge as the active edge (which is the most common), this means that
    -- the /rising/ edge is the /active/ edge and the /falling/ edge is the /other/
    -- edge.
    --
    -- Of the input pair @(i0, i1)@, @i0@ is the data clocked out on the /active/
    -- edge and @i1@ is the data clocked out on the /other/ edge, and @i0@ comes
    -- before @i1@ in time. With a domain where the rising edge is the active edge,
    -- this means @i0@ is clocked out on the rising clock edge and @i1@ is clocked
    -- out on the falling clock edge. For a domain with the falling edge as the
    -- active edge, this is the other way around, but @i0@ still comes before @i1@
    -- in time.
    --
    -- >>> 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 pair @(i0, i1)@
      -> Signal fast a
      -- ^ DDR output signal
    ddrOut :: 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, 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)
    Signal slow (a, a) -> Unbundled slow (a, a)
    forall a (dom :: Domain).
    Bundle a =>
    Signal dom a -> Unbundled dom a
    forall (dom :: Domain). Signal dom (a, a) -> Unbundled dom (a, 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# :: 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
    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
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE ddrOut# #-}
    {-# ANN ddrOut# hasBlackBox #-}