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

    Block RAM primitives

    = Using RAMs #usingrams#

    We will show a rather elaborate example on how you can, and why you might want
    to use block RAMs. We will build a \"small\" CPU + Memory + Program ROM where we
    will slowly evolve to using block RAMs. Note that the code is /not/ meant as a
    de-facto standard on how to do CPU design in Clash.

    We start with the definition of the Instructions, Register names and machine
    codes:

    @
    {\-\# LANGUAGE RecordWildCards, TupleSections, DeriveAnyClass \#-\}

    module CPU where

    import Clash.Explicit.Prelude

    type InstrAddr = Unsigned 8
    type MemAddr   = Unsigned 5
    type Value     = Signed 8

    data Instruction
      = Compute Operator Reg Reg Reg
      | Branch Reg Value
      | Jump Value
      | Load MemAddr Reg
      | Store Reg MemAddr
      | Nop
      deriving (Eq, Show, Generic, NFDataX)

    data Reg
      = Zero
      | PC
      | RegA
      | RegB
      | RegC
      | RegD
      | RegE
      deriving (Eq, Show, Enum, Generic, NFDataX)

    data Operator = Add | Sub | Incr | Imm | CmpGt
      deriving (Eq, Show, Generic, NFDataX)

    data MachCode
      = MachCode
      { inputX  :: Reg
      , inputY  :: Reg
      , result  :: Reg
      , aluCode :: Operator
      , ldReg   :: Reg
      , rdAddr  :: MemAddr
      , wrAddrM :: Maybe MemAddr
      , jmpM    :: Maybe Value
      }

    nullCode =
      MachCode
        { inputX = Zero
        , inputY = Zero
        , result = Zero
        , aluCode = Imm
        , ldReg = Zero
        , rdAddr = 0
        , wrAddrM = Nothing
        , jmpM = Nothing
        }
    @

    Next we define the CPU and its ALU:

    @
    cpu
      :: Vec 7 Value          -- ^ Register bank
      -> (Value,Instruction)  -- ^ (Memory output, Current instruction)
      -> ( Vec 7 Value
         , (MemAddr, Maybe (MemAddr,Value), InstrAddr)
         )
    cpu regbank (memOut, instr) =
      (regbank', (rdAddr, (,aluOut) '<$>' wrAddrM, bitCoerce ipntr))
     where
      -- Current instruction pointer
      ipntr = regbank 'Clash.Sized.Vector.!!' PC

      -- Decoder
      (MachCode {..}) = case instr of
        Compute op rx ry res -> nullCode {inputX=rx,inputY=ry,result=res,aluCode=op}
        Branch cr a          -> nullCode {inputX=cr,jmpM=Just a}
        Jump a               -> nullCode {aluCode=Incr,jmpM=Just a}
        Load a r             -> nullCode {ldReg=r,rdAddr=a}
        Store r a            -> nullCode {inputX=r,wrAddrM=Just a}
        Nop                  -> nullCode

      -- ALU
      regX   = regbank 'Clash.Sized.Vector.!!' inputX
      regY   = regbank 'Clash.Sized.Vector.!!' inputY
      aluOut = alu aluCode regX regY

      -- next instruction
      nextPC =
        case jmpM of
          Just a | aluOut /= 0 -> ipntr + a
          _                    -> ipntr + 1

      -- update registers
      regbank' = 'Clash.Sized.Vector.replace' Zero   0
               $ 'Clash.Sized.Vector.replace' PC     nextPC
               $ 'Clash.Sized.Vector.replace' result aluOut
               $ 'Clash.Sized.Vector.replace' ldReg  memOut
               $ regbank

    alu Add   x y = x + y
    alu Sub   x y = x - y
    alu Incr  x _ = x + 1
    alu Imm   x _ = x
    alu CmpGt x y = if x > y then 1 else 0
    @

    We initially create a memory out of simple registers:

    @
    dataMem
      :: KnownDomain dom
      => Clock dom
      -> Reset dom
      -> Enable dom
      -> Signal dom MemAddr
      -- ^ Read address
      -> Signal dom (Maybe (MemAddr,Value))
      -- ^ (write address, data in)
      -> Signal dom Value
      -- ^ data out
    dataMem clk rst en rd wrM =
      'Clash.Explicit.Mealy.mealy' clk rst en dataMemT ('Clash.Sized.Vector.replicate' d32 0) (bundle (rd,wrM))
     where
      dataMemT mem (rd,wrM) = (mem',dout)
        where
          dout = mem 'Clash.Sized.Vector.!!' rd
          mem' =
            case wrM of
              Just (wr,din) -> 'Clash.Sized.Vector.replace' wr din mem
              _             -> mem
    @

    And then connect everything:

    @
    system
      :: ( KnownDomain dom
         , KnownNat n )
      => Vec n Instruction
      -> Clock dom
      -> Reset dom
      -> Enable dom
      -> Signal dom Value
    system instrs clk rst en = memOut
     where
      memOut = dataMem clk rst en rdAddr dout
      (rdAddr,dout,ipntr) = 'Clash.Explicit.Mealy.mealyB' clk rst en cpu ('Clash.Sized.Vector.replicate' d7 0) (memOut,instr)
      instr  = 'Clash.Explicit.Prelude.asyncRom' instrs '<$>' ipntr
    @

    Create a simple program that calculates the GCD of 4 and 6:

    @
    -- Compute GCD of 4 and 6
    prog = -- 0 := 4
           Compute Incr Zero RegA RegA :>
           replicate d3 (Compute Incr RegA Zero RegA) ++
           Store RegA 0 :>
           -- 1 := 6
           Compute Incr Zero RegA RegA :>
           replicate d5 (Compute Incr RegA Zero RegA) ++
           Store RegA 1 :>
           -- A := 4
           Load 0 RegA :>
           -- B := 6
           Load 1 RegB :>
           -- start
           Compute CmpGt RegA RegB RegC :>
           Branch RegC 4 :>
           Compute CmpGt RegB RegA RegC :>
           Branch RegC 4 :>
           Jump 5 :>
           -- (a > b)
           Compute Sub RegA RegB RegA :>
           Jump (-6) :>
           -- (b > a)
           Compute Sub RegB RegA RegB :>
           Jump (-8) :>
           -- end
           Store RegA 2 :>
           Load 2 RegC :>
           Nil
    @

    And test our system:

    @
    >>> sampleN 32 $ system prog systemClockGen resetGen enableGen
    [0,0,0,0,0,0,4,4,4,4,4,4,4,4,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2]

    @

    to see that our system indeed calculates that the GCD of 6 and 4 is 2.

    === Improvement 1: using @asyncRam@

    As you can see, it's fairly straightforward to build a memory using registers
    and read ('Clash.Sized.Vector.!!') and write ('Clash.Sized.Vector.replace')
    logic. This might however not result in the most efficient hardware structure,
    especially when building an ASIC.

    Instead it is preferable to use the 'Clash.Prelude.RAM.asyncRam' function which
    has the potential to be translated to a more efficient structure:

    @
    system2
      :: ( KnownDomain dom
         , KnownNat n )
      => Vec n Instruction
      -> Clock dom
      -> Reset dom
      -> Enable dom
      -> Signal dom Value
    system2 instrs clk rst en = memOut
     where
      memOut = 'Clash.Explicit.RAM.asyncRam' clk clk en d32 rdAddr dout
      (rdAddr,dout,ipntr) = 'Clash.Explicit.Prelude.mealyB' clk rst en cpu ('Clash.Sized.Vector.replicate' d7 0) (memOut,instr)
      instr  = 'Clash.Prelude.ROM.asyncRom' instrs '<$>' ipntr
    @

    Again, we can simulate our system and see that it works. This time however,
    we need to disregard the first few output samples, because the initial content of an
    'Clash.Prelude.RAM.asyncRam' is /undefined/, and consequently, the first few
    output samples are also /undefined/. We use the utility function
    'Clash.XException.printX' to conveniently filter out the undefinedness and
    replace it with the string @\"undefined\"@ in the first few leading outputs.

    @
    >>> printX $ sampleN 32 $ system2 prog systemClockGen resetGen enableGen
    [undefined,undefined,undefined,undefined,undefined,undefined,4,4,4,4,4,4,4,4,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2]

    @

    === Improvement 2: using @blockRam@

    Finally we get to using 'blockRam'. On FPGAs, 'Clash.Prelude.RAM.asyncRam' will
    be implemented in terms of LUTs, and therefore take up logic resources. FPGAs
    also have large(r) memory structures called /block RAMs/, which are preferred,
    especially as the memories we need for our application get bigger. The
    'blockRam' function will be translated to such a /block RAM/.

    One important aspect of block RAMs is that they have a /synchronous/ read port,
    meaning unlike an 'Clash.Prelude.RAM.asyncRam', the result of a read command
    given at time @t@ is output at time @t + 1@.

    For us that means we need to change the design of our CPU. Right now, upon a
    load instruction we generate a read address for the memory, and the value at
    that read address is immediately available to be put in the register bank. We
    will be using a block RAM, so the value is delayed until the next cycle. Thus,
    we will also need to delay the register address to which the memory address is
    loaded:

    @
    cpu2
      :: (Vec 7 Value,Reg)    -- ^ (Register bank, Load reg addr)
      -> (Value,Instruction)  -- ^ (Memory output, Current instruction)
      -> ( (Vec 7 Value, Reg)
         , (MemAddr, Maybe (MemAddr,Value), InstrAddr)
         )
    cpu2 (regbank, ldRegD) (memOut, instr) =
      ((regbank', ldRegD'), (rdAddr, (,aluOut) '<$>' wrAddrM, bitCoerce ipntr))
     where
      -- Current instruction pointer
      ipntr = regbank 'Clash.Sized.Vector.!!' PC

      -- Decoder
      (MachCode {..}) = case instr of
        Compute op rx ry res -> nullCode {inputX=rx,inputY=ry,result=res,aluCode=op}
        Branch cr a          -> nullCode {inputX=cr,jmpM=Just a}
        Jump a               -> nullCode {aluCode=Incr,jmpM=Just a}
        Load a r             -> nullCode {ldReg=r,rdAddr=a}
        Store r a            -> nullCode {inputX=r,wrAddrM=Just a}
        Nop                  -> nullCode

      -- ALU
      regX   = regbank 'Clash.Sized.Vector.!!' inputX
      regY   = regbank 'Clash.Sized.Vector.!!' inputY
      aluOut = alu aluCode regX regY

      -- next instruction
      nextPC =
        case jmpM of
          Just a | aluOut /= 0 -> ipntr + a
          _                    -> ipntr + 1

      -- update registers
      ldRegD'  = ldReg  -- Delay the ldReg by 1 cycle
      regbank' = 'Clash.Sized.Vector.replace' Zero   0
               $ 'Clash.Sized.Vector.replace' PC     nextPC
               $ 'Clash.Sized.Vector.replace' result aluOut
               $ 'Clash.Sized.Vector.replace' ldRegD memOut
               $ regbank
    @

    We can now finally instantiate our system with a 'blockRam':

    @
    system3
      :: ( KnownDomain dom
         , KnownNat n )
      => Vec n Instruction
      -> Clock dom
      -> Reset dom
      -> Enable dom
      -> Signal dom Value
    system3 instrs clk rst en = memOut
     where
      memOut = 'blockRam' clk en (replicate d32 0) rdAddr dout
      (rdAddr,dout,ipntr) = 'Clash.Explicit.Prelude.mealyB' clk rst en cpu2 (('Clash.Sized.Vector.replicate' d7 0),Zero) (memOut,instr)
      instr  = 'Clash.Explicit.Prelude.asyncRom' instrs '<$>' ipntr
    @

    We are, however, not done. We will also need to update our program. The reason
    being that values that we try to load in our registers won't be loaded into the
    register until the next cycle. This is a problem when the next instruction
    immediately depends on this memory value. In our example, this was only the case
    when we loaded the value @6@, which was stored at address @1@, into @RegB@.
    Our updated program is thus:

    @
    prog2 = -- 0 := 4
           Compute Incr Zero RegA RegA :>
           replicate d3 (Compute Incr RegA Zero RegA) ++
           Store RegA 0 :>
           -- 1 := 6
           Compute Incr Zero RegA RegA :>
           replicate d5 (Compute Incr RegA Zero RegA) ++
           Store RegA 1 :>
           -- A := 4
           Load 0 RegA :>
           -- B := 6
           Load 1 RegB :>
           Nop :> -- Extra NOP
           -- start
           Compute CmpGt RegA RegB RegC :>
           Branch RegC 4 :>
           Compute CmpGt RegB RegA RegC :>
           Branch RegC 4 :>
           Jump 5 :>
           -- (a > b)
           Compute Sub RegA RegB RegA :>
           Jump (-6) :>
           -- (b > a)
           Compute Sub RegB RegA RegB :>
           Jump (-8) :>
           -- end
           Store RegA 2 :>
           Load 2 RegC :>
           Nil
    @

    When we simulate our system we see that it works. This time again,
    we need to disregard the first sample, because the initial output of a
    'blockRam' is /undefined/. We use the utility function 'Clash.XException.printX'
    to conveniently filter out the undefinedness and replace it with the string @\"undefined\"@.

    @
    >>> printX $ sampleN 34 $ system3 prog2 systemClockGen resetGen enableGen
    [undefined,0,0,0,0,0,0,4,4,4,4,4,4,4,4,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2]

    @

    This concludes the short introduction to using 'blockRam'.

    -}

    {-# LANGUAGE CPP #-}
    {-# LANGUAGE DeriveAnyClass #-}
    {-# LANGUAGE GADTs #-}
    {-# LANGUAGE NoImplicitPrelude #-}
    {-# LANGUAGE QuasiQuotes #-}
    {-# LANGUAGE TemplateHaskellQuotes #-}

    {-# LANGUAGE Trustworthy #-}

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

    -- See [Note: eta port names for trueDualPortBlockRam]
    {-# OPTIONS_GHC -fno-do-lambda-eta-expansion #-}

    -- See: https://github.com/clash-lang/clash-compiler/commit/721fcfa9198925661cd836668705f817bddaae3c
    -- as to why we need this.
    {-# OPTIONS_GHC -fno-cpr-anal #-}

    module Clash.Explicit.BlockRam
      ( -- * Block RAM synchronized to an arbitrary clock
        blockRam
      , blockRamPow2
      , blockRamU
      , blockRam1
      , ResetStrategy(..)
        -- ** Read/write conflict resolution
      , readNew
        -- * True dual-port block RAM
        -- $tdpbram
      , trueDualPortBlockRam
      , RamOp(..)
        -- * Internal
      , blockRam#
      , blockRamU#
      , blockRam1#
      , trueDualPortBlockRam#
      )
    where

    import Clash.HaskellPrelude

    import Control.Exception (catch, throw)
    import Control.Monad (forM_)
    import Control.Monad.ST (ST, runST)
    import Control.Monad.ST.Unsafe (unsafeInterleaveST, unsafeIOToST, unsafeSTToIO)
    import Data.Array.MArray (newListArray)
    import Data.List.Infinite (Infinite(..), (...))
    import Data.Maybe (isJust)
    import Data.Sequence (Seq)
    import Data.String.Interpolate (__i)
    import GHC.Arr (STArray, unsafeReadSTArray, unsafeWriteSTArray)
    import GHC.Generics (Generic)
    import GHC.Stack (HasCallStack, withFrozenCallStack)
    import GHC.TypeLits (KnownNat, type (^), type (<=))
    import Unsafe.Coerce (unsafeCoerce)

    import Clash.Annotations.Primitive
      (Primitive(InlineYamlPrimitive), HDL(..), hasBlackBox)
    import Clash.Class.AutoReg (AutoReg(autoReg))
    import Clash.Class.BitPack (bitToBool, msb)
    import Clash.Class.Num (SaturationMode(SatBound), satSucc)
    import Clash.Explicit.BlockRam.Model (TdpbramModelConfig(..), tdpbramModel)
    import Clash.Explicit.Signal (KnownDomain, Enable, register, fromEnable, andEnable)
    import Clash.Promoted.Nat (SNat(..))
    import Clash.Signal.Bundle (unbundle)
    import Clash.Signal.Internal
      (Clock(..), Reset, Signal (..), invertReset, (.&&.), mux)
    import Clash.Sized.BitVector (BitVector)
    import Clash.Sized.Index (Index)
    import Clash.Sized.Unsigned (Unsigned)
    import Clash.Sized.Vector (Vec, replicate, iterateI)
    import Clash.XException
     (maybeIsX, NFDataX(deepErrorX), defaultSeqX, fromJustX, undefined,
     XException (..), seqX, errorX)
    import Clash.XException.MaybeX (MaybeX(..), andX)

    import qualified Data.Sequence as Seq
    import qualified Data.List as L

    import qualified Clash.Sized.Vector as CV

    {- $tdpbram
    A true dual-port block RAM has two fully independent, fully functional access
    ports: port A and port B. Either port can do both RAM reads and writes. These
    two ports can even be on distinct clock domains, but the memory itself is shared
    between the ports. This also makes a true dual-port block RAM suitable as a
    component in a domain crossing circuit (but it needs additional logic for it to
    be safe, see e.g. 'Clash.Explicit.Synchronizer.asyncFIFOSynchronizer').

    A version with implicit clocks can be found in "Clash.Prelude.BlockRam".
    -}

    -- start benchmark only
    -- import GHC.Arr (listArray, unsafeThawSTArray)
    -- end benchmark only

    {- $setup
    >>> import Clash.Explicit.Prelude as C
    >>> import qualified Data.List as L
    >>> :set -XDataKinds -XRecordWildCards -XTupleSections -XDeriveAnyClass -XDeriveGeneric
    >>> type InstrAddr = Unsigned 8
    >>> type MemAddr = Unsigned 5
    >>> type Value = Signed 8
    >>> :{
    data Reg
      = Zero
      | PC
      | RegA
      | RegB
      | RegC
      | RegD
      | RegE
      deriving (Eq,Show,Enum,C.Generic,NFDataX)
    :}

    >>> :{
    data Operator = Add | Sub | Incr | Imm | CmpGt
      deriving (Eq, Show, Generic, NFDataX)
    :}

    >>> :{
    data Instruction
      = Compute Operator Reg Reg Reg
      | Branch Reg Value
      | Jump Value
      | Load MemAddr Reg
      | Store Reg MemAddr
      | Nop
      deriving (Eq, Show, Generic, NFDataX)
    :}

    >>> :{
    data MachCode
      = MachCode
      { inputX  :: Reg
      , inputY  :: Reg
      , result  :: Reg
      , aluCode :: Operator
      , ldReg   :: Reg
      , rdAddr  :: MemAddr
      , wrAddrM :: Maybe MemAddr
      , jmpM    :: Maybe Value
      }
    :}

    >>> :{
    nullCode = MachCode { inputX = Zero, inputY = Zero, result = Zero, aluCode = Imm
                        , ldReg = Zero, rdAddr = 0, wrAddrM = Nothing
                        , jmpM = Nothing
                        }
    :}

    >>> :{
    alu Add   x y = x + y
    alu Sub   x y = x - y
    alu Incr  x _ = x + 1
    alu Imm   x _ = x
    alu CmpGt x y = if x > y then 1 else 0
    :}

    >>> :{
    let cpu :: Vec 7 Value          -- ^ Register bank
            -> (Value,Instruction)  -- ^ (Memory output, Current instruction)
            -> ( Vec 7 Value
               , (MemAddr,Maybe (MemAddr,Value),InstrAddr)
               )
        cpu regbank (memOut,instr) = (regbank',(rdAddr,(,aluOut) <$> wrAddrM,bitCoerce ipntr))
          where
            -- Current instruction pointer
            ipntr = regbank C.!! PC
            -- Decoder
            (MachCode {..}) = case instr of
              Compute op rx ry res -> nullCode {inputX=rx,inputY=ry,result=res,aluCode=op}
              Branch cr a          -> nullCode {inputX=cr,jmpM=Just a}
              Jump a               -> nullCode {aluCode=Incr,jmpM=Just a}
              Load a r             -> nullCode {ldReg=r,rdAddr=a}
              Store r a            -> nullCode {inputX=r,wrAddrM=Just a}
              Nop                  -> nullCode
            -- ALU
            regX   = regbank C.!! inputX
            regY   = regbank C.!! inputY
            aluOut = alu aluCode regX regY
            -- next instruction
            nextPC = case jmpM of
                       Just a | aluOut /= 0 -> ipntr + a
                       _                    -> ipntr + 1
            -- update registers
            regbank' = replace Zero   0
                     $ replace PC     nextPC
                     $ replace result aluOut
                     $ replace ldReg  memOut
                     $ regbank
    :}

    >>> :{
    let dataMem
          :: KnownDomain dom
          => Clock  dom
          -> Reset  dom
          -> Enable dom
          -> Signal dom MemAddr
          -> Signal dom (Maybe (MemAddr,Value))
          -> Signal dom Value
        dataMem clk rst en rd wrM = mealy clk rst en dataMemT (C.replicate d32 0) (bundle (rd,wrM))
          where
            dataMemT mem (rd,wrM) = (mem',dout)
              where
                dout = mem C.!! rd
                mem' = case wrM of
                         Just (wr,din) -> replace wr din mem
                         Nothing       -> mem
    :}

    >>> :{
    let system
          :: ( KnownDomain dom
             , KnownNat n )
          => Vec n Instruction
          -> Clock dom
          -> Reset dom
          -> Enable dom
          -> Signal dom Value
        system instrs clk rst en = memOut
          where
            memOut = dataMem clk rst en rdAddr dout
            (rdAddr,dout,ipntr) = mealyB clk rst en cpu (C.replicate d7 0) (memOut,instr)
            instr  = asyncRom instrs <$> ipntr
    :}

    >>> :{
    -- Compute GCD of 4 and 6
    prog = -- 0 := 4
           Compute Incr Zero RegA RegA :>
           C.replicate d3 (Compute Incr RegA Zero RegA) C.++
           Store RegA 0 :>
           -- 1 := 6
           Compute Incr Zero RegA RegA :>
           C.replicate d5 (Compute Incr RegA Zero RegA) C.++
           Store RegA 1 :>
           -- A := 4
           Load 0 RegA :>
           -- B := 6
           Load 1 RegB :>
           -- start
           Compute CmpGt RegA RegB RegC :>
           Branch RegC 4 :>
           Compute CmpGt RegB RegA RegC :>
           Branch RegC 4 :>
           Jump 5 :>
           -- (a > b)
           Compute Sub RegA RegB RegA :>
           Jump (-6) :>
           -- (b > a)
           Compute Sub RegB RegA RegB :>
           Jump (-8) :>
           -- end
           Store RegA 2 :>
           Load 2 RegC :>
           Nil
    :}

    >>> :{
    let system2
          :: ( KnownDomain dom
             , KnownNat n )
          => Vec n Instruction
          -> Clock dom
          -> Reset dom
          -> Enable dom
          -> Signal dom Value
        system2 instrs clk rst en = memOut
          where
            memOut = asyncRam clk clk en d32 rdAddr dout
            (rdAddr,dout,ipntr) = mealyB clk rst en cpu (C.replicate d7 0) (memOut,instr)
            instr  = asyncRom instrs <$> ipntr
    :}

    >>> :{
    let cpu2 :: (Vec 7 Value,Reg)    -- ^ (Register bank, Load reg addr)
             -> (Value,Instruction)  -- ^ (Memory output, Current instruction)
             -> ( (Vec 7 Value,Reg)
                , (MemAddr,Maybe (MemAddr,Value),InstrAddr)
                )
        cpu2 (regbank,ldRegD) (memOut,instr) = ((regbank',ldRegD'),(rdAddr,(,aluOut) <$> wrAddrM,bitCoerce ipntr))
          where
            -- Current instruction pointer
            ipntr = regbank C.!! PC
            -- Decoder
            (MachCode {..}) = case instr of
              Compute op rx ry res -> nullCode {inputX=rx,inputY=ry,result=res,aluCode=op}
              Branch cr a          -> nullCode {inputX=cr,jmpM=Just a}
              Jump a               -> nullCode {aluCode=Incr,jmpM=Just a}
              Load a r             -> nullCode {ldReg=r,rdAddr=a}
              Store r a            -> nullCode {inputX=r,wrAddrM=Just a}
              Nop                  -> nullCode
            -- ALU
            regX   = regbank C.!! inputX
            regY   = regbank C.!! inputY
            aluOut = alu aluCode regX regY
            -- next instruction
            nextPC = case jmpM of
                       Just a | aluOut /= 0 -> ipntr + a
                       _                    -> ipntr + 1
            -- update registers
            ldRegD'  = ldReg -- Delay the ldReg by 1 cycle
            regbank' = replace Zero   0
                     $ replace PC     nextPC
                     $ replace result aluOut
                     $ replace ldRegD memOut
                     $ regbank
    :}

    >>> :{
    let system3
          :: ( KnownDomain dom
             , KnownNat n )
          => Vec n Instruction
          -> Clock dom
          -> Reset dom
          -> Enable dom
          -> Signal dom Value
        system3 instrs clk rst en = memOut
          where
            memOut = blockRam clk en (C.replicate d32 0) rdAddr dout
            (rdAddr,dout,ipntr) = mealyB clk rst en cpu2 ((C.replicate d7 0),Zero) (memOut,instr)
            instr  = asyncRom instrs <$> ipntr
    :}

    >>> :{
    prog2 = -- 0 := 4
           Compute Incr Zero RegA RegA :>
           C.replicate d3 (Compute Incr RegA Zero RegA) C.++
           Store RegA 0 :>
           -- 1 := 6
           Compute Incr Zero RegA RegA :>
           C.replicate d5 (Compute Incr RegA Zero RegA) C.++
           Store RegA 1 :>
           -- A := 4
           Load 0 RegA :>
           -- B := 6
           Load 1 RegB :>
           Nop :> -- Extra NOP
           -- start
           Compute CmpGt RegA RegB RegC :>
           Branch RegC 4 :>
           Compute CmpGt RegB RegA RegC :>
           Branch RegC 4 :>
           Jump 5 :>
           -- (a > b)
           Compute Sub RegA RegB RegA :>
           Jump (-6) :>
           -- (b > a)
           Compute Sub RegB RegA RegB :>
           Jump (-8) :>
           -- end
           Store RegA 2 :>
           Load 2 RegC :>
           Nil
    :}

    -}

    -- | 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
    -- 'XException'
    --
    -- === See also:
    --
    -- * See "Clash.Explicit.BlockRam#usingrams" for more information on how to use a
    -- block RAM.
    -- * Use the adapter 'readNew' for obtaining write-before-read semantics like
    -- this: @'readNew' clk rst en ('blockRam' clk inits) rd wrM@.
    -- * A large 'Vec' for the initial content may be too inefficient, depending
    -- on how it is constructed. See 'Clash.Explicit.BlockRam.File.blockRamFile' and
    -- 'Clash.Explicit.BlockRam.Blob.blockRamBlob' for different approaches that
    -- scale well.
    --
    -- === __Example__
    -- @
    -- bram40
    --   :: 'Clock'  dom
    --   -> 'Enable'  dom
    --   -> 'Signal' dom ('Unsigned' 6)
    --   -> 'Signal' dom (Maybe ('Unsigned' 6, 'Clash.Sized.BitVector.Bit'))
    --   -> 'Signal' dom 'Clash.Sized.BitVector.Bit'
    -- bram40 clk en = 'blockRam' clk en ('Clash.Sized.Vector.replicate' d40 1)
    -- @
    blockRam
      :: ( KnownDomain dom
         , HasCallStack
         , NFDataX a
         , Enum addr
         , NFDataX addr )
      => Clock dom
      -- ^ 'Clock' to synchronize to
      -> Enable dom
      -- ^ 'Enable' line
      -> Vec n a
      -- ^ Initial content of the BRAM, also determines the size, @n@, of the BRAM
       --
       -- __NB__: __MUST__ be a constant
      -> Signal dom addr
      -- ^ Read address @r@
      -> Signal dom (Maybe (addr, a))
      -- ^ (write address @w@, value to write)
      -> Signal dom a
      -- ^ Value of the BRAM at address @r@ from the previous clock cycle
    blockRam :: forall (dom :: Domain) a addr (n :: Nat).
    (KnownDomain dom, HasCallStack, NFDataX a, Enum addr,
     NFDataX addr) =>
    Clock dom
    -> Enable dom
    -> Vec n a
    -> Signal dom addr
    -> Signal dom (Maybe (addr, a))
    -> Signal dom a
    blockRam = \Clock dom
    clk Enable dom
    gen Vec n a
    content Signal dom addr
    rd Signal dom (Maybe (addr, a))
    wrM ->
      let en :: Signal dom Bool
    en       = Maybe (addr, a) -> Bool
    forall a. Maybe a -> Bool
    isJust (Maybe (addr, a) -> Bool)
    -> Signal dom (Maybe (addr, a)) -> Signal dom Bool
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Maybe (addr, a))
    wrM
          (Signal dom addr
    wr,Signal dom a
    din) = Signal dom (addr, a) -> Unbundled dom (addr, a)
    forall a (dom :: Domain).
    Bundle a =>
    Signal dom a -> Unbundled dom a
    forall (dom :: Domain).
    Signal dom (addr, a) -> Unbundled dom (addr, a)
    unbundle (Maybe (addr, a) -> (addr, a)
    forall a. (HasCallStack, NFDataX a) => Maybe a -> a
    fromJustX (Maybe (addr, a) -> (addr, a))
    -> Signal dom (Maybe (addr, a)) -> Signal dom (addr, a)
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Maybe (addr, a))
    wrM)
      in  (HasCallStack => Signal dom a) -> Signal dom a
    forall a. HasCallStack => (HasCallStack => a) -> a
    withFrozenCallStack
          (Clock dom
    -> Enable dom
    -> Vec n a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    forall (dom :: Domain) a (n :: Nat).
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> Vec n a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRam# Clock dom
    clk Enable dom
    gen Vec n a
    content (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    rd) Signal dom Bool
    en (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    wr) Signal dom a
    din)
    {-# INLINE blockRam #-}

    -- | 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
    -- 'XException'
    --
    -- === See also:
    --
    -- * See "Clash.Prelude.BlockRam#usingrams" for more information on how to use a
    -- block RAM.
    -- * Use the adapter 'readNew' for obtaining write-before-read semantics like
    -- this: @'readNew' clk rst en ('blockRamPow2' clk inits) rd wrM@.
    -- * A large 'Vec' for the initial content may be too inefficient, depending
    -- on how it is constructed. See 'Clash.Explicit.BlockRam.File.blockRamFilePow2'
    -- and 'Clash.Explicit.BlockRam.Blob.blockRamBlobPow2' for different approaches
    -- that scale well.
    --
    -- === __Example__
    -- @
    -- bram32
    --   :: 'Clock' dom
    --   -> 'Enable' dom
    --   -> 'Signal' dom ('Unsigned' 5)
    --   -> 'Signal' dom (Maybe ('Unsigned' 5, 'Clash.Sized.BitVector.Bit'))
    --   -> 'Signal' dom 'Clash.Sized.BitVector.Bit'
    -- bram32 clk en = 'blockRamPow2' clk en ('Clash.Sized.Vector.replicate' d32 1)
    -- @
    blockRamPow2
      :: ( KnownDomain dom
         , HasCallStack
         , NFDataX a
         , KnownNat n )
      => Clock dom
      -- ^ 'Clock' to synchronize to
      -> Enable dom
      -- ^ 'Enable' line
      -> Vec (2^n) a
      -- ^ Initial content of the BRAM
      --
      -- __NB__: __MUST__ be a constant
      -> 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 BRAM at address @r@ from the previous clock cycle
    blockRamPow2 :: forall (dom :: Domain) a (n :: Nat).
    (KnownDomain dom, HasCallStack, NFDataX a, KnownNat n) =>
    Clock dom
    -> Enable dom
    -> Vec (2 ^ n) a
    -> Signal dom (Unsigned n)
    -> Signal dom (Maybe (Unsigned n, a))
    -> Signal dom a
    blockRamPow2 = \Clock dom
    clk Enable dom
    en Vec (2 ^ n) a
    cnt 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
      (Clock dom
    -> Enable dom
    -> Vec (2 ^ n) a
    -> Signal dom (Unsigned n)
    -> Signal dom (Maybe (Unsigned n, a))
    -> Signal dom a
    forall (dom :: Domain) a addr (n :: Nat).
    (KnownDomain dom, HasCallStack, NFDataX a, Enum addr,
     NFDataX addr) =>
    Clock dom
    -> Enable dom
    -> Vec n a
    -> Signal dom addr
    -> Signal dom (Maybe (addr, a))
    -> Signal dom a
    blockRam Clock dom
    clk Enable dom
    en Vec (2 ^ n) a
    cnt Signal dom (Unsigned n)
    rd Signal dom (Maybe (Unsigned n, a))
    wrM)
    {-# INLINE blockRamPow2 #-}

    data ResetStrategy (r :: Bool) where
      ClearOnReset :: ResetStrategy 'True
      NoClearOnReset :: ResetStrategy 'False

    -- | A version of 'blockRam' that has no default values set. May be cleared to
    -- an arbitrary state using a reset function.
    blockRamU
       :: forall n dom a r addr
       . ( KnownDomain dom
         , HasCallStack
         , NFDataX a
         , Enum addr
         , NFDataX addr
         , 1 <= n )
      => Clock dom
      -- ^ 'Clock' to synchronize to
      -> Reset dom
      -- ^ 'Reset' line. This needs to be asserted for at least /n/ cycles in order
      -- for the BRAM to be reset to its initial state.
      -> Enable dom
      -- ^ 'Enable' line
      -> ResetStrategy r
      -- ^ Whether to clear BRAM on asserted reset ('ClearOnReset') or
      -- not ('NoClearOnReset'). The reset needs to be asserted for at least /n/
      -- cycles to clear the BRAM.
      -> SNat n
      -- ^ Number of elements in BRAM
      -> (Index n -> a)
      -- ^ If applicable (see 'ResetStrategy' argument), reset BRAM using this function
      -> Signal dom addr
      -- ^ Read address @r@
      -> Signal dom (Maybe (addr, a))
      -- ^ (write address @w@, value to write)
      -> Signal dom a
      -- ^ Value of the BRAM at address @r@ from the previous clock cycle
    blockRamU :: forall (n :: Nat) (dom :: Domain) a (r :: Bool) addr.
    (KnownDomain dom, HasCallStack, NFDataX a, Enum addr, NFDataX addr,
     1 <= n) =>
    Clock dom
    -> Reset dom
    -> Enable dom
    -> ResetStrategy r
    -> SNat n
    -> (Index n -> a)
    -> Signal dom addr
    -> Signal dom (Maybe (addr, a))
    -> Signal dom a
    blockRamU Clock dom
    clk Reset dom
    rst0 Enable dom
    en ResetStrategy r
    rstStrategy n :: SNat n
    n@SNat n
    SNat Index n -> a
    initF Signal dom addr
    rd0 Signal dom (Maybe (addr, a))
    mw0 =
      case ResetStrategy r
    rstStrategy of
        ResetStrategy r
    ClearOnReset ->
          -- Use reset infrastructure
          Clock dom
    -> Enable dom
    -> SNat n
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    forall (n :: Nat) (dom :: Domain) a.
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> SNat n
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRamU# Clock dom
    clk Enable dom
    en SNat n
    n Signal dom Int
    rd1 Signal dom Bool
    we1 Signal dom Int
    wa1 Signal dom a
    w1
        ResetStrategy r
    NoClearOnReset ->
          -- Ignore reset infrastructure, pass values unchanged
          Clock dom
    -> Enable dom
    -> SNat n
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    forall (n :: Nat) (dom :: Domain) a.
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> SNat n
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRamU# Clock dom
    clk Enable dom
    en SNat n
    n
            (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    rd0)
            Signal dom Bool
    we0
            (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    wa0)
            Signal dom a
    w0
     where
      rstBool :: Signal dom Bool
    rstBool = Clock dom
    -> Reset dom
    -> Enable dom
    -> Bool
    -> Signal dom Bool
    -> Signal dom Bool
    forall (dom :: Domain) a.
    (KnownDomain dom, NFDataX a) =>
    Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    register Clock dom
    clk Reset dom
    rst0 Enable dom
    en Bool
    True (Bool -> Signal dom Bool
    forall a. a -> Signal dom a
    forall (f :: Type -> Type) a. Applicative f => a -> f a
    pure Bool
    False)
      rstInv :: Reset dom
    rstInv = Reset dom -> Reset dom
    forall (dom :: Domain). KnownDomain dom => Reset dom -> Reset dom
    invertReset Reset dom
    rst0

      waCounter :: Signal dom (Index n)
      waCounter :: Signal dom (Index n)
    waCounter = Clock dom
    -> Reset dom
    -> Enable dom
    -> Index n
    -> Signal dom (Index n)
    -> Signal dom (Index n)
    forall (dom :: Domain) a.
    (KnownDomain dom, NFDataX a) =>
    Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    register Clock dom
    clk Reset dom
    rstInv Enable dom
    en Index n
    0 (SaturationMode -> Index n -> Index n
    forall a. SaturatingNum a => SaturationMode -> a -> a
    satSucc SaturationMode
    SatBound (Index n -> Index n)
    -> Signal dom (Index n) -> Signal dom (Index n)
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Index n)
    waCounter)

      wa0 :: Signal dom addr
    wa0 = (addr, a) -> addr
    forall a b. (a, b) -> a
    fst ((addr, a) -> addr)
    -> (Maybe (addr, a) -> (addr, a)) -> Maybe (addr, a) -> addr
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Maybe (addr, a) -> (addr, a)
    forall a. (HasCallStack, NFDataX a) => Maybe a -> a
    fromJustX (Maybe (addr, a) -> addr)
    -> Signal dom (Maybe (addr, a)) -> Signal dom addr
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Maybe (addr, a))
    mw0
      w0 :: Signal dom a
    w0  = (addr, a) -> a
    forall a b. (a, b) -> b
    snd ((addr, a) -> a)
    -> (Maybe (addr, a) -> (addr, a)) -> Maybe (addr, a) -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Maybe (addr, a) -> (addr, a)
    forall a. (HasCallStack, NFDataX a) => Maybe a -> a
    fromJustX (Maybe (addr, a) -> a)
    -> Signal dom (Maybe (addr, a)) -> Signal dom a
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Maybe (addr, a))
    mw0
      we0 :: Signal dom Bool
    we0 = Maybe (addr, a) -> Bool
    forall a. Maybe a -> Bool
    isJust (Maybe (addr, a) -> Bool)
    -> Signal dom (Maybe (addr, a)) -> Signal dom Bool
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Maybe (addr, a))
    mw0

      rd1 :: Signal dom Int
    rd1 = Signal dom Bool
    -> Signal dom Int -> Signal dom Int -> Signal dom Int
    forall (f :: Type -> Type) a.
    Applicative f =>
    f Bool -> f a -> f a -> f a
    mux Signal dom Bool
    rstBool Signal dom Int
    0 (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    rd0)
      we1 :: Signal dom Bool
    we1 = Signal dom Bool
    -> Signal dom Bool -> Signal dom Bool -> Signal dom Bool
    forall (f :: Type -> Type) a.
    Applicative f =>
    f Bool -> f a -> f a -> f a
    mux Signal dom Bool
    rstBool (Bool -> Signal dom Bool
    forall a. a -> Signal dom a
    forall (f :: Type -> Type) a. Applicative f => a -> f a
    pure Bool
    True) Signal dom Bool
    we0
      wa1 :: Signal dom Int
    wa1 = Signal dom Bool
    -> Signal dom Int -> Signal dom Int -> Signal dom Int
    forall (f :: Type -> Type) a.
    Applicative f =>
    f Bool -> f a -> f a -> f a
    mux Signal dom Bool
    rstBool (Integer -> Int
    forall a. Num a => Integer -> a
    fromInteger (Integer -> Int) -> (Index n -> Integer) -> Index n -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Index n -> Integer
    forall a. Integral a => a -> Integer
    toInteger (Index n -> Int) -> Signal dom (Index n) -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Index n)
    waCounter) (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    wa0)
      w1 :: Signal dom a
    w1  = Signal dom Bool -> Signal dom a -> Signal dom a -> Signal dom a
    forall (f :: Type -> Type) a.
    Applicative f =>
    f Bool -> f a -> f a -> f a
    mux Signal dom Bool
    rstBool (Index n -> a
    initF (Index n -> a) -> Signal dom (Index n) -> Signal dom a
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Index n)
    waCounter) Signal dom a
    w0

    -- | blockRAMU primitive
    blockRamU#
      :: forall n dom a
       . ( KnownDomain dom
         , HasCallStack
         , NFDataX a )
      => Clock dom
      -- ^ 'Clock' to synchronize to
      -> Enable dom
      -- ^ 'Enable' line
      -> SNat n
      -- ^ Number of elements in BRAM
      -> Signal dom Int
      -- ^ Read address @r@
      -> Signal dom Bool
      -- ^ Write enable
      -> Signal dom Int
      -- ^ Write address @w@
      -> Signal dom a
      -- ^ Value to write (at address @w@)
      -> Signal dom a
      -- ^ Value of the BRAM at address @r@ from the previous clock cycle
    blockRamU# :: forall (n :: Nat) (dom :: Domain) a.
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> SNat n
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRamU# Clock dom
    clk Enable dom
    en SNat n
    SNat =
      -- TODO: Generalize to single BRAM primitive taking an initialization function
      Clock dom
    -> Enable dom
    -> Vec n a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    forall (dom :: Domain) a (n :: Nat).
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> Vec n a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRam#
        Clock dom
    clk
        Enable dom
    en
        ((Int -> a) -> Vec n Int -> Vec n a
    forall a b (n :: Nat). (a -> b) -> Vec n a -> Vec n b
    CV.map
          (\Int
    i -> String -> a
    forall a. (NFDataX a, HasCallStack) => String -> a
    deepErrorX (String -> a) -> String -> a
    forall a b. (a -> b) -> a -> b
    $ String
    "Initial value at index " String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> Int -> String
    forall a. Show a => a -> String
    show Int
    i String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    " undefined.")
          (forall (n :: Nat) a. KnownNat n => (a -> a) -> a -> Vec n a
    iterateI @n Int -> Int
    forall a. Enum a => a -> a
    succ (Int
    0 :: Int)))
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE blockRamU# #-}
    {-# ANN blockRamU# hasBlackBox #-}

    -- | A version of 'blockRam' that is initialized with the same value on all
    -- memory positions
    blockRam1
       :: forall n dom a r addr
       . ( KnownDomain dom
         , HasCallStack
         , NFDataX a
         , Enum addr
         , NFDataX addr
         , 1 <= n )
      => Clock dom
      -- ^ 'Clock' to synchronize to
      -> Reset dom
      -- ^ 'Reset' line. This needs to be asserted for at least /n/ cycles in order
      -- for the BRAM to be reset to its initial state.
      -> Enable dom
      -- ^ 'Enable' line
      -> ResetStrategy r
      -- ^ Whether to clear BRAM on asserted reset ('ClearOnReset') or
      -- not ('NoClearOnReset'). The reset needs to be asserted for at least /n/
      -- cycles to clear the BRAM.
      -> SNat n
      -- ^ Number of elements in BRAM
      -> a
      -- ^ Initial content of the BRAM (replicated /n/ times)
      -> Signal dom addr
      -- ^ Read address @r@
      -> Signal dom (Maybe (addr, a))
      -- ^ (write address @w@, value to write)
      -> Signal dom a
      -- ^ Value of the BRAM at address @r@ from the previous clock cycle
    blockRam1 :: forall (n :: Nat) (dom :: Domain) a (r :: Bool) addr.
    (KnownDomain dom, HasCallStack, NFDataX a, Enum addr, NFDataX addr,
     1 <= n) =>
    Clock dom
    -> Reset dom
    -> Enable dom
    -> ResetStrategy r
    -> SNat n
    -> a
    -> Signal dom addr
    -> Signal dom (Maybe (addr, a))
    -> Signal dom a
    blockRam1 Clock dom
    clk Reset dom
    rst0 Enable dom
    en ResetStrategy r
    rstStrategy n :: SNat n
    n@SNat n
    SNat a
    a Signal dom addr
    rd0 Signal dom (Maybe (addr, a))
    mw0 =
      case ResetStrategy r
    rstStrategy of
        ResetStrategy r
    ClearOnReset ->
          -- Use reset infrastructure
          Clock dom
    -> Enable dom
    -> SNat n
    -> a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    forall (n :: Nat) (dom :: Domain) a.
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> SNat n
    -> a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRam1# Clock dom
    clk Enable dom
    en SNat n
    n a
    a Signal dom Int
    rd1 Signal dom Bool
    we1 Signal dom Int
    wa1 Signal dom a
    w1
        ResetStrategy r
    NoClearOnReset ->
          -- Ignore reset infrastructure, pass values unchanged
          Clock dom
    -> Enable dom
    -> SNat n
    -> a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    forall (n :: Nat) (dom :: Domain) a.
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> SNat n
    -> a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRam1# Clock dom
    clk Enable dom
    en SNat n
    n a
    a
            (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    rd0)
            Signal dom Bool
    we0
            (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    wa0)
            Signal dom a
    w0
     where
      rstBool :: Signal dom Bool
    rstBool = Clock dom
    -> Reset dom
    -> Enable dom
    -> Bool
    -> Signal dom Bool
    -> Signal dom Bool
    forall (dom :: Domain) a.
    (KnownDomain dom, NFDataX a) =>
    Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    register Clock dom
    clk Reset dom
    rst0 Enable dom
    en Bool
    True (Bool -> Signal dom Bool
    forall a. a -> Signal dom a
    forall (f :: Type -> Type) a. Applicative f => a -> f a
    pure Bool
    False)
      rstInv :: Reset dom
    rstInv = Reset dom -> Reset dom
    forall (dom :: Domain). KnownDomain dom => Reset dom -> Reset dom
    invertReset Reset dom
    rst0

      waCounter :: Signal dom (Index n)
      waCounter :: Signal dom (Index n)
    waCounter = Clock dom
    -> Reset dom
    -> Enable dom
    -> Index n
    -> Signal dom (Index n)
    -> Signal dom (Index n)
    forall (dom :: Domain) a.
    (KnownDomain dom, NFDataX a) =>
    Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    register Clock dom
    clk Reset dom
    rstInv Enable dom
    en Index n
    0 (SaturationMode -> Index n -> Index n
    forall a. SaturatingNum a => SaturationMode -> a -> a
    satSucc SaturationMode
    SatBound (Index n -> Index n)
    -> Signal dom (Index n) -> Signal dom (Index n)
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Index n)
    waCounter)

      wa0 :: Signal dom addr
    wa0 = (addr, a) -> addr
    forall a b. (a, b) -> a
    fst ((addr, a) -> addr)
    -> (Maybe (addr, a) -> (addr, a)) -> Maybe (addr, a) -> addr
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Maybe (addr, a) -> (addr, a)
    forall a. (HasCallStack, NFDataX a) => Maybe a -> a
    fromJustX (Maybe (addr, a) -> addr)
    -> Signal dom (Maybe (addr, a)) -> Signal dom addr
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Maybe (addr, a))
    mw0
      w0 :: Signal dom a
    w0  = (addr, a) -> a
    forall a b. (a, b) -> b
    snd ((addr, a) -> a)
    -> (Maybe (addr, a) -> (addr, a)) -> Maybe (addr, a) -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Maybe (addr, a) -> (addr, a)
    forall a. (HasCallStack, NFDataX a) => Maybe a -> a
    fromJustX (Maybe (addr, a) -> a)
    -> Signal dom (Maybe (addr, a)) -> Signal dom a
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Maybe (addr, a))
    mw0
      we0 :: Signal dom Bool
    we0 = Maybe (addr, a) -> Bool
    forall a. Maybe a -> Bool
    isJust (Maybe (addr, a) -> Bool)
    -> Signal dom (Maybe (addr, a)) -> Signal dom Bool
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Maybe (addr, a))
    mw0

      rd1 :: Signal dom Int
    rd1 = Signal dom Bool
    -> Signal dom Int -> Signal dom Int -> Signal dom Int
    forall (f :: Type -> Type) a.
    Applicative f =>
    f Bool -> f a -> f a -> f a
    mux Signal dom Bool
    rstBool Signal dom Int
    0 (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    rd0)
      we1 :: Signal dom Bool
    we1 = Signal dom Bool
    -> Signal dom Bool -> Signal dom Bool -> Signal dom Bool
    forall (f :: Type -> Type) a.
    Applicative f =>
    f Bool -> f a -> f a -> f a
    mux Signal dom Bool
    rstBool (Bool -> Signal dom Bool
    forall a. a -> Signal dom a
    forall (f :: Type -> Type) a. Applicative f => a -> f a
    pure Bool
    True) Signal dom Bool
    we0
      wa1 :: Signal dom Int
    wa1 = Signal dom Bool
    -> Signal dom Int -> Signal dom Int -> Signal dom Int
    forall (f :: Type -> Type) a.
    Applicative f =>
    f Bool -> f a -> f a -> f a
    mux Signal dom Bool
    rstBool (Integer -> Int
    forall a. Num a => Integer -> a
    fromInteger (Integer -> Int) -> (Index n -> Integer) -> Index n -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Index n -> Integer
    forall a. Integral a => a -> Integer
    toInteger (Index n -> Int) -> Signal dom (Index n) -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (Index n)
    waCounter) (addr -> Int
    forall a. Enum a => a -> Int
    fromEnum (addr -> Int) -> Signal dom addr -> Signal dom Int
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    wa0)
      w1 :: Signal dom a
    w1  = Signal dom Bool -> Signal dom a -> Signal dom a -> Signal dom a
    forall (f :: Type -> Type) a.
    Applicative f =>
    f Bool -> f a -> f a -> f a
    mux Signal dom Bool
    rstBool (a -> Signal dom a
    forall a. a -> Signal dom a
    forall (f :: Type -> Type) a. Applicative f => a -> f a
    pure a
    a) Signal dom a
    w0

    -- | blockRAM1 primitive
    blockRam1#
      :: forall n dom a
       . ( KnownDomain dom
         , HasCallStack
         , NFDataX a )
      => Clock dom
      -- ^ 'Clock' to synchronize to
      -> Enable dom
      -- ^ 'Enable' line
      -> SNat n
      -- ^ Number of elements in BRAM
      -> a
      -- ^ Initial content of the BRAM (replicated /n/ times)
      -> Signal dom Int
      -- ^ Read address @r@
      -> Signal dom Bool
      -- ^ Write enable
      -> Signal dom Int
      -- ^ Write address @w@
      -> Signal dom a
      -- ^ Value to write (at address @w@)
      -> Signal dom a
      -- ^ Value of the BRAM at address @r@ from the previous clock cycle
    blockRam1# :: forall (n :: Nat) (dom :: Domain) a.
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> SNat n
    -> a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRam1# Clock dom
    clk Enable dom
    en SNat n
    n a
    a =
      -- TODO: Generalize to single BRAM primitive taking an initialization function
      Clock dom
    -> Enable dom
    -> Vec n a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    forall (dom :: Domain) a (n :: Nat).
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> Vec n a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRam# Clock dom
    clk Enable dom
    en (SNat n -> a -> Vec n a
    forall (n :: Nat) a. SNat n -> a -> Vec n a
    replicate SNat n
    n a
    a)
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE blockRam1# #-}
    {-# ANN blockRam1# hasBlackBox #-}

    -- | blockRAM primitive
    blockRam#
      :: forall dom a n
       . ( KnownDomain dom
         , HasCallStack
         , NFDataX a )
      => Clock dom
      -- ^ 'Clock' to synchronize to
      -> Enable dom
      -- ^ 'Enable' line
      -> Vec n a
      -- ^ Initial content of the BRAM, also determines the size, @n@, of the BRAM
      --
      -- __NB__: __MUST__ be a constant
      -> Signal dom Int
      -- ^ Read address @r@
      -> Signal dom Bool
      -- ^ Write enable
      -> Signal dom Int
      -- ^ Write address @w@
      -> Signal dom a
      -- ^ Value to write (at address @w@)
      -> Signal dom a
      -- ^ Value of the BRAM at address @r@ from the previous clock cycle
    blockRam# :: forall (dom :: Domain) a (n :: Nat).
    (KnownDomain dom, HasCallStack, NFDataX a) =>
    Clock dom
    -> Enable dom
    -> Vec n a
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    blockRam# (Clock SSymbol dom
    _ Maybe (Signal dom Femtoseconds)
    Nothing) Enable dom
    gen Vec n a
    content = \Signal dom Int
    rd Signal dom Bool
    wen Signal dom Int
    waS Signal dom a
    wd -> (forall s. ST s (Signal dom a)) -> Signal dom a
    forall a. (forall s. ST s a) -> a
    runST ((forall s. ST s (Signal dom a)) -> Signal dom a)
    -> (forall s. ST s (Signal dom a)) -> Signal dom a
    forall a b. (a -> b) -> a -> b
    $ do
      ramStart <- (Int, Int) -> [a] -> ST s (STArray s Int a)
    forall (a :: Type -> Type -> Type) e (m :: Type -> Type) i.
    (MArray a e m, Ix i) =>
    (i, i) -> [e] -> m (a i e)
    newListArray (Int
    0,Int
    szIInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    1) [a]
    contentL
      -- start benchmark only
      -- ramStart <- unsafeThawSTArray ramArr
      -- end benchmark only
      go
        ramStart
        (withFrozenCallStack (deepErrorX "blockRam: intial value undefined"))
        (fromEnable gen)
        rd
        (fromEnable gen .&&. wen)
        waS
        wd
     where
      contentL :: [a]
    contentL = Vec n a -> [a]
    forall a b. a -> b
    unsafeCoerce Vec n a
    content :: [a]
      szI :: Int
    szI = [a] -> Int
    forall a. [a] -> Int
    forall (t :: Type -> Type) a. Foldable t => t a -> Int
    L.length [a]
    contentL
      -- start benchmark only
      -- ramArr = listArray (0,szI-1) contentL
      -- end benchmark only

      go :: STArray s Int a -> a -> Signal dom Bool -> Signal dom Int
         -> Signal dom Bool -> Signal dom Int -> Signal dom a
         -> ST s (Signal dom a)
      go :: forall s.
    STArray s Int a
    -> a
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> ST s (Signal dom a)
    go !STArray s Int a
    ram a
    o ret :: Signal dom Bool
    ret@(~(Bool
    re :- Signal dom Bool
    res)) rt :: Signal dom Int
    rt@(~(Int
    r :- Signal dom Int
    rs)) et :: Signal dom Bool
    et@(~(Bool
    e :- Signal dom Bool
    en)) wt :: Signal dom Int
    wt@(~(Int
    w :- Signal dom Int
    wr)) dt :: Signal dom a
    dt@(~(a
    d :- Signal dom a
    din)) = do
        a
    o a -> ST s (Signal dom a) -> ST s (Signal dom a)
    forall a b. a -> b -> b
    `seqX` (a
    o a -> Signal dom a -> Signal dom a
    forall (dom :: Domain) a. a -> Signal dom a -> Signal dom a
    :-) (Signal dom a -> Signal dom a)
    -> ST s (Signal dom a) -> ST s (Signal dom a)
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> (Signal dom Bool
    ret Signal dom Bool -> ST s (Signal dom a) -> ST s (Signal dom a)
    forall a b. a -> b -> b
    `seq` Signal dom Int
    rt Signal dom Int -> ST s (Signal dom a) -> ST s (Signal dom a)
    forall a b. a -> b -> b
    `seq` Signal dom Bool
    et Signal dom Bool -> ST s (Signal dom a) -> ST s (Signal dom a)
    forall a b. a -> b -> b
    `seq` Signal dom Int
    wt Signal dom Int -> ST s (Signal dom a) -> ST s (Signal dom a)
    forall a b. a -> b -> b
    `seq` Signal dom a
    dt Signal dom a -> ST s (Signal dom a) -> ST s (Signal dom a)
    forall a b. a -> b -> b
    `seq`
          ST s (Signal dom a) -> ST s (Signal dom a)
    forall s a. ST s a -> ST s a
    unsafeInterleaveST
            (do o' <- IO a -> ST s a
    forall a s. IO a -> ST s a
    unsafeIOToST
                        (IO a -> (XException -> IO a) -> IO a
    forall e a. Exception e => IO a -> (e -> IO a) -> IO a
    catch (if Bool
    re then ST s a -> IO a
    forall s a. ST s a -> IO a
    unsafeSTToIO (STArray s Int a
    ram STArray s Int a -> Int -> ST s a
    forall s. HasCallStack => STArray s Int a -> Int -> ST s a
    `safeAt` Int
    r) else a -> IO a
    forall a. a -> IO a
    forall (f :: Type -> Type) a. Applicative f => a -> f a
    pure a
    o)
                        (\err :: XException
    err@XException {} -> a -> IO a
    forall a. a -> IO a
    forall (f :: Type -> Type) a. Applicative f => a -> f a
    pure (XException -> a
    forall a e. (HasCallStack, Exception e) => e -> a
    throw XException
    err)))
                d `defaultSeqX` upd ram e (fromEnum w) d
                go ram o' res rs en wr din))

      upd :: STArray s Int a -> Bool -> Int -> a -> ST s ()
      upd :: forall s. STArray s Int a -> Bool -> Int -> a -> ST s ()
    upd STArray s Int a
    ram Bool
    we Int
    waddr a
    d = case Bool -> Maybe Bool
    forall a. a -> Maybe a
    maybeIsX Bool
    we of
        Maybe Bool
    Nothing -> case Int -> Maybe Int
    forall a. a -> Maybe a
    maybeIsX Int
    waddr of
          Maybe Int
    Nothing -> -- Put the XException from `waddr` as the value in all
                     -- locations of `ram`.
                     [Int] -> (Int -> ST s ()) -> ST s ()
    forall (t :: Type -> Type) (m :: Type -> Type) a b.
    (Foldable t, Monad m) =>
    t a -> (a -> m b) -> m ()
    forM_ [Int
    0..(Int
    szIInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    1)] (\Int
    i -> STArray s Int a -> Int -> a -> ST s ()
    forall s i e. STArray s i e -> Int -> e -> ST s ()
    unsafeWriteSTArray STArray s Int a
    ram Int
    i (Int -> a -> a
    forall a b. a -> b -> b
    seq Int
    waddr a
    d))
          Just Int
    wa -> -- Put the XException from `we` as the value at address
                     -- `waddr`.
                     Int -> a -> STArray s Int a -> ST s ()
    forall s. HasCallStack => Int -> a -> STArray s Int a -> ST s ()
    safeUpdate Int
    wa (Bool -> a -> a
    forall a b. a -> b -> b
    seq Bool
    we a
    d) STArray s Int a
    ram
        Just Bool
    True -> case Int -> Maybe Int
    forall a. a -> Maybe a
    maybeIsX Int
    waddr of
          Maybe Int
    Nothing -> -- Put the XException from `waddr` as the value in all
                     -- locations of `ram`.
                     [Int] -> (Int -> ST s ()) -> ST s ()
    forall (t :: Type -> Type) (m :: Type -> Type) a b.
    (Foldable t, Monad m) =>
    t a -> (a -> m b) -> m ()
    forM_ [Int
    0..(Int
    szIInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    1)] (\Int
    i -> STArray s Int a -> Int -> a -> ST s ()
    forall s i e. STArray s i e -> Int -> e -> ST s ()
    unsafeWriteSTArray STArray s Int a
    ram Int
    i (Int -> a -> a
    forall a b. a -> b -> b
    seq Int
    waddr a
    d))
          Just Int
    wa -> Int -> a -> STArray s Int a -> ST s ()
    forall s. HasCallStack => Int -> a -> STArray s Int a -> ST s ()
    safeUpdate Int
    wa a
    d STArray s Int a
    ram
        Maybe Bool
    _ -> () -> ST s ()
    forall a. a -> ST s a
    forall (m :: Type -> Type) a. Monad m => a -> m a
    return ()

      safeAt :: HasCallStack => STArray s Int a -> Int -> ST s a
      safeAt :: forall s. HasCallStack => STArray s Int a -> Int -> ST s a
    safeAt STArray s Int a
    s 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
          STArray s Int a -> Int -> ST s a
    forall s i e. STArray s i e -> Int -> ST s e
    unsafeReadSTArray STArray s Int a
    s Int
    i
        else a -> ST s a
    forall a. a -> ST s a
    forall (f :: Type -> Type) a. Applicative f => a -> f a
    pure (a -> ST s a) -> a -> ST s a
    forall a b. (a -> b) -> a -> b
    $
          (HasCallStack => a) -> a
    forall a. HasCallStack => (HasCallStack => a) -> a
    withFrozenCallStack
            (String -> a
    forall a. (NFDataX a, HasCallStack) => String -> a
    deepErrorX (String
    "blockRam: read address " String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> Int -> String
    forall a. Show a => a -> String
    show Int
    i String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <>
                         String
    " not in range [0.." String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> Int -> String
    forall a. Show a => a -> String
    show Int
    szI String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    ")"))
      {-# INLINE safeAt #-}

      safeUpdate :: HasCallStack => Int -> a -> STArray s Int a -> ST s ()
      safeUpdate :: forall s. HasCallStack => Int -> a -> STArray s Int a -> ST s ()
    safeUpdate Int
    i a
    a STArray s Int a
    s =
        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
          STArray s Int a -> Int -> a -> ST s ()
    forall s i e. STArray s i e -> Int -> e -> ST s ()
    unsafeWriteSTArray STArray s Int a
    s Int
    i a
    a
        else
          let d :: a
    d = (HasCallStack => a) -> a
    forall a. HasCallStack => (HasCallStack => a) -> a
    withFrozenCallStack
                    (String -> a
    forall a. (NFDataX a, HasCallStack) => String -> a
    deepErrorX (String
    "blockRam: write address " String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> Int -> String
    forall a. Show a => a -> String
    show Int
    i String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <>
                                 String
    " not in range [0.." String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> Int -> String
    forall a. Show a => a -> String
    show Int
    szI String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    ")"))
           in [Int] -> (Int -> ST s ()) -> ST s ()
    forall (t :: Type -> Type) (m :: Type -> Type) a b.
    (Foldable t, Monad m) =>
    t a -> (a -> m b) -> m ()
    forM_ [Int
    0..(Int
    szIInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    1)] (\Int
    j -> STArray s Int a -> Int -> a -> ST s ()
    forall s i e. STArray s i e -> Int -> e -> ST s ()
    unsafeWriteSTArray STArray s Int a
    s Int
    j a
    d)
      {-# INLINE safeUpdate #-}
    blockRam# Clock dom
    _ Enable dom
    _ Vec n a
    _ = String
    -> Signal dom Int
    -> Signal dom Bool
    -> Signal dom Int
    -> Signal dom a
    -> Signal dom a
    forall a. HasCallStack => String -> a
    error String
    "blockRam#: dynamic clocks not supported"
    {-# ANN blockRam# hasBlackBox #-}
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE blockRam# #-}

    -- | Create a read-after-write block RAM from a read-before-write one
    readNew
      :: ( KnownDomain dom
         , NFDataX a
         , Eq addr )
      => Clock dom
      -> Reset dom
      -> Enable dom
      -> (Signal dom addr -> Signal dom (Maybe (addr, a)) -> Signal dom a)
      -- ^ The BRAM component
      -> Signal dom addr
      -- ^ Read address @r@
      -> Signal dom (Maybe (addr, a))
      -- ^ (Write address @w@, value to write)
      -> Signal dom a
      -- ^ Value of the BRAM at address @r@ from the previous clock cycle
    readNew :: forall (dom :: Domain) a addr.
    (KnownDomain dom, NFDataX a, Eq addr) =>
    Clock dom
    -> Reset dom
    -> Enable dom
    -> (Signal dom addr
        -> Signal dom (Maybe (addr, a)) -> Signal dom a)
    -> Signal dom addr
    -> Signal dom (Maybe (addr, a))
    -> Signal dom a
    readNew Clock dom
    clk Reset dom
    rst Enable dom
    en Signal dom addr -> Signal dom (Maybe (addr, a)) -> Signal dom a
    ram Signal dom addr
    rdAddr Signal dom (Maybe (addr, a))
    wrM = Signal dom Bool -> Signal dom a -> Signal dom a -> Signal dom a
    forall (f :: Type -> Type) a.
    Applicative f =>
    f Bool -> f a -> f a -> f a
    mux Signal dom Bool
    wasSame Signal dom a
    wasWritten (Signal dom a -> Signal dom a) -> Signal dom a -> Signal dom a
    forall a b. (a -> b) -> a -> b
    $ Signal dom addr -> Signal dom (Maybe (addr, a)) -> Signal dom a
    ram Signal dom addr
    rdAddr Signal dom (Maybe (addr, a))
    wrM
      where readNewT :: a -> Maybe (a, b) -> (Bool, b)
    readNewT a
    rd (Just (a
    wr, b
    wrdata)) = (a
    wr a -> a -> Bool
    forall a. Eq a => a -> a -> Bool
    == a
    rd, b
    wrdata)
            readNewT a
    _  Maybe (a, b)
    Nothing             = (Bool
    False   , b
    forall a. HasCallStack => a
    undefined)

            (Signal dom Bool
    wasSame,Signal dom a
    wasWritten) =
              Signal dom (Bool, a) -> Unbundled dom (Bool, a)
    forall a (dom :: Domain).
    Bundle a =>
    Signal dom a -> Unbundled dom a
    forall (dom :: Domain).
    Signal dom (Bool, a) -> Unbundled dom (Bool, a)
    unbundle (Clock dom
    -> Reset dom
    -> Enable dom
    -> (Bool, a)
    -> Signal dom (Bool, a)
    -> Signal dom (Bool, a)
    forall (dom :: Domain) a.
    (KnownDomain dom, NFDataX a) =>
    Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    register Clock dom
    clk Reset dom
    rst Enable dom
    en (Bool
    False, a
    forall a. HasCallStack => a
    undefined)
                                 (addr -> Maybe (addr, a) -> (Bool, a)
    forall {a} {b}. Eq a => a -> Maybe (a, b) -> (Bool, b)
    readNewT (addr -> Maybe (addr, a) -> (Bool, a))
    -> Signal dom addr -> Signal dom (Maybe (addr, a) -> (Bool, a))
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom addr
    rdAddr Signal dom (Maybe (addr, a) -> (Bool, a))
    -> Signal dom (Maybe (addr, a)) -> Signal dom (Bool, a)
    forall a b. Signal dom (a -> b) -> Signal dom a -> Signal dom b
    forall (f :: Type -> Type) a b.
    Applicative f =>
    f (a -> b) -> f a -> f b
    <*> Signal dom (Maybe (addr, a))
    wrM))

    -- | Port operation
    data RamOp n a
      = RamRead (Index n)
      -- ^ Read from address
      | RamWrite (Index n) a
      -- ^ Write data to address
      | RamNoOp
      -- ^ No operation
      deriving ((forall x. RamOp n a -> Rep (RamOp n a) x)
    -> (forall x. Rep (RamOp n a) x -> RamOp n a)
    -> Generic (RamOp n a)
    forall (n :: Nat) a x. Rep (RamOp n a) x -> RamOp n a
    forall (n :: Nat) a x. RamOp n a -> Rep (RamOp n a) x
    forall x. Rep (RamOp n a) x -> RamOp n a
    forall x. RamOp n a -> Rep (RamOp n a) x
    forall a.
    (forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
    $cfrom :: forall (n :: Nat) a x. RamOp n a -> Rep (RamOp n a) x
    from :: forall x. RamOp n a -> Rep (RamOp n a) x
    $cto :: forall (n :: Nat) a x. Rep (RamOp n a) x -> RamOp n a
    to :: forall x. Rep (RamOp n a) x -> RamOp n a
    Generic, HasCallStack => String -> RamOp n a
    RamOp n a -> Bool
    RamOp n a -> ()
    RamOp n a -> RamOp n a
    (HasCallStack => String -> RamOp n a)
    -> (RamOp n a -> Bool)
    -> (RamOp n a -> RamOp n a)
    -> (RamOp n a -> ())
    -> NFDataX (RamOp n a)
    forall (n :: Nat) a.
    (NFDataX a, HasCallStack) =>
    String -> RamOp n a
    forall (n :: Nat) a. NFDataX a => RamOp n a -> Bool
    forall (n :: Nat) a. NFDataX a => RamOp n a -> ()
    forall (n :: Nat) a. NFDataX a => RamOp n a -> RamOp n a
    forall a.
    (HasCallStack => String -> a)
    -> (a -> Bool) -> (a -> a) -> (a -> ()) -> NFDataX a
    $cdeepErrorX :: forall (n :: Nat) a.
    (NFDataX a, HasCallStack) =>
    String -> RamOp n a
    deepErrorX :: HasCallStack => String -> RamOp n a
    $chasUndefined :: forall (n :: Nat) a. NFDataX a => RamOp n a -> Bool
    hasUndefined :: RamOp n a -> Bool
    $censureSpine :: forall (n :: Nat) a. NFDataX a => RamOp n a -> RamOp n a
    ensureSpine :: RamOp n a -> RamOp n a
    $crnfX :: forall (n :: Nat) a. NFDataX a => RamOp n a -> ()
    rnfX :: RamOp n a -> ()
    NFDataX, Int -> RamOp n a -> String -> String
    [RamOp n a] -> String -> String
    RamOp n a -> String
    (Int -> RamOp n a -> String -> String)
    -> (RamOp n a -> String)
    -> ([RamOp n a] -> String -> String)
    -> Show (RamOp n a)
    forall (n :: Nat) a. Show a => Int -> RamOp n a -> String -> String
    forall (n :: Nat) a. Show a => [RamOp n a] -> String -> String
    forall (n :: Nat) a. Show a => RamOp n a -> String
    forall a.
    (Int -> a -> String -> String)
    -> (a -> String) -> ([a] -> String -> String) -> Show a
    $cshowsPrec :: forall (n :: Nat) a. Show a => Int -> RamOp n a -> String -> String
    showsPrec :: Int -> RamOp n a -> String -> String
    $cshow :: forall (n :: Nat) a. Show a => RamOp n a -> String
    show :: RamOp n a -> String
    $cshowList :: forall (n :: Nat) a. Show a => [RamOp n a] -> String -> String
    showList :: [RamOp n a] -> String -> String
    Show)

    instance (AutoReg a, KnownNat n) => AutoReg (RamOp n a) where
      autoReg :: forall (dom :: Domain).
    (HasCallStack, KnownDomain dom) =>
    Clock dom
    -> Reset dom
    -> Enable dom
    -> RamOp n a
    -> Signal dom (RamOp n a)
    -> Signal dom (RamOp n a)
    autoReg Clock dom
    clk Reset dom
    rst Enable dom
    en RamOp n a
    initVal Signal dom (RamOp n a)
    input =
        BitVector 2 -> Index n -> a -> RamOp n a
    forall {a} {a} {n :: Nat}.
    (Eq a, Num a, NFDataX a) =>
    a -> Index n -> a -> RamOp n a
    createRamOp (BitVector 2 -> Index n -> a -> RamOp n a)
    -> Signal dom (BitVector 2)
    -> Signal dom (Index n -> a -> RamOp n a)
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (BitVector 2)
    tagR Signal dom (Index n -> a -> RamOp n a)
    -> Signal dom (Index n) -> Signal dom (a -> RamOp n a)
    forall a b. Signal dom (a -> b) -> Signal dom a -> Signal dom b
    forall (f :: Type -> Type) a b.
    Applicative f =>
    f (a -> b) -> f a -> f b
    <*> Signal dom (Index n)
    valAddr Signal dom (a -> RamOp n a)
    -> Signal dom a -> Signal dom (RamOp n a)
    forall a b. Signal dom (a -> b) -> Signal dom a -> Signal dom b
    forall (f :: Type -> Type) a b.
    Applicative f =>
    f (a -> b) -> f a -> f b
    <*> Signal dom a
    valValue
        where
          tag :: Signal dom (BitVector 2)
    tag = RamOp n a -> BitVector 2
    forall {n :: Nat} {a}. RamOp n a -> BitVector 2
    toTag (RamOp n a -> BitVector 2)
    -> Signal dom (RamOp n a) -> Signal dom (BitVector 2)
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (RamOp n a)
    input

          toTag :: RamOp n a -> BitVector 2
    toTag RamOp n a
    op = case RamOp n a
    op of
            RamOp n a
    RamNoOp    -> BitVector 2
    0b00 :: BitVector 2
            RamRead{}  -> BitVector 2
    0b01
            RamWrite{} -> BitVector 2
    0b10

          tagInit :: BitVector 2
    tagInit = RamOp n a -> BitVector 2
    forall {n :: Nat} {a}. RamOp n a -> BitVector 2
    toTag RamOp n a
    initVal
          tagR :: Signal dom (BitVector 2)
    tagR = Clock dom
    -> Reset dom
    -> Enable dom
    -> BitVector 2
    -> Signal dom (BitVector 2)
    -> Signal dom (BitVector 2)
    forall (dom :: Domain) a.
    (KnownDomain dom, NFDataX a) =>
    Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    register Clock dom
    clk Reset dom
    rst Enable dom
    en BitVector 2
    tagInit Signal dom (BitVector 2)
    tag

          toAddr :: RamOp n a -> Index n
    toAddr RamOp n a
    op = case RamOp n a
    op of
            RamOp n a
    RamNoOp         -> String -> Index n
    forall a. (NFDataX a, HasCallStack) => String -> a
    deepErrorX String
    "autoReg'.ramOpAddr"
            RamRead Index n
    addr    -> Index n
    addr
            RamWrite Index n
    addr a
    _ -> Index n
    addr

          toValue :: RamOp n a -> a
    toValue RamOp n a
    op = case RamOp n a
    op of
            RamWrite Index n
    _ a
    a -> a
    a
            RamOp n a
    _ -> String -> a
    forall a. (NFDataX a, HasCallStack) => String -> a
    deepErrorX String
    "autoReg'.ramOpValue"

          opAddr :: Signal dom (Index n)
    opAddr  = RamOp n a -> Index n
    forall {n :: Nat} {a}. RamOp n a -> Index n
    toAddr  (RamOp n a -> Index n)
    -> Signal dom (RamOp n a) -> Signal dom (Index n)
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (RamOp n a)
    input
          opValue :: Signal dom a
    opValue = RamOp n a -> a
    forall {a} {n :: Nat}. NFDataX a => RamOp n a -> a
    toValue (RamOp n a -> a) -> Signal dom (RamOp n a) -> Signal dom a
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (RamOp n a)
    input

          addrInit :: Index n
    addrInit = RamOp n a -> Index n
    forall {n :: Nat} {a}. RamOp n a -> Index n
    toAddr  RamOp n a
    initVal
          valInit :: a
    valInit  = RamOp n a -> a
    forall {a} {n :: Nat}. NFDataX a => RamOp n a -> a
    toValue RamOp n a
    initVal

          valAddr :: Signal dom (Index n)
    valAddr  = Clock dom
    -> Reset dom
    -> Enable dom
    -> Index n
    -> Signal dom (Index n)
    -> Signal dom (Index n)
    forall a (dom :: Domain).
    (AutoReg a, HasCallStack, KnownDomain dom) =>
    Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    forall (dom :: Domain).
    (HasCallStack, KnownDomain dom) =>
    Clock dom
    -> Reset dom
    -> Enable dom
    -> Index n
    -> Signal dom (Index n)
    -> Signal dom (Index n)
    autoReg Clock dom
    clk Reset dom
    rst (Enable dom -> Signal dom Bool -> Enable dom
    forall (dom :: Domain). Enable dom -> Signal dom Bool -> Enable dom
    andEnable Enable dom
    en           ((BitVector 2 -> BitVector 2 -> Bool
    forall a. Eq a => a -> a -> Bool
    /=BitVector 2
    0) (BitVector 2 -> Bool)
    -> Signal dom (BitVector 2) -> Signal dom Bool
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (BitVector 2)
    tag)) Index n
    addrInit Signal dom (Index n)
    opAddr
          valValue :: Signal dom a
    valValue = Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    forall a (dom :: Domain).
    (AutoReg a, HasCallStack, KnownDomain dom) =>
    Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    forall (dom :: Domain).
    (HasCallStack, KnownDomain dom) =>
    Clock dom
    -> Reset dom -> Enable dom -> a -> Signal dom a -> Signal dom a
    autoReg Clock dom
    clk Reset dom
    rst (Enable dom -> Signal dom Bool -> Enable dom
    forall (dom :: Domain). Enable dom -> Signal dom Bool -> Enable dom
    andEnable Enable dom
    en (Bit -> Bool
    bitToBool (Bit -> Bool) -> (BitVector 2 -> Bit) -> BitVector 2 -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . BitVector 2 -> Bit
    forall a. BitPack a => a -> Bit
    msb (BitVector 2 -> Bool)
    -> Signal dom (BitVector 2) -> Signal dom Bool
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal dom (BitVector 2)
    tag)) a
    valInit Signal dom a
    opValue

          createRamOp :: a -> Index n -> a -> RamOp n a
    createRamOp a
    t Index n
    addr a
    val = case a
    t of
            a
    0b00 -> RamOp n a
    forall (n :: Nat) a. RamOp n a
    RamNoOp
            a
    0b01 -> Index n -> RamOp n a
    forall (n :: Nat) a. Index n -> RamOp n a
    RamRead Index n
    addr
            a
    0b10 -> Index n -> a -> RamOp n a
    forall (n :: Nat) a. Index n -> a -> RamOp n a
    RamWrite Index n
    addr a
    val
            a
    _ -> String -> RamOp n a
    forall a. (NFDataX a, HasCallStack) => String -> a
    deepErrorX  String
    "autoReg'.createRamOp: impossible"

      {-# INLINE autoReg #-}

    ramOpAddr :: RamOp n a -> Index n
    ramOpAddr :: forall {n :: Nat} {a}. RamOp n a -> Index n
    ramOpAddr (RamRead Index n
    addr)    = Index n
    addr
    ramOpAddr (RamWrite Index n
    addr a
    _) = Index n
    addr
    ramOpAddr RamOp n a
    RamNoOp           = String -> Index n
    forall a. HasCallStack => String -> a
    errorX String
    "Address for No operation undefined"

    isRamWrite :: RamOp n a -> Bool
    isRamWrite :: forall (n :: Nat) a. RamOp n a -> Bool
    isRamWrite (RamWrite {}) = Bool
    True
    isRamWrite RamOp n a
    _             = Bool
    False

    ramOpWriteVal :: RamOp n a -> Maybe a
    ramOpWriteVal :: forall (n :: Nat) a. RamOp n a -> Maybe a
    ramOpWriteVal (RamWrite Index n
    _ a
    val) = a -> Maybe a
    forall a. a -> Maybe a
    Just a
    val
    ramOpWriteVal RamOp n a
    _                = Maybe a
    forall a. Maybe a
    Nothing

    isOp :: RamOp n a -> Bool
    isOp :: forall (n :: Nat) a. RamOp n a -> Bool
    isOp RamOp n a
    RamNoOp = Bool
    False
    isOp RamOp n a
    _       = Bool
    True

    -- | Produces vendor-agnostic HDL that will be inferred as a true dual-port
    -- block RAM
    --
    -- Any value that is being written on a particular port is also the
    -- value that will be read on that port, i.e. the same-port read/write behavior
    -- is: WriteFirst. For mixed-port read/write, when port A writes to the address
    -- port B reads from, the output of port B is undefined, and vice versa.
    trueDualPortBlockRam ::
      forall nAddrs domA domB a .
      ( HasCallStack
      , KnownNat nAddrs
      , KnownDomain domA
      , KnownDomain domB
      , NFDataX a
      )
      => Clock domA
      -- ^ Clock for port A
      -> Clock domB
      -- ^ Clock for port B
      -> Signal domA (RamOp nAddrs a)
      -- ^ RAM operation for port A
      -> Signal domB (RamOp nAddrs a)
      -- ^ RAM operation for port B
      -> (Signal domA a, Signal domB a)
      -- ^ Outputs data on /next/ cycle. When writing, the data written
      -- will be echoed. When reading, the read data is returned.

    {-# INLINE trueDualPortBlockRam #-}
    trueDualPortBlockRam :: forall (nAddrs :: Nat) (domA :: Domain) (domB :: Domain) a.
    (HasCallStack, KnownNat nAddrs, KnownDomain domA, KnownDomain domB,
     NFDataX a) =>
    Clock domA
    -> Clock domB
    -> Signal domA (RamOp nAddrs a)
    -> Signal domB (RamOp nAddrs a)
    -> (Signal domA a, Signal domB a)
    trueDualPortBlockRam = \Clock domA
    clkA Clock domB
    clkB Signal domA (RamOp nAddrs a)
    opA Signal domB (RamOp nAddrs a)
    opB ->
      Clock domA
    -> Signal domA Bool
    -> Signal domA Bool
    -> Signal domA (Index nAddrs)
    -> Signal domA a
    -> Clock domB
    -> Signal domB Bool
    -> Signal domB Bool
    -> Signal domB (Index nAddrs)
    -> Signal domB a
    -> (Signal domA a, Signal domB a)
    forall (nAddrs :: Nat) (domA :: Domain) (domB :: Domain) a.
    (HasCallStack, KnownNat nAddrs, KnownDomain domA, KnownDomain domB,
     NFDataX a) =>
    Clock domA
    -> Signal domA Bool
    -> Signal domA Bool
    -> Signal domA (Index nAddrs)
    -> Signal domA a
    -> Clock domB
    -> Signal domB Bool
    -> Signal domB Bool
    -> Signal domB (Index nAddrs)
    -> Signal domB a
    -> (Signal domA a, Signal domB a)
    trueDualPortBlockRamWrapper
        Clock domA
    clkA (RamOp nAddrs a -> Bool
    forall (n :: Nat) a. RamOp n a -> Bool
    isOp (RamOp nAddrs a -> Bool)
    -> Signal domA (RamOp nAddrs a) -> Signal domA Bool
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal domA (RamOp nAddrs a)
    opA) (RamOp nAddrs a -> Bool
    forall (n :: Nat) a. RamOp n a -> Bool
    isRamWrite (RamOp nAddrs a -> Bool)
    -> Signal domA (RamOp nAddrs a) -> Signal domA Bool
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal domA (RamOp nAddrs a)
    opA) (RamOp nAddrs a -> Index nAddrs
    forall {n :: Nat} {a}. RamOp n a -> Index n
    ramOpAddr (RamOp nAddrs a -> Index nAddrs)
    -> Signal domA (RamOp nAddrs a) -> Signal domA (Index nAddrs)
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal domA (RamOp nAddrs a)
    opA) (Maybe a -> a
    forall a. (HasCallStack, NFDataX a) => Maybe a -> a
    fromJustX (Maybe a -> a)
    -> (RamOp nAddrs a -> Maybe a) -> RamOp nAddrs a -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . RamOp nAddrs a -> Maybe a
    forall (n :: Nat) a. RamOp n a -> Maybe a
    ramOpWriteVal (RamOp nAddrs a -> a)
    -> Signal domA (RamOp nAddrs a) -> Signal domA a
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal domA (RamOp nAddrs a)
    opA)
        Clock domB
    clkB (RamOp nAddrs a -> Bool
    forall (n :: Nat) a. RamOp n a -> Bool
    isOp (RamOp nAddrs a -> Bool)
    -> Signal domB (RamOp nAddrs a) -> Signal domB Bool
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal domB (RamOp nAddrs a)
    opB) (RamOp nAddrs a -> Bool
    forall (n :: Nat) a. RamOp n a -> Bool
    isRamWrite (RamOp nAddrs a -> Bool)
    -> Signal domB (RamOp nAddrs a) -> Signal domB Bool
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal domB (RamOp nAddrs a)
    opB) (RamOp nAddrs a -> Index nAddrs
    forall {n :: Nat} {a}. RamOp n a -> Index n
    ramOpAddr (RamOp nAddrs a -> Index nAddrs)
    -> Signal domB (RamOp nAddrs a) -> Signal domB (Index nAddrs)
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal domB (RamOp nAddrs a)
    opB) (Maybe a -> a
    forall a. (HasCallStack, NFDataX a) => Maybe a -> a
    fromJustX (Maybe a -> a)
    -> (RamOp nAddrs a -> Maybe a) -> RamOp nAddrs a -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . RamOp nAddrs a -> Maybe a
    forall (n :: Nat) a. RamOp n a -> Maybe a
    ramOpWriteVal (RamOp nAddrs a -> a)
    -> Signal domB (RamOp nAddrs a) -> Signal domB a
    forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
    <$> Signal domB (RamOp nAddrs a)
    opB)

    -- [Note: eta port names for trueDualPortBlockRam]
    --
    -- By naming all the arguments and setting the -fno-do-lambda-eta-expansion GHC
    -- option for this module, the generated HDL also contains names based on the
    -- argument names used here. This greatly improves readability of the HDL.

    -- [Note: true dual-port blockRAM separate architecture]
    --
    -- A multi-clock true dual-port block RAM is only inferred from the generated HDL
    -- when it lives in its own Verilog module / VHDL architecture. Add any other
    -- logic to the module / architecture, and synthesis will no longer infer a
    -- multi-clock true dual-port block RAM. This wrapper pushes the primitive out
    -- into its own module / architecture.
    trueDualPortBlockRamWrapper :: forall (nAddrs :: Nat) (domA :: Domain) (domB :: Domain) a.
    (HasCallStack, KnownNat nAddrs, KnownDomain domA, KnownDomain domB,
     NFDataX a) =>
    Clock domA
    -> Signal domA Bool
    -> Signal domA Bool
    -> Signal domA (Index nAddrs)
    -> Signal domA a
    -> Clock domB
    -> Signal domB Bool
    -> Signal domB Bool
    -> Signal domB (Index nAddrs)
    -> Signal domB a
    -> (Signal domA a, Signal domB a)
    trueDualPortBlockRamWrapper Clock domA
    clkA Signal domA Bool
    enA Signal domA Bool
    weA Signal domA (Index nAddrs)
    addrA Signal domA a
    datA Clock domB
    clkB Signal domB Bool
    enB Signal domB Bool
    weB Signal domB (Index nAddrs)
    addrB Signal domB a
    datB =
      Clock domA
    -> Signal domA Bool
    -> Signal domA Bool
    -> Signal domA (Index nAddrs)
    -> Signal domA a
    -> Clock domB
    -> Signal domB Bool
    -> Signal domB Bool
    -> Signal domB (Index nAddrs)
    -> Signal domB a
    -> (Signal domA a, Signal domB a)
    forall (nAddrs :: Nat) (domA :: Domain) (domB :: Domain) a.
    (HasCallStack, KnownNat nAddrs, KnownDomain domA, KnownDomain domB,
     NFDataX a) =>
    Clock domA
    -> Signal domA Bool
    -> Signal domA Bool
    -> Signal domA (Index nAddrs)
    -> Signal domA a
    -> Clock domB
    -> Signal domB Bool
    -> Signal domB Bool
    -> Signal domB (Index nAddrs)
    -> Signal domB a
    -> (Signal domA a, Signal domB a)
    trueDualPortBlockRam# Clock domA
    clkA Signal domA Bool
    enA Signal domA Bool
    weA Signal domA (Index nAddrs)
    addrA Signal domA a
    datA Clock domB
    clkB Signal domB Bool
    enB Signal domB Bool
    weB Signal domB (Index nAddrs)
    addrB Signal domB a
    datB
    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE trueDualPortBlockRamWrapper #-}

    -- See: https://github.com/clash-lang/clash-compiler/pull/2511
    {-# CLASH_OPAQUE trueDualPortBlockRam# #-}
    {-# ANN trueDualPortBlockRam# hasBlackBox #-}
    {-# ANN trueDualPortBlockRam# (
      let
        bbName = show 'trueDualPortBlockRam#
        _hasCallStack
         :< knownNatAddrs
         :< _knownDomainA
         :< _knownDomainB
         :< _nfdataX

         :< clockA
         :< enaA
         :< wenaA
         :< addrA
         :< datA

         :< clockB
         :< enaB
         :< wenaB
         :< addrB
         :< datB

         :< _ = ((0 :: Int)...)

        symBlockName
         :< symDoutA
         :< symDoutB
         :< _ = ((0 :: Int)...)
      in InlineYamlPrimitive [VHDL] [__i|
        BlackBox:
          name: "#{bbName}"
          kind: Declaration
          template: |-
            -- trueDualPortBlockRam begin
            ~GENSYM[~RESULT_trueDualPortBlockRam][#{symBlockName}] : block
              -- Shared memory
              type mem_type is array ( ~LIT[#{knownNatAddrs}]-1 downto 0 ) of ~TYP[#{datA}];
              shared variable mem : mem_type;
              signal ~GENSYM[a_dout][#{symDoutA}] : ~TYP[#{datA}];
              signal ~GENSYM[b_dout][#{symDoutB}] : ~TYP[#{datB}];
            begin

              -- Port A
              process(~ARG[#{clockA}])
              begin
                  if(rising_edge(~ARG[#{clockA}])) then
                        if(~ARG[#{enaA}]) then
                          if(~ARG[#{wenaA}]) then
                              mem(~IF~SIZE[~TYP[#{addrA}]]~THENto_integer(~ARG[#{addrA}])~ELSE0~FI) := ~ARG[#{datA}];
                          end if;
                          ~SYM[#{symDoutA}] <= mem(~IF~SIZE[~TYP[#{addrA}]]~THENto_integer(~ARG[#{addrA}])~ELSE0~FI);
                      end if;
                  end if;
              end process;

              -- Port B
              process(~ARG[#{clockB}])
              begin
                  if(rising_edge(~ARG[#{clockB}])) then
                      if(~ARG[#{enaB}]) then
                          if(~ARG[#{wenaB}]) then
                              mem(~IF~SIZE[~TYP[#{addrB}]]~THENto_integer(~ARG[#{addrB}])~ELSE0~FI) := ~ARG[#{datB}];
                          end if;
                          ~SYM[#{symDoutB}] <= mem(~IF~SIZE[~TYP[#{addrB}]]~THENto_integer(~ARG[#{addrB}])~ELSE0~FI);
                      end if;
                  end if;
              end process;

              ~RESULT <= (~SYM[#{symDoutA}], ~SYM[#{symDoutB}]);
            end block;
            -- end trueDualPortBlockRam
    |]) #-}
    {-# ANN trueDualPortBlockRam# (
      let
        bbName = show 'trueDualPortBlockRam#
        _hasCallStack
         :< knownNatAddrs
         :< knownDomainA
         :< knownDomainB
         :< _nfdataX

         :< clockA
         :< enaA
         :< wenaA
         :< addrA
         :< datA

         :< clockB
         :< enaB
         :< wenaB
         :< addrB
         :< datB

         :< _ = ((0 :: Int)...)
        symMem
         :< symDoutA
         :< symDoutB
         :< _ = ((0 :: Int)...)
      in InlineYamlPrimitive [SystemVerilog] [__i|
        BlackBox:
          name: "#{bbName}"
          kind: Declaration
          template: |-
            // trueDualPortBlockRam begin
            // Shared memory
            logic [~SIZE[~TYP[#{datA}]]-1:0] ~GENSYM[mem][#{symMem}] [~LIT[#{knownNatAddrs}]-1:0];

            ~SIGD[~GENSYM[a_dout][#{symDoutA}]][#{datA}];
            ~SIGD[~GENSYM[b_dout][#{symDoutB}]][#{datB}];

            // Port A
            always @(~IF~ACTIVEEDGE[Rising][#{knownDomainA}]~THENposedge~ELSEnegedge~FI ~ARG[#{clockA}]) begin
                if(~ARG[#{enaA}]) begin
                    ~SYM[#{symDoutA}] <= ~SYM[#{symMem}][~IF~SIZE[~TYP[#{addrA}]]~THEN~ARG[#{addrA}]~ELSE0~FI];
                    if(~ARG[#{wenaA}]) begin
                        ~SYM[#{symDoutA}] <= ~ARG[#{datA}];
                        ~SYM[#{symMem}][~IF~SIZE[~TYP[#{addrA}]]~THEN~ARG[#{addrA}]~ELSE0~FI] <= ~ARG[#{datA}];
                    end
                end
            end

            // Port B
            always @(~IF~ACTIVEEDGE[Rising][#{knownDomainB}]~THENposedge~ELSEnegedge~FI ~ARG[#{clockB}]) begin
                if(~ARG[#{enaB}]) begin
                    ~SYM[#{symDoutB}] <= ~SYM[#{symMem}][~IF~SIZE[~TYP[#{addrB}]]~THEN~ARG[#{addrB}]~ELSE0~FI];
                    if(~ARG[#{wenaB}]) begin
                        ~SYM[#{symDoutB}] <= ~ARG[#{datB}];
                        ~SYM[#{symMem}][~IF~SIZE[~TYP[#{addrB}]]~THEN~ARG[#{addrB}]~ELSE0~FI] <= ~ARG[#{datB}];
                    end
                end
            end

            assign ~RESULT = {~SYM[#{symDoutA}], ~SYM[#{symDoutB}]};
            // end trueDualPortBlockRam
    |]) #-}
    {-# ANN trueDualPortBlockRam# (
      let
        bbName = show 'trueDualPortBlockRam#
        _hasCallStack
         :< knownNatAddrs
         :< knownDomainA
         :< knownDomainB
         :< _nfdataX

         :< clockA
         :< enaA
         :< wenaA
         :< addrA
         :< datA

         :< clockB
         :< enaB
         :< wenaB
         :< addrB
         :< datB

         :< _ = ((0 :: Int)...)

        symMem
         :< symDoutA
         :< symDoutB
         :< _ = ((0 :: Int)...)
      in InlineYamlPrimitive [Verilog] [__i|
        BlackBox:
          name: "#{bbName}"
          kind: Declaration
          template: |-
            // trueDualPortBlockRam begin
            // Shared memory
            reg [~SIZE[~TYP[#{datA}]]-1:0] ~GENSYM[mem][#{symMem}] [~LIT[#{knownNatAddrs}]-1:0];

            reg ~SIGD[~GENSYM[a_dout][#{symDoutA}]][#{datA}];
            reg ~SIGD[~GENSYM[b_dout][#{symDoutB}]][#{datB}];

            // Port A
            always @(~IF~ACTIVEEDGE[Rising][#{knownDomainA}]~THENposedge~ELSEnegedge~FI ~ARG[#{clockA}]) begin
                if(~ARG[#{enaA}]) begin
                    ~SYM[#{symDoutA}] <= ~SYM[#{symMem}][~IF~SIZE[~TYP[#{addrA}]]~THEN~ARG[#{addrA}]~ELSE0~FI];
                    if(~ARG[#{wenaA}]) begin
                        ~SYM[#{symDoutA}] <= ~ARG[#{datA}];
                        ~SYM[#{symMem}][~IF~SIZE[~TYP[#{addrA}]]~THEN~ARG[#{addrA}]~ELSE0~FI] <= ~ARG[#{datA}];
                    end
                end
            end

            // Port B
            always @(~IF~ACTIVEEDGE[Rising][#{knownDomainB}]~THENposedge~ELSEnegedge~FI ~ARG[#{clockB}]) begin
                if(~ARG[#{enaB}]) begin
                    ~SYM[#{symDoutB}] <= ~SYM[#{symMem}][~IF~SIZE[~TYP[#{addrB}]]~THEN~ARG[#{addrB}]~ELSE0~FI];
                    if(~ARG[#{wenaB}]) begin
                        ~SYM[#{symDoutB}] <= ~ARG[#{datB}];
                        ~SYM[#{symMem}][~IF~SIZE[~TYP[#{addrB}]]~THEN~ARG[#{addrB}]~ELSE0~FI] <= ~ARG[#{datB}];
                    end
                end
            end

            assign ~RESULT = {~SYM[#{symDoutA}], ~SYM[#{symDoutB}]};

            // end trueDualPortBlockRam
    |]) #-}

    -- | Primitive for 'trueDualPortBlockRam'
    --
    trueDualPortBlockRam#, trueDualPortBlockRamWrapper ::
      forall nAddrs domA domB a .
      ( HasCallStack
      , KnownNat nAddrs
      , KnownDomain domA
      , KnownDomain domB
      , NFDataX a
      ) =>

      Clock domA ->
      -- | Enable
      Signal domA Bool ->
      -- | Write enable
      Signal domA Bool ->
      -- | Address
      Signal domA (Index nAddrs) ->
      -- | Write data
      Signal domA a ->

      Clock domB ->
      -- | Enable
      Signal domB Bool ->
      -- | Write enable
      Signal domB Bool ->
      -- | Address
      Signal domB (Index nAddrs) ->
      -- | Write data
      Signal domB a ->

      (Signal domA a, Signal domB a)
    trueDualPortBlockRam# :: forall (nAddrs :: Nat) (domA :: Domain) (domB :: Domain) a.
    (HasCallStack, KnownNat nAddrs, KnownDomain domA, KnownDomain domB,
     NFDataX a) =>
    Clock domA
    -> Signal domA Bool
    -> Signal domA Bool
    -> Signal domA (Index nAddrs)
    -> Signal domA a
    -> Clock domB
    -> Signal domB Bool
    -> Signal domB Bool
    -> Signal domB (Index nAddrs)
    -> Signal domB a
    -> (Signal domA a, Signal domB a)
    trueDualPortBlockRam# Clock domA
    clkA Signal domA Bool
    enA Signal domA Bool
    weA Signal domA (Index nAddrs)
    addrA Signal domA a
    datA Clock domB
    clkB Signal domB Bool
    enB Signal domB Bool
    weB Signal domB (Index nAddrs)
    addrB Signal domB a
    datB =
      TdpbramModelConfig Bool a
    -> Clock domA
    -> Signal domA Bool
    -> Signal domA (Index nAddrs)
    -> Signal domA Bool
    -> Signal domA a
    -> Clock domB
    -> Signal domB Bool
    -> Signal domB (Index nAddrs)
    -> Signal domB Bool
    -> Signal domB a
    -> (Signal domA a, Signal domB a)
    forall (nAddrs :: Nat) (domA :: Domain) (domB :: Domain) a
           writeEnable.
    (HasCallStack, KnownNat nAddrs, KnownDomain domA, KnownDomain domB,
     NFDataX a) =>
    TdpbramModelConfig writeEnable a
    -> Clock domA
    -> Signal domA Bool
    -> Signal domA (Index nAddrs)
    -> Signal domA writeEnable
    -> Signal domA a
    -> Clock domB
    -> Signal domB Bool
    -> Signal domB (Index nAddrs)
    -> Signal domB writeEnable
    -> Signal domB a
    -> (Signal domA a, Signal domB a)
    tdpbramModel
        TdpbramModelConfig
          { tdpIsActiveWriteEnable :: MaybeX Bool -> MaybeX Bool
    tdpIsActiveWriteEnable = MaybeX Bool -> MaybeX Bool
    forall a. a -> a
    id
          , tdpMergeWriteEnable :: MaybeX Bool -> MaybeX Bool -> MaybeX Bool
    tdpMergeWriteEnable = MaybeX Bool -> MaybeX Bool -> MaybeX Bool
    andX
          , tdpUpdateRam :: Int -> MaybeX Bool -> a -> Seq a -> Seq a
    tdpUpdateRam = Int -> MaybeX Bool -> a -> Seq a -> Seq a
    updateRam }
        Clock domA
    clkA Signal domA Bool
    enA Signal domA (Index nAddrs)
    addrA Signal domA Bool
    weA Signal domA a
    datA
        Clock domB
    clkB Signal domB Bool
    enB Signal domB (Index nAddrs)
    addrB Signal domB Bool
    weB Signal domB a
    datB
     where
      updateRam :: Int -> MaybeX Bool -> a -> Seq a -> Seq a
      updateRam :: Int -> MaybeX Bool -> a -> Seq a -> Seq a
    updateRam Int
    addr MaybeX Bool
    writeEnable a
    dat Seq a
    mem =
        case MaybeX Bool
    writeEnable of
          IsDefined Bool
    False -> Seq a
    mem
          IsDefined Bool
    True -> Int -> a -> Seq a -> Seq a
    forall a. Int -> a -> Seq a -> Seq a
    Seq.update Int
    addr a
    dat Seq a
    mem
          IsX String
    msg -> Int -> a -> Seq a -> Seq a
    forall a. Int -> a -> Seq a -> Seq a
    Seq.update Int
    addr a
    dat (Seq a -> Seq a) -> Seq a -> Seq a
    forall a b. (a -> b) -> a -> b
    $ String -> Seq a
    forall a. (NFDataX a, HasCallStack) => String -> a
    deepErrorX (String -> Seq a) -> String -> Seq a
    forall a b. (a -> b) -> a -> b
    $
              String
    "Write enable unknown; position" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> Int -> String
    forall a. Show a => a -> String
    show Int
    addr String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <>
            String
    "\nWrite enable error message: " String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    msg