From f797bbf780ef0ca9731a0deeabf815fb22d107dc Mon Sep 17 00:00:00 2001 From: Martijn Bastiaan Date: Mon, 4 Mar 2024 17:00:09 +0100 Subject: [PATCH] Handle inputs and outputs in correct order Eleminates the need for prepending singals with dummy elements --- clash-vexriscv-sim/clash-vexriscv-sim.cabal | 2 + clash-vexriscv-sim/src/Utils/Cpu.hs | 11 - clash-vexriscv/src/VexRiscv.hs | 229 ++++++++++++-------- clash-vexriscv/src/VexRiscv/FFI.hsc | 80 ++++--- clash-vexriscv/src/ffi/impl.cpp | 154 +++++++++---- clash-vexriscv/src/ffi/interface.h | 39 +--- 6 files changed, 303 insertions(+), 212 deletions(-) diff --git a/clash-vexriscv-sim/clash-vexriscv-sim.cabal b/clash-vexriscv-sim/clash-vexriscv-sim.cabal index ee6504f..fe75768 100644 --- a/clash-vexriscv-sim/clash-vexriscv-sim.cabal +++ b/clash-vexriscv-sim/clash-vexriscv-sim.cabal @@ -120,6 +120,8 @@ test-suite unittests default-language: Haskell2010 hs-source-dirs: tests type: exitcode-stdio-1.0 + -- TODO: enable parallel tests: + -- ghc-options: -threaded -rtsopts -with-rtsopts=-N ghc-options: -threaded main-is: tests.hs build-depends: diff --git a/clash-vexriscv-sim/src/Utils/Cpu.hs b/clash-vexriscv-sim/src/Utils/Cpu.hs index e887d6c..325492c 100644 --- a/clash-vexriscv-sim/src/Utils/Cpu.hs +++ b/clash-vexriscv-sim/src/Utils/Cpu.hs @@ -18,17 +18,6 @@ import GHC.Stack (HasCallStack) import Utils.ProgramLoad (Memory) import Utils.Interconnect (interconnectTwo) -emptyInput :: Input -emptyInput = - Input - { timerInterrupt = low, - externalInterrupt = low, - softwareInterrupt = low, - iBusWbS2M = (emptyWishboneS2M @(BitVector 32)) {readData = 0}, - dBusWbS2M = (emptyWishboneS2M @(BitVector 32)) {readData = 0} - } - - {- Address space diff --git a/clash-vexriscv/src/VexRiscv.hs b/clash-vexriscv/src/VexRiscv.hs index 24a7154..16c2963 100644 --- a/clash-vexriscv/src/VexRiscv.hs +++ b/clash-vexriscv/src/VexRiscv.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022-2023 Google LLC +-- SPDX-FileCopyrightText: 2022-2024 Google LLC -- -- SPDX-License-Identifier: Apache-2.0 @@ -16,17 +16,23 @@ import Clash.Prelude import Clash.Annotations.Primitive import Clash.Signal.Internal +import Data.Bifunctor (first) import Data.String.Interpolate (__i) +import Data.Word (Word64) +import Foreign (Ptr) import Foreign.Marshal (alloca) import Foreign.Storable import GHC.IO (unsafePerformIO) import GHC.Stack (HasCallStack) import Language.Haskell.TH.Syntax import Protocols.Wishbone + +import VexRiscv.ClockTicks import VexRiscv.FFI import VexRiscv.TH import VexRiscv.VecToTuple +import qualified VexRiscv.FFI as FFI data Input = Input { timerInterrupt :: "TIMER_INTERRUPT" ::: Bit @@ -43,48 +49,6 @@ data Output = Output } deriving (Generic, NFDataX, ShowX, Eq, BitPack) -inputToFFI :: Bool -> Input -> INPUT -inputToFFI reset Input {..} = - INPUT - { reset = boolToBit reset - , timerInterrupt - , externalInterrupt - , softwareInterrupt - , iBusWishbone_ACK = boolToBit $ acknowledge iBusWbS2M - , iBusWishbone_DAT_MISO = unpack $ readData iBusWbS2M - , iBusWishbone_ERR = boolToBit $ err iBusWbS2M - , dBusWishbone_ACK = boolToBit $ acknowledge dBusWbS2M - , dBusWishbone_DAT_MISO = unpack $ readData dBusWbS2M - , dBusWishbone_ERR = boolToBit $ err dBusWbS2M - } - -outputFromFFI :: OUTPUT -> Output -outputFromFFI OUTPUT {..} = - Output - { iBusWbM2S = - (emptyWishboneM2S @30 @(BitVector 32)) - { busCycle = bitToBool iBusWishbone_CYC, - strobe = bitToBool iBusWishbone_STB, - writeEnable = bitToBool iBusWishbone_WE, - addr = truncateB $ pack iBusWishbone_ADR, - writeData = pack iBusWishbone_DAT_MOSI, - busSelect = resize $ pack iBusWishbone_SEL, - cycleTypeIdentifier = unpack $ resize $ pack iBusWishbone_CTI, - burstTypeExtension = unpack $ resize $ pack iBusWishbone_BTE - }, - dBusWbM2S = - (emptyWishboneM2S @30 @(BitVector 32)) - { busCycle = bitToBool dBusWishbone_CYC, - strobe = bitToBool dBusWishbone_STB, - writeEnable = bitToBool dBusWishbone_WE, - addr = truncateB $ pack dBusWishbone_ADR, - writeData = pack dBusWishbone_DAT_MOSI, - busSelect = resize $ pack dBusWishbone_SEL, - cycleTypeIdentifier = unpack $ resize $ pack dBusWishbone_CTI, - burstTypeExtension = unpack $ resize $ pack dBusWishbone_BTE - } - } - -- When passing S2M values from Haskell to VexRiscv over the FFI, undefined -- bits/values cause errors when forcing their evaluation to something that can -- be passed through the FFI. @@ -127,9 +91,7 @@ vexRiscv input = where (unbundle -> (timerInterrupt, externalInterrupt, softwareInterrupt, iBusS2M, dBusS2M)) - -- A hack that enables us to both generate synthesizable HDL and simulate vexRisc in Haskell/Clash - = (<$> if clashSimulation then unpack 0 :- input else input) - $ \(Input a b c d e) -> (a, b, c, d, e) + = (\(Input a b c d e) -> (a, b, c, d, e)) <$> input (unbundle -> (iBus_DAT_MISO, iBus_ACK, iBus_ERR)) = (\(WishboneS2M a b c _ _) -> (a, b, c)) @@ -222,7 +184,7 @@ vexRiscv# , Signal dom (BitVector 3) -- ^ dBus_CTI , Signal dom (BitVector 2) -- ^ dBus_BTE ) -vexRiscv# !_sourcePath !_clk rst0 +vexRiscv# !_sourcePath clk rst0 timerInterrupt externalInterrupt softwareInterrupt @@ -236,48 +198,98 @@ vexRiscv# !_sourcePath !_clk rst0 = let - iBusS2M = WishboneS2M <$> iBus_DAT_MISO <*> iBus_ACK <*> iBus_ERR <*> pure False <*> pure False - dBusS2M = WishboneS2M <$> dBus_DAT_MISO <*> dBus_ACK <*> dBus_ERR <*> pure False <*> pure False - - input = Input <$> timerInterrupt <*> externalInterrupt <*> softwareInterrupt <*> iBusS2M <*> dBusS2M - - output = unsafePerformIO $ do - (step, _) <- vexCPU - pure $ go step (unsafeFromReset rst0) input - - (unbundle -> (iBusM2S, dBusM2S)) = (<$> output) $ \(Output iBus dBus) -> (iBus, dBus) - - (unbundle -> (iBus_ADR, iBus_DAT_MOSI, iBus_SEL, iBus_CYC, iBus_STB, iBus_WE, iBus_CTI, iBus_BTE)) = - (<$> iBusM2S) $ \(WishboneM2S a b c _ e f g h i) -> (a, b, c, e, f, g, h, i) - - (unbundle -> (dBus_ADR, dBus_DAT_MOSI, dBus_SEL, dBus_CYC, dBus_STB, dBus_WE, dBus_CTI, dBus_BTE)) = - (<$> dBusM2S) $ \(WishboneM2S a b c _ e f g h i) -> (a, b, c, e, f, g, h, i) + (v, initStage1, initStage2, stepRising, stepFalling, _shutDown) = unsafePerformIO vexCPU + + nonCombInput = NON_COMB_INPUT + <$> (boolToBit <$> unsafeToActiveHigh 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) + + wordCast = fromInteger . toInteger + + simInitThenCycles :: + Signal dom NON_COMB_INPUT -> + Signal dom COMB_INPUT -> + Signal dom OUTPUT + simInitThenCycles (cnc :- cncs) ~(cc :- ccs) = + let + -- Note: we don't need @ticks@ for the initialization stages, because this + -- first cycle of a 'Signal' is meant to model what happens _before_ a + -- clock edge. + out0 = unsafePerformIO (initStage1 v cnc) + stage2Out = unsafePerformIO (initStage2 v cc) + ticks = first wordCast <$> singleClockEdgesRelative clk + out1 = simCycles ticks cncs ccs + in + out0 :- (out0 `seq` (stage2Out `seq` out1)) + + simCycles :: + [(Word64, ActiveEdge)] -> + Signal dom NON_COMB_INPUT -> + Signal dom COMB_INPUT -> + Signal dom OUTPUT + simCycles ((fsSinceLastEvent, Rising) : ts) (cnc :- cncs) ccs = + let + out0 = unsafePerformIO (stepRising v fsSinceLastEvent cnc) + out1 = simCycles ts cncs ccs + in + out0 :- (out0 `seq` out1) + + simCycles ((fsSinceLastEvent, Falling) : ts) cncs (cc :- ccs) = + let !() = unsafePerformIO (stepFalling v fsSinceLastEvent cc) + in simCycles ts cncs ccs + + simCycles [] _ _ = error "Empty ticks: should never happen" + + output = simInitThenCycles nonCombInput combInput + + iBus_CYC = FFI.iBusWishbone_CYC <$> output + iBus_STB = FFI.iBusWishbone_STB <$> output + iBus_WE = FFI.iBusWishbone_WE <$> output + iBus_ADR = FFI.iBusWishbone_ADR <$> output + iBus_DAT_MOSI = FFI.iBusWishbone_DAT_MOSI <$> output + iBus_SEL = FFI.iBusWishbone_SEL <$> output + iBus_CTI = FFI.iBusWishbone_CTI <$> output + iBus_BTE = FFI.iBusWishbone_BTE <$> output + + dBus_CYC = FFI.dBusWishbone_CYC <$> output + dBus_STB = FFI.dBusWishbone_STB <$> output + dBus_WE = FFI.dBusWishbone_WE <$> output + dBus_ADR = FFI.dBusWishbone_ADR <$> output + dBus_DAT_MOSI = FFI.dBusWishbone_DAT_MOSI <$> output + dBus_SEL = FFI.dBusWishbone_SEL <$> output + dBus_CTI = FFI.dBusWishbone_CTI <$> output + dBus_BTE = FFI.dBusWishbone_BTE <$> output in ( -- iBus - iBus_CYC - , iBus_STB - , iBus_WE - , iBus_ADR - , iBus_DAT_MOSI - , iBus_SEL - , pack <$> iBus_CTI - , pack <$> iBus_BTE + bitToBool <$> iBus_CYC + , bitToBool <$> iBus_STB + , bitToBool <$> iBus_WE + , truncateB . pack <$> iBus_ADR + , pack <$> iBus_DAT_MOSI + , truncateB . pack <$> iBus_SEL + , truncateB . pack <$> iBus_CTI + , truncateB . pack <$> iBus_BTE -- dBus - , dBus_CYC - , dBus_STB - , dBus_WE - , dBus_ADR - , dBus_DAT_MOSI - , dBus_SEL - , pack <$> dBus_CTI - , pack <$> dBus_BTE + , bitToBool <$> dBus_CYC + , bitToBool <$> dBus_STB + , bitToBool <$> dBus_WE + , truncateB . pack <$> dBus_ADR + , pack <$> dBus_DAT_MOSI + , truncateB . pack <$> dBus_SEL + , truncateB . pack <$> dBus_CTI + , truncateB . pack <$> dBus_BTE ) - where - {-# NOINLINE go #-} - go step (rst :- rsts) (input :- inputs) = unsafePerformIO $ do - out <- step rst input - pure $ out :- go step rsts inputs {-# NOINLINE vexRiscv# #-} {-# ANN vexRiscv# ( let @@ -401,16 +413,49 @@ vexRiscv# !_sourcePath !_clk rst0 |] ) #-} + -- | Return a function that performs an execution step and a function to free -- the internal CPU state -vexCPU :: IO (Bool -> Input -> IO Output, IO ()) +vexCPU :: IO + ( Ptr VexRiscv + , Ptr VexRiscv -> NON_COMB_INPUT -> IO OUTPUT -- initStage1 + , Ptr VexRiscv -> COMB_INPUT -> IO () -- initStage2 + , Ptr VexRiscv -> Word64 -> NON_COMB_INPUT -> IO OUTPUT -- rising + , Ptr VexRiscv -> Word64 -> COMB_INPUT -> IO () -- falling + , Ptr VexRiscv -> IO () + ) vexCPU = do v <- vexrInit + let - step reset input = alloca $ \inputFFI -> alloca $ \outputFFI -> do - poke inputFFI (inputToFFI reset input) - vexrStep v inputFFI outputFFI - outVal <- peek outputFFI - pure $ outputFromFFI outVal - shutDown = vexrShutdown v - pure (step, shutDown) + {-# NOINLINE initStage1 #-} + initStage1 vPtr nonCombInput = + alloca $ \nonCombInputFFI -> alloca $ \outputFFI -> do + poke nonCombInputFFI nonCombInput + vexrInitStage1 vPtr nonCombInputFFI outputFFI + output <- peek outputFFI + pure output + + {-# NOINLINE initStage2 #-} + initStage2 vPtr combInput = + alloca $ \combInputFFI -> do + poke combInputFFI combInput + vexrInitStage2 vPtr combInputFFI + + {-# NOINLINE stepRising #-} + stepRising vPtr fsSinceLastEvent nonCombInput = + alloca $ \nonCombInputFFI -> alloca $ \outputFFI -> do + poke nonCombInputFFI nonCombInput + vexrStepRisingEdge vPtr fsSinceLastEvent nonCombInputFFI outputFFI + output <- peek outputFFI + pure output + + {-# NOINLINE stepFalling #-} + stepFalling vPtr fsSinceLastEvent combInput = + alloca $ \combInputFFI -> do + poke combInputFFI combInput + vexrStepFallingEdge vPtr fsSinceLastEvent combInputFFI + + shutDown = vexrShutdown + + pure (v, initStage1, initStage2, stepRising, stepFalling, shutDown) diff --git a/clash-vexriscv/src/VexRiscv/FFI.hsc b/clash-vexriscv/src/VexRiscv/FFI.hsc index 47b154b..9fd456c 100644 --- a/clash-vexriscv/src/VexRiscv/FFI.hsc +++ b/clash-vexriscv/src/VexRiscv/FFI.hsc @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Google LLC +-- SPDX-FileCopyrightText: 2022-2024 Google LLC -- -- SPDX-License-Identifier: Apache-2.0 @@ -19,19 +19,27 @@ import Data.Word data VexRiscv foreign import ccall unsafe "vexr_init" vexrInit :: IO (Ptr VexRiscv) - foreign import ccall unsafe "vexr_shutdown" vexrShutdown :: Ptr VexRiscv -> IO () -foreign import ccall unsafe "vexr_step" vexrStep :: Ptr VexRiscv -> Ptr INPUT -> Ptr OUTPUT -> IO () +foreign import ccall unsafe "vexr_init_stage1" vexrInitStage1 :: Ptr VexRiscv -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO () +foreign import ccall unsafe "vexr_init_stage2" vexrInitStage2 :: Ptr VexRiscv -> Ptr COMB_INPUT -> IO () +foreign import ccall unsafe "vexr_step_rising_edge" vexrStepRisingEdge :: Ptr VexRiscv -> Word64 -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO () +foreign import ccall unsafe "vexr_step_falling_edge" vexrStepFallingEdge :: Ptr VexRiscv -> Word64 -> Ptr COMB_INPUT -> IO () -data INPUT = INPUT +-- | CPU input that cannot combinatorially depend on the CPU output +data NON_COMB_INPUT = NON_COMB_INPUT { reset :: Bit , timerInterrupt :: Bit , externalInterrupt :: Bit , softwareInterrupt :: Bit - , iBusWishbone_ACK :: Bit + } + +-- | CPU input that can combinatorially depend on the CPU output +data COMB_INPUT = COMB_INPUT + { iBusWishbone_ACK :: Bit , iBusWishbone_DAT_MISO :: Word32 , iBusWishbone_ERR :: Bit + , dBusWishbone_ACK :: Bit , dBusWishbone_DAT_MISO :: Word32 , dBusWishbone_ERR :: Bit @@ -47,6 +55,7 @@ data OUTPUT = OUTPUT , iBusWishbone_SEL :: Word8 , iBusWishbone_CTI :: Word8 , iBusWishbone_BTE :: Word8 + , dBusWishbone_CYC :: Bit , dBusWishbone_STB :: Bit , dBusWishbone_WE :: Bit @@ -64,34 +73,45 @@ instance Storable Bit where peek = fmap boolToBit . peek . castPtr poke ptr = poke (castPtr ptr) . bitToBool -instance Storable INPUT where - alignment _ = #alignment INPUT - sizeOf _ = #size INPUT +instance Storable NON_COMB_INPUT where + alignment _ = #alignment NON_COMB_INPUT + sizeOf _ = #size NON_COMB_INPUT + {-# INLINE peek #-} + peek ptr = const NON_COMB_INPUT <$> pure () + <*> (#peek NON_COMB_INPUT, reset) ptr + <*> (#peek NON_COMB_INPUT, timerInterrupt) ptr + <*> (#peek NON_COMB_INPUT, externalInterrupt) ptr + <*> (#peek NON_COMB_INPUT, softwareInterrupt) ptr + + {-# INLINE poke #-} + poke ptr this = do + (#poke NON_COMB_INPUT, reset) ptr (reset this) + (#poke NON_COMB_INPUT, timerInterrupt) ptr (timerInterrupt this) + (#poke NON_COMB_INPUT, externalInterrupt) ptr (externalInterrupt this) + (#poke NON_COMB_INPUT, softwareInterrupt) ptr (softwareInterrupt this) + return () + +instance Storable COMB_INPUT where + alignment _ = #alignment COMB_INPUT + sizeOf _ = #size COMB_INPUT {-# INLINE peek #-} - peek ptr = const INPUT <$> pure () - <*> (#peek INPUT, reset) ptr - <*> (#peek INPUT, timerInterrupt) ptr - <*> (#peek INPUT, externalInterrupt) ptr - <*> (#peek INPUT, softwareInterrupt) ptr - <*> (#peek INPUT, iBusWishbone_ACK) ptr - <*> (#peek INPUT, iBusWishbone_DAT_MISO) ptr - <*> (#peek INPUT, iBusWishbone_ERR) ptr - <*> (#peek INPUT, dBusWishbone_ACK) ptr - <*> (#peek INPUT, dBusWishbone_DAT_MISO) ptr - <*> (#peek INPUT, dBusWishbone_ERR) ptr + peek ptr = const COMB_INPUT <$> pure () + <*> (#peek COMB_INPUT, iBusWishbone_ACK) ptr + <*> (#peek COMB_INPUT, iBusWishbone_DAT_MISO) ptr + <*> (#peek COMB_INPUT, iBusWishbone_ERR) ptr + <*> (#peek COMB_INPUT, dBusWishbone_ACK) ptr + <*> (#peek COMB_INPUT, dBusWishbone_DAT_MISO) ptr + <*> (#peek COMB_INPUT, dBusWishbone_ERR) ptr {-# INLINE poke #-} poke ptr this = do - (#poke INPUT, reset) ptr (reset this) - (#poke INPUT, timerInterrupt) ptr (timerInterrupt this) - (#poke INPUT, externalInterrupt) ptr (externalInterrupt this) - (#poke INPUT, softwareInterrupt) ptr (softwareInterrupt this) - (#poke INPUT, iBusWishbone_ACK) ptr (iBusWishbone_ACK this) - (#poke INPUT, iBusWishbone_DAT_MISO) ptr (iBusWishbone_DAT_MISO this) - (#poke INPUT, iBusWishbone_ERR) ptr (iBusWishbone_ERR this) - (#poke INPUT, dBusWishbone_ACK) ptr (dBusWishbone_ACK this) - (#poke INPUT, dBusWishbone_DAT_MISO) ptr (dBusWishbone_DAT_MISO this) - (#poke INPUT, dBusWishbone_ERR) ptr (dBusWishbone_ERR this) + (#poke COMB_INPUT, iBusWishbone_ACK) ptr (iBusWishbone_ACK this) + (#poke COMB_INPUT, iBusWishbone_DAT_MISO) ptr (iBusWishbone_DAT_MISO this) + (#poke COMB_INPUT, iBusWishbone_ERR) ptr (iBusWishbone_ERR this) + + (#poke COMB_INPUT, dBusWishbone_ACK) ptr (dBusWishbone_ACK this) + (#poke COMB_INPUT, dBusWishbone_DAT_MISO) ptr (dBusWishbone_DAT_MISO this) + (#poke COMB_INPUT, dBusWishbone_ERR) ptr (dBusWishbone_ERR this) return () instance Storable OUTPUT where @@ -107,6 +127,7 @@ instance Storable OUTPUT where <*> (#peek OUTPUT, iBusWishbone_SEL) ptr <*> (#peek OUTPUT, iBusWishbone_CTI) ptr <*> (#peek OUTPUT, iBusWishbone_BTE) ptr + <*> (#peek OUTPUT, dBusWishbone_CYC) ptr <*> (#peek OUTPUT, dBusWishbone_STB) ptr <*> (#peek OUTPUT, dBusWishbone_WE) ptr @@ -126,6 +147,7 @@ instance Storable OUTPUT where (#poke OUTPUT, iBusWishbone_SEL) ptr (iBusWishbone_SEL this) (#poke OUTPUT, iBusWishbone_CTI) ptr (iBusWishbone_CTI this) (#poke OUTPUT, iBusWishbone_BTE) ptr (iBusWishbone_BTE this) + (#poke OUTPUT, dBusWishbone_CYC) ptr (dBusWishbone_CYC this) (#poke OUTPUT, dBusWishbone_STB) ptr (dBusWishbone_STB this) (#poke OUTPUT, dBusWishbone_WE) ptr (dBusWishbone_WE this) diff --git a/clash-vexriscv/src/ffi/impl.cpp b/clash-vexriscv/src/ffi/impl.cpp index a7bbba0..230e2c6 100644 --- a/clash-vexriscv/src/ffi/impl.cpp +++ b/clash-vexriscv/src/ffi/impl.cpp @@ -7,57 +7,123 @@ #include "interface.h" extern "C" { - VVexRiscv* vexr_init(); - void vexr_shutdown(VVexRiscv *top); - void vexr_step(VVexRiscv *top, const INPUT *input, OUTPUT *output); + VVexRiscv* vexr_init(); + void vexr_shutdown(VVexRiscv *top); + + void vexr_init_stage1(VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output); + void vexr_init_stage2(VVexRiscv *top, const COMB_INPUT *input); + void vexr_step_rising_edge(VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output); + void vexr_step_falling_edge(VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input); } +static VerilatedContext* contextp = 0; VVexRiscv* vexr_init() { - return new VVexRiscv(); + contextp = new VerilatedContext; + VVexRiscv *v = new VVexRiscv(contextp); + Verilated::traceEverOn(true); + v->clk = false; + return v; +} + +// Set all inputs that cannot combinationaly depend on outputs. I.e., all inputs +// except the Wishbone buses. +void set_non_comb_inputs(VVexRiscv *top, const NON_COMB_INPUT *input) +{ + top->reset = input->reset; + top->timerInterrupt = input->timerInterrupt; + top->externalInterrupt = input->externalInterrupt; + top->softwareInterrupt = input->softwareInterrupt; +} + +// Set all inputs that can combinationaly depend on outputs. I.e., the Wishbone +// buses. +void set_comb_inputs(VVexRiscv *top, const COMB_INPUT *input) +{ + top->iBusWishbone_ACK = input->iBusWishbone_ACK; + top->iBusWishbone_DAT_MISO = input->iBusWishbone_DAT_MISO; + top->iBusWishbone_ERR = input->iBusWishbone_ERR; + top->dBusWishbone_ACK = input->dBusWishbone_ACK; + top->dBusWishbone_DAT_MISO = input->dBusWishbone_DAT_MISO; + top->dBusWishbone_ERR = input->dBusWishbone_ERR; +} + +// Set all outputs +void set_ouputs(VVexRiscv *top, OUTPUT *output) +{ + output->iBusWishbone_CYC = top->iBusWishbone_CYC; + output->iBusWishbone_STB = top->iBusWishbone_STB; + output->iBusWishbone_WE = top->iBusWishbone_WE; + output->iBusWishbone_ADR = top->iBusWishbone_ADR; + output->iBusWishbone_DAT_MOSI = top->iBusWishbone_DAT_MOSI; + output->iBusWishbone_SEL = top->iBusWishbone_SEL; + output->iBusWishbone_CTI = top->iBusWishbone_CTI; + output->iBusWishbone_BTE = top->iBusWishbone_BTE; + output->dBusWishbone_CYC = top->dBusWishbone_CYC; + output->dBusWishbone_STB = top->dBusWishbone_STB; + output->dBusWishbone_WE = top->dBusWishbone_WE; + output->dBusWishbone_ADR = top->dBusWishbone_ADR; + output->dBusWishbone_DAT_MOSI = top->dBusWishbone_DAT_MOSI; + output->dBusWishbone_SEL = top->dBusWishbone_SEL; + output->dBusWishbone_CTI = top->dBusWishbone_CTI; + output->dBusWishbone_BTE = top->dBusWishbone_BTE; +} + +void vexr_init_stage1(VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output) +{ + // Set all inputs that cannot combinationaly depend on outputs. I.e., all inputs + // except the Wishbone buses. + set_non_comb_inputs(top, input); + + // Combinatorially respond to the inputs + top->eval(); + set_ouputs(top, output); + + // Advance time by 50 nanoseconds. This is an arbitrary value. Ideally, we would + // do something similar to Clash's template tag "~LONGESTPERIOD". + contextp->timeInc(50000); +} + +void vexr_init_stage2(VVexRiscv *top, const COMB_INPUT *input) +{ + set_comb_inputs(top, input); } void vexr_shutdown(VVexRiscv *top) { - delete top; -} - -void vexr_step(VVexRiscv *top, const INPUT *input, OUTPUT *output) -{ - // set inputs - top->reset = input->reset; - top->timerInterrupt = input->timerInterrupt; - top->externalInterrupt = input->externalInterrupt; - top->softwareInterrupt = input->softwareInterrupt; - top->iBusWishbone_ACK = input->iBusWishbone_ACK; - top->iBusWishbone_DAT_MISO = input->iBusWishbone_DAT_MISO; - top->iBusWishbone_ERR = input->iBusWishbone_ERR; - top->dBusWishbone_ACK = input->dBusWishbone_ACK; - top->dBusWishbone_DAT_MISO = input->dBusWishbone_DAT_MISO; - top->dBusWishbone_ERR = input->dBusWishbone_ERR; - - // run one cycle of the simulation - top->clk = true; - top->eval(); - top->clk = false; - top->eval(); - - // update outputs - output->iBusWishbone_CYC = top->iBusWishbone_CYC; - output->iBusWishbone_STB = top->iBusWishbone_STB; - output->iBusWishbone_WE = top->iBusWishbone_WE; - output->iBusWishbone_ADR = top->iBusWishbone_ADR; - output->iBusWishbone_DAT_MOSI = top->iBusWishbone_DAT_MOSI; - output->iBusWishbone_SEL = top->iBusWishbone_SEL; - output->iBusWishbone_CTI = top->iBusWishbone_CTI; - output->iBusWishbone_BTE = top->iBusWishbone_BTE; - output->dBusWishbone_CYC = top->dBusWishbone_CYC; - output->dBusWishbone_STB = top->dBusWishbone_STB; - output->dBusWishbone_WE = top->dBusWishbone_WE; - output->dBusWishbone_ADR = top->dBusWishbone_ADR; - output->dBusWishbone_DAT_MOSI = top->dBusWishbone_DAT_MOSI; - output->dBusWishbone_SEL = top->dBusWishbone_SEL; - output->dBusWishbone_CTI = top->dBusWishbone_CTI; - output->dBusWishbone_BTE = top->dBusWishbone_BTE; + delete top; + delete contextp; + contextp = 0; +} + + +void vexr_step_rising_edge(VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output) +{ + // Advance time since last event. Note that this is 0 for the first call to + // this function. To get a sensisble waveform, vexr_init has already advanced + // time. + contextp->timeInc(time_add); // XXX: time_add is in femtoseconds, timeinc expects picoseconds + + // docssss + set_non_comb_inputs(top, input); + + top->clk = true; + top->eval(); + + // Set all outputs + set_ouputs(top, output); +} + +void vexr_step_falling_edge(VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input) +{ + // advance time since last event + contextp->timeInc(time_add); // time_add is in femtoseconds, timeinc expects picoseconds + + // Update inputs + top->clk = false; + set_comb_inputs(top, input); + + // Evaluate the simulation + top->eval(); } diff --git a/clash-vexriscv/src/ffi/interface.h b/clash-vexriscv/src/ffi/interface.h index 312c68a..e82759a 100644 --- a/clash-vexriscv/src/ffi/interface.h +++ b/clash-vexriscv/src/ffi/interface.h @@ -14,7 +14,9 @@ typedef struct { bit timerInterrupt; bit externalInterrupt; bit softwareInterrupt; +} NON_COMB_INPUT; +typedef struct { bit iBusWishbone_ACK; uint32_t iBusWishbone_DAT_MISO; bit iBusWishbone_ERR; @@ -22,7 +24,7 @@ typedef struct { bit dBusWishbone_ACK; uint32_t dBusWishbone_DAT_MISO; bit dBusWishbone_ERR; -} INPUT; +} COMB_INPUT; typedef struct { bit iBusWishbone_CYC; @@ -44,39 +46,4 @@ typedef struct { uint8_t dBusWishbone_BTE; } OUTPUT; - #endif - -/* - input reset - input timerInterrupt, - input externalInterrupt, - input softwareInterrupt, - - input iBusWishbone_ACK, - input [31:0] iBusWishbone_DAT_MISO, - input iBusWishbone_ERR, - - input dBusWishbone_ACK, - input [31:0] dBusWishbone_DAT_MISO, - input dBusWishbone_ERR, - - - output iBusWishbone_CYC, - output iBusWishbone_STB, - output iBusWishbone_WE, - output [29:0] iBusWishbone_ADR, - output [31:0] iBusWishbone_DAT_MOSI, - output [3:0] iBusWishbone_SEL, - output [2:0] iBusWishbone_CTI, - output [1:0] iBusWishbone_BTE, - - output dBusWishbone_CYC, - output dBusWishbone_STB, - output dBusWishbone_WE, - output [29:0] dBusWishbone_ADR, - output [31:0] dBusWishbone_DAT_MOSI, - output reg [3:0] dBusWishbone_SEL, - output [2:0] dBusWishbone_CTI, - output [1:0] dBusWishbone_BTE, -*/