diff --git a/clash-vexriscv/clash-vexriscv.cabal b/clash-vexriscv/clash-vexriscv.cabal index 7ae96cb..fe48780 100644 --- a/clash-vexriscv/clash-vexriscv.cabal +++ b/clash-vexriscv/clash-vexriscv.cabal @@ -108,6 +108,7 @@ library hs-source-dirs: src default-language: Haskell2010 exposed-modules: + VexRiscv.Random VexRiscv VexRiscv.ClockTicks VexRiscv.FFI @@ -126,6 +127,7 @@ library Glob, network, process >= 1.6 && < 1.8, + random, string-interpolate, tagged, template-haskell, @@ -141,6 +143,7 @@ test-suite unittests other-modules: Tests.Extra Tests.VexRiscv.ClockTicks + Tests.VexRiscv.Random build-depends: HUnit, base, diff --git a/clash-vexriscv/src/VexRiscv.hs b/clash-vexriscv/src/VexRiscv.hs index 098aba3..3b0c03d 100644 --- a/clash-vexriscv/src/VexRiscv.hs +++ b/clash-vexriscv/src/VexRiscv.hs @@ -33,6 +33,7 @@ import VexRiscv.ClockTicks import VexRiscv.FFI import VexRiscv.TH import VexRiscv.VecToTuple +import VexRiscv.Random import qualified VexRiscv.FFI as FFI @@ -259,23 +260,39 @@ vexRiscv# !_sourcePath clk rst0 jtag_TDI = unsafePerformIO $ do (v, initStage1, initStage2, stepRising, stepFalling, _shutDown) <- vexCPU + -- Make sure all the inputs are defined + let + rst0' = unsafePerformIO . makeDefinedRandom <$> unsafeToActiveHigh rst0 + timerInterrupt' = unsafePerformIO . makeDefinedRandom <$> timerInterrupt + externalInterrupt' = unsafePerformIO . makeDefinedRandom <$> externalInterrupt + softwareInterrupt' = unsafePerformIO . makeDefinedRandom <$> softwareInterrupt + iBus_ACK' = unsafePerformIO . makeDefinedRandom <$> iBus_ACK + iBus_DAT_MISO' = unsafePerformIO . makeDefinedRandom <$> iBus_DAT_MISO + iBus_ERR' = unsafePerformIO . makeDefinedRandom <$> iBus_ERR + dBus_ACK' = unsafePerformIO . makeDefinedRandom <$> dBus_ACK + dBus_DAT_MISO' = unsafePerformIO . makeDefinedRandom <$> dBus_DAT_MISO + dBus_ERR' = unsafePerformIO . makeDefinedRandom <$> dBus_ERR + jtag_TCK' = unsafePerformIO . makeDefinedRandom <$> jtag_TCK + jtag_TMS' = unsafePerformIO . makeDefinedRandom <$> jtag_TMS + jtag_TDI' = unsafePerformIO . makeDefinedRandom <$> jtag_TDI + let nonCombInput = NON_COMB_INPUT - <$> (boolToBit <$> unsafeToActiveHigh rst0) - <*> timerInterrupt - <*> externalInterrupt - <*> softwareInterrupt + <$> (boolToBit <$> rst0') + <*> timerInterrupt' + <*> externalInterrupt' + <*> softwareInterrupt' combInput = COMB_INPUT - <$> (boolToBit <$> iBus_ACK) - <*> (unpack <$> iBus_DAT_MISO) - <*> (boolToBit <$> iBus_ERR) - <*> (boolToBit <$> dBus_ACK) - <*> (unpack <$> dBus_DAT_MISO) - <*> (boolToBit <$> dBus_ERR) - <*> jtag_TCK - <*> jtag_TMS - <*> jtag_TDI + <$> (boolToBit <$> iBus_ACK') + <*> (unpack <$> iBus_DAT_MISO') + <*> (boolToBit <$> iBus_ERR') + <*> (boolToBit <$> dBus_ACK') + <*> (unpack <$> dBus_DAT_MISO') + <*> (boolToBit <$> dBus_ERR') + <*> jtag_TCK' + <*> jtag_TMS' + <*> jtag_TDI' simInitThenCycles :: Signal dom NON_COMB_INPUT -> diff --git a/clash-vexriscv/src/VexRiscv/Random.hs b/clash-vexriscv/src/VexRiscv/Random.hs new file mode 100644 index 0000000..59ed029 --- /dev/null +++ b/clash-vexriscv/src/VexRiscv/Random.hs @@ -0,0 +1,49 @@ +-- SPDX-FileCopyrightText: 2024 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 +module VexRiscv.Random where + +import Clash.Prelude +import Numeric.Natural +import Clash.Sized.Internal.BitVector +import System.Random + +class DefinedRandom a where + makeDefinedRandom :: a -> IO a + +instance DefinedRandom Bool where + makeDefinedRandom b + | hasUndefined b = randomIO + | otherwise = pure b + +instance DefinedRandom Bit where + makeDefinedRandom b@(Bit 0 _) = pure b + makeDefinedRandom _ = do + d <- randomRIO (0, 1) + pure (Bit 1 d) + +instance KnownNat n => DefinedRandom (BitVector n) where + makeDefinedRandom :: KnownNat n => BitVector n -> IO (BitVector n) + makeDefinedRandom (BV mask dat) = do + let + (BV _ maxVal) = (maxBound :: BitVector n) + (BV maxMask _) = deepErrorX "" :: BitVector n + randomInt <- genNatural (0, fromIntegral maxVal) + + pure $ BV 0 ((dat .&. (maxMask `xor` mask)) .|. (randomInt .&. mask)) + +genNatural :: (Natural, Natural) -> IO Natural +genNatural (lo, hi) + | lo > hi = error "genNatural: lower bound > upper bound" + | otherwise = (lo +) <$> go (hi - lo) + where + intMax = fromIntegral (maxBound :: Int) + intBits = finiteBitSize (0 :: Int) + + go :: Natural -> IO Natural + go h + | h <= intMax = fmap fromIntegral (randomRIO (0, fromIntegral h) :: IO Int) + | otherwise = do + x <- fmap fromIntegral (randomRIO (0, maxBound) :: IO Int) + y <- go (shiftR h intBits) + pure (x + shiftL y intBits) diff --git a/clash-vexriscv/tests/unittests/Tests/VexRiscv/Random.hs b/clash-vexriscv/tests/unittests/Tests/VexRiscv/Random.hs new file mode 100644 index 0000000..3127871 --- /dev/null +++ b/clash-vexriscv/tests/unittests/Tests/VexRiscv/Random.hs @@ -0,0 +1,27 @@ +-- SPDX-FileCopyrightText: 2024 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 +module Tests.VexRiscv.Random where + +import Clash.Prelude + +import Hedgehog +import Test.Tasty +import Test.Tasty.Hedgehog + +import VexRiscv.Random + +import qualified Hedgehog.Gen as Gen +import qualified Hedgehog.Range as Range + +tests :: TestTree +tests = testGroup "VexRiscv.Random" + [ testProperty "genNatural" prop_genNatural + ] + +prop_genNatural :: Property +prop_genNatural = property $ do + lo <- forAll $ Gen.integral (Range.linear 0 (shiftL 1 1024)) + hi <- forAll $ Gen.integral (Range.linear lo (shiftL 1 1024)) + n <- evalIO $ genNatural (lo, hi) + assert ((n >= lo) && (n <= hi)) diff --git a/clash-vexriscv/tests/unittests/main.hs b/clash-vexriscv/tests/unittests/main.hs index 4793f75..03a50be 100644 --- a/clash-vexriscv/tests/unittests/main.hs +++ b/clash-vexriscv/tests/unittests/main.hs @@ -10,10 +10,12 @@ import Test.Tasty import Test.Tasty.Hedgehog import qualified Tests.VexRiscv.ClockTicks +import qualified Tests.VexRiscv.Random tests :: TestTree tests = testGroup "Tests" [ Tests.VexRiscv.ClockTicks.tests + , Tests.VexRiscv.Random.tests ] setDefaultHedgehogTestLimit :: HedgehogTestLimit -> HedgehogTestLimit