content: {-|
  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>
  
  = Initializing a ROM with a data file #usingromfiles#
  
  ROMs 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 'memFile':
  
  @
  writeFile "memory.bin" (memFile Nothing [7 :: Unsigned 9 .. 13])
  @
  
  We can instantiate a synchronous ROM 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' '<$>' 'romFile' d7 \"memory.bin\" rd
  @
  
  And 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 rd = 'Clash.Class.BitPack.unpack' '<$>' 'romFile' d7 \"memory.bin\" rd
  @
  
  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 CPP #-}
  {-# LANGUAGE FlexibleContexts #-}
  
  {-# LANGUAGE Unsafe #-}
  
  {-# OPTIONS_HADDOCK show-extensions #-}
  
  module Clash.Prelude.ROM.File
    ( -- * Asynchronous ROM
      asyncRomFile
    , asyncRomFilePow2
      -- * Synchronous ROM synchronized to an arbitrary clock
    , romFile
    , romFilePow2
      -- * Producing files
    , memFile
      -- * Internal
    , asyncRomFile#
    )
  where
  
  import           Data.Array                   (listArray,(!))
  import           GHC.TypeLits                 (KnownNat)
  import           System.IO.Unsafe             (unsafePerformIO)
  
  import           Clash.Annotations.Primitive (hasBlackBox)
  import           Clash.Explicit.BlockRam.File (initMem, memFile)
  import qualified Clash.Explicit.ROM.File      as E
  import           Clash.Promoted.Nat           (SNat (..), pow2SNat, snatToNum)
  import           Clash.Signal
  import           Clash.Sized.BitVector        (BitVector)
  import           Clash.Sized.Unsigned         (Unsigned)
  
  -- | An asynchronous/combinational ROM with space for @n@ elements
  --
  -- * __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.ROM.File#usingromfiles" for more information on how
  -- to instantiate a ROM with the contents of a data file.
  -- * See "Clash.Sized.Fixed#creatingdatafiles" for ideas on how to create your
  -- own data files.
  -- * When you notice that 'asyncRomFile' is significantly slowing down your
  -- simulation, give it a /monomorphic/ type signature. So instead of leaving
  -- the type to be inferred:
  --
  --     @
  --     myRomData = asyncRomFile d512 "memory.bin"
  --     @
  --
  --     or giving it a /polymorphic/ type signature:
  --
  --     @
  --     myRomData :: Enum addr => addr -> BitVector 16
  --     myRomData = asyncRomFile d512 "memory.bin"
  --     @
  --
  --     you __should__ give it a /monomorphic/ type signature:
  --
  --     @
  --     myRomData :: Unsigned 9 -> BitVector 16
  --     myRomData = asyncRomFile d512 "memory.bin"
  --     @
  asyncRomFile
    :: (KnownNat m, Enum addr)
    => SNat n
    -- ^ Size of the ROM
    -> FilePath
    -- ^ File describing the content of the ROM
    -> addr
    -- ^ Read address @r@
    -> BitVector m
    -- ^ The value of the ROM at address @r@
  asyncRomFile sz file = asyncRomFile# sz file . fromEnum
  -- Leave 'asyncRomFile#' eta-reduced, see Note [Eta-reduction and unsafePerformIO initMem]
  {-# INLINE asyncRomFile #-}
  
  -- Note [Eta-reduction and unsafePerformIO initMem]
  --
  -- The 'initMem' function initializes a @[BitVector n]@ from file. Ideally,
  -- we want this IO action to happen only once. When we call 'unsafePerformIO'
  -- on @initMem file@, it becomes a thunk in that function, so is hence evaluated
  -- only once. However, me must ensure that any code calling using of the
  -- @unsafePerformIO (initMem file)@ thunk also becomes a thunk. We do this by
  -- eta-reducing function where needed so that a thunk is returned.
  --
  -- For example, instead of writing:
  --
  -- > asyncRomFile# sz file rd = (content ! rd)
  -- >   where
  -- >     mem = unsafePerformIO (initMem file)
  -- >     content = listArray (0,szI-1) mem
  -- >     szI     = snatToNum sz
  --
  -- We write:
  --
  -- > asyncRomFile# sz file = (content !)
  -- >   where
  -- >     mem     = unsafePerformIO (initMem file)
  -- >     content = listArray (0,szI-1) mem
  -- >     szI     = snatToNum sz
  --
  -- Where instead of returning the BitVector defined by @(content ! rd)@, we
  -- return the function (thunk) @(content !)@.
  
  -- | An asynchronous/combinational ROM with space for 2^@n@ elements
  --
  -- * __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.ROM.File#usingromfiles" for more information on how
  -- to instantiate a ROM with the contents of a data file.
  -- * See "Clash.Sized.Fixed#creatingdatafiles" for ideas on how to create your
  -- own data files.
  -- * When you notice that 'asyncRomFilePow2' is significantly slowing down your
  -- simulation, give it a /monomorphic/ type signature. So instead of leaving the
  -- type to be inferred:
  --
  --     @
  --     myRomData = asyncRomFilePow2 "memory.bin"
  --     @
  --
  --     you __should__ give it a /monomorphic/ type signature:
  --
  --     @
  --     myRomData :: Unsigned 9 -> BitVector 16
  --     myRomData = asyncRomFilePow2 "memory.bin"
  --     @
  asyncRomFilePow2
    :: forall n m
     . (KnownNat m, KnownNat n)
    => FilePath
    -- ^ File describing the content of the ROM
    -> Unsigned n
    -- ^ Read address @r@
    -> BitVector m
    -- ^ The value of the ROM at address @r@
  asyncRomFilePow2 = asyncRomFile (pow2SNat (SNat @n))
  {-# INLINE asyncRomFilePow2 #-}
  
  -- | asyncRomFile primitive
  asyncRomFile#
    :: KnownNat m
    => SNat n
    -- ^ Size of the ROM
    -> FilePath
    -- ^ File describing the content of the ROM
    -> Int
    -- ^ Read address @r@
    -> BitVector m
    -- ^ The value of the ROM at address @r@
  asyncRomFile# sz file = (content !) -- Leave "(content !)" eta-reduced, see
    where                             -- Note [Eta-reduction and unsafePerformIO initMem]
      mem     = unsafePerformIO (initMem file)
      content = listArray (0,szI-1) mem
      szI     = snatToNum sz
  -- See: https://github.com/clash-lang/clash-compiler/pull/2511
  {-# CLASH_OPAQUE asyncRomFile# #-}
  {-# ANN asyncRomFile# 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'
  -- * __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.ROM.File#usingromfiles" for more information on how
  -- to instantiate a ROM with the contents of a data file.
  -- * See "Clash.Sized.Fixed#creatingdatafiles" for ideas on how to create your
  -- own data files.
  romFile
    :: ( KnownNat m
       , KnownNat n
       , HiddenClock dom
       , HiddenEnable dom
       , Enum addr
       )
    => SNat n
    -- ^ Size of the ROM
    -> FilePath
    -- ^ File describing the content of the ROM
    -> Signal dom addr
    -- ^ Read address @r@
    -> Signal dom (BitVector m)
    -- ^ The value of the ROM at address @r@ from the previous clock cycle
  romFile = hideEnable (hideClock E.romFile)
  {-# INLINE romFile #-}
  
  -- | 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'
  -- * __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.ROM.File#usingromfiles" for more information on how
  -- to instantiate a ROM with the contents of a data file.
  -- * See "Clash.Sized.Fixed#creatingdatafiles" for ideas on how to create your
  -- own data files.
  romFilePow2
    :: forall n m dom
     . ( KnownNat m
       , KnownNat n
       , HiddenClock dom
       , HiddenEnable dom
       )
    => FilePath
    -- ^ File describing the content of the ROM
    -> Signal dom (Unsigned n)
    -- ^ Read address @r@
    -> Signal dom (BitVector m)
    -- ^ The value of the ROM at address @r@ from the previous clock cycle
  romFilePow2 = hideEnable (hideClock E.romFilePow2)
  {-# INLINE romFilePow2 #-}
  