content: {-# LANGUAGE NoImplicitPrelude #-}
  
  module Clash.Tests.BlockRam (tests) where
  
  import qualified Data.List as List
  import Test.Tasty
  import Test.Tasty.HUnit
  
  import qualified Clash.Explicit.Prelude as E
  import Clash.Explicit.BlockRam (blockRam#)
  import Clash.Prelude
  
  readRam
    :: (HiddenClockResetEnable dom)
    => Signal dom (Unsigned 4)
    -> Signal dom (Unsigned 8)
  readRam addr = mux (register False $ addr .< 8) ram (pure 0xff)
    where
      ram = blockRam1 NoClearOnReset (SNat @8) 0 addr (pure Nothing)
  
  -- If the block RAM uses the address argument too strictly, then it will
  -- attempt to access an out of bounds address when using readRam.
  --
  addrNotTooStrict :: Assertion
  addrNotTooStrict =
    let addr = fromList [0..15]
     in List.drop 1 (sampleN @System 15 (readRam addr)) @?=
          [255,0,0,0,0,0,0,0,255,255,255,255,255,255]
  
  primRam
    :: Signal System Int
    -> Signal System Bool
    -> Signal System Int
    -> Signal System Int
    -> Signal System (Maybe Int)
  primRam rd we wr din =
    maybeIsX <$> blockRam# clockGen enableGen (0 :> 1 :> Nil) rd we wr din
  
  type PrimSamples = [(Int, Bool, Int, Int, Maybe Int)]
  
  undefEn :: PrimSamples
  
  -- Undefined enable:
  --    The written-to address should read 'undefined', but other addresses
  --    should still have their data.
  
  --                               rd  enable     waddr      wdata      dout
  
  undefEn =                     [ ( 0, undefined, 0        , 2        , Nothing)
                                , ( 0, False    , 0        , 3        , Just 0 )
                                , ( 1, False    , 0        , 3        , Nothing)
                                , ( 1, False    , 0        , 3        , Just 1 )
                                ]
  
  primRamAssertion
    :: PrimSamples
    -> Assertion
  primRamAssertion samples = actual @?= expectedOutput
   where
    (rd, we, wr, din, expectedOutput) = List.unzip5 samples
    actual = E.sampleN (List.length samples) $ primRam (fromList rd)
                                                       (fromList we)
                                                       (fromList wr)
                                                       (fromList din)
  
  tests :: TestTree
  tests = testGroup "BlockRam"
    [ testCase "Address strictness" addrNotTooStrict
    , testCase "Undefined enable" $ primRamAssertion undefEn
    ]
  