    {-|
    Copyright  :  (C) 2015-2016, University of Twente,
                      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>

    = Initializing a block RAM with a data file #usingramfiles#

    Block RAM primitives that can be initialized with a data file. The BNF grammar
    for this data file is simple:

    > FILE = LINE+
    > LINE = BIT+
    > BIT  = '0'
    >      | '1'

    Consecutive @LINE@s correspond to consecutive memory addresses starting at @0@.
    For example, a data file @memory.bin@ containing the 9-bit unsigned numbers
    @7@ to @13@ looks like:

    > 000000111
    > 000001000
    > 000001001
    > 000001010
    > 000001011
    > 000001100
    > 000001101

    Such a file can be produced with 'E.memFile':

    @
    writeFile "memory.bin" (memFile Nothing [7 :: Unsigned 9 .. 13])
    @

    We can instantiate a block RAM using the contents of the file above like so:

    @
    f :: (HiddenClock dom, HiddenEnable dom)
      => Signal dom (Unsigned 3)
      -> Signal dom (Unsigned 9)
    f rd = 'Clash.Class.BitPack.unpack' '<$>' 'blockRamFile' d7 \"memory.bin\" rd (pure Nothing)
    @

    In the example above, we basically treat the block RAM as a synchronous ROM.
    We can see that it works as expected:

    @
    __>>> import qualified Data.List as L__
    __>>> L.tail $ sampleN 4 $ f (fromList [3..5])__
    [10,11,12]
    @

    However, we can also interpret the same data as a tuple of a 6-bit unsigned
    number, and a 3-bit signed number:

    @
    g :: (HiddenClock dom, HiddenEnable dom)
       => Signal dom (Unsigned 3)
       -> Signal dom (Unsigned 6,Signed 3)
    g clk rd = 'Clash.Class.BitPack.unpack' '<$>' 'blockRamFile' d7 \"memory.bin\" rd (pure Nothing)
    @

    And then we would see:

    @
    __>>> import qualified Data.List as L__
    __>>> L.tail $ sampleN 4 $ g (fromList [3..5])__
    [(1,2),(1,3)(1,-4)]
    @

    -}

    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE GADTs #-}

    {-# LANGUAGE Unsafe #-}

    {-# OPTIONS_HADDOCK show-extensions #-}

    module Clash.Prelude.BlockRam.File
      ( -- * Block RAM synchronized to an arbitrary clock
        blockRamFile
      , blockRamFilePow2
        -- * Producing files
      , E.memFile
      )
    where

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

    import qualified Clash.Explicit.BlockRam.File as E
    import           Clash.Promoted.Nat           (SNat)
    import           Clash.Signal
      (HiddenClock, HiddenEnable, Signal, hideClock, hideEnable)
    import           Clash.Sized.BitVector        (BitVector)
    import           Clash.Sized.Unsigned         (Unsigned)
    import           Clash.XException             (NFDataX)

    -- | Create a block RAM 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'
    -- * __NB__: This function might not work for specific combinations of
    -- code-generation backends and hardware targets. Please check the support table
    -- below:
    --
    -- +----------------+----------+----------+---------------+
    -- |                | VHDL     | Verilog  | SystemVerilog |
    -- +================+==========+==========+===============+
    -- | Altera/Quartus | Broken   | Works    | Works         |
    -- +----------------+----------+----------+---------------+
    -- | Xilinx/ISE     | Works    | Works    | Works         |
    -- +----------------+----------+----------+---------------+
    -- | ASIC           | Untested | Untested | Untested      |
    -- +----------------+----------+----------+---------------+
    --
    -- === See also:
    --
    -- * See "Clash.Prelude.BlockRam#usingrams" for more information on how to use a
    -- block RAM.
    -- * Use the adapter 'Clash.Prelude.BlockRam.readNew' for obtaining write-before-read semantics like this: @'Clash.Prelude.BlockRam.readNew' ('blockRamFilePow2' file) rd wrM@.
    -- * See "Clash.Prelude.BlockRam.File#usingramfiles" for more information on how
    -- to instantiate a block RAM with the contents of a data file.
    -- * See "Clash.Sized.Fixed#creatingdatafiles" for ideas on how to create your
    -- own data files.
    blockRamFilePow2
      :: forall dom  n m
       . ( KnownNat m
         , KnownNat n
         , HiddenClock dom
         , HiddenEnable dom
         , HasCallStack )
      => FilePath
      -- ^ File describing the initial content of the BRAM
      -> Signal dom (Unsigned n)
      -- ^ Read address @r@
      -> Signal dom (Maybe (Unsigned n, BitVector m))
      -- ^ (write address @w@, value to write)
      -> Signal dom (BitVector m)
      -- ^ Value of the BRAM at address @r@ from the previous clock cycle
    blockRamFilePow2 :: forall (dom :: Domain) (n :: Nat) (m :: Nat).
    (KnownNat m, KnownNat n, HiddenClock dom, HiddenEnable dom,
     HasCallStack) =>
    FilePath
    -> Signal dom (Unsigned n)
    -> Signal dom (Maybe (Unsigned n, BitVector m))
    -> Signal dom (BitVector m)
    blockRamFilePow2 = \FilePath
    fp Signal dom (Unsigned n)
    rd Signal dom (Maybe (Unsigned n, BitVector m))
    wrM -> (HasCallStack => Signal dom (BitVector m))
    -> Signal dom (BitVector m)
    forall a. HasCallStack => (HasCallStack => a) -> a
    withFrozenCallStack
      ((Enable dom
     -> FilePath
     -> Signal dom (Unsigned n)
     -> Signal dom (Maybe (Unsigned n, BitVector m))
     -> Signal dom (BitVector m))
    -> FilePath
    -> Signal dom (Unsigned n)
    -> Signal dom (Maybe (Unsigned n, BitVector m))
    -> Signal dom (BitVector m)
    forall (dom :: Domain) r.
    HiddenEnable dom =>
    (Enable dom -> r) -> r
    hideEnable ((Clock dom
     -> Enable dom
     -> FilePath
     -> Signal dom (Unsigned n)
     -> Signal dom (Maybe (Unsigned n, BitVector m))
     -> Signal dom (BitVector m))
    -> Enable dom
    -> FilePath
    -> Signal dom (Unsigned n)
    -> Signal dom (Maybe (Unsigned n, BitVector m))
    -> Signal dom (BitVector m)
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock Clock dom
    -> Enable dom
    -> FilePath
    -> Signal dom (Unsigned n)
    -> Signal dom (Maybe (Unsigned n, BitVector m))
    -> Signal dom (BitVector m)
    forall (dom :: Domain) (n :: Nat) (m :: Nat).
    (KnownDomain dom, KnownNat m, KnownNat n, HasCallStack) =>
    Clock dom
    -> Enable dom
    -> FilePath
    -> Signal dom (Unsigned n)
    -> Signal dom (Maybe (Unsigned n, BitVector m))
    -> Signal dom (BitVector m)
    E.blockRamFilePow2) FilePath
    fp Signal dom (Unsigned n)
    rd Signal dom (Maybe (Unsigned n, BitVector m))
    wrM)
    {-# INLINE blockRamFilePow2 #-}

    -- | Create a block RAM 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'
    -- * __NB__: This function might not work for specific combinations of
    -- code-generation backends and hardware targets. Please check the support table
    -- below:
    --
    -- +----------------+----------+----------+---------------+
    -- |                | VHDL     | Verilog  | SystemVerilog |
    -- +================+==========+==========+===============+
    -- | Altera/Quartus | Broken   | Works    | Works         |
    -- +----------------+----------+----------+---------------+
    -- | Xilinx/ISE     | Works    | Works    | Works         |
    -- +----------------+----------+----------+---------------+
    -- | ASIC           | Untested | Untested | Untested      |
    -- +----------------+----------+----------+---------------+
    --
    -- === See also:
    --
    -- * See "Clash.Prelude.BlockRam#usingrams" for more information on how to use a
    -- block RAM.
    -- * Use the adapter 'Clash.Prelude.BlockRam.readNew' for obtaining write-before-read semantics like this: @'Clash.Prelude.BlockRam.readNew' ('blockRamFile' size file) rd wrM@.
    -- * See "Clash.Prelude.BlockRam.File#usingramfiles" for more information on how
    -- to instantiate a block RAM with the contents of a data file.
    -- * See "Clash.Sized.Fixed#creatingdatafiles" for ideas on how to create your
    -- own data files.
    blockRamFile
      :: ( KnownNat m
         , Enum addr
         , NFDataX addr
         , HiddenClock dom
         , HiddenEnable dom
         , HasCallStack )
      => SNat n
      -- ^ Size of the BRAM
      -> FilePath
      -- ^ File describing the initial content of the BRAM
      -> Signal dom addr
      -- ^ Read address @r@
      -> Signal dom (Maybe (addr, BitVector m))
      -- ^ (write address @w@, value to write)
      -> Signal dom (BitVector m)
      -- ^ Value of the BRAM at address @r@ from the previous clock cycle
    blockRamFile :: forall (m :: Nat) addr (dom :: Domain) (n :: Nat).
    (KnownNat m, Enum addr, NFDataX addr, HiddenClock dom,
     HiddenEnable dom, HasCallStack) =>
    SNat n
    -> FilePath
    -> Signal dom addr
    -> Signal dom (Maybe (addr, BitVector m))
    -> Signal dom (BitVector m)
    blockRamFile = \SNat n
    sz FilePath
    fp Signal dom addr
    rd Signal dom (Maybe (addr, BitVector m))
    wrM -> (HasCallStack => Signal dom (BitVector m))
    -> Signal dom (BitVector m)
    forall a. HasCallStack => (HasCallStack => a) -> a
    withFrozenCallStack
      ((Enable dom
     -> SNat n
     -> FilePath
     -> Signal dom addr
     -> Signal dom (Maybe (addr, BitVector m))
     -> Signal dom (BitVector m))
    -> SNat n
    -> FilePath
    -> Signal dom addr
    -> Signal dom (Maybe (addr, BitVector m))
    -> Signal dom (BitVector m)
    forall (dom :: Domain) r.
    HiddenEnable dom =>
    (Enable dom -> r) -> r
    hideEnable ((Clock dom
     -> Enable dom
     -> SNat n
     -> FilePath
     -> Signal dom addr
     -> Signal dom (Maybe (addr, BitVector m))
     -> Signal dom (BitVector m))
    -> Enable dom
    -> SNat n
    -> FilePath
    -> Signal dom addr
    -> Signal dom (Maybe (addr, BitVector m))
    -> Signal dom (BitVector m)
    forall (dom :: Domain) r. HiddenClock dom => (Clock dom -> r) -> r
    hideClock Clock dom
    -> Enable dom
    -> SNat n
    -> FilePath
    -> Signal dom addr
    -> Signal dom (Maybe (addr, BitVector m))
    -> Signal dom (BitVector m)
    forall (dom :: Domain) (m :: Nat) addr (n :: Nat).
    (KnownDomain dom, KnownNat m, Enum addr, NFDataX addr,
     HasCallStack) =>
    Clock dom
    -> Enable dom
    -> SNat n
    -> FilePath
    -> Signal dom addr
    -> Signal dom (Maybe (addr, BitVector m))
    -> Signal dom (BitVector m)
    E.blockRamFile) SNat n
    sz FilePath
    fp Signal dom addr
    rd Signal dom (Maybe (addr, BitVector m))
    wrM)
    {-# INLINE blockRamFile #-}