    {-|
    Copyright  :  (C) 2015-2016, University of Twente,
                      2017-2019, Myrtle Software Ltd
                      2017     , Google Inc.,
                      2021-2022, QBayLogic B.V.
    License    :  BSD2 (see the file LICENSE)
    Maintainer :  QBayLogic B.V. <devops@qbaylogic.com>

    RAM primitives with a combinational read port
    -}

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

    {-# LANGUAGE Safe #-}

    {-# OPTIONS_HADDOCK show-extensions #-}

    module Clash.Prelude.RAM
      ( -- * RAM synchronized to an arbitrary clock
        asyncRam
      , asyncRamPow2
      )
    where

    import           GHC.TypeLits         (KnownNat)
    import           GHC.Stack            (HasCallStack, withFrozenCallStack)

    import qualified Clash.Explicit.RAM   as E
    import           Clash.Promoted.Nat   (SNat)
    import           Clash.Signal
    import           Clash.Sized.Unsigned (Unsigned)
    import           Clash.XException     (NFDataX)

    -- | Create a RAM with space for @n@ elements
    --
    -- * __NB__: Initial content of the RAM is /undefined/, reading it will throw an
    -- 'Clash.XException.XException'
    --
    -- === See also:
    --
    -- * See "Clash.Prelude.BlockRam#usingrams" for more information on how to use a
    -- RAM.
    asyncRam
      :: ( Enum addr
         , NFDataX addr
         , HiddenClock dom
         , HiddenEnable dom
         , HasCallStack
         , NFDataX a
         )
      => SNat n
      -- ^ Size @n@ of the RAM
      -> Signal dom addr
      -- ^ Read address @r@
      -> Signal dom (Maybe (addr, a))
       -- ^ (write address @w@, value to write)
      -> Signal dom a
       -- ^ Value of the RAM at address @r@
    asyncRam :: forall addr (dom :: Domain) a (n :: Nat).
    (Enum addr, NFDataX addr, HiddenClock dom, HiddenEnable dom,
     HasCallStack, NFDataX a) =>
    SNat n
    -> Signal dom addr -> Signal dom (Maybe (addr, a)) -> Signal dom a
    asyncRam = \SNat n
    sz Signal dom addr
    rd Signal dom (Maybe (addr, a))
    wrM -> (HasCallStack => Signal dom a) -> Signal dom a
    forall a. HasCallStack => (HasCallStack => a) -> a
    withFrozenCallStack
      ((Enable dom -> Signal dom a) -> Signal dom a
    forall (dom :: Domain) r.
    HiddenEnable dom =>
    (Enable dom -> r) -> r
    hideEnable (\Enable dom
    en -> (Clock dom -> Signal dom a) -> Signal dom a
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock (\Clock dom
    clk -> Clock dom
    -> Clock dom
    -> Enable dom
    -> SNat n
    -> Signal dom addr
    -> Signal dom (Maybe (addr, a))
    -> Signal dom a
    forall addr (wdom :: Domain) (rdom :: Domain) a (n :: Nat).
    (Enum addr, NFDataX addr, HasCallStack, KnownDomain wdom,
     KnownDomain rdom, NFDataX a) =>
    Clock wdom
    -> Clock rdom
    -> Enable wdom
    -> SNat n
    -> Signal rdom addr
    -> Signal wdom (Maybe (addr, a))
    -> Signal rdom a
    E.asyncRam Clock dom
    clk Clock dom
    clk Enable dom
    en SNat n
    sz Signal dom addr
    rd Signal dom (Maybe (addr, a))
    wrM)))
    {-# INLINE asyncRam #-}

    -- | Create a RAM with space for 2^@n@ elements
    --
    -- * __NB__: Initial content of the RAM is /undefined/, reading it will throw an
    -- 'Clash.XException.XException'
    --
    -- === See also:
    --
    -- * See "Clash.Prelude.BlockRam#usingrams" for more information on how to use a
    -- RAM.
    asyncRamPow2
      :: ( KnownNat n
         , HiddenClock dom
         , HiddenEnable dom
         , HasCallStack
         , NFDataX a
         )
      => Signal dom (Unsigned n)
      -- ^ Read address @r@
      -> Signal dom (Maybe (Unsigned n, a))
      -- ^ (write address @w@, value to write)
      -> Signal dom a
      -- ^ Value of the RAM at address @r@
    asyncRamPow2 :: forall (n :: Nat) (dom :: Domain) a.
    (KnownNat n, HiddenClock dom, HiddenEnable dom, HasCallStack,
     NFDataX a) =>
    Signal dom (Unsigned n)
    -> Signal dom (Maybe (Unsigned n, a)) -> Signal dom a
    asyncRamPow2 = \Signal dom (Unsigned n)
    rd Signal dom (Maybe (Unsigned n, a))
    wrM -> (HasCallStack => Signal dom a) -> Signal dom a
    forall a. HasCallStack => (HasCallStack => a) -> a
    withFrozenCallStack
      ((Enable dom -> Signal dom a) -> Signal dom a
    forall (dom :: Domain) r.
    HiddenEnable dom =>
    (Enable dom -> r) -> r
    hideEnable (\Enable dom
    en -> ((Clock dom -> Signal dom a) -> Signal dom a
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock (\Clock dom
    clk -> Clock dom
    -> Clock dom
    -> Enable dom
    -> Signal dom (Unsigned n)
    -> Signal dom (Maybe (Unsigned n, a))
    -> Signal dom a
    forall (wdom :: Domain) (rdom :: Domain) (n :: Nat) a.
    (KnownNat n, HasCallStack, KnownDomain wdom, KnownDomain rdom,
     NFDataX a) =>
    Clock wdom
    -> Clock rdom
    -> Enable wdom
    -> Signal rdom (Unsigned n)
    -> Signal wdom (Maybe (Unsigned n, a))
    -> Signal rdom a
    E.asyncRamPow2 Clock dom
    clk Clock dom
    clk Enable dom
    en Signal dom (Unsigned n)
    rd Signal dom (Maybe (Unsigned n, a))
    wrM))))
    {-# INLINE asyncRamPow2 #-}