content: -- Assert correct behavior:
  --
  -- Undefined enable:
  --    The written-to address should read 'undefined', but other addresses
  --    should still have their data.
  --
  -- Undefined write address:
  --    All addresses should read 'undefined'.
  --
  -- OOB write address
  --    All addresses should read 'undefined.
  --
  -- Undefined write data:
  --    The written-to address should read 'undefined', but other addresses
  --    should still have their data.
  --
  -- Deasserted enable
  --    It shouldn't matter that other write inputs are 'undefined'.
  --
  -- Deasserted enable, OOB address
  --    It shouldn't matter that it is out of bounds.
  --
  -- OOB read address
  --    The sample should be undefined, nothing more.
  --
  -- Read address strictness
  --    If the read result is not used, out-of-bounds read address shouldn't
  --    matter (equivalent to issue #1458).
  
  {-# LANGUAGE NoImplicitPrelude #-}
  
  module Clash.Tests.Ram (tests) where
  
  import qualified Data.List as L
  import Test.Tasty
  import Test.Tasty.HUnit
  
  import Clash.Explicit.Prelude
  import Clash.Explicit.RAM
  
  type Ram = (   Signal System Int
              -> Signal System Bool
              -> Signal System Int
              -> Signal System Int
              -> Signal System (Maybe Int)
             )
  
  ram :: Ram
  ram rd we wr din =
    maybeIsX <$> asyncRam# clockGen clockGen enableGen d2 rd we wr din
  
  maskOobRead :: Ram
  maskOobRead rd we wr din =
    maybeIsX <$> mux (rd .< 2) ram0 (pure 4)
   where
    ram0 = asyncRam# clockGen clockGen enableGen d2 rd we wr din
  
  type Samples = [(Int, Bool, Int, Int, Maybe Int)]
  
  initMem, undefEn, undefWAddr, oobWAddr, undefWData, enFalse, enFalseOobWAddr,
    oobRAddr, oobRAddrStrict
    :: Samples
  
  --                               rd  enable     waddr      wdata      dout
  initMem =                     [ ( 0, True     , 0        , 0        , Nothing)
                                , ( 0, True     , 1        , 1        , Just 0 )
                                ]
  
  undefEn = initMem <>          [ ( 0, undefined, 0        , 2        , Just 0 )
                                , ( 0, False    , 0        , 3        , Nothing)
                                , ( 1, False    , 0        , 3        , Just 1 )
                                ]
  
  undefWAddr = initMem <>       [ ( 0, True     , undefined, 2        , Just 0 )
                                , ( 0, False    , 0        , 3        , Nothing)
                                , ( 1, False    , 0        , 3        , Nothing)
                                ]
  
  oobWAddr = initMem <>         [ ( 0, True     , 3        , 2        , Just 0 )
                                , ( 0, False    , 0        , 3        , Nothing)
                                , ( 1, False    , 0        , 3        , Nothing)
                                ]
  
  undefWData = initMem <>       [ ( 0, True     , 0        , undefined, Just 0 )
                                , ( 0, False    , 0        , 3        , Nothing)
                                , ( 1, False    , 0        , 3        , Just 1 )
                                ]
  
  enFalse = initMem <>          [ ( 0, False    , undefined, undefined, Just 0)
                                , ( 0, False    , undefined, undefined, Just 0)
                                , ( 1, False    , undefined, undefined, Just 1)
                                ]
  
  enFalseOobWAddr = initMem <>  [ ( 0, False    , 255      , 2        , Just 0 )
                                , ( 0, False    , 0        , 3        , Just 0 )
                                , ( 1, False    , 0        , 3        , Just 1 )
                                ]
  
  oobRAddr = initMem <>         [ ( 2, False    , 0        , 3        , Nothing)
                                , ( 0, False    , 0        , 3        , Just 0 )
                                , ( 1, False    , 0        , 3        , Just 1 )
                                ]
  
  oobRAddrStrict = initMem <>   [ ( 1, False    , 0        , 3        , Just 1 )
                                , ( 2, False    , 0        , 3        , Just 4 )
                                , ( 0, False    , 0        , 3        , Just 0 )
                                ]
  
  ramAssertion
    :: Ram
    -> Samples
    -> Assertion
  ramAssertion ram0 samples = actual @?= expectedOutput
   where
    (rd, we, wr, din, expectedOutput) = L.unzip5 samples
    actual = sampleN (L.length samples) $ ram0 (fromList rd)
                                               (fromList we)
                                               (fromList wr)
                                               (fromList din)
  tests :: TestTree
  tests = testGroup "Ram"
    [ testCase "Undefined enable" $ ramAssertion ram undefEn
    , testCase "Undefined write address" $ ramAssertion ram undefWAddr
    , testCase "OOB write address" $ ramAssertion ram oobWAddr
    , testCase "Undefined write data" $ ramAssertion ram undefWData
    , testCase "Deasserted enable" $ ramAssertion ram enFalse
    , testCase "Deasserted enable, OOB address" $ ramAssertion ram enFalseOobWAddr
    , testCase "OOB read address" $ ramAssertion ram oobRAddr
    , testCase "Read address strictness" $ ramAssertion maskOobRead oobRAddrStrict
    ]
  