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

    ROMs
    -}

    {-# LANGUAGE CPP #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE RankNTypes #-}

    {-# LANGUAGE Trustworthy #-}

    {-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
    {-# OPTIONS_HADDOCK show-extensions #-}

    module Clash.Prelude.ROM
      ( -- * Asynchronous ROM
        asyncRom
      , asyncRomPow2
        -- * Synchronous ROM synchronized to an arbitrary clock
      , rom
      , romPow2
        -- * Internal
      , asyncRom#
      )
    where

    import           Data.Array           (listArray)
    import           Data.Array.Base      (unsafeAt)
    import           GHC.Stack            (withFrozenCallStack)
    import           GHC.TypeLits         (KnownNat, type (^))
    import           Prelude              hiding (length)

    import           Clash.Annotations.Primitive (hasBlackBox)
    import qualified Clash.Explicit.ROM   as E
    import           Clash.Signal
    import           Clash.Sized.Unsigned (Unsigned)
    import           Clash.Sized.Vector   (Vec, length, toList)

    import           Clash.XException     (NFDataX, deepErrorX)

    -- | An asynchronous/combinational ROM with space for @n@ elements
    --
    -- === See also:
    --
    -- * See "Clash.Sized.Fixed#creatingdatafiles" and "Clash.Prelude.BlockRam#usingrams"
    -- for ideas on how to use ROMs and RAMs.
    -- * A large 'Vec' for the content may be too inefficient, depending on how it
    -- is constructed. See 'Clash.Prelude.ROM.File.asyncRomFile' and
    -- 'Clash.Prelude.ROM.Blob.asyncRomBlob' for different approaches that scale
    -- well.
    asyncRom
      :: ( KnownNat n
         , Enum addr
         , NFDataX a
         )
      => Vec n a
      -- ^ ROM content, also determines the size, @n@, of the ROM
      --
      -- __NB__: __MUST__ be a constant
      -> addr
      -- ^ Read address @r@
      -> a
      -- ^ The value of the ROM at address @r@
    asyncRom :: forall (n :: Nat) addr a.
    (KnownNat n, Enum addr, NFDataX a) =>
    Vec n a -> addr -> a
    asyncRom = \Vec n a
    content addr
    rd -> Vec n a -> Int -> a
    forall (n :: Nat) a. (KnownNat n, NFDataX a) => Vec n a -> Int -> a
    asyncRom# Vec n a
    content (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum addr
    rd)
    {-# INLINE asyncRom #-}

    -- | An asynchronous/combinational ROM with space for 2^@n@ elements
    --
    -- === See also:
    --
    -- * See "Clash.Sized.Fixed#creatingdatafiles" and "Clash.Prelude.BlockRam#usingrams"
    -- for ideas on how to use ROMs and RAMs.
    -- * A large 'Vec' for the content may be too inefficient, depending on how it
    -- is constructed. See 'Clash.Prelude.ROM.File.asyncRomFilePow2' and
    -- 'Clash.Prelude.ROM.Blob.asyncRomBlobPow2' for different approaches that scale
    -- well.
    asyncRomPow2
      :: ( KnownNat n
         , NFDataX a
         )
      => Vec (2^n) a
      -- ^ ROM content
      --
      -- __NB__: __MUST__ be a constant
      -> Unsigned n
      -- ^ Read address @r@
      -> a
      -- ^ The value of the ROM at address @r@
    asyncRomPow2 :: forall (n :: Nat) a.
    (KnownNat n, NFDataX a) =>
    Vec (2 ^ n) a -> Unsigned n -> a
    asyncRomPow2 = Vec (2 ^ n) a -> Unsigned n -> a
    forall (n :: Nat) addr a.
    (KnownNat n, Enum addr, NFDataX a) =>
    Vec n a -> addr -> a
    asyncRom
    {-# INLINE asyncRomPow2 #-}

    -- | asyncRom primitive
    asyncRom#
      :: forall n a
       . ( KnownNat n
         , NFDataX a
         )
      => Vec n a
      -- ^ ROM content, also determines the size, @n@, of the ROM
      --
      -- __NB__: __MUST__ be a constant
      -> Int
      -- ^ Read address @r@
      -> a
      -- ^ The value of the ROM at address @r@
    asyncRom# :: forall (n :: Nat) a. (KnownNat n, NFDataX a) => Vec n a -> Int -> a
    asyncRom# Vec n a
    content = Int -> a
    safeAt
      where
        szI :: Int
    szI = Vec n a -> Int
    forall (n :: Nat) a. KnownNat n => Vec n a -> Int
    length Vec n a
    content
        arr :: Array Int a
    arr = (Int, Int) -> [a] -> Array Int a
    forall i e. Ix i => (i, i) -> [e] -> Array i e
    listArray (Int
    0,Int
    szIInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    1) (Vec n a -> [a]
    forall (n :: Nat) a. Vec n a -> [a]
    toList Vec n a
    content)

        safeAt :: Int -> a
        safeAt :: Int -> a
    safeAt Int
    i =
          if (Int
    0 Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    i) Bool -> Bool -> Bool
    && (Int
    i Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    szI) then
            Array Int a -> Int -> a
    forall i. Ix i => Array i a -> Int -> a
    forall (a :: Type -> Type -> Type) e i.
    (IArray a e, Ix i) =>
    a i e -> Int -> e
    unsafeAt Array Int a
    arr Int
    i
          else
            (HasCallStack => a) -> a
    forall a. HasCallStack => (HasCallStack => a) -> a
    withFrozenCallStack
              (String -> a
    forall a. (NFDataX a, HasCallStack) => String -> a
    deepErrorX (String
    "asyncRom: address " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ Int -> String
    forall a. Show a => a -> String
    show Int
    i String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++
                           String
    " not in range [0.." String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ Int -> String
    forall a. Show a => a -> String
    show Int
    szI String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"))
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE asyncRom# #-}
    {-# ANN asyncRom# hasBlackBox #-}

    -- | A ROM with a synchronous read port, with space for @n@ elements
    --
    -- * __NB__: Read value is delayed by 1 cycle
    -- * __NB__: Initial output value is /undefined/, reading it will throw an
    -- 'Clash.XException.XException'
    --
    -- === See also:
    --
    -- * See "Clash.Sized.Fixed#creatingdatafiles" and "Clash.Prelude.BlockRam#usingrams"
    -- for ideas on how to use ROMs and RAMs.
    -- * A large 'Vec' for the content may be too inefficient, depending on how it
    -- is constructed. See 'Clash.Prelude.ROM.File.romFile' and
    -- 'Clash.Prelude.ROM.Blob.romBlob' for different approaches that scale well.
    rom
      :: forall dom n m a
       . ( NFDataX a
         , KnownNat n
         , KnownNat m
         , HiddenClock dom
         , HiddenEnable dom  )
      => Vec n a
      -- ^ ROM content, also determines the size, @n@, of the ROM
      --
      -- __NB__: __MUST__ be a constant
      -> Signal dom (Unsigned m)
      -- ^ Read address @r@
      -> Signal dom a
      -- ^ The value of the ROM at address @r@ from the previous clock cycle
    rom :: forall (dom :: Domain) (n :: Nat) (m :: Nat) a.
    (NFDataX a, KnownNat n, KnownNat m, HiddenClock dom,
     HiddenEnable dom) =>
    Vec n a -> Signal dom (Unsigned m) -> Signal dom a
    rom = (Enable dom -> Vec n a -> Signal dom (Unsigned m) -> Signal dom a)
    -> Vec n a -> Signal dom (Unsigned m) -> Signal dom a
    forall (dom :: Domain) r.
    HiddenEnable dom =>
    (Enable dom -> r) -> r
    hideEnable ((Clock dom
     -> Enable dom
     -> Vec n a
     -> Signal dom (Unsigned m)
     -> Signal dom a)
    -> Enable dom -> Vec n a -> Signal dom (Unsigned m) -> Signal dom a
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock Clock dom
    -> Enable dom -> Vec n a -> Signal dom (Unsigned m) -> Signal dom a
    forall (dom :: Domain) (n :: Nat) a addr.
    (KnownDomain dom, KnownNat n, NFDataX a, Enum addr) =>
    Clock dom
    -> Enable dom -> Vec n a -> Signal dom addr -> Signal dom a
    E.rom)
    {-# INLINE rom #-}

    -- | A ROM with a synchronous read port, with space for 2^@n@ elements
    --
    -- * __NB__: Read value is delayed by 1 cycle
    -- * __NB__: Initial output value is /undefined/, reading it will throw an
    -- 'Clash.XException.XException'
    --
    -- === See also:
    --
    -- * See "Clash.Sized.Fixed#creatingdatafiles" and "Clash.Prelude.BlockRam#usingrams"
    -- for ideas on how to use ROMs and RAMs.
    -- * A large 'Vec' for the content may be too inefficient, depending on how it
    -- is constructed. See 'Clash.Prelude.ROM.File.romFilePow2' and
    -- 'Clash.Prelude.ROM.Blob.romBlobPow2' for different approaches that scale
    -- well.
    romPow2
      :: forall dom n a
       . ( KnownNat n
         , NFDataX a
         , HiddenClock dom
         , HiddenEnable dom  )
      => Vec (2^n) a
      -- ^ ROM content
      --
      -- __NB__: __MUST__ be a constant
      -> Signal dom (Unsigned n)
      -- ^ Read address @r@
      -> Signal dom a
      -- ^ The value of the ROM at address @r@ from the previous clock cycle
    romPow2 :: forall (dom :: Domain) (n :: Nat) a.
    (KnownNat n, NFDataX a, HiddenClock dom, HiddenEnable dom) =>
    Vec (2 ^ n) a -> Signal dom (Unsigned n) -> Signal dom a
    romPow2 = (Enable dom
     -> Vec (2 ^ n) a -> Signal dom (Unsigned n) -> Signal dom a)
    -> Vec (2 ^ n) a -> Signal dom (Unsigned n) -> Signal dom a
    forall (dom :: Domain) r.
    HiddenEnable dom =>
    (Enable dom -> r) -> r
    hideEnable ((Clock dom
     -> Enable dom
     -> Vec (2 ^ n) a
     -> Signal dom (Unsigned n)
     -> Signal dom a)
    -> Enable dom
    -> Vec (2 ^ n) a
    -> Signal dom (Unsigned n)
    -> Signal dom a
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock Clock dom
    -> Enable dom
    -> Vec (2 ^ n) a
    -> Signal dom (Unsigned n)
    -> Signal dom a
    forall (dom :: Domain) (n :: Nat) a.
    (KnownDomain dom, KnownNat n, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> Vec (2 ^ n) a
    -> Signal dom (Unsigned n)
    -> Signal dom a
    E.romPow2)
    {-# INLINE romPow2 #-}