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

RAM primitives with a combinational read port.
-}

{-# LANGUAGE BangPatterns        #-}
{-# LANGUAGE CPP                 #-}
{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE MagicHash           #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE TypeOperators       #-}

{-# 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)


-- | Create a RAM with space for @n@ elements.
--
-- * __NB__: Initial content of the RAM is 'undefined'
--
-- Additional helpful information:
--
-- * See "Clash.Prelude.BlockRam#usingrams" for more information on how to use a
-- RAM.
asyncRam
  :: ( Enum addr
     , HiddenClock dom
     , HiddenEnable dom
     , HasCallStack
     )
  => 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 :: SNat n
-> Signal dom addr -> Signal dom (Maybe (addr, a)) -> Signal dom a
asyncRam = \sz :: SNat n
sz rd :: Signal dom addr
rd wrM :: 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 :: Symbol) r.
HiddenEnable dom =>
(Enable dom -> r) -> r
hideEnable (\en :: Enable dom
en -> (Clock dom -> Signal dom a) -> Signal dom a
forall (dom :: Symbol) r. HiddenClock dom => (Clock dom -> r) -> r
hideClock (\clk :: 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 :: Symbol) (rdom :: Symbol) (n :: Nat) a.
(Enum addr, HasCallStack, KnownDomain wdom, KnownDomain rdom) =>
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'
--
-- Additional helpful information:
--
-- * See "Clash.Prelude.BlockRam#usingrams" for more information on how to use a
-- RAM.
asyncRamPow2
  :: ( KnownNat n
     , HiddenClock dom
     , HiddenEnable dom
     , HasCallStack )
  => 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 :: Signal dom (Unsigned n)
-> Signal dom (Maybe (Unsigned n, a)) -> Signal dom a
asyncRamPow2 = \rd :: Signal dom (Unsigned n)
rd wrM :: 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 :: Symbol) r.
HiddenEnable dom =>
(Enable dom -> r) -> r
hideEnable (\en :: Enable dom
en -> ((Clock dom -> Signal dom a) -> Signal dom a
forall (dom :: Symbol) r. HiddenClock dom => (Clock dom -> r) -> r
hideClock (\clk :: Clock dom
clk -> Clock dom
-> Clock dom
-> Enable dom
-> Signal dom (Unsigned n)
-> Signal dom (Maybe (Unsigned n, a))
-> Signal dom a
forall (wdom :: Symbol) (rdom :: Symbol) (n :: Nat) a.
(KnownNat n, HasCallStack, KnownDomain wdom, KnownDomain rdom) =>
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 #-}