diff --git a/clash-ffi/clash-ffi.cabal b/clash-ffi/clash-ffi.cabal index 36b194cb75..86860034bd 100644 --- a/clash-ffi/clash-ffi.cabal +++ b/clash-ffi/clash-ffi.cabal @@ -1,7 +1,7 @@ cabal-version: 2.2 name: clash-ffi -version: 1.5.0 +version: 1.7.0 synopsis: Interact with Simulators from Clash description: Interact with Simulators from Clash bug-reports: https://github.com/clash-lang/clash-compiler/issues @@ -41,14 +41,15 @@ library DeriveGeneric DerivingStrategies GeneralizedNewtypeDeriving + LambdaCase + RecordWildCards + TupleSections build-depends: derive-storable >= 0.3 && < 0.4, derive-storable-plugin >= 0.2 && < 0.3, - mtl >= 2.2 && < 2.4, hs-source-dirs: src c-sources: cbits/entry_vpi.c exposed-modules: - Clash.FFI.Monad Clash.FFI.View Clash.FFI.VPI.Callback Clash.FFI.VPI.Callback.Reason diff --git a/clash-ffi/example/Simulate.hs b/clash-ffi/example/Simulate.hs index 26af7e7622..8e056cf17f 100644 --- a/clash-ffi/example/Simulate.hs +++ b/clash-ffi/example/Simulate.hs @@ -9,7 +9,6 @@ import Data.Bits (complement) import Data.List (intercalate, zip5) import Control.Exception (SomeException, try) import Control.Monad (void) -import Control.Monad.IO.Class (liftIO) import Foreign.C.String (newCString) import Foreign.Marshal.Alloc (free) @@ -20,7 +19,6 @@ import Clash.Prelude , low, high, pack, unpack, resize ) -import Clash.FFI.Monad import Clash.FFI.VPI.Info import Clash.FFI.VPI.IO import Clash.FFI.VPI.Callback @@ -49,7 +47,7 @@ foreign export ccall "clash_ffi_main" ffiMain :: IO () ffiMain :: IO () -ffiMain = runSimAction $ do +ffiMain = do -------------------------- -- print simulator info -- -------------------------- @@ -122,7 +120,7 @@ ffiMain = runSimAction $ do 4 -> "<=>" -- mixed input-output _ -> "x" -- no direction -assignInputs :: (?state :: State) => SimAction () +assignInputs :: (?state :: State) => IO () assignInputs = do SimTime time <- receiveTime Sim $ Just top @@ -163,7 +161,7 @@ assignInputs = do sendValue port (BitVectorVal SNat $ pack v) $ InertialDelay $ SimTime 0 return $ Just v -readOutputs :: (?state :: State) => SimAction () +readOutputs :: (?state :: State) => IO () readOutputs = do SimTime time <- receiveTime Sim $ Just top receiveValue VectorFmt dataOut >>= \case @@ -174,14 +172,14 @@ readOutputs = do } _ -> return () - if (steps > 0) then do + if steps > 0 then do let ?state = ?state { steps = steps - 1 } nextCB ReadWriteSynch 1 assignInputs else do putStrLn "" putStrLn "[ Simulation done ]" - liftIO $ void $ try @SomeException $ runSimAction + void $ try @SomeException $ controlSimulator $ Finish NoDiagnostics where @@ -226,31 +224,31 @@ updates = Updates 0 Nothing Nothing Nothing Nothing Nothing nextCB :: (Maybe Object -> Time -> CallbackReason) -> Int64 -> - SimAction () -> - SimAction () + IO () -> + IO () nextCB reason time action = void $ registerCallback CallbackInfo { cbReason = reason Nothing (SimTime time) - , cbRoutine = const (runSimAction action >> return 0) + , cbRoutine = const (action >> return 0) , cbIndex = 0 , cbData = B.empty } getByName :: (Coercible a Object, Show a, Typeable a, Coercible Object b) => - Maybe a -> B.ByteString -> SimCont o b + Maybe a -> B.ByteString -> IO b getByName m name = do - ref <- liftIO $ newCString $ B.unpack name + ref <- newCString $ B.unpack name obj <- getChild ref m - liftIO $ free ref + free ref return obj -putStr :: String -> SimAction () +putStr :: String -> IO () putStr = simPutStr . B.pack -putStrLn :: String -> SimAction () +putStrLn :: String -> IO () putStrLn = simPutStrLn . B.pack -print :: Show a => a -> SimAction () +print :: Show a => a -> IO () print = simPutStrLn . B.pack . show diff --git a/clash-ffi/src/Clash/FFI/Monad.hs b/clash-ffi/src/Clash/FFI/Monad.hs deleted file mode 100644 index a687e2f6ad..0000000000 --- a/clash-ffi/src/Clash/FFI/Monad.hs +++ /dev/null @@ -1,172 +0,0 @@ -{-| -Copyright: (C) 2022 Google Inc. -License: BSD2 (see the file LICENSE) -Maintainer: QBayLogic B.V. --} - -{-# LANGUAGE CPP #-} - -module Clash.FFI.Monad - ( SimCont - , SimAction - , runSimAction - , liftCont - , stackPtr - , heapPtr - , withNewPtr - , readPtr - , unsafeFreeWith - , unsafeFreePtr - , freePtr - , throw - ) where - -import Control.Exception (Exception) -import qualified Control.Exception as IO (throwIO) -import qualified Control.Monad as Monad (unless) -import Control.Monad.IO.Class (MonadIO) -import qualified Control.Monad.IO.Class as IO (liftIO) -import Control.Monad.Cont (ContT(ContT), MonadCont) -import qualified Control.Monad.Cont as Cont (runContT) -import qualified Foreign.Marshal.Alloc as FFI (alloca, free, malloc) -import Foreign.Ptr (Ptr) -import qualified Foreign.Ptr as FFI (nullPtr) -import Foreign.Storable (Storable) -import qualified Foreign.Storable as FFI (peek) -import GHC.Stack (HasCallStack) - -#if MIN_VERSION_base(4,9,0) -#ifdef MIN_VERSION_GLASGOW_HASKELL -#if MIN_VERSION_GLASGOW_HASKELL(8,8,4,0) -#else -import Control.Monad.Fail (MonadFail) -#endif -#endif -#endif - -{- -NOTE [continuation-based API] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For clash-ffi, the API is designed around a continuation monad. The reason for -this is that we want control over allocated memory, but cannot use `ForeignPtr` -as we do not own a lot of the allocated memory. - -We could have stayed in IO directly, however the API given in `base` can -quickly become burdensome to read and write, since `alloca` has the type - - alloca :: Storable a => (Ptr a -> IO b) -> IO b - -which can lead to an excess of nesting in functions. Using continuations we -can provide a more consistent API where stack and heap pointers are used in the -same way, e.g. - - do - x <- stackPtr - y <- heapPtr - result <- f x y - - freePtr y - pure result --} - --- | The type of FFI actions with an input @i@ and an output @o@. For arbitrary --- actions to be run in FFI, the type will be @SimCont o A@, where @A@ is the --- result of the action (since this API is continuation-based the output of --- one action is the input to the continuation). --- --- For the "main" action performed by @clash-ffi@, there is no input and no --- output. The 'SimAction' synonym is intended for this case. Consequently, --- there is no @runSimCont@ function, as actions should be run with --- 'runSimAction'. --- -newtype SimCont o i = SimCont (ContT o IO i) - deriving newtype - ( Applicative, Functor, Monad, MonadCont, MonadIO -#if MIN_VERSION_base(4,9,0) - , MonadFail -#endif - ) - --- | The type of an VPI "main" action run in @clash-ffi@. For the more general --- type of FFI computations, use 'SimCont'. --- -type SimAction a = SimCont a a - --- | Run a VPI "main" action. See 'SimAction' and 'SimCont' for more --- information. --- -runSimAction :: SimAction a -> IO a -runSimAction (SimCont cont) = Cont.runContT cont pure - --- | Lift a continuation into a simulation FFI action. --- -liftCont :: ((i -> IO o) -> IO o) -> SimCont o i -liftCont = SimCont . ContT - --- | Allocate memory that will be automatically deallocated when the action --- has finished. For long-lasting allocations, see 'heapPtr'. --- -stackPtr :: (HasCallStack, Storable a) => SimCont b (Ptr a) -stackPtr = SimCont (ContT FFI.alloca) - --- | Allocate memory that will not be automatically deallocated when the action --- has finished. This must be deallocated with 'freePtr'. For memory which is --- only needed temporarily, 'stackPtr' should be preferred. --- -heapPtr :: (HasCallStack, Storable a) => SimCont b (Ptr a) -heapPtr = IO.liftIO FFI.malloc - --- | Allocate memory using the provided strategy (see 'stackPtr' and 'heapPtr') --- and perform the given action with the newly allocated pointer. Both the --- allocated pointer and the result of the action are returned, so that the --- pointer can be deallocated later if necessary. --- -withNewPtr - :: Storable a - => SimCont c (Ptr a) - -> (Ptr a -> IO b) - -> SimCont c (Ptr a, b) -withNewPtr alloc set = do - ptr <- alloc - res <- IO.liftIO (set ptr) - pure (ptr, res) - --- | Dereference a pointer, returning its current value. The caller is --- responsible for ensuring the pointer is valid. --- -readPtr :: HasCallStack => Storable a => Ptr a -> SimCont b a -readPtr = IO.liftIO . FFI.peek - --- | Free allocated memory using the provided function. If the memory was --- allocated with 'heapPtr' then 'unsafeFreePtr' should be used instead. --- --- This function does not check if the pointer given is NULL, meaning it will --- panic if given NULL. It should only be used for pointers known to be valid. --- -unsafeFreeWith :: HasCallStack => (a -> IO ()) -> a -> SimCont b () -unsafeFreeWith f = IO.liftIO . f - --- | Free allocated memory that was allocated with 'heapPtr'. If the memory --- requires a destructor other than 'FFI.free', or a type other than 'Ptr a' --- then 'unsafeFreeWith' should be used instead. --- --- This function does not check if the pointer given is NULL, meaning it will --- panic if given NULL. It should only be used for pointers known to be valid. --- -unsafeFreePtr :: HasCallStack => Ptr a -> SimCont b () -unsafeFreePtr = unsafeFreeWith FFI.free - --- | Free allocated memory that was allocated with 'heapPtr'. If the memory --- requires a destructor other than 'FFI.free', or a type other than 'Ptr a' --- then 'unsafeFreeWith' should be used instead. --- -freePtr :: HasCallStack => Ptr a -> SimCont b () -freePtr = unsafeFreeWith (\p -> Monad.unless (p == FFI.nullPtr) (FFI.free p)) - --- | Throw an exception in simulation. Unless caught this will cause the GHC --- RTS to exit, which will cause the simulator to stop / hang / enter a prompt. --- -throw :: (HasCallStack, Exception e) => e -> SimCont b a -throw ex = IO.liftIO (IO.throwIO ex) - --- TODO I should also provide catch here, and probably bracket diff --git a/clash-ffi/src/Clash/FFI/VPI/Callback.hs b/clash-ffi/src/Clash/FFI/VPI/Callback.hs index e498497f0e..097161cf28 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Callback.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Callback.hs @@ -6,7 +6,6 @@ Maintainer: QBayLogic B.V. {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} @@ -22,15 +21,20 @@ module Clash.FFI.VPI.Callback , removeCallback #ifndef IVERILOG , getCallbackInfo + , withCallbackInfo + , unsafeReceiveCallbackInfo + , receiveCallbackInfo #endif , module Clash.FFI.VPI.Callback.Reason ) where -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import qualified Control.Monad as Monad (unless) -import qualified Control.Monad.IO.Class as IO (liftIO) import Foreign.C.String (CString) import Foreign.C.Types (CInt(..)) +#ifndef IVERILOG +import qualified Foreign.Marshal.Alloc as FFI (alloca, malloc) +#endif import Foreign.Ptr (FunPtr, Ptr) import qualified Foreign.Ptr as FFI (castPtr) import Foreign.Storable (Storable) @@ -38,8 +42,6 @@ import Foreign.Storable.Generic (GStorable) import GHC.Generics (Generic) import GHC.Stack (CallStack, HasCallStack, callStack, prettyCallStack) -import Clash.FFI.Monad (SimCont) -import qualified Clash.FFI.Monad as Sim import Clash.FFI.View import Clash.FFI.VPI.Callback.Reason import Clash.FFI.VPI.Object @@ -82,22 +84,21 @@ foreign import ccall "wrapper" type instance CRepr (CallbackInfo _) = CCallbackInfo instance (UnsafeSend extra, CRepr extra ~ Ptr a) => UnsafeSend (CallbackInfo extra) where - unsafeSend CallbackInfo{..} = do - (creason, cobject, ctime, cvalue) <- unsafeSend cbReason - croutine <- IO.liftIO (sendRoutine cbRoutine) - let cindex = fromIntegral cbIndex - bytes <- FFI.castPtr <$> unsafeSend cbData - - pure (CCallbackInfo creason croutine cobject ctime cvalue cindex bytes) + unsafeSend CallbackInfo{..} f = + unsafeSend cbReason $ \(ccbReason, ccbObject, ccbTime, ccbValue) -> do + ccbRoutine <- sendRoutine cbRoutine + let ccbIndex = fromIntegral cbIndex + unsafeSend cbData $ \ptr -> do + let ccbData = FFI.castPtr ptr + f CCallbackInfo{..} instance (Send extra, CRepr extra ~ Ptr a) => Send (CallbackInfo extra) where send CallbackInfo{..} = do - (creason, cobject, ctime, cvalue) <- send cbReason - croutine <- IO.liftIO (sendRoutine cbRoutine) - let cindex = fromIntegral cbIndex - bytes <- FFI.castPtr <$> send cbData - - pure (CCallbackInfo creason croutine cobject ctime cvalue cindex bytes) + (ccbReason, ccbObject, ccbTime, ccbValue) <- send cbReason + ccbRoutine <- sendRoutine cbRoutine + let ccbIndex = fromIntegral cbIndex + ccbData <- FFI.castPtr <$> send cbData + pure CCallbackInfo{..} foreign import ccall "dynamic" receiveRoutine @@ -106,21 +107,19 @@ foreign import ccall "dynamic" instance (UnsafeReceive extra, CRepr extra ~ Ptr a) => UnsafeReceive (CallbackInfo extra) where unsafeReceive CCallbackInfo{..} = do - reason <- unsafeReceive (ccbReason, ccbObject, ccbTime, ccbValue) - let routine = receiveRoutine ccbRoutine - let index = fromIntegral ccbIndex - extra <- unsafeReceive (FFI.castPtr ccbData) - - pure (CallbackInfo reason routine index extra) + cbReason <- unsafeReceive (ccbReason, ccbObject, ccbTime, ccbValue) + let cbRoutine = receiveRoutine ccbRoutine + let cbIndex = fromIntegral ccbIndex + cbData <- unsafeReceive $ FFI.castPtr ccbData + pure CallbackInfo{..} instance (Receive extra, CRepr extra ~ Ptr a) => Receive (CallbackInfo extra) where receive CCallbackInfo{..} = do - reason <- receive (ccbReason, ccbObject, ccbTime, ccbValue) - let routine = receiveRoutine ccbRoutine - let index = fromIntegral ccbIndex - extra <- receive (FFI.castPtr ccbData) - - pure (CallbackInfo reason routine index extra) + cbReason <- receive (ccbReason, ccbObject, ccbTime, ccbValue) + let cbRoutine = receiveRoutine ccbRoutine + let cbIndex = fromIntegral ccbIndex + cbData <- receive $ FFI.castPtr ccbData + pure CallbackInfo{..} foreign import ccall "vpi_user.h vpi_register_cb" c_vpi_register_cb :: Ptr CCallbackInfo -> IO Callback @@ -143,14 +142,13 @@ newtype Callback -- the callback is triggered, and what data it has available. -- registerCallback - :: forall extra o + :: forall extra . UnsafeSend extra => CRepr extra ~ CString => CallbackInfo extra - -> SimCont o Callback -registerCallback cb = do - ptr <- unsafePokeSend cb - IO.liftIO (c_vpi_register_cb ptr) + -> IO Callback +registerCallback = + (`unsafePokeSend` c_vpi_register_cb) -- | An exception thrown when VPI could not remove a callback from the running -- simulator. If this is thrown the callback may still be active in simulation. @@ -174,14 +172,11 @@ foreign import ccall "vpi_user.h vpi_remove_cb" -- | Remove a callback from the simulator. Removing a callback also frees the -- callback object, so 'freeObject' does not need to be called. -- -removeCallback :: forall o. HasCallStack => Callback -> SimCont o () +removeCallback :: HasCallStack => Callback -> IO () removeCallback cb = do - status <- IO.liftIO (c_vpi_remove_cb cb) - + status <- c_vpi_remove_cb cb Monad.unless status $ - Sim.throw (CouldNotUnregisterCallback cb callStack) - - pure () + throwIO $ CouldNotUnregisterCallback cb callStack -- iverilog just decided not to implement this VPI call... #ifndef IVERILOG @@ -189,17 +184,22 @@ foreign import ccall "vpi_user.h vpi_get_cb_info" c_vpi_get_cb_info :: Callback -> Ptr CCallbackInfo -> IO () -- | Get the low-level representation of the information for the given callback --- object. This can be converted to the high-level representation using --- 'Receive'. If only the high-level representation is needed then consider --- using 'receiveCallbackInfo' or 'unsafeReceiveCallbackInfo' instead. +-- object, which is allocated on the heap. This can be converted to the high-level +-- representation using 'Receive'. If only the high-level representation is needed +-- then consider using 'receiveCallbackInfo' or 'unsafeReceiveCallbackInfo' instead. +-- +getCallbackInfo :: Callback -> IO (Ptr CCallbackInfo) +getCallbackInfo callback = + FFI.malloc >>= \ptr -> c_vpi_get_cb_info callback ptr >> return ptr + +-- | Get the low-level representation of the information for the given callback +-- object, which is allocated on the stack. This can be converted to the high-level +-- representation using 'Receive'. If only the high-level representation is needed +-- then consider using 'receiveCallbackInfo' or 'unsafeReceiveCallbackInfo' instead. -- -getCallbackInfo - :: forall o - . SimCont o (Ptr CCallbackInfo) - -> Callback - -> SimCont o (Ptr CCallbackInfo) -getCallbackInfo alloc callback = - Sim.withNewPtr alloc (c_vpi_get_cb_info callback) +withCallbackInfo :: Callback -> (Ptr CCallbackInfo -> IO a) -> IO a +withCallbackInfo callback f = + FFI.alloca $ \ptr -> c_vpi_get_cb_info callback ptr >> f ptr -- | Get the high-level representation of the information for the given -- callback object. The value is unsafely read, meaning it may be corrupted if @@ -211,13 +211,13 @@ getCallbackInfo alloc callback = -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- unsafeReceiveCallbackInfo - :: forall extra a o + :: forall extra a . UnsafeReceive extra => CRepr extra ~ Ptr a => Callback - -> SimCont o (CallbackInfo extra) + -> IO (CallbackInfo extra) unsafeReceiveCallbackInfo callback = - getCallbackInfo Sim.stackPtr callback >>= unsafeReceive + withCallbackInfo callback unsafePeekReceive -- | Get the high-level representation of the information for the given -- callback object. The value is safely read meaning it will not become @@ -226,11 +226,11 @@ unsafeReceiveCallbackInfo callback = -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- receiveCallbackInfo - :: forall extra a o + :: forall extra a . Receive extra => CRepr extra ~ Ptr a => Callback - -> SimCont o (CallbackInfo extra) + -> IO (CallbackInfo extra) receiveCallbackInfo callback = - getCallbackInfo Sim.stackPtr callback >>= receive + getCallbackInfo callback >>= peekReceive #endif diff --git a/clash-ffi/src/Clash/FFI/VPI/Callback/Reason.hs b/clash-ffi/src/Clash/FFI/VPI/Callback/Reason.hs index ca22553c87..7c8f43f484 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Callback/Reason.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Callback/Reason.hs @@ -7,7 +7,6 @@ Maintainer: QBayLogic B.V. {-# LANGUAGE CPP #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} module Clash.FFI.VPI.Callback.Reason @@ -15,16 +14,15 @@ module Clash.FFI.VPI.Callback.Reason , UnknownCallbackReason(..) ) where -import Control.Exception (Exception) -import qualified Control.Monad.IO.Class as IO (liftIO) +import Control.Exception (Exception, throwIO) import Data.Coerce import Foreign.C.Types (CInt) +import qualified Foreign.Marshal.Alloc as FFI (alloca, malloc) import Foreign.Ptr (Ptr) import qualified Foreign.Ptr as FFI (nullPtr) import qualified Foreign.Storable as FFI (peekByteOff, pokeByteOff) import GHC.Stack (CallStack, callStack, prettyCallStack) -import qualified Clash.FFI.Monad as Sim import Clash.FFI.View import Clash.FFI.VPI.Object @@ -154,177 +152,177 @@ data CallbackReason type instance CRepr CallbackReason = (CInt, Object, Ptr CTime, Ptr CValue) instance UnsafeSend CallbackReason where - unsafeSend = \case + unsafeSend cbr f = case cbr of AfterValueChange object timeTy valueFmt -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) - - cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) - - pure (1, coerce object, ctime, cvalue) + FFI.alloca $ \ctime -> do + FFI.pokeByteOff ctime 0 ctimeTy + cfmt <- send valueFmt + FFI.alloca $ \cvalue -> do + FFI.pokeByteOff cvalue 0 cfmt + f (1, coerce object, ctime, cvalue) BeforeStatement object timeTy -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) - - pure (2, coerce object, ctime, FFI.nullPtr) + FFI.alloca $ \ctime -> do + FFI.pokeByteOff ctime 0 ctimeTy + f (2, coerce object, ctime, FFI.nullPtr) AfterForce mObject timeTy valueFmt -> do let object = maybe nullObject coerce mObject - ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) - - cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) - - pure (3, object, ctime, cvalue) + FFI.alloca $ \ctime -> do + FFI.pokeByteOff ctime 0 ctimeTy + cfmt <- send valueFmt + FFI.alloca $ \cvalue -> do + FFI.pokeByteOff cvalue 0 cfmt + f (3, object, ctime, cvalue) AfterRelease mObject timeTy valueFmt -> do let object = maybe nullObject coerce mObject - ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) - - cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) - - pure (4, object, ctime, cvalue) + FFI.alloca $ \ctime -> do + FFI.pokeByteOff ctime 0 ctimeTy + cfmt <- send valueFmt + FFI.alloca $ \cvalue -> do + FFI.pokeByteOff cvalue 0 cfmt + f (4, object, ctime, cvalue) AtStartOfSimTime mObject time -> do let object = maybe nullObject coerce mObject ctime <- pokeSend time - pure (5, object, ctime, FFI.nullPtr) + f (5, object, ctime, FFI.nullPtr) ReadWriteSynch mObject time -> do let object = maybe nullObject coerce mObject ctime <- pokeSend time - pure (6, object, ctime, FFI.nullPtr) + f (6, object, ctime, FFI.nullPtr) ReadOnlySynch mObject time -> do let object = maybe nullObject coerce mObject ctime <- pokeSend time - pure (7, object, ctime, FFI.nullPtr) + f (7, object, ctime, FFI.nullPtr) NextSimTime mObject timeTy -> do let object = maybe nullObject coerce mObject - ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) - - pure (8, object, ctime, FFI.nullPtr) + FFI.alloca $ \ctime -> do + FFI.pokeByteOff ctime 0 ctimeTy + f (8, object, ctime, FFI.nullPtr) AfterDelay mObject time -> do let object = maybe nullObject coerce mObject ctime <- pokeSend time - pure (9, object, ctime, FFI.nullPtr) + f (9, object, ctime, FFI.nullPtr) EndOfCompile -> - pure (10, nullObject, FFI.nullPtr, FFI.nullPtr) + f (10, nullObject, FFI.nullPtr, FFI.nullPtr) StartOfSimulation -> - pure (11, nullObject, FFI.nullPtr, FFI.nullPtr) + f (11, nullObject, FFI.nullPtr, FFI.nullPtr) EndOfSimulation -> - pure (12, nullObject, FFI.nullPtr, FFI.nullPtr) + f (12, nullObject, FFI.nullPtr, FFI.nullPtr) RuntimeError -> - pure (13, nullObject, FFI.nullPtr, FFI.nullPtr) + f (13, nullObject, FFI.nullPtr, FFI.nullPtr) TchkViolation -> - pure (14, nullObject, FFI.nullPtr, FFI.nullPtr) + f (14, nullObject, FFI.nullPtr, FFI.nullPtr) StartOfSave -> - pure (15, nullObject, FFI.nullPtr, FFI.nullPtr) + f (15, nullObject, FFI.nullPtr, FFI.nullPtr) EndOfSave -> - pure (16, nullObject, FFI.nullPtr, FFI.nullPtr) + f (16, nullObject, FFI.nullPtr, FFI.nullPtr) StartOfRestart -> - pure (17, nullObject, FFI.nullPtr, FFI.nullPtr) + f (17, nullObject, FFI.nullPtr, FFI.nullPtr) EndOfRestart -> - pure (18, nullObject, FFI.nullPtr, FFI.nullPtr) + f (18, nullObject, FFI.nullPtr, FFI.nullPtr) StartOfReset -> - pure (19, nullObject, FFI.nullPtr, FFI.nullPtr) + f (19, nullObject, FFI.nullPtr, FFI.nullPtr) EndOfReset -> - pure (20, nullObject, FFI.nullPtr, FFI.nullPtr) + f (20, nullObject, FFI.nullPtr, FFI.nullPtr) EnterInteractive -> - pure (21, nullObject, FFI.nullPtr, FFI.nullPtr) + f (21, nullObject, FFI.nullPtr, FFI.nullPtr) ExitInteractive -> - pure (22, nullObject, FFI.nullPtr, FFI.nullPtr) + f (22, nullObject, FFI.nullPtr, FFI.nullPtr) InteractiveScopeChange -> - pure (23, nullObject, FFI.nullPtr, FFI.nullPtr) + f (23, nullObject, FFI.nullPtr, FFI.nullPtr) UnresolvedSysTf -> - pure (24, nullObject, FFI.nullPtr, FFI.nullPtr) + f (24, nullObject, FFI.nullPtr, FFI.nullPtr) #if defined(VERILOG_2001) AfterAssign object timeTy valueFmt -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) - - cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) - - pure (25, coerce object, ctime, cvalue) + FFI.alloca $ \ctime -> do + FFI.pokeByteOff ctime 0 ctimeTy + cfmt <- send valueFmt + FFI.alloca $ \cvalue -> do + FFI.pokeByteOff cvalue 0 cfmt + f (25, coerce object, ctime, cvalue) AfterDeassign object timeTy valueFmt -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) - - cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) - - pure (26, coerce object, ctime, cvalue) + FFI.alloca $ \ctime -> do + FFI.pokeByteOff ctime 0 ctimeTy + cfmt <- send valueFmt + FFI.alloca $ \cvalue -> do + FFI.pokeByteOff cvalue 0 cfmt + f (26, coerce object, ctime, cvalue) AfterDisable object timeTy valueFmt -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) - - cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.stackPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) - - pure (27, coerce object, ctime, cvalue) + FFI.alloca $ \ctime -> do + FFI.pokeByteOff ctime 0 ctimeTy + cfmt <- send valueFmt + FFI.alloca $ \cvalue -> do + FFI.pokeByteOff cvalue 0 cfmt + f (27, coerce object, ctime, cvalue) PliError -> - pure (28, nullObject, FFI.nullPtr, FFI.nullPtr) + f (28, nullObject, FFI.nullPtr, FFI.nullPtr) Signal -> - pure (29, nullObject, FFI.nullPtr, FFI.nullPtr) + f (29, nullObject, FFI.nullPtr, FFI.nullPtr) #endif #if defined(VERILOG_2005) NbaSynch mObject time -> do let object = maybe nullObject coerce mObject ctime <- pokeSend time - pure (30, object, ctime, FFI.nullPtr) + f (30, object, ctime, FFI.nullPtr) AtEndOfSimTime mObject time -> do let object = maybe nullObject coerce mObject ctime <- pokeSend time - pure (31, object, ctime, FFI.nullPtr) + f (31, object, ctime, FFI.nullPtr) #endif instance Send CallbackReason where send = \case AfterValueChange object timeTy valueFmt -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) + ctime <- FFI.malloc + FFI.pokeByteOff ctime 0 ctimeTy cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) + cvalue <- FFI.malloc + FFI.pokeByteOff cvalue 0 cfmt pure (1, coerce object, ctime, cvalue) BeforeStatement object timeTy -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) + ctime <- FFI.malloc + FFI.pokeByteOff ctime 0 ctimeTy pure (2, coerce object, ctime, FFI.nullPtr) @@ -332,10 +330,12 @@ instance Send CallbackReason where let object = maybe nullObject coerce mObject ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) + ctime <- FFI.malloc + FFI.pokeByteOff ctime 0 ctimeTy cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) + cvalue <- FFI.malloc + FFI.pokeByteOff cvalue 0 cfmt pure (3, object, ctime, cvalue) @@ -343,10 +343,12 @@ instance Send CallbackReason where let object = maybe nullObject coerce mObject ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) + ctime <- FFI.malloc + FFI.pokeByteOff ctime 0 ctimeTy cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) + cvalue <- FFI.malloc + FFI.pokeByteOff cvalue 0 cfmt pure (4, object, ctime, cvalue) @@ -369,7 +371,8 @@ instance Send CallbackReason where let object = maybe nullObject coerce mObject ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) + ctime <- FFI.malloc + FFI.pokeByteOff ctime 0 ctimeTy pure (8, object, ctime, FFI.nullPtr) @@ -426,28 +429,34 @@ instance Send CallbackReason where #if defined(VERILOG_2001) AfterAssign object timeTy valueFmt -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) + ctime <- FFI.malloc + FFI.pokeByteOff ctime 0 ctimeTy cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) + cvalue <- FFI.malloc + FFI.pokeByteOff cvalue 0 cfmt pure (25, coerce object, ctime, cvalue) AfterDeassign object timeTy valueFmt -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) + ctime <- FFI.malloc + FFI.pokeByteOff ctime 0 ctimeTy cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) + cvalue <- FFI.malloc + FFI.pokeByteOff cvalue 0 cfmt pure (26, coerce object, ctime, cvalue) AfterDisable object timeTy valueFmt -> do ctimeTy <- send timeTy - ctime <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 ctimeTy) + ctime <- FFI.malloc + FFI.pokeByteOff ctime 0 ctimeTy cfmt <- send valueFmt - cvalue <- fst <$> Sim.withNewPtr Sim.heapPtr (\ptr -> FFI.pokeByteOff ptr 0 cfmt) + cvalue <- FFI.malloc + FFI.pokeByteOff cvalue 0 cfmt pure (27, coerce object, ctime, cvalue) @@ -477,8 +486,8 @@ data UnknownCallbackReason deriving anyclass (Exception) instance Show UnknownCallbackReason where - show (UnknownCallbackReason x c) = - mconcat + show = \case + UnknownCallbackReason x c -> mconcat [ "Unknown callback reason: " , show x , "\n" @@ -490,43 +499,43 @@ instance UnsafeReceive CallbackReason where let mObject = if isNullObject object then Nothing else Just object in case creason of 1 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterValueChange object timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterValueChange object timeTy valueFmt 2 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - pure (BeforeStatement object timeTy) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + pure $ BeforeStatement object timeTy 3 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterForce mObject timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterForce mObject timeTy valueFmt 4 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterRelease mObject timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterRelease mObject timeTy valueFmt 5 -> do time <- peekReceive ctime - pure (AtStartOfSimTime mObject time) + pure $ AtStartOfSimTime mObject time 6 -> do time <- peekReceive ctime - pure (ReadWriteSynch mObject time) + pure $ ReadWriteSynch mObject time 7 -> do time <- peekReceive ctime - pure (ReadOnlySynch mObject time) + pure $ ReadOnlySynch mObject time 8 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - pure (NextSimTime mObject timeTy) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + pure $ NextSimTime mObject timeTy 9 -> do time <- peekReceive ctime - pure (AfterDelay mObject time) + pure $ AfterDelay mObject time 10 -> pure EndOfCompile @@ -575,19 +584,19 @@ instance UnsafeReceive CallbackReason where #if defined(VERILOG_2001) 25 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterAssign object timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterAssign object timeTy valueFmt 26 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterDeassign object timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterDeassign object timeTy valueFmt 27 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterDisable object timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterDisable object timeTy valueFmt 28 -> pure PliError @@ -598,57 +607,57 @@ instance UnsafeReceive CallbackReason where #if defined(VERILOG_2005) 30 -> do time <- peekReceive ctime - pure (NbaSynch mObject time) + pure $ NbaSynch mObject time 31 -> do time <- peekReceive ctime - pure (AtEndOfSimTime mObject time) + pure $ AtEndOfSimTime mObject time #endif - n -> Sim.throw (UnknownCallbackReason n callStack) + n -> throwIO $ UnknownCallbackReason n callStack instance Receive CallbackReason where receive (creason, object, ctime, cvalue) = let mObject = if isNullObject object then Nothing else Just object in case creason of 1 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterValueChange object timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterValueChange object timeTy valueFmt 2 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - pure (BeforeStatement object timeTy) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + pure $ BeforeStatement object timeTy 3 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterForce mObject timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterForce mObject timeTy valueFmt 4 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterRelease mObject timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterRelease mObject timeTy valueFmt 5 -> do time <- peekReceive ctime - pure (AtStartOfSimTime mObject time) + pure $ AtStartOfSimTime mObject time 6 -> do time <- peekReceive ctime - pure (ReadWriteSynch mObject time) + pure $ ReadWriteSynch mObject time 7 -> do time <- peekReceive ctime - pure (ReadOnlySynch mObject time) + pure $ ReadOnlySynch mObject time 8 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - pure (NextSimTime mObject timeTy) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + pure $ NextSimTime mObject timeTy 9 -> do time <- peekReceive ctime - pure (AfterDelay mObject time) + pure $ AfterDelay mObject time 10 -> pure EndOfCompile @@ -697,19 +706,19 @@ instance Receive CallbackReason where #if defined(VERILOG_2001) 25 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterAssign object timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterAssign object timeTy valueFmt 26 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterDeassign object timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterDeassign object timeTy valueFmt 27 -> do - timeTy <- IO.liftIO (FFI.peekByteOff ctime 0) >>= receive - valueFmt <- IO.liftIO (FFI.peekByteOff cvalue 0) >>= receive - pure (AfterDisable object timeTy valueFmt) + timeTy <- FFI.peekByteOff ctime 0 >>= receive + valueFmt <- FFI.peekByteOff cvalue 0 >>= receive + pure $ AfterDisable object timeTy valueFmt 28 -> pure PliError @@ -720,11 +729,11 @@ instance Receive CallbackReason where #if defined(VERILOG_2005) 30 -> do time <- peekReceive ctime - pure (NbaSynch mObject time) + pure $ NbaSynch mObject time 31 -> do time <- peekReceive ctime - pure (AtEndOfSimTime mObject time) + pure $ AtEndOfSimTime mObject time #endif - n -> Sim.throw (UnknownCallbackReason n callStack) + n -> throwIO $ UnknownCallbackReason n callStack diff --git a/clash-ffi/src/Clash/FFI/VPI/Control.hs b/clash-ffi/src/Clash/FFI/VPI/Control.hs index 9515f31f48..8db615b83b 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Control.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Control.hs @@ -5,8 +5,6 @@ Maintainer: QBayLogic B.V. -} {-# LANGUAGE CPP #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} module Clash.FFI.VPI.Control @@ -18,15 +16,12 @@ module Clash.FFI.VPI.Control , controlSimulator ) where -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import qualified Control.Monad as Monad (unless) -import qualified Control.Monad.IO.Class as IO (liftIO) import Data.Maybe (fromMaybe) import Foreign.C.Types (CInt(..)) import GHC.Stack (CallStack, callStack, prettyCallStack) -import Clash.FFI.Monad (SimCont) -import qualified Clash.FFI.Monad as Sim (throw) import Clash.FFI.View -- | A control command to send to the simulator. Depending on the simulator, @@ -47,12 +42,11 @@ data Control type instance CRepr Control = (CInt, CInt, CInt, CInt) instance Send Control where - send = - \case - Stop d -> (66, 0, 0, ) <$> send d - Finish d -> (67, 0, 0, ) <$> send d - Reset s r d -> - (68,,,) <$> send s <*> pure (fromMaybe 0 r) <*> send d + send = \case + Stop d -> (66, 0, 0, ) <$> send d + Finish d -> (67, 0, 0, ) <$> send d + Reset s r d -> + (68,,,) <$> send s <*> pure (fromMaybe 0 r) <*> send d -- | When resetting the simulator, the stop value determines whether the -- simulator will enter interactive mode or immediately start processing again. @@ -65,10 +59,9 @@ data StopValue type instance CRepr StopValue = CInt instance Send StopValue where - send = - pure . \case - Interactive -> 0 - Processing -> 1 + send = pure . \case + Interactive -> 0 + Processing -> 1 -- | When stopping data DiagnosticLevel @@ -80,11 +73,10 @@ data DiagnosticLevel type instance CRepr DiagnosticLevel = CInt instance Send DiagnosticLevel where - send = - pure . \case - NoDiagnostics -> 0 - TimeAndLocation -> 1 - TimeLocationAndStats -> 2 + send = pure . \case + NoDiagnostics -> 0 + TimeAndLocation -> 1 + TimeLocationAndStats -> 2 -- | An exception thrown when the simulator could not perform a control action. -- @@ -111,19 +103,17 @@ foreign import ccall "vpi_user.h vpi_control" -- restarted or reset. If the simulator does not accept the control action, a -- 'CouldNotControl' exception is thrown. -- -controlSimulator :: forall o. Control -> SimCont o () +controlSimulator :: Control -> IO () controlSimulator control = do (c, s, r, d) <- send control - success <- - case control of - Reset{} -> IO.liftIO (c_vpi_control_restart c s r d) - _ -> IO.liftIO (c_vpi_control_end c d) + success <- case control of + Reset{} -> c_vpi_control_restart c s r d + _ -> c_vpi_control_end c d Monad.unless success $ - Sim.throw (CouldNotControl control callStack) + throwIO $ CouldNotControl control callStack - pure () #else () where #endif diff --git a/clash-ffi/src/Clash/FFI/VPI/Error.hs b/clash-ffi/src/Clash/FFI/VPI/Error.hs index 4ef5316e59..ecd815c691 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Error.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Error.hs @@ -15,6 +15,7 @@ module Clash.FFI.VPI.Error ( CErrorInfo(..) , ErrorInfo(..) , getErrorInfo + , withErrorInfo , receiveErrorLevel , receiveErrorInfo , unsafeReceiveErrorInfo @@ -22,18 +23,15 @@ module Clash.FFI.VPI.Error , module Clash.FFI.VPI.Error.State ) where -import qualified Control.Monad.IO.Class as IO (liftIO) import Data.ByteString (ByteString) -import Data.Typeable (Typeable) import Foreign.C.String (CString) import Foreign.C.Types (CInt(..)) +import qualified Foreign.Marshal.Alloc as FFI (alloca, malloc) import Foreign.Ptr (Ptr) import qualified Foreign.Ptr as FFI (nullPtr) import Foreign.Storable.Generic (GStorable) import GHC.Generics (Generic) -import Clash.FFI.Monad (SimCont) -import qualified Clash.FFI.Monad as Sim (stackPtr, withNewPtr) import Clash.FFI.View import Clash.FFI.VPI.Error.Level import Clash.FFI.VPI.Error.State @@ -73,44 +71,51 @@ data ErrorInfo = ErrorInfo type instance CRepr ErrorInfo = CErrorInfo instance UnsafeReceive ErrorInfo where - unsafeReceive cerror = do - state <- receive (cerrorState cerror) - level <- receive (cerrorLevel cerror) - msg <- unsafeReceive (cerrorMessage cerror) - prod <- unsafeReceive (cerrorProduct cerror) - code <- unsafeReceive (cerrorCode cerror) - file <- receiveString (cerrorFile cerror) - let line = fromIntegral (cerrorLine cerror) + unsafeReceive CErrorInfo{..} = do + errorState <- receive cerrorState + errorLevel <- receive cerrorLevel + errorMessage <- unsafeReceive cerrorMessage + errorProduct <- unsafeReceive cerrorProduct + errorCode <- unsafeReceive cerrorCode + errorFile <- receiveString cerrorFile + let errorLine = fromIntegral cerrorLine - pure (ErrorInfo state level msg prod code file line) + pure ErrorInfo{..} instance Receive ErrorInfo where - receive cerror = do - state <- receive (cerrorState cerror) - level <- receive (cerrorLevel cerror) - msg <- receive (cerrorMessage cerror) - prod <- receive (cerrorProduct cerror) - code <- receive (cerrorCode cerror) - file <- receiveString (cerrorFile cerror) - let line = fromIntegral (cerrorLine cerror) + receive CErrorInfo{..} = do + errorState <- receive cerrorState + errorLevel <- receive cerrorLevel + errorMessage <- receive cerrorMessage + errorProduct <- receive cerrorProduct + errorCode <- receive cerrorCode + errorFile <- receiveString cerrorFile + let errorLine = fromIntegral cerrorLine - pure (ErrorInfo state level msg prod code file line) + pure ErrorInfo{..} foreign import ccall "vpi_user.h vpi_chk_error" c_vpi_chk_error :: Ptr CErrorInfo -> IO CInt --- | Get the low-level representation of the current error information. This --- can be converted to the high-level representation using 'Receive'. If only --- the high-level representation is needed then consider using --- 'receiveErrorInfo' or 'unsafeReceiveErrorInfo' instead. +-- | Get the low-level representation of the current error information, which +-- is allocated on the heap. This can be converted to the high-level +-- representation using 'Receive'. If only the high-level representation is +-- needed then consider using 'receiveErrorInfo' or 'unsafeReceiveErrorInfo' +-- instead. -- -getErrorInfo - :: forall o - . Typeable o - => SimCont o (Ptr CErrorInfo) - -> SimCont o (Ptr CErrorInfo) -getErrorInfo alloc = - fst <$> Sim.withNewPtr alloc c_vpi_chk_error +getErrorInfo :: IO (Ptr CErrorInfo) +getErrorInfo = + FFI.malloc >>= \ptr -> c_vpi_chk_error ptr >> return ptr + +-- | Get the low-level representation of the current error information, which +-- is allocated on the stack. This can be converted to the high-level +-- representation using 'Receive'. If only the high-level representation is +-- needed then consider using 'receiveErrorInfo' or 'unsafeReceiveErrorInfo' +-- instead. +-- +withErrorInfo :: (Ptr CErrorInfo -> IO a) -> IO a +withErrorInfo f = + FFI.alloca $ \ptr -> c_vpi_chk_error ptr >> f ptr -- | Get the high-level representation of the current error information. The -- value is unsafely read, meaning it may be corrupted if the low-level @@ -121,12 +126,9 @@ getErrorInfo alloc = -- -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- -unsafeReceiveErrorInfo - :: forall o - . Typeable o - => SimCont o ErrorInfo +unsafeReceiveErrorInfo :: IO ErrorInfo unsafeReceiveErrorInfo = - getErrorInfo Sim.stackPtr >>= unsafePeekReceive + withErrorInfo unsafePeekReceive -- | Get the high-level representation of the current error information. The -- value is safely read, meaning it will not become corrupted if the low-level @@ -134,21 +136,15 @@ unsafeReceiveErrorInfo = -- -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- -receiveErrorInfo - :: forall o - . Typeable o - => SimCont o ErrorInfo +receiveErrorInfo :: IO ErrorInfo receiveErrorInfo = - getErrorInfo Sim.stackPtr >>= peekReceive + getErrorInfo >>= peekReceive -- | Get the error level of the current error information. For more complete -- error information, use 'receiveErrorInfo' or 'unsafeReceiveErrorInfo' for -- the high-level representation, or 'getErrorInfo' for the low-level -- representation. -- -receiveErrorLevel - :: forall o - . Typeable o - => SimCont o ErrorLevel +receiveErrorLevel :: IO ErrorLevel receiveErrorLevel = - IO.liftIO (c_vpi_chk_error FFI.nullPtr) >>= receive + c_vpi_chk_error FFI.nullPtr >>= receive diff --git a/clash-ffi/src/Clash/FFI/VPI/Error/Level.hs b/clash-ffi/src/Clash/FFI/VPI/Error/Level.hs index dd3ebcc788..d779b57241 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Error/Level.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Error/Level.hs @@ -4,7 +4,6 @@ License: BSD2 (see the file LICENSE) Maintainer: QBayLogic B.V. -} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} module Clash.FFI.VPI.Error.Level @@ -12,11 +11,10 @@ module Clash.FFI.VPI.Error.Level , UnknownErrorLevel(..) ) where -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import Foreign.C.Types (CInt) import GHC.Stack (CallStack, callStack, prettyCallStack) -import qualified Clash.FFI.Monad as Sim (throw) import Clash.FFI.View (CRepr, Receive(..)) -- | The level, or severity of an error returned by a call to @vpi_chk_error@. @@ -40,8 +38,8 @@ data UnknownErrorLevel deriving anyclass (Exception) instance Show UnknownErrorLevel where - show (UnknownErrorLevel x c) = - mconcat + show = \case + UnknownErrorLevel x c -> mconcat [ "Unknown error level: " , show x , "\n" @@ -58,4 +56,4 @@ instance Receive ErrorLevel where 3 -> pure Error 4 -> pure System 5 -> pure Internal - n -> Sim.throw (UnknownErrorLevel n callStack) + n -> throwIO $ UnknownErrorLevel n callStack diff --git a/clash-ffi/src/Clash/FFI/VPI/Error/State.hs b/clash-ffi/src/Clash/FFI/VPI/Error/State.hs index a97e9acd0f..148a869898 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Error/State.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Error/State.hs @@ -4,7 +4,6 @@ License: BSD2 (see the file LICENSE) Maintainer: QBayLogic B.V. -} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} module Clash.FFI.VPI.Error.State @@ -12,11 +11,10 @@ module Clash.FFI.VPI.Error.State , UnknownErrorState(..) ) where -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import Foreign.C.Types (CInt) import GHC.Stack (CallStack, callStack, prettyCallStack) -import qualified Clash.FFI.Monad as Sim (throw) import Clash.FFI.View (CRepr, Receive(..)) -- | The state of the simulator when an error occurred. This specifies whether @@ -37,8 +35,8 @@ data UnknownErrorState deriving anyclass (Exception) instance Show UnknownErrorState where - show (UnknownErrorState x c) = - mconcat + show = \case + UnknownErrorState x c -> mconcat [ "Unknown error state: " , show x , "\n" @@ -52,4 +50,4 @@ instance Receive ErrorState where 1 -> pure CompileError 2 -> pure PliError 3 -> pure RunError - n -> Sim.throw (UnknownErrorState n callStack) + n -> throwIO $ UnknownErrorState n callStack diff --git a/clash-ffi/src/Clash/FFI/VPI/IO.hs b/clash-ffi/src/Clash/FFI/VPI/IO.hs index f761da7882..b456fb5787 100644 --- a/clash-ffi/src/Clash/FFI/VPI/IO.hs +++ b/clash-ffi/src/Clash/FFI/VPI/IO.hs @@ -10,18 +10,14 @@ module Clash.FFI.VPI.IO , simFlushIO ) where -import Control.Exception (Exception) -import Control.Monad ((>=>)) +import Control.Exception (Exception, throwIO) import qualified Control.Monad as Monad (void, when) -import qualified Control.Monad.IO.Class as IO (liftIO) import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BS (snoc) import Foreign.C.String (CString) import Foreign.C.Types (CInt(..)) import GHC.Stack (CallStack, HasCallStack, callStack, prettyCallStack) -import Clash.FFI.Monad (SimCont) -import qualified Clash.FFI.Monad as Sim (throw) import Clash.FFI.View (unsafeSend, ensureNullTerminated) foreign import ccall "vpi_user.h vpi_printf" @@ -32,18 +28,18 @@ foreign import ccall "vpi_user.h vpi_printf" -- not output anything in some simulators, but this function will. -- simPutStr - :: forall o - . HasCallStack + :: HasCallStack => ByteString - -> SimCont o () -simPutStr = - (unsafeSend >=> IO.liftIO . Monad.void . c_vpi_printf) . ensureNullTerminated + -> IO () +simPutStr bs = + unsafeSend (ensureNullTerminated bs) + $ Monad.void . c_vpi_printf -- | A version of 'putStrLn' which outputs to the handle used by the simulator. -- When running a VPI callback, the normal functions provided in @base@ may -- not output anything in some simulators, but this function will. -- -simPutStrLn :: HasCallStack => ByteString -> SimCont o () +simPutStrLn :: HasCallStack => ByteString -> IO () simPutStrLn = simPutStr . (`BS.snoc` '\n') @@ -63,11 +59,9 @@ foreign import ccall "vpi_user.h vpi_flush" -- | Flush the IO output buffer controlled by the simulator. -- -simFlushIO :: HasCallStack => SimCont o () +simFlushIO :: HasCallStack => IO () simFlushIO = do - failed <- IO.liftIO c_vpi_flush + failed <- c_vpi_flush Monad.when failed $ - Sim.throw (CouldNotFlushIO callStack) - - pure () + throwIO $ CouldNotFlushIO callStack diff --git a/clash-ffi/src/Clash/FFI/VPI/Info.hs b/clash-ffi/src/Clash/FFI/VPI/Info.hs index d6106d4024..46823bf7b0 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Info.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Info.hs @@ -5,7 +5,6 @@ Maintainer: QBayLogic B.V. -} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE RecordWildCards #-} -- Used to improve the performance of derived instances. {-# OPTIONS_GHC -fplugin=Foreign.Storable.Generic.Plugin #-} @@ -16,25 +15,23 @@ module Clash.FFI.VPI.Info , Info(..) , CouldNotGetInfo(..) , getInfo + , withInfo , receiveSimulatorInfo , unsafeReceiveSimulatorInfo ) where -import Control.Exception (Exception) -import qualified Control.Exception as IO (throwIO) +import Control.Exception (Exception, throwIO) import qualified Control.Monad as Monad (unless) import Data.ByteString (ByteString) -import Data.Typeable (Typeable) import Foreign.C.String (CString) import Foreign.C.Types (CInt(..)) +import qualified Foreign.Marshal.Alloc as FFI (alloca, malloc) import Foreign.Ptr (Ptr) import qualified Foreign.Ptr as FFI (nullPtr) import Foreign.Storable.Generic (GStorable) import GHC.Generics (Generic) import GHC.Stack (CallStack, HasCallStack, callStack, prettyCallStack) -import Clash.FFI.Monad (SimCont) -import qualified Clash.FFI.Monad as Sim (stackPtr, withNewPtr) import Clash.FFI.View -- | The low-level representation of the VPI information structure, as returned @@ -68,54 +65,69 @@ instance UnsafeReceive Info where unsafeReceive CInfo{..} = do -- When passing +RTS to some simulators, they may replace the whole -- argument with NULL, so we check in addition to argc. - args <- unsafeReceiveArray0 (fromEnum cinfoArgc) FFI.nullPtr cinfoArgv - prod <- unsafeReceive cinfoProduct - ver <- unsafeReceive cinfoVersion + infoArgs <- unsafeReceiveArray0 (fromEnum cinfoArgc) FFI.nullPtr cinfoArgv + infoProduct <- unsafeReceive cinfoProduct + infoVersion <- unsafeReceive cinfoVersion - pure (Info args prod ver) + pure Info{..} instance Receive Info where receive CInfo{..} = do - args <- receiveArray0 (fromEnum cinfoArgc) FFI.nullPtr cinfoArgv - prod <- receive cinfoProduct - ver <- receive cinfoVersion + infoArgs <- receiveArray0 (fromEnum cinfoArgc) FFI.nullPtr cinfoArgv + infoProduct <- receive cinfoProduct + infoVersion <- receive cinfoVersion - pure (Info args prod ver) + pure Info{..} foreign import ccall "vpi_user.h vpi_get_vlog_info" c_vpi_get_vlog_info :: Ptr CInfo -> IO Bool -- | An exception thrown when the VPI call to get the simulator info fails. -- -data CouldNotGetInfo +newtype CouldNotGetInfo = CouldNotGetInfo CallStack deriving anyclass (Exception) instance Show CouldNotGetInfo where - show (CouldNotGetInfo c) = - mconcat + show = \case + CouldNotGetInfo c -> mconcat [ "Could not identify the running simulator\n" , prettyCallStack c ] --- | Get the low-level representation of the simulator information. This can be --- converted to the high-level representation using 'Receive'. If only the --- high-level representation is needed then consider using --- 'receiveSimulatorInfo' or 'unsafeReceiveSimulatorInfo' instead. +-- | Get the low-level representation of the simulator information, which is +-- allocated on the heap. This can be converted to the high-level representation +-- using 'Receive'. If only the high-level representation is needed then consider +-- using 'receiveSimulatorInfo' or 'unsafeReceiveSimulatorInfo' instead. -- getInfo - :: forall o - . HasCallStack - => SimCont o (Ptr CInfo) - -> SimCont o (Ptr CInfo) -getInfo alloc = - fmap fst . Sim.withNewPtr alloc $ \ptr -> do + :: HasCallStack + => IO (Ptr CInfo) +getInfo = do + ptr <- FFI.malloc + isSuccess <- c_vpi_get_vlog_info ptr + + Monad.unless isSuccess $ + throwIO $ CouldNotGetInfo callStack + + return ptr + +-- | Get the low-level representation of the simulator information, which is +-- allocated on the stack. This can be converted to the high-level representation +-- using 'Receive'. If only the high-level representation is needed then consider +-- using 'receiveSimulatorInfo' or 'unsafeReceiveSimulatorInfo' instead. +-- +withInfo + :: HasCallStack + => (Ptr CInfo -> IO a) -> IO a +withInfo f = + FFI.alloca $ \ptr -> do isSuccess <- c_vpi_get_vlog_info ptr Monad.unless isSuccess $ - IO.throwIO (CouldNotGetInfo callStack) + throwIO $ CouldNotGetInfo callStack - pure isSuccess + f ptr -- | Get the high-level representation of the simulator information. The value -- is unsafely read, meaning it may be corrupted if the low-level @@ -127,12 +139,10 @@ getInfo alloc = -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- unsafeReceiveSimulatorInfo - :: forall o - . HasCallStack - => Typeable o - => SimCont o Info + :: HasCallStack + => IO Info unsafeReceiveSimulatorInfo = - getInfo Sim.stackPtr >>= unsafePeekReceive + withInfo unsafePeekReceive -- | Get the high-level representation of the simulator information. The value -- is safely read, meaning it will not become corrupted if the low-level @@ -141,9 +151,7 @@ unsafeReceiveSimulatorInfo = -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- receiveSimulatorInfo - :: forall o - . HasCallStack - => Typeable o - => SimCont o Info + :: HasCallStack + => IO Info receiveSimulatorInfo = - getInfo Sim.stackPtr >>= peekReceive + getInfo >>= peekReceive diff --git a/clash-ffi/src/Clash/FFI/VPI/Iterator.hs b/clash-ffi/src/Clash/FFI/VPI/Iterator.hs index ed8a56e0c5..d9a5a7dfbd 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Iterator.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Iterator.hs @@ -16,12 +16,10 @@ module Clash.FFI.VPI.Iterator import Prelude hiding (iterate) import Control.DeepSeq (NFData, deepseq) -import qualified Control.Monad.IO.Class as IO (liftIO) import Data.Coerce import Foreign.C.Types (CInt(..)) import Foreign.Storable (Storable) -import Clash.FFI.Monad (SimCont) import Clash.FFI.View import Clash.FFI.VPI.Object @@ -53,16 +51,15 @@ foreign import ccall "vpi_user.h vpi_iterate" -- manually freed with 'freeObject'. -- iterate - :: forall p o + :: forall p . Coercible p Object => ObjectType -> Maybe p - -> SimCont o Iterator + -> IO Iterator iterate objTy parent = do cobjTy <- send objTy let object = maybe nullObject coerce parent - - IO.liftIO (c_vpi_iterate cobjTy object) + c_vpi_iterate cobjTy object foreign import ccall "vpi_user.h vpi_scan" c_vpi_scan :: Iterator -> IO Object @@ -72,18 +69,19 @@ foreign import ccall "vpi_user.h vpi_scan" -- (and does not need 'freeObject' to be called on it). -- scan - :: forall c o + :: forall c . IsObject c => Coercible Object c => Iterator - -> SimCont o (Maybe c) + -> IO (Maybe c) scan iterator - | isNullObject (iteratorObject iterator) - = pure Nothing - - | otherwise - = do next <- IO.liftIO (c_vpi_scan iterator) - pure (if isNullObject next then Nothing else Just (coerce next)) + | isNullObject (iteratorObject iterator) = pure Nothing + | otherwise = do + next <- c_vpi_scan iterator + pure $ + if isNullObject next + then Nothing + else Just $ coerce next -- | Create an iterator for objects of a given type under the specified parent -- object, and completely traverse the iterator using repeated calls to 'scan'. @@ -91,14 +89,14 @@ scan iterator -- offers less control over iteration. -- iterateAll - :: forall c p o + :: forall c p . IsObject c => NFData c => Coercible Object c => Coercible p Object => ObjectType -> Maybe p - -> SimCont o [c] + -> IO [c] iterateAll objTy parent = do iterator <- iterate objTy parent items <- takeWhileNonNull iterator @@ -113,5 +111,5 @@ iterateAll objTy parent = do scanned <- scan iterator case scanned of - Just next -> fmap (next :) (takeWhileNonNull iterator) + Just next -> (next :) <$> takeWhileNonNull iterator Nothing -> pure [] diff --git a/clash-ffi/src/Clash/FFI/VPI/Module.hs b/clash-ffi/src/Clash/FFI/VPI/Module.hs index d016241964..579d991536 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Module.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Module.hs @@ -20,7 +20,6 @@ import Foreign.Storable (Storable) import GHC.Stack (HasCallStack) import Clash.FFI.View (ensureNullTerminated) -import Clash.FFI.Monad (SimCont) import Clash.FFI.VPI.Iterator import Clash.FFI.VPI.Object import Clash.FFI.VPI.Net (Net(..)) @@ -43,13 +42,13 @@ newtype Module -- | Iterate the top-level of a design, finding all the modules. -- -topModules :: HasCallStack => SimCont o [Module] +topModules :: HasCallStack => IO [Module] topModules = iterateAll @_ @Object ObjModule Nothing -- | Find a top-level module in a design by name. This throws an 'UnknownChild' -- exception if no top-level module with the given name is found in the design. -- -findTopModule :: HasCallStack => ByteString -> SimCont o Module +findTopModule :: HasCallStack => ByteString -> IO Module findTopModule name = unsafeSendChildRef @_ @Object (ensureNullTerminated name) Nothing @@ -57,26 +56,26 @@ findTopModule name = -- large designs it may be more efficient to use -- 'Clash.FFI.VPI.Iterator.iterate' and 'scan' manually. -- -moduleNets :: HasCallStack => Module -> SimCont o [Net] +moduleNets :: HasCallStack => Module -> IO [Net] moduleNets = iterateAll ObjNet . Just -- | Iterate all the parameters in a module. This will iterate all nets at -- once, for large designs it may be more efficient to use -- 'Clash.FFI.VPI.Iterator.iterate' and 'scan' manually. -- -moduleParameters :: HasCallStack => Module -> SimCont o [Parameter] +moduleParameters :: HasCallStack => Module -> IO [Parameter] moduleParameters = iterateAll ObjParameter . Just -- | Iterate all the ports in a module. This will iterate all nets at once, for -- large designs it may be more efficient to use -- 'Clash.FFI.VPI.Iterator.iterate' and 'scan' manually. -- -modulePorts :: HasCallStack => Module -> SimCont o [Port] +modulePorts :: HasCallStack => Module -> IO [Port] modulePorts = iterateAll ObjPort . Just -- | Iterate all the registers in a module. This will iterate all nets at once, -- for large designs it may be more efficient to use -- 'Clash.FFI.VPI.Iterator.iterate' and 'scan' manually. -- -moduleRegs :: HasCallStack => Module -> SimCont o [Reg] +moduleRegs :: HasCallStack => Module -> IO [Reg] moduleRegs = iterateAll ObjReg . Just diff --git a/clash-ffi/src/Clash/FFI/VPI/Object.hs b/clash-ffi/src/Clash/FFI/VPI/Object.hs index 83c23899d5..ac12238385 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Object.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Object.hs @@ -9,7 +9,6 @@ Maintainer: QBayLogic B.V. {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeSynonymInstances #-} module Clash.FFI.VPI.Object ( Object(..) @@ -27,10 +26,12 @@ module Clash.FFI.VPI.Object -- * Time , module Clash.FFI.VPI.Object.Time , getTime + , withTime , receiveTime -- * Value , module Clash.FFI.VPI.Object.Value , getValue + , withValue , receiveValue , unsafeReceiveValue , sendValue @@ -38,9 +39,8 @@ module Clash.FFI.VPI.Object ) where import Control.DeepSeq (NFData) -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import qualified Control.Monad as Monad (unless, void, when) -import qualified Control.Monad.IO.Class as IO (liftIO) import Data.Coerce #if defined(SYSTEMVERILOG) @@ -51,6 +51,7 @@ import qualified Data.List as List (genericLength) import Data.Typeable (Typeable) import Foreign.C.String (CString) import Foreign.C.Types (CInt(..)) +import qualified Foreign.Marshal.Alloc as FFI (alloca, malloc) import qualified Foreign.Marshal.Utils as FFI (toBool) import Foreign.Ptr (Ptr) import qualified Foreign.Ptr as FFI (castPtr, nullPtr) @@ -58,8 +59,6 @@ import Foreign.Storable (Storable) import qualified Foreign.Storable as FFI import GHC.Stack (CallStack, HasCallStack, callStack, prettyCallStack) -import Clash.FFI.Monad (SimCont) -import qualified Clash.FFI.Monad as Sim (heapPtr, stackPtr, throw, withNewPtr) import Clash.FFI.View import Clash.FFI.VPI.Object.Property import Clash.FFI.VPI.Object.Time @@ -119,13 +118,13 @@ class IsObject a where -- | Deallocate the object. The object should not be used for any calls after -- this is called, as the object is no longer valid. -- - freeObject :: a -> SimCont o () + freeObject :: a -> IO () -- | Equality on VPI objects. This function is not pure, as the current -- assignment of identifiers to objects in the simulator may change over time -- (so a deallocated object's identifier may be used for a new object). -- - compareObjects :: a -> a -> SimCont o Bool + compareObjects :: a -> a -> IO Bool instance IsObject Object where nullObject = Object FFI.nullPtr @@ -135,7 +134,6 @@ instance IsObject Object where freeObject obj = Monad.unless (isNullObject obj) - . IO.liftIO . Monad.void #if defined(VERILOG) $ c_vpi_free_object obj @@ -145,8 +143,8 @@ instance IsObject Object where #error "Neither VERILOG or SYSTEMVERILOG is defined in VPI implementation" #endif - compareObjects x y = - IO.liftIO (c_vpi_compare_objects x y) + compareObjects = + c_vpi_compare_objects {- NOTE [use of Coercible in public API] @@ -189,7 +187,7 @@ class IsChildRef i where -- an 'UnknownChild' exception is thrown. -- getChild - :: forall a b o + :: forall a b . HasCallStack => Coercible a Object => Show a @@ -197,7 +195,7 @@ class IsChildRef i where => Coercible Object b => i -> Maybe a - -> SimCont o b + -> IO b -- | An exception thrown when attempting to access a child object using a -- reference of type @i@ which does not exist under the parent object @a@. @@ -207,8 +205,8 @@ data UnknownChild i a deriving anyclass (Exception) instance (Show i, Show a) => Show (UnknownChild i a) where - show (UnknownChild i a c) = - mconcat + show = \case + UnknownChild i a c -> mconcat [ "Unknown child " , show i , " for object " @@ -224,12 +222,12 @@ instance IsChildRef ObjectType where getChild objTy mParent = do cobjTy <- send objTy let parent = maybe nullObject coerce mParent - child <- IO.liftIO (c_vpi_handle cobjTy parent) + child <- c_vpi_handle cobjTy parent Monad.when (isNullObject child) $ - Sim.throw (UnknownChild objTy parent callStack) + throwIO $ UnknownChild objTy parent callStack - pure (coerce child) + pure $ coerce child foreign import ccall "vpi_user.h vpi_handle_by_name" c_vpi_handle_by_name :: CString -> Object -> IO Object @@ -237,12 +235,12 @@ foreign import ccall "vpi_user.h vpi_handle_by_name" instance IsChildRef CString where getChild str mParent = do let parent = maybe nullObject coerce mParent - child <- IO.liftIO (c_vpi_handle_by_name str parent) + child <- c_vpi_handle_by_name str parent Monad.when (isNullObject child) $ - Sim.throw (UnknownChild str parent callStack) + throwIO $ UnknownChild str parent callStack - pure (coerce child) + pure $ coerce child foreign import ccall "vpi_user.h vpi_handle_by_index" c_vpi_handle_by_index :: Object -> CInt -> IO Object @@ -250,12 +248,12 @@ foreign import ccall "vpi_user.h vpi_handle_by_index" instance IsChildRef CInt where getChild ix mParent = do let parent = maybe nullObject coerce mParent - child <- IO.liftIO (c_vpi_handle_by_index parent ix) + child <- c_vpi_handle_by_index parent ix Monad.when (isNullObject child) $ - Sim.throw (UnknownChild ix parent callStack) + throwIO $ UnknownChild ix parent callStack - pure (coerce child) + pure $ coerce child foreign import ccall "vpi_user.h vpi_handle_by_multi_index" c_vpi_handle_by_multi_index :: Object -> CInt -> Ptr CInt -> IO Object @@ -263,14 +261,14 @@ foreign import ccall "vpi_user.h vpi_handle_by_multi_index" instance IsChildRef [CInt] where getChild ixs mParent = do let len = List.genericLength ixs - ptr <- unsafeSend ixs - let parent = maybe nullObject coerce mParent - child <- IO.liftIO (c_vpi_handle_by_multi_index parent len ptr) + unsafeSend ixs $ \ptr -> do + let parent = maybe nullObject coerce mParent + child <- c_vpi_handle_by_multi_index parent len ptr - Monad.when (isNullObject child) $ - Sim.throw (UnknownChild ixs parent callStack) + Monad.when (isNullObject child) $ + throwIO $ UnknownChild ixs parent callStack - pure (coerce child) + pure $ coerce child -- | Get a child object by a reference of type @i@ from an optional parent -- object of type @a@. The reference given is a high-level representation which @@ -284,7 +282,7 @@ instance IsChildRef [CInt] where -- For more information about safety, see 'Send' and 'UnsafeSend'. -- unsafeSendChildRef - :: forall i a b o + :: forall i a b . HasCallStack => UnsafeSend i => IsChildRef (CRepr i) @@ -294,9 +292,9 @@ unsafeSendChildRef => Coercible Object b => i -> Maybe a - -> SimCont o b + -> IO b unsafeSendChildRef ref parent = - unsafeSend ref >>= (`getChild` parent) + unsafeSend ref (`getChild` parent) -- | Get a child object by reference of type @i@ from an optional parent object -- of type @a@. The reference given is a high-level representation which is @@ -309,7 +307,7 @@ unsafeSendChildRef ref parent = -- For more information about safety, see 'Send' and 'UnsafeSend'. -- sendChildRef - :: forall i a b o + :: forall i a b . HasCallStack => Send i => IsChildRef (CRepr i) @@ -319,7 +317,7 @@ sendChildRef => Coercible Object b => i -> Maybe a - -> SimCont o b + -> IO b sendChildRef ref parent = send ref >>= (`getChild` parent) @@ -382,7 +380,7 @@ class IsProperty p where => Typeable a => Property p -> a - -> SimCont o p + -> IO p foreign import ccall "vpi_user.h vpi_get" c_vpi_get :: CInt -> Object -> IO CInt @@ -390,22 +388,22 @@ foreign import ccall "vpi_user.h vpi_get" instance IsProperty CInt where getProperty prop object = do cprop <- send prop - value <- IO.liftIO (c_vpi_get cprop (coerce object)) + value <- c_vpi_get cprop $ coerce object Monad.when (value == -1) $ - Sim.throw (InvalidProperty prop object callStack) + throwIO $ InvalidProperty prop object callStack pure value instance IsProperty Bool where getProperty prop object = do cprop <- send prop - value <- IO.liftIO (c_vpi_get cprop (coerce object)) + value <- c_vpi_get cprop $ coerce object Monad.when (value == -1) $ - Sim.throw (InvalidProperty prop object callStack) + throwIO $ InvalidProperty prop object callStack - pure (FFI.toBool value) + pure $ FFI.toBool value #if defined(SYSTEMVERILOG) foreign import ccall "vpi_user.h vpi_get64" @@ -414,10 +412,10 @@ foreign import ccall "vpi_user.h vpi_get64" instance IsProperty Int64 where getProperty prop object = do cprop <- send prop - value <- IO.liftIO (c_vpi_get64 cprop (coerce object)) + value <- c_vpi_get64 cprop $ coerce object Monad.when (value == -1) $ - Sim.throw (InvalidProperty prop object callStack) + throwIO $ InvalidProperty prop object callStack pure value #endif @@ -428,10 +426,10 @@ foreign import ccall "vpi_user.h vpi_get_str" instance IsProperty CString where getProperty prop object = do cprop <- send prop - value <- IO.liftIO (c_vpi_get_str cprop (coerce object)) + value <- c_vpi_get_str cprop $ coerce object Monad.when (value == FFI.nullPtr) $ - Sim.throw (InvalidProperty prop object callStack) + throwIO $ InvalidProperty prop object callStack pure value @@ -443,17 +441,16 @@ instance IsProperty CString where -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- receiveProperty - :: forall p a o + :: forall p a . HasCallStack => Receive p => IsProperty (CRepr p) => Coercible a Object => Show a => Typeable a - => Typeable o => Property (CRepr p) -> a - -> SimCont o p + -> IO p receiveProperty prop object = getProperty prop object >>= receive @@ -461,35 +458,66 @@ foreign import ccall "vpi_user.h vpi_get_time" c_vpi_get_time :: Object -> Ptr CTime -> IO () -- | Get a pointer to the low-level representation of the current simulation --- time. The pointer is allocated using the given allocation function. The time --- returned is given in the specified format, and is either the global --- simulation time or the time of a particular object in the simulation. +-- time, where the pointer is allocated on the heap. The time returned is +-- given in the specified format, and is either the global simulation time or +-- the time of a particular object in the simulation. -- -- The 'SuppressTime' format cannot be used for this function. Requesting this -- as the format will throw an 'InvalidTimeType' exception. -- -- The retuned value can be converted to the high-level representation using -- 'Receive'. If only the high-level representation is needed then consider --- using 'receiveTime' or 'unsafeReceiveTime' instead. +-- using 'receiveTime' instead. -- getTime - :: forall a o + :: forall a . HasCallStack => Coercible a Object - => SimCont o (Ptr CTime) - -> TimeType + => TimeType -> Maybe a - -> SimCont o (Ptr CTime) -getTime alloc ty mObject = do + -> IO (Ptr CTime) +getTime ty mObject = do Monad.when (ty == SuppressTime) $ - Sim.throw (InvalidTimeType ty callStack) + throwIO $ InvalidTimeType ty callStack cty <- send ty - fmap fst . Sim.withNewPtr alloc $ \ptr -> do + FFI.malloc >>= \ptr -> do let object = maybe nullObject coerce mObject - FFI.poke ptr (CTime cty 0 0 0.0) + FFI.poke ptr $ CTime cty 0 0 0.0 c_vpi_get_time object ptr + return ptr + +-- | Get a pointer to the low-level representation of the current simulation +-- time, where the pointer is allocated on the stack. The time returned is +-- given in the specified format, and is either the global simulation time or +-- the time of a particular object in the simulation. +-- +-- The 'SuppressTime' format cannot be used for this function. Requesting this +-- as the format will throw an 'InvalidTimeType' exception. +-- +-- The retuned value can be converted to the high-level representation using +-- 'Receive'. If only the high-level representation is needed then consider +-- using 'receiveTime' instead. +-- +withTime + :: forall a b + . HasCallStack + => Coercible a Object + => TimeType + -> Maybe a + -> (Ptr CTime -> IO b) -> IO b +withTime ty mObject f = do + Monad.when (ty == SuppressTime) $ + throwIO $ InvalidTimeType ty callStack + + cty <- send ty + + FFI.alloca $ \ptr -> do + let object = maybe nullObject coerce mObject + FFI.poke ptr $ CTime cty 0 0 0.0 + c_vpi_get_time object ptr + f ptr -- | Get the high-level representation of the current simulation time. The -- value is safely read, meaning it will not become corrupted if the low-level @@ -501,22 +529,21 @@ getTime alloc ty mObject = do -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- receiveTime - :: forall a o + :: forall a . HasCallStack => Coercible a Object - => Typeable o => TimeType -> Maybe a - -> SimCont o Time + -> IO Time receiveTime timeTy mObject = - getTime Sim.stackPtr timeTy mObject >>= peekReceive + withTime timeTy mObject peekReceive foreign import ccall "vpi_user.h vpi_get_value" c_vpi_get_value :: Object -> Ptr CValue -> IO () -- | Get a pointer to the low-level representation of the current value --- associated with the given object. The pointer is allocated using the given --- allocation function. The value is returned in the requested format. +-- associated with the given object, where the pointer is allocated on the +-- heap. The value is returned in the requested format. -- -- The 'SuppressValue' format cannot be used for this function. Requesting this -- as the format will throw an 'InvalidFormat' exception. @@ -528,18 +555,39 @@ foreign import ccall "vpi_user.h vpi_get_value" getValue :: HasCallStack => Coercible a Object - => SimCont o (Ptr CValue) - -> ValueFormat + => ValueFormat -> a - -> SimCont o (Ptr CValue) -getValue alloc fmt object = do + -> IO (Ptr CValue) +getValue fmt object = do cfmt <- send fmt - - fmap fst . Sim.withNewPtr alloc $ \ptr -> do + FFI.malloc >>= \ptr -> do FFI.pokeByteOff ptr 0 cfmt c_vpi_get_value (coerce object) ptr + return ptr - pure () +-- | Get a pointer to the low-level representation of the current value +-- associated with the given object, where the pointer is allocated on the +-- stack. The value is returned in the requested format. +-- +-- The 'SuppressValue' format cannot be used for this function. Requesting this +-- as the format will throw an 'InvalidFormat' exception. +-- +-- The returned value can be converted to the high-level representation using +-- 'Receive'. If only the high-level representation is needed then consider +-- using 'receiveValue' or 'unsafeReceiveValue' instead. +-- +withValue + :: HasCallStack + => Coercible a Object + => ValueFormat + -> a + -> (Ptr CValue -> IO b) -> IO b +withValue fmt object f = do + cfmt <- send fmt + FFI.alloca $ \ptr -> do + FFI.pokeByteOff ptr 0 cfmt + c_vpi_get_value (coerce object) ptr + f ptr -- | Get the high-level representation of the current value associated with the -- given object. The value is converted from a low-level representation with @@ -554,21 +602,19 @@ getValue alloc fmt object = do -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- unsafeReceiveValue - :: forall a o + :: forall a . HasCallStack => Coercible a Object => Show a => Typeable a - => Typeable o => ValueFormat -> a - -> SimCont o Value -unsafeReceiveValue fmt object = do - ptr <- getValue Sim.stackPtr fmt (coerce @a @Object object) - cvalue <- IO.liftIO (FFI.peek ptr) - size <- getProperty Size object - - unsafeReceive (CValueSized cvalue size) + -> IO Value +unsafeReceiveValue fmt object = + withValue fmt (coerce @a @Object object) $ \ptr -> do + cvalue <- FFI.peek ptr + size <- getProperty Size object + unsafeReceive $ CValueSized cvalue size -- | Get the high-level representation of the current value associated with the -- given object. The value is converted from a low-level representation with @@ -580,21 +626,19 @@ unsafeReceiveValue fmt object = do -- For more information about safety, see 'Receive' and 'UnsafeReceive'. -- receiveValue - :: forall a o + :: forall a . HasCallStack => Coercible a Object => Show a => Typeable a - => Typeable o => ValueFormat -> a - -> SimCont o Value + -> IO Value receiveValue fmt object = do - ptr <- getValue Sim.heapPtr fmt (coerce @a @Object object) - cvalue <- IO.liftIO (FFI.peek ptr) + ptr <- getValue fmt $ coerce @a @Object object + cvalue <- FFI.peek ptr size <- getProperty Size object - - receive (CValueSized cvalue size) + receive $ CValueSized cvalue size foreign import ccall "vpi_user.h vpi_put_value" c_vpi_put_value :: Object -> Ptr CValue -> Ptr CTime -> CInt -> IO Object @@ -629,15 +673,14 @@ unsafeSendValue => a -> Value -> DelayMode - -> SimCont o () + -> IO () -- No return object, see NOTE [vpi_put_value and events] -unsafeSendValue object value delay = do +unsafeSendValue object value delay = -- Safe use of castPtr to turn (CValue, CInt) into CValue - valuePtr <- FFI.castPtr <$> unsafePokeSend value - (timePtr, flags) <- send delay - - Monad.void . IO.liftIO $ - c_vpi_put_value (coerce object) valuePtr timePtr flags + unsafePokeSend value $ \ptr -> do + let valuePtr = FFI.castPtr ptr + (timePtr, flags) <- send delay + Monad.void $ c_vpi_put_value (coerce object) valuePtr timePtr flags -- | Update the value of the given object via the given 'DelayMode'. The value -- is safely sent, meaning the FFI call will succeed if the inputs to this @@ -655,12 +698,10 @@ sendValue => a -> Value -> DelayMode - -> SimCont o () + -> IO () -- No return object, see NOTE [vpi_put_value and events] sendValue object value delay = do -- Safe use of castPtr to turn (CValue, CInt) into CValue valuePtr <- FFI.castPtr <$> pokeSend value (timePtr, flags) <- send delay - - Monad.void . IO.liftIO $ - c_vpi_put_value (coerce object) valuePtr timePtr flags + Monad.void $ c_vpi_put_value (coerce object) valuePtr timePtr flags diff --git a/clash-ffi/src/Clash/FFI/VPI/Object/Time.hs b/clash-ffi/src/Clash/FFI/VPI/Object/Time.hs index 0d877bde5f..2d487ade4e 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Object/Time.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Object/Time.hs @@ -4,7 +4,6 @@ License: BSD2 (see the file LICENSE) Maintainer: QBayLogic B.V. -} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} -- Used to improve the performance of derived instances. @@ -19,7 +18,7 @@ module Clash.FFI.VPI.Object.Time , Time(..) ) where -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import Data.Bits ((.|.), (.&.), unsafeShiftL, unsafeShiftR) import Data.Int (Int64) import Foreign.C.Types (CDouble(..), CInt(..), CUInt(..)) @@ -27,7 +26,6 @@ import Foreign.Storable.Generic (GStorable) import GHC.Generics (Generic) import GHC.Stack (CallStack, callStack, prettyCallStack) -import qualified Clash.FFI.Monad as Sim (throw) import Clash.FFI.View -- | The low level representation of a VPI time value, as sent and received by @@ -69,8 +67,8 @@ data UnknownTimeType deriving anyclass (Exception) instance Show UnknownTimeType where - show (UnknownTimeType x c) = - mconcat + show = \case + UnknownTimeType x c -> mconcat [ "Unknown time type constant: " , show x , "\n" @@ -91,7 +89,7 @@ instance Receive TimeType where 1 -> pure ScaledReal 2 -> pure Sim 3 -> pure SuppressTime - n -> Sim.throw (UnknownTimeType n callStack) + n -> throwIO $ UnknownTimeType n callStack -- | A value of time, used as either the current point in time or a duration -- depending on the context. This represents time as either the number of units @@ -147,7 +145,7 @@ instance Receive Time where Sim -> let high = fromIntegral (ctimeHigh ctime) `unsafeShiftL` 32 low = fromIntegral (ctimeLow ctime) - in pure (SimTime (high .|. low)) + in pure $ SimTime (high .|. low) SuppressTime -> - Sim.throw (InvalidTimeType SuppressTime callStack) + throwIO $ InvalidTimeType SuppressTime callStack diff --git a/clash-ffi/src/Clash/FFI/VPI/Object/Type.hs b/clash-ffi/src/Clash/FFI/VPI/Object/Type.hs index 4607db7d1e..8e5e0abb40 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Object/Type.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Object/Type.hs @@ -5,7 +5,6 @@ Maintainer: QBayLogic B.V. -} {-# LANGUAGE CPP #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} module Clash.FFI.VPI.Object.Type @@ -13,11 +12,10 @@ module Clash.FFI.VPI.Object.Type , UnknownObjectType(..) ) where -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import Foreign.C.Types (CInt) import GHC.Stack (CallStack, callStack, prettyCallStack) -import qualified Clash.FFI.Monad as Sim (throw) import Clash.FFI.View -- | The type of the object according to the VPI specification. This can be @@ -59,8 +57,8 @@ data UnknownObjectType deriving anyclass (Exception) instance Show UnknownObjectType where - show (UnknownObjectType x c) = - mconcat + show = \case + UnknownObjectType x c -> mconcat [ "Unknown object type: " , show x , "\n" @@ -77,4 +75,4 @@ instance Receive ObjectType where #if defined(VERILOG_2001) 107 -> pure ObjCallback #endif - ty -> Sim.throw (UnknownObjectType ty callStack) + ty -> throwIO $ UnknownObjectType ty callStack diff --git a/clash-ffi/src/Clash/FFI/VPI/Object/Value.hs b/clash-ffi/src/Clash/FFI/VPI/Object/Value.hs index 8a68c122f7..6c5907f0e0 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Object/Value.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Object/Value.hs @@ -7,7 +7,6 @@ Maintainer: QBayLogic B.V. {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} -- Used to improve the performance of derived instances. @@ -32,6 +31,7 @@ import Data.ByteString (ByteString) import Foreign.C.String (CString) import Foreign.C.Types (CDouble, CInt(..)) import Foreign.Ptr (Ptr) +import qualified Foreign.Marshal.Alloc as FFI (alloca, malloc) import Foreign.Storable as FFI (Storable(..)) import Foreign.Storable.Generic (GStorable) import GHC.Generics (Generic) @@ -42,7 +42,6 @@ import Clash.Promoted.Nat (SNat(..), snatProxy, snatToNum) import Clash.Sized.BitVector (Bit, BitVector) import Clash.Sized.Signed (Signed) -import qualified Clash.FFI.Monad as Sim (heapPtr, stackPtr, withNewPtr) import Clash.FFI.View import Clash.FFI.VPI.Object.Time import Clash.FFI.VPI.Object.Value.Delay @@ -146,6 +145,7 @@ instance Storable CValue where CTimeVal time -> FFI.pokeByteOff ptr 0 TimeFmt *> FFI.pokeByteOff ptr 8 time +-- | A 'CValue' packed together with it's size. data CValueSized = CValueSized { cvsValue :: CValue , cvsSize :: CInt @@ -171,84 +171,85 @@ data Value where instance Show Value where show = \case - BitVal bit -> show bit + BitVal bit -> show bit BitVectorVal SNat bv -> show bv - IntVal int -> show int - RealVal real -> show real - StringVal _ str -> show str - TimeVal time -> show time + IntVal int -> show int + RealVal real -> show real + StringVal _ str -> show str + TimeVal time -> show time -- | A value is always sent with it's size, this is needed to properly decode -- some formats. type instance CRepr Value = CValueSized instance UnsafeSend Value where - unsafeSend = \case + unsafeSend v f = case v of BitVal bit -> do cvalue <- CScalarVal <$> send (bitToScalar bit) - pure (CValueSized cvalue 1) + f $ CValueSized cvalue 1 - BitVectorVal n@SNat bv -> do + BitVectorVal n@SNat bv -> #if defined(VERILOG_2005) && defined(VPI_VECVAL) - cvalue <- CVectorVal <$> unsafeSend bv - pure (CValueSized cvalue (snatToNum n)) + unsafeSend bv $ \x -> + f $ CValueSized (CVectorVal x) $ snatToNum n #else error "UnsafeSend.Value: BitVector without VPI_VECVAL" #endif IntVal int -> - pure (CValueSized (CIntVal (fromIntegral int)) 32) + f $ CValueSized (CIntVal (fromIntegral int)) 32 RealVal real -> #if defined(IVERILOG) - pure (CValueSized (CRealVal (realToFrac real)) 1) + f $ CValueSized (CRealVal (realToFrac real)) 1 #else - pure (CValueSized (CRealVal (realToFrac real)) 64) + f $ CValueSized (CRealVal (realToFrac real)) 64 #endif - StringVal size str -> do - cvalue <- CStringVal <$> unsafeSend (ensureNullTerminated str) - pure (CValueSized cvalue (snatToNum size)) + StringVal size str -> + unsafeSend (ensureNullTerminated str) $ \x -> + f $ CValueSized (CStringVal x) $ snatToNum size TimeVal time -> do ctime <- send @Time time - ptr <- fst <$> Sim.withNewPtr Sim.stackPtr (`FFI.poke` ctime) - - pure (CValueSized (CTimeVal ptr) 64) + FFI.alloca $ \ptr -> do + FFI.poke ptr ctime + f $ CValueSized (CTimeVal ptr) 64 instance Send Value where send = \case BitVal bit -> do cvalue <- CScalarVal <$> send (bitToScalar bit) - pure (CValueSized cvalue 1) + pure $ CValueSized cvalue 1 BitVectorVal n@SNat bv -> do #if defined(VERILOG_2005) && defined(VPI_VECVAL) cvalue <- CVectorVal <$> send bv - pure (CValueSized cvalue (snatToNum n)) + pure $ CValueSized cvalue $ snatToNum n #else error "Send.Value: BitVector without VPI_VECVAL" #endif IntVal int -> - pure (CValueSized (CIntVal (fromIntegral int)) 32) + pure $ CValueSized (CIntVal (fromIntegral int)) 32 RealVal real -> #if defined(IVERILOG) - pure (CValueSized (CRealVal (realToFrac real)) 1) + pure $ CValueSized (CRealVal (realToFrac real)) 1 #else - pure (CValueSized (CRealVal (realToFrac real)) 64) + pure $ CValueSized (CRealVal (realToFrac real)) 64 #endif StringVal size str -> do cvalue <- CStringVal <$> send (ensureNullTerminated str) - pure (CValueSized cvalue (snatToNum size)) + pure $ CValueSized cvalue $ snatToNum size TimeVal time -> do ctime <- send time - ptr <- fst <$> Sim.withNewPtr Sim.heapPtr (`FFI.poke` ctime) + ptr <- FFI.malloc + FFI.poke ptr ctime - pure (CValueSized (CTimeVal ptr) 64) + pure $ CValueSized (CTimeVal ptr) 64 instance UnsafeReceive Value where unsafeReceive (CValueSized cvalue size) = @@ -277,10 +278,10 @@ instance UnsafeReceive Value where BitVal . scalarToBit <$> receive scalar CIntVal int -> - pure (IntVal (fromIntegral int)) + pure $ IntVal $ fromIntegral int CRealVal real -> - pure (RealVal (realToFrac real)) + pure $ RealVal $ realToFrac real CStringVal str -> do case someNatVal (fromIntegral size) of @@ -322,10 +323,10 @@ instance Receive Value where BitVal . scalarToBit <$> receive scalar CIntVal int -> - pure (IntVal (fromIntegral int)) + pure $ IntVal $ fromIntegral int CRealVal real -> - pure (RealVal (realToFrac real)) + pure $ RealVal $ realToFrac real CStringVal str -> do case someNatVal (fromIntegral size) of diff --git a/clash-ffi/src/Clash/FFI/VPI/Object/Value/Format.hs b/clash-ffi/src/Clash/FFI/VPI/Object/Value/Format.hs index 05e9f09c4c..3c81f96bd7 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Object/Value/Format.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Object/Value/Format.hs @@ -5,7 +5,6 @@ Maintainer: QBayLogic B.V. -} {-# LANGUAGE CPP #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} module Clash.FFI.VPI.Object.Value.Format @@ -14,9 +13,7 @@ module Clash.FFI.VPI.Object.Value.Format , InvalidFormat(..) ) where -import Control.Exception (Exception) -import qualified Control.Exception as IO (throwIO) -import qualified Control.Monad.IO.Class as IO (liftIO) +import Control.Exception (Exception, throwIO) import Foreign.C.Types (CInt) import qualified Foreign.Ptr as FFI (castPtr) import Foreign.Storable (Storable(..)) @@ -90,8 +87,8 @@ data UnknownFormat deriving anyclass (Exception) instance Show UnknownFormat where - show (UnknownFormat f c) = - mconcat + show = \case + UnknownFormat f c -> mconcat [ "Unknown format constant " , show f , "\n" @@ -131,7 +128,7 @@ cintToFormat = \case #endif 11 -> pure TimeFmt 12 -> pure ObjTypeFmt - n -> IO.throwIO (UnknownFormat n callStack) + n -> throwIO $ UnknownFormat n callStack formatToCInt :: ValueFormat -> CInt formatToCInt = \case @@ -167,4 +164,4 @@ instance Send ValueFormat where send = pure . formatToCInt instance Receive ValueFormat where - receive = IO.liftIO . cintToFormat + receive = cintToFormat diff --git a/clash-ffi/src/Clash/FFI/VPI/Object/Value/Parse.hs b/clash-ffi/src/Clash/FFI/VPI/Object/Value/Parse.hs index 221ae734ec..79c3d48519 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Object/Value/Parse.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Object/Value/Parse.hs @@ -16,13 +16,11 @@ module Clash.FFI.VPI.Object.Value.Parse , ImpreciseBitString(..) ) where -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import qualified Control.Monad as Monad (foldM) -import qualified Control.Monad.IO.Class as IO (liftIO) import Data.Bits (shiftL) import Data.Char (toLower) import Data.Function (fix) -import Data.Typeable (Typeable) import Foreign.C.String (CString) import Foreign.C.Types (CInt) import GHC.Stack (CallStack, HasCallStack, callStack, prettyCallStack) @@ -34,8 +32,6 @@ import Clash.Sized.BitVector (BitVector) import Clash.XException (deepErrorX) import Clash.FFI.View (peekCStringBound) -import Clash.FFI.Monad (SimCont) -import qualified Clash.FFI.Monad as Sim import Clash.FFI.VPI.Object.Value.Format (ValueFormat(..)) -- | An exception thrown when the bit string to parse contains characters @@ -46,8 +42,8 @@ data InvalidBitString deriving anyclass (Exception) instance Show InvalidBitString where - show (InvalidBitString f s c) = - mconcat + show = \case + InvalidBitString f s c -> mconcat [ "Invalid bit-string for format " , show f , ": " @@ -59,24 +55,26 @@ instance Show InvalidBitString where -- TODO: These parsers are all defined in terms of replaceBit, however that -- may be a bottleneck in applications reading a lot of values / large values. +-- | Parses a binary string (consisting only of the characters @0@, @1@, @x@, +-- and @z@) and turns it into a 'BitVector'. parseBinStr - :: forall n o + :: forall n . HasCallStack => KnownNat n => CInt -> CString - -> SimCont o (BitVector n) + -> IO (BitVector n) parseBinStr bitSize bin = do let is = [bitSize - 1, bitSize - 2 .. 0] - str <- IO.liftIO $ peekCStringBound (fromEnum bitSize) bin + str <- peekCStringBound (fromEnum bitSize) bin let go acc (i, x) = case x of - '0' -> pure (replaceBit i 0 acc) - '1' -> pure (replaceBit i 1 acc) + '0' -> pure $ replaceBit i 0 acc + '1' -> pure $ replaceBit i 1 acc 'x' -> pure acc 'z' -> pure acc - _ -> Sim.throw (InvalidBitString BinStrFmt str callStack) + _ -> throwIO $ InvalidBitString BinStrFmt str callStack Monad.foldM go (deepErrorX "parseBinStr: undefined") (zip is str) @@ -99,15 +97,15 @@ parseBinStr bitSize bin = do -- provide a way to recover. If this exception is caught, the catcher can -- amend the string, and call the supplied continuation to retry the parse. -- -data ImpreciseBitString n o = ImpreciseBitString +data ImpreciseBitString n = ImpreciseBitString { sourceFormat :: ValueFormat , sourceString :: String - , retryParse :: String -> SimCont o (BitVector n) + , retryParse :: String -> IO (BitVector n) , parseStack :: CallStack } deriving anyclass (Exception) -instance Show (ImpreciseBitString n o) where +instance Show (ImpreciseBitString n) where show (ImpreciseBitString f s _ c) = mconcat [ "Loss of precision in bit string for format: " @@ -120,34 +118,35 @@ instance Show (ImpreciseBitString n o) where , prettyCallStack c ] +-- | Parses an octal string (consisting only of the characters @0@-@7@, @x@, +-- and @z@) and turns it into a 'BitVector'. parseOctStr - :: forall n o + :: forall n . HasCallStack => KnownNat n - => Typeable o => CInt -> CString - -> SimCont o (BitVector n) + -> IO (BitVector n) parseOctStr bitSize oct = do let bound = bitSize `div` 3 + if bitSize `mod` 3 == 0 then 0 else 1 is = [0, 3 .. bitSize - 1] - str <- IO.liftIO $ peekCStringBound (fromEnum bound) oct + str <- peekCStringBound (fromEnum bound) oct let go acc (i, x) = case x of - '0' -> pure (replaceSlice (0, 0, 0) i acc) - '1' -> pure (replaceSlice (0, 0, 1) i acc) - '2' -> pure (replaceSlice (0, 1, 0) i acc) - '3' -> pure (replaceSlice (0, 1, 1) i acc) - '4' -> pure (replaceSlice (1, 0, 0) i acc) - '5' -> pure (replaceSlice (1, 0, 1) i acc) - '6' -> pure (replaceSlice (1, 1, 0) i acc) - '7' -> pure (replaceSlice (1, 1, 1) i acc) + '0' -> pure $ replaceSlice (0, 0, 0) i acc + '1' -> pure $ replaceSlice (0, 0, 1) i acc + '2' -> pure $ replaceSlice (0, 1, 0) i acc + '3' -> pure $ replaceSlice (0, 1, 1) i acc + '4' -> pure $ replaceSlice (1, 0, 0) i acc + '5' -> pure $ replaceSlice (1, 0, 1) i acc + '6' -> pure $ replaceSlice (1, 1, 0) i acc + '7' -> pure $ replaceSlice (1, 1, 1) i acc 'x' -> pure acc - 'X' -> Sim.throw (ImpreciseBitString OctStrFmt str parse callStack) + 'X' -> throwIO $ ImpreciseBitString OctStrFmt str parse callStack 'z' -> pure acc - 'Z' -> Sim.throw (ImpreciseBitString OctStrFmt str parse callStack) - _ -> Sim.throw (InvalidBitString OctStrFmt str callStack) + 'Z' -> throwIO $ ImpreciseBitString OctStrFmt str parse callStack + _ -> throwIO $ InvalidBitString OctStrFmt str callStack parse = Monad.foldM go (deepErrorX "parseOctStr: undefined") . zip is . reverse @@ -164,70 +163,73 @@ parseOctStr bitSize oct = do | otherwise = replaceBit (i + 2) x . replaceBit (i + 1) y . replaceBit i z +-- | Parses a decimal string (consisting only of the characters @0@-@9@) and +-- turns it into a 'BitVector'. parseDecStr - :: forall n o + :: forall n . HasCallStack => KnownNat n => CInt -> CString - -> SimCont o (BitVector n) + -> IO (BitVector n) parseDecStr bitSize dec = do let bound = fromInteger $ fix (\f a x -> if x < 10 then a else f (a + 1) $ div x 10) 1 $ shiftL (1 :: Integer) $ fromEnum bitSize - str <- IO.liftIO $ peekCStringBound bound dec + str <- peekCStringBound bound dec -- I don't think you can have X or Z in the decimal strings, although the -- standard doesn't mention you can have x or z here either... case str of - "" -> pure (fromInteger 0) - "x" -> pure (deepErrorX "parseDecStr: x") - "z" -> pure (deepErrorX "parseDecStr: z") + "" -> pure 0 + "x" -> pure $ deepErrorX "parseDecStr: x" + "z" -> pure $ deepErrorX "parseDecStr: z" _ -> maybe - (Sim.throw (InvalidBitString DecStrFmt str callStack)) + (throwIO $ InvalidBitString DecStrFmt str callStack) (pure . fromInteger) (readMaybe str) +-- | Parses a hexadecimal string (consisting only of the characters @0@-@9@, +-- @a@-@f@, and @A@-@F@) and turns it into a 'BitVector'. parseHexStr - :: forall n o + :: forall n . HasCallStack => KnownNat n - => Typeable o => CInt -> CString - -> SimCont o (BitVector n) + -> IO (BitVector n) parseHexStr bitSize hex = do let bound = bitSize `div` 4 + if bitSize `mod` 4 == 0 then 0 else 1 is = [0, 4 .. bitSize - 1] - str <- IO.liftIO $ peekCStringBound (fromEnum bound) hex + str <- peekCStringBound (fromEnum bound) hex let go acc (i, x) = case toLower x of - '0' -> pure (replaceSlice (0, 0, 0, 0) i acc) - '1' -> pure (replaceSlice (0, 0, 0, 1) i acc) - '2' -> pure (replaceSlice (0, 0, 1, 0) i acc) - '3' -> pure (replaceSlice (0, 0, 1, 1) i acc) - '4' -> pure (replaceSlice (0, 1, 0, 0) i acc) - '5' -> pure (replaceSlice (0, 1, 0, 1) i acc) - '6' -> pure (replaceSlice (0, 1, 1, 0) i acc) - '7' -> pure (replaceSlice (0, 1, 1, 1) i acc) - '8' -> pure (replaceSlice (1, 0, 0, 0) i acc) - '9' -> pure (replaceSlice (1, 0, 0, 1) i acc) - 'a' -> pure (replaceSlice (1, 0, 1, 0) i acc) - 'b' -> pure (replaceSlice (1, 0, 1, 1) i acc) - 'c' -> pure (replaceSlice (1, 1, 0, 0) i acc) - 'd' -> pure (replaceSlice (1, 1, 0, 1) i acc) - 'e' -> pure (replaceSlice (1, 1, 1, 0) i acc) - 'f' -> pure (replaceSlice (1, 1, 1, 1) i acc) - - 'x' | x == 'X' -> Sim.throw (ImpreciseBitString HexStrFmt str parse callStack) + '0' -> pure $ replaceSlice (0, 0, 0, 0) i acc + '1' -> pure $ replaceSlice (0, 0, 0, 1) i acc + '2' -> pure $ replaceSlice (0, 0, 1, 0) i acc + '3' -> pure $ replaceSlice (0, 0, 1, 1) i acc + '4' -> pure $ replaceSlice (0, 1, 0, 0) i acc + '5' -> pure $ replaceSlice (0, 1, 0, 1) i acc + '6' -> pure $ replaceSlice (0, 1, 1, 0) i acc + '7' -> pure $ replaceSlice (0, 1, 1, 1) i acc + '8' -> pure $ replaceSlice (1, 0, 0, 0) i acc + '9' -> pure $ replaceSlice (1, 0, 0, 1) i acc + 'a' -> pure $ replaceSlice (1, 0, 1, 0) i acc + 'b' -> pure $ replaceSlice (1, 0, 1, 1) i acc + 'c' -> pure $ replaceSlice (1, 1, 0, 0) i acc + 'd' -> pure $ replaceSlice (1, 1, 0, 1) i acc + 'e' -> pure $ replaceSlice (1, 1, 1, 0) i acc + 'f' -> pure $ replaceSlice (1, 1, 1, 1) i acc + + 'x' | x == 'X' -> throwIO $ ImpreciseBitString HexStrFmt str parse callStack | otherwise -> pure acc - 'z' | x == 'Z' -> Sim.throw (ImpreciseBitString HexStrFmt str parse callStack) + 'z' | x == 'Z' -> throwIO $ ImpreciseBitString HexStrFmt str parse callStack | otherwise -> pure acc - _ -> Sim.throw (InvalidBitString HexStrFmt str callStack) + _ -> throwIO $ InvalidBitString HexStrFmt str callStack parse = Monad.foldM go (deepErrorX "parseHexStr: undefined") . zip is . reverse diff --git a/clash-ffi/src/Clash/FFI/VPI/Object/Value/Scalar.hs b/clash-ffi/src/Clash/FFI/VPI/Object/Value/Scalar.hs index 53a74d2e02..7ecd373e0e 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Object/Value/Scalar.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Object/Value/Scalar.hs @@ -4,7 +4,6 @@ License: BSD2 (see the file LICENSE) Maintainer: QBayLogic B.V. -} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} @@ -17,14 +16,13 @@ module Clash.FFI.VPI.Object.Value.Scalar , bitToScalar ) where -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import Foreign.C.Types (CInt) import GHC.Stack (CallStack, callStack, prettyCallStack) import Clash.Sized.Internal.BitVector import Clash.XException (hasUndefined) -import qualified Clash.FFI.Monad as Sim (throw) import Clash.FFI.View -- | A VPI scalar value. This is similar to the 9-value logic type from VHDL, @@ -67,8 +65,8 @@ data UnknownScalarValue deriving anyclass (Exception) instance Show UnknownScalarValue where - show (UnknownScalarValue x c) = - mconcat + show = \case + UnknownScalarValue x c -> mconcat [ "Unknown scalar value: " , show x , "\n" @@ -84,20 +82,22 @@ instance Receive Scalar where 4 -> pure SH 5 -> pure SL 6 -> pure S_ - n -> Sim.throw (UnknownScalarValue n callStack) + n -> throwIO $ UnknownScalarValue n callStack +-- | 'Bit' to 'Scalar' conversion. bitToScalar :: Bit -> Scalar bitToScalar b - | hasUndefined b = SX - | b == low = S0 - | b == high = S1 - | otherwise = SX + | hasUndefined b = SX + | b == low = S0 + | b == high = S1 + | otherwise = SX type instance CRepr Bit = CRepr Scalar instance Send Bit where send = send . bitToScalar +-- | 'Scalar' to 'Bit' conversion. scalarToBit :: Scalar -> Bit scalarToBit = \case S0 -> low diff --git a/clash-ffi/src/Clash/FFI/VPI/Object/Value/Vector.hs b/clash-ffi/src/Clash/FFI/VPI/Object/Value/Vector.hs index 55fe25f497..f56265f7b5 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Object/Value/Vector.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Object/Value/Vector.hs @@ -10,7 +10,6 @@ Maintainer: QBayLogic B.V. {-# LANGUAGE DataKinds #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeSynonymInstances #-} @@ -28,7 +27,6 @@ module Clash.FFI.VPI.Object.Value.Vector , vectorToBitVector ) where -import qualified Control.Monad.IO.Class as IO (liftIO) import Data.Bits (clearBit, setBit, testBit) import Data.Proxy import Foreign.C.Types (CInt) @@ -83,7 +81,7 @@ vectorToCVectorList vec = go [] 0 size = fromIntegral $ natVal (Proxy @n) replaceScalar :: Int -> Scalar -> CVector -> CVector - replaceScalar ix s (CVector as bs) = + replaceScalar ix s CVector{..} = let (aMod, bMod) = case s of @@ -95,7 +93,7 @@ vectorToCVectorList vec = go [] 0 SX -> ( setBit, setBit) S_ -> ( setBit, setBit) in - CVector (aMod as ix) (bMod bs ix) + CVector (aMod cvectorA ix) (bMod cvectorB ix) go :: [CVector] -> Int -> [CVector] go a i = @@ -136,8 +134,8 @@ cvectorListToVector = go (Vec.repeat SX) 0 | otherwise = error "cvectorListToVector: Array is not the specified size" getScalar :: Int -> CVector -> Scalar - getScalar ix (CVector as bs) = - case (testBit as ix, testBit bs ix) of + getScalar ix CVector{..} = + case (testBit cvectorA ix, testBit cvectorB ix) of (False, False) -> S0 (True, False) -> S1 (False, True) -> SZ @@ -145,16 +143,18 @@ cvectorListToVector = go (Vec.repeat SX) 0 instance (KnownNat n) => UnsafeReceive (Vec n Scalar) where unsafeReceive = - let size = fromIntegral (natVal (Proxy @n)) + let size = fromIntegral $ natVal $ Proxy @n len = div (size - 1) 32 + 1 - in fmap cvectorListToVector . IO.liftIO . FFI.peekArray len + in fmap cvectorListToVector . FFI.peekArray len instance (KnownNat n) => Receive (Vec n Scalar) where receive = - let size = fromIntegral (natVal (Proxy @n)) + let size = fromIntegral $ natVal $ Proxy @n len = div (size - 1) 32 + 1 - in fmap cvectorListToVector . IO.liftIO . FFI.peekArray len + in fmap cvectorListToVector . FFI.peekArray len + +-- | Turns a 'BitVector' into a vector of 'Scalar' values. bitVectorToVector :: forall n . KnownNat n @@ -163,6 +163,7 @@ bitVectorToVector bitVectorToVector = fmap bitToScalar . unpack +-- | Turns a vector of 'Scalar' values into a 'BitVector'. vectorToBitVector :: forall n . KnownNat n @@ -172,7 +173,7 @@ vectorToBitVector vec = Vec.ifoldr go (deepErrorX "vectorToBitVector") vec where go :: Index n -> Scalar -> BitVector n -> BitVector n - go ix s = replaceBit ix (scalarToBit s) + go ix s = replaceBit ix $ scalarToBit s type instance CRepr (BitVector n) = CRepr (Vec n Scalar) diff --git a/clash-ffi/src/Clash/FFI/VPI/Port.hs b/clash-ffi/src/Clash/FFI/VPI/Port.hs index 8075601706..a524c6295d 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Port.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Port.hs @@ -11,10 +11,8 @@ module Clash.FFI.VPI.Port ) where import Control.DeepSeq (NFData) -import Data.Typeable (Typeable) import Foreign.Storable (Storable) -import Clash.FFI.Monad (SimCont) import Clash.FFI.VPI.Object import Clash.FFI.VPI.Port.Direction @@ -36,5 +34,5 @@ newtype Port -- | The direction of the port, as specified in the design. -- -direction :: Typeable o => Port -> SimCont o Direction +direction :: Port -> IO Direction direction = receiveProperty Direction diff --git a/clash-ffi/src/Clash/FFI/VPI/Port/Direction.hs b/clash-ffi/src/Clash/FFI/VPI/Port/Direction.hs index ca8bdc6631..d07c0d5a36 100644 --- a/clash-ffi/src/Clash/FFI/VPI/Port/Direction.hs +++ b/clash-ffi/src/Clash/FFI/VPI/Port/Direction.hs @@ -4,7 +4,6 @@ License: BSD2 (see the file LICENSE) Maintainer: QBayLogic B.V. -} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} module Clash.FFI.VPI.Port.Direction @@ -12,11 +11,10 @@ module Clash.FFI.VPI.Port.Direction , UnknownDirection(..) ) where -import Control.Exception (Exception) +import Control.Exception (Exception, throwIO) import Foreign.C.Types (CInt) import GHC.Stack (CallStack, callStack, prettyCallStack) -import qualified Clash.FFI.Monad as Sim (throw) import Clash.FFI.View -- | The direction of a port in a module. This does not include the mixed IO @@ -45,8 +43,8 @@ data UnknownDirection deriving anyclass (Exception) instance Show UnknownDirection where - show (UnknownDirection d c) = - mconcat + show = \case + UnknownDirection d c -> mconcat [ "Unknown port direction: " , show d , "\n" @@ -62,4 +60,4 @@ instance Receive Direction where 3 -> pure InOut 4 -> pure MixedIO 5 -> pure NoDirection - n -> Sim.throw (UnknownDirection n callStack) + n -> throwIO $ UnknownDirection n callStack diff --git a/clash-ffi/src/Clash/FFI/View.hs b/clash-ffi/src/Clash/FFI/View.hs index 1203372fd6..ee5bff3efe 100644 --- a/clash-ffi/src/Clash/FFI/View.hs +++ b/clash-ffi/src/Clash/FFI/View.hs @@ -31,15 +31,13 @@ module Clash.FFI.View , ensureNullTerminated ) where -import qualified Control.Monad.IO.Class as IO (liftIO) import Data.ByteString (ByteString) import qualified Data.ByteString as BS (length, packCString, null, last, snoc) import qualified Data.ByteString.Unsafe as BS -import Data.Typeable (Typeable) import Foreign.C.String (CString) import qualified Foreign.C.String as FFI import Foreign.C.Types (CChar) -import qualified Foreign.Marshal.Alloc as FFI (mallocBytes) +import qualified Foreign.Marshal.Alloc as FFI (mallocBytes, alloca) import qualified Foreign.Marshal.Array as FFI import qualified Foreign.Marshal.Utils as FFI (copyBytes) import Foreign.Ptr (Ptr) @@ -48,9 +46,6 @@ import Foreign.Storable (Storable, sizeOf) import qualified Foreign.Storable as FFI (peek, poke, peekElemOff) import GHC.Stack (HasCallStack) -import Clash.FFI.Monad (SimCont) -import qualified Clash.FFI.Monad as Sim - -- | A type family for determining the representation of a type @a@ when it is -- sent / received over FFI. Types are converted to their C representation -- using 'Send' and converted from their C representation using 'Receive'. @@ -75,7 +70,7 @@ type instance CRepr (Maybe a) = Ptr (CRepr a) -- see 'Receive' and 'UnsafeReceive'. -- class UnsafeSend a where - unsafeSend :: HasCallStack => a -> SimCont b (CRepr a) + unsafeSend :: HasCallStack => a -> (CRepr a -> IO b) -> IO b -- | A class for data with raw values which can be safely sent over the FFI. -- Safely sending data involves making new copies where necessary, so the @@ -86,16 +81,16 @@ class UnsafeSend a where -- 'UnsafeSend' instead as it does not need to copy the data first. -- class Send a where - send :: HasCallStack => a -> SimCont b (CRepr a) + send :: HasCallStack => a -> IO (CRepr a) instance Storable a => UnsafeSend [a] where - unsafeSend xs = Sim.liftCont (FFI.withArray xs) + unsafeSend = FFI.withArray instance Storable a => Send [a] where - send = IO.liftIO . FFI.newArray + send = FFI.newArray instance (UnsafeSend a, Storable (CRepr a)) => UnsafeSend (Maybe a) where - unsafeSend = maybe (pure FFI.nullPtr) unsafePokeSend + unsafeSend = maybe ($ FFI.nullPtr) unsafePokeSend instance (Send a, Storable (CRepr a)) => Send (Maybe a) where send = maybe (pure FFI.nullPtr) pokeSend @@ -107,10 +102,12 @@ instance (Send a, Storable (CRepr a)) => Send (Maybe a) where unsafePokeSend :: (UnsafeSend a, Storable (CRepr a)) => a - -> SimCont b (Ptr (CRepr a)) -unsafePokeSend x = do - raw <- unsafeSend x - fst <$> Sim.withNewPtr Sim.stackPtr (`FFI.poke` raw) + -> (Ptr (CRepr a) -> IO b) -> IO b +unsafePokeSend x f = + unsafeSend x $ \raw -> + FFI.alloca $ \ptr -> do + FFI.poke ptr raw + f ptr -- | Safely send a value, then allocate a new pointer on the stack and assign -- this value to the pointer. The caller is responsible for deallocating the @@ -119,30 +116,29 @@ unsafePokeSend x = do pokeSend :: (Send a, Storable (CRepr a)) => a - -> SimCont b (Ptr (CRepr a)) + -> IO (Ptr (CRepr a)) pokeSend x = do raw <- send x - fst <$> Sim.withNewPtr Sim.stackPtr (`FFI.poke` raw) + FFI.alloca $ \ptr -> FFI.poke ptr raw >> return ptr -- | Send a string by taking a temporary view of the String as a CString. -- -unsafeSendString :: String -> SimCont b CString -unsafeSendString str = Sim.liftCont (FFI.withCString str) +unsafeSendString :: String -> (CString -> IO a) -> IO a +unsafeSendString = FFI.withCString -- | Send a string by allocating a new CString which must be explicitly freed. -- -sendString :: String -> SimCont b CString -sendString str = IO.liftIO (FFI.newCString str) +sendString :: String -> IO CString +sendString = FFI.newCString instance UnsafeSend ByteString where - unsafeSend str = Sim.liftCont (BS.unsafeUseAsCString str) + unsafeSend = BS.unsafeUseAsCString instance Send ByteString where - send str = do - cstr <- unsafeSend str - let len = BS.length str + 1 + send str = + unsafeSend str $ \cstr -> do + let len = BS.length str + 1 - IO.liftIO $ do bytes <- FFI.mallocBytes len FFI.copyBytes bytes cstr len @@ -159,7 +155,7 @@ instance Send ByteString where -- see 'Send' and 'UnsafeSend'. -- class UnsafeReceive a where - unsafeReceive :: (HasCallStack, Typeable b) => CRepr a -> SimCont b a + unsafeReceive :: HasCallStack => CRepr a -> IO a -- | A class for data with raw values which can be safely received over the FFI. -- Safely receiving data involves making new copies where necessary, so the @@ -170,75 +166,69 @@ class UnsafeReceive a where -- using 'UnsafeReceive', provided it will not be later mutated. -- class Receive a where - receive :: (HasCallStack, Typeable b) => CRepr a -> SimCont b a + receive :: HasCallStack => CRepr a -> IO a instance (UnsafeReceive a, Storable (CRepr a)) => UnsafeReceive (Maybe a) where unsafeReceive ptr - | ptr == FFI.nullPtr - = pure Nothing - - | otherwise - = Just <$> unsafePeekReceive ptr + | ptr == FFI.nullPtr = pure Nothing + | otherwise = Just <$> unsafePeekReceive ptr instance (Receive a, Storable (CRepr a)) => Receive (Maybe a) where receive ptr - | ptr == FFI.nullPtr - = pure Nothing - - | otherwise - = Just <$> peekReceive ptr + | ptr == FFI.nullPtr = pure Nothing + | otherwise = Just <$> peekReceive ptr instance UnsafeReceive ByteString where - unsafeReceive = IO.liftIO . BS.unsafePackCString + unsafeReceive = BS.unsafePackCString instance Receive ByteString where - receive = IO.liftIO . BS.packCString + receive = BS.packCString -- | Deference a pointer, then unsafely receive the value of the pointer. Since -- the value is unsafely received, any change to the pointed to value will -- corrupt the received value. -- unsafePeekReceive - :: (UnsafeReceive a, Storable (CRepr a), Typeable b) + :: (UnsafeReceive a, Storable (CRepr a)) => Ptr (CRepr a) - -> SimCont b a + -> IO a unsafePeekReceive ptr = - IO.liftIO (FFI.peek ptr) >>= unsafeReceive + FFI.peek ptr >>= unsafeReceive -- | Dereference a pointer, then safely receive the value of the pointer. The -- caller is responsible for deallocating the received value if necessary. -- peekReceive - :: (Receive a, Storable (CRepr a), Typeable b) + :: (Receive a, Storable (CRepr a)) => Ptr (CRepr a) - -> SimCont b a + -> IO a peekReceive ptr = - IO.liftIO (FFI.peek ptr) >>= receive + FFI.peek ptr >>= receive -- | Unsafely receive an array of values, with the end of the array -- marked by the given final element. The search for the marker is --- bounded by 'bound'. Each element of the array is unsafely received. +-- bounded by @bound@. Each element of the array is unsafely received. unsafeReceiveArray0 - :: (UnsafeReceive a, Eq (CRepr a), Storable (CRepr a), Typeable b) + :: (UnsafeReceive a, Eq (CRepr a), Storable (CRepr a)) => Int -> CRepr a -> Ptr (CRepr a) - -> SimCont b [a] + -> IO [a] unsafeReceiveArray0 bound end ptr = - IO.liftIO (boundedPeekArray0 bound end ptr) >>= traverse unsafeReceive + boundedPeekArray0 bound end ptr >>= traverse unsafeReceive -- | Safely receive an array of values, with the end of the array -- marked by the given final element. The search for the marker is --- bounded by 'bound'. The caller is responsible for deallocating the +-- bounded by @bound@. The caller is responsible for deallocating the -- elements of the array if necessary. receiveArray0 - :: (Receive a, Eq (CRepr a), Storable (CRepr a), Typeable b) + :: (Receive a, Eq (CRepr a), Storable (CRepr a)) => Int -> CRepr a -> Ptr (CRepr a) - -> SimCont b [a] + -> IO [a] receiveArray0 bound end ptr = - IO.liftIO (boundedPeekArray0 bound end ptr) >>= traverse receive + boundedPeekArray0 bound end ptr >>= traverse receive -- | Variant of 'Foreign.Marshal.Array.lengthArray0' using an upper -- bound on the elements when searching for the terminator. @@ -278,9 +268,8 @@ peekCStringBound bound cp = do -- | Safely receive a string. Users are recommended to use -- 'ByteString' instead which supports safe and unsafe sending / -- receiving. -receiveString :: CString -> SimCont b String -receiveString = - IO.liftIO . FFI.peekCString +receiveString :: CString -> IO String +receiveString = FFI.peekCString -- | Ensure that the given 'ByteString' is a null-terminated 'ByteString' ensureNullTerminated :: ByteString -> ByteString diff --git a/clash-ffi/tests/Clash/FFI/Test.hs b/clash-ffi/tests/Clash/FFI/Test.hs index 391af55f62..4b18c7a6ff 100644 --- a/clash-ffi/tests/Clash/FFI/Test.hs +++ b/clash-ffi/tests/Clash/FFI/Test.hs @@ -23,7 +23,6 @@ import Test.SmallCheck.Series (Positive) import Clash.Prelude (BitSize, SNat(..), Bit, Signed, snatToNum) -import Clash.FFI.Monad (SimAction, SimCont, runSimAction) import Clash.FFI.VPI.Module (Module(..), findTopModule) import Clash.FFI.VPI.Net (Net(..)) import Clash.FFI.VPI.Object ( IsObject, Object(..), Value(..) @@ -102,7 +101,7 @@ objectType = \case _ -> SomeObjectType (Proxy @Object) -- | Returns the top module named @top@. -topModule :: SimCont o Module +topModule :: IO Module topModule = findTopModule $ pack "top" -- | Returns the special top module named @special@. Calls of @@ -113,7 +112,7 @@ topModule = findTopModule $ pack "top" -- their corresponding Haskell representation. specialTop :: (?pipe :: Handle) => IO Module specialTop = - runSimAction (findTopModule (pack "special") %% 3) + findTopModule (pack "special") %% 3 -- | Runs tests over lists in the 'monadic' context. testM :: Testable m [b] => (a -> m b) -> [a] -> Property m @@ -123,21 +122,21 @@ testM xs = monadic . mapM xs -- the output printed at the C side. receiveAndCompare :: (TShow a, ?pipe :: Handle) => - SimAction a -> + IO a -> Positive Int -> IO TestResult receiveAndCompare action = - const $ runSimAction action >>= outputEQ + const $ action >>= outputEQ -- | Sends some value to the C side via a Clash FFI action and -- compares the output printed at the C side with the sent value. sendAndCompare :: (TShow a, ?pipe :: Handle) => - (a -> SimAction ()) -> + (a -> IO ()) -> a -> IO TestResult sendAndCompare action input = do - runSimAction $ action input + action input inputEQ input -- | Sends some value to the C side via a Clash FFI action and @@ -145,11 +144,11 @@ sendAndCompare action input = do -- same is done for the value returned by the action. sendReceiveAndCompare :: (TShow a, TShow b, ?pipe :: Handle) => - (a -> SimAction b) -> + (a -> IO b) -> a -> IO [TestResult] sendReceiveAndCompare action input = do - output <- runSimAction $ action input + output <- action input sequence [ inputEQ input , outputEQ output @@ -160,11 +159,11 @@ sendReceiveAndCompare action input = do -- same is done for the value returned by the action. sendReceiveAndCompare2 :: (TShow a, TShow b, TShow c, ?pipe :: Handle) => - (a -> b -> SimAction c) -> + (a -> b -> IO c) -> (a, b) -> IO [TestResult] sendReceiveAndCompare2 action (i1, i2) = do - output <- runSimAction $ action i1 i2 + output <- action i1 i2 sequence [ inputEQ i1 , inputEQ i2 diff --git a/clash-ffi/tests/Main.hs b/clash-ffi/tests/Main.hs index 11f8cb6b81..9c12a8ec76 100644 --- a/clash-ffi/tests/Main.hs +++ b/clash-ffi/tests/Main.hs @@ -50,7 +50,6 @@ import Test.Tasty.HUnit (testCase, assertFailure) import Test.Tasty.SmallCheck (testProperty) import Test.SmallCheck.Series (Positive) -import Clash.FFI.Monad import Clash.FFI.VPI.Callback import Clash.FFI.VPI.Control import Clash.FFI.VPI.Error @@ -79,7 +78,7 @@ main = withPipe $ do [ testProperty "registerCallback (vpi_register_cb)" $ testM $ sendReceiveAndCompare @(CallbackInfo BSNT) registerCallback , testProperty "removeCallback (vpi_remove_cb)" - $ testM $ \cbInfo -> runSimAction $ do + $ testM $ \cbInfo -> do cb <- registerCallback (cbInfo :: CallbackInfo BSNT) %% 2 removeCallback cb inputEQ cb @@ -115,12 +114,12 @@ main = withPipe $ do $ testM $ sendReceiveAndCompare2 @_ @(Maybe Object) iterate , testProperty "scan (vpi_scan)" $ testM $ \x -> do - iterator <- runSimAction (iterate ObjModule (Nothing @Object) %% 3) + iterator <- iterate ObjModule (Nothing @Object) %% 3 receiveAndCompare @(Maybe Module) (scan iterator) x , testProperty "iterateAll (vpi_iterate, vpi_scan)" $ testM $ \(input, mObj) -> case objectType input of SomeObjectType (Proxy :: Proxy t) -> do - xs <- runSimAction $ iterateAll input mObj + xs <- iterateAll input mObj -- check vpi_iterate output cInput <- sequence [ inputEQ input @@ -134,21 +133,21 @@ main = withPipe $ do ] , testGroup "Clash.FFI.VPI.Module" [ testCase "topModules (vpi_iterate, vpi_scan)" $ do - modules <- runSimAction topModules %% 3 + modules <- topModules %% 3 mapM_ (assert . outputEQ . Just) modules assert $ outputEQ (Nothing @Module) , testCase "findTopModule (vpi_handle_by_name)" $ do let known = pack "top" unknown = pack "unknown" empty = pack "" - top <- runSimAction $ findTopModule known + top <- findTopModule known mapM_ assert [ inputEQ (SerialBS known) , inputEQ (Nothing @Module) , outputEQ top ] catch - ( runSimAction (findTopModule unknown) + ( findTopModule unknown >> assertFailure "expected Exception" ) $ \(_ :: SomeException) -> return () mapM_ assert @@ -156,7 +155,7 @@ main = withPipe $ do , inputEQ (Nothing @Module) ] catch - ( runSimAction (findTopModule empty) + ( findTopModule empty >> assertFailure "expected Exception" ) $ \(_ :: SomeException) -> return () mapM_ assert @@ -164,19 +163,19 @@ main = withPipe $ do , inputEQ (Nothing @Module) ] , testCase "moduleNets (vpi_iterate, vpi_scan)" $ - runSimAction ((topModule >>= moduleNets) %% 6) + ((topModule >>= moduleNets) %% 6) >>= mapM_ (assert . outputEQ . Just) >> assert (outputEQ (Nothing @Net)) , testCase "moduleParameters (vpi_iterate, vpi_scan)" $ - runSimAction ((topModule >>= moduleParameters) %% 6) + ((topModule >>= moduleParameters) %% 6) >>= mapM_ (assert . outputEQ . Just) >> assert (outputEQ (Nothing @Parameter)) , testCase "modulePorts (vpi_iterate, vpi_scan)" $ - runSimAction ((topModule >>= modulePorts) %% 6) + ((topModule >>= modulePorts) %% 6) >>= mapM_ (assert . outputEQ . Just) >> assert (outputEQ (Nothing @Port)) , testCase "moduleRegs (vpi_iterate, vpi_scan)" $ - runSimAction ((topModule >>= moduleRegs) %% 6) + ((topModule >>= moduleRegs) %% 6) >>= mapM_ (assert . outputEQ . Just) >> assert (outputEQ (Nothing @Reg)) ] @@ -189,22 +188,21 @@ main = withPipe $ do let none = Nothing @Object objTypeTop = ObjModule objTypePort = ObjPort - runSimAction $ do - top <- getChild objTypeTop none - mapM_ assert - [ inputEQ objTypeTop - , inputEQ none - , outputEQ (top :: Module) - ] - let topRef = Just top - port <- getChild objTypePort topRef - mapM_ assert - [ inputEQ objTypePort - , inputEQ topRef - , outputEQ (port :: Port) - ] + top <- getChild objTypeTop none + mapM_ assert + [ inputEQ objTypeTop + , inputEQ none + , outputEQ (top :: Module) + ] + let topRef = Just top + port <- getChild objTypePort topRef + mapM_ assert + [ inputEQ objTypePort + , inputEQ topRef + , outputEQ (port :: Port) + ] catch - ( (runSimAction (getChild objTypePort none) :: IO Port) + ( (getChild objTypePort none :: IO Port) >> assertFailure "expected Exception" ) $ \(_ :: SomeException) -> return () mapM_ assert @@ -215,22 +213,21 @@ main = withPipe $ do let none = Nothing @Object topName = pack "top" portName = pack "port" - runSimAction $ do - top <- sendChildRef topName none - mapM_ assert - [ inputEQ (SerialBS topName) - , inputEQ none - , outputEQ (top :: Module) - ] - let topRef = Just top - port <- sendChildRef portName topRef - mapM_ assert - [ inputEQ (SerialBS portName) - , inputEQ topRef - , outputEQ (port :: Port) - ] + top <- sendChildRef topName none + mapM_ assert + [ inputEQ (SerialBS topName) + , inputEQ none + , outputEQ (top :: Module) + ] + let topRef = Just top + port <- sendChildRef portName topRef + mapM_ assert + [ inputEQ (SerialBS portName) + , inputEQ topRef + , outputEQ (port :: Port) + ] catch - ( (runSimAction (sendChildRef portName none) :: IO Port) + ( (sendChildRef portName none :: IO Port) >> assertFailure "expected Exception" ) $ \(_ :: SomeException) -> return () mapM_ assert @@ -241,22 +238,21 @@ main = withPipe $ do let none = Nothing @Object topName = pack "top" portName = pack "port" - runSimAction $ do - top <- unsafeSendChildRef topName none - mapM_ assert - [ inputEQ (SerialBS topName) - , inputEQ none - , outputEQ (top :: Module) - ] - let topRef = Just top - port <- unsafeSendChildRef portName topRef - mapM_ assert - [ inputEQ (SerialBS portName) - , inputEQ topRef - , outputEQ (port :: Port) - ] + top <- unsafeSendChildRef topName none + mapM_ assert + [ inputEQ (SerialBS topName) + , inputEQ none + , outputEQ (top :: Module) + ] + let topRef = Just top + port <- unsafeSendChildRef portName topRef + mapM_ assert + [ inputEQ (SerialBS portName) + , inputEQ topRef + , outputEQ (port :: Port) + ] catch - ( (runSimAction (unsafeSendChildRef portName none) :: IO Port) + ( (unsafeSendChildRef portName none :: IO Port) >> assertFailure "expected Exception" ) $ \(_ :: SomeException) -> return () mapM_ assert @@ -268,15 +264,15 @@ main = withPipe $ do netNameRef = pack "top.net" existingNetBitIdx = (0 :: CInt) missingNetBitIdx = (20 :: CInt) - net <- Just <$> runSimAction (sendChildRef netNameRef none %% 3) - obj <- runSimAction $ getChild existingNetBitIdx (net :: Maybe Net) + net <- Just <$> sendChildRef netNameRef none %% 3 + obj <- getChild existingNetBitIdx (net :: Maybe Net) mapM_ assert [ inputEQ existingNetBitIdx , inputEQ net , outputEQ (obj :: Object) ] catch - ( (runSimAction (getChild missingNetBitIdx net) :: IO Port) + ( (getChild missingNetBitIdx net :: IO Port) >> assertFailure "expected Exception" ) $ \(_ :: SomeException) -> return () mapM_ assert @@ -288,15 +284,15 @@ main = withPipe $ do netNameRef = pack "top.reg" existingRegBit = [0 :: CInt, 1 :: CInt] missingRegBit = [1 :: CInt, 2 :: CInt] - reg <- Just <$> runSimAction (sendChildRef netNameRef none %% 3) - bit <- runSimAction $ getChild existingRegBit (reg :: Maybe Reg) + reg <- Just <$> sendChildRef netNameRef none %% 3 + bit <- getChild existingRegBit (reg :: Maybe Reg) mapM_ assert [ inputEQ existingRegBit , inputEQ reg , outputEQ (bit :: Object) ] catch - ( (runSimAction (getChild missingRegBit reg) :: IO Port) + ( (getChild missingRegBit reg :: IO Port) >> assertFailure "expected Exception" ) $ \(_ :: SomeException) -> return () mapM_ assert @@ -328,7 +324,7 @@ main = withPipe $ do ) . filter ((`notElem` [SuppressValue, ObjTypeFmt]) . fst) , testProperty "sendValue (vpi_put_value)" - $ testM $ \(value, delayMode) -> runSimAction $ do + $ testM $ \(value, delayMode) -> do let none = Nothing @Object portNameRef = pack "top.port" port <- sendChildRef portNameRef none %% 3 @@ -340,7 +336,7 @@ main = withPipe $ do , inputEQ delayMode ] , testProperty "unsafeSendValue (vpi_put_value)" - $ testM $ \(value, delayMode) -> runSimAction $ do + $ testM $ \(value, delayMode) -> do let none = Nothing @Object portNameRef = pack "top.port" port <- sendChildRef portNameRef none %% 3 @@ -352,7 +348,7 @@ main = withPipe $ do , inputEQ delayMode ] , testProperty "compare send & receive" - $ testM $ \(value, delayMode) -> runSimAction $ do + $ testM $ \(value, delayMode) -> do let none = Nothing @Object portNameRef = pack "top.port" port <- sendChildRef portNameRef none %% 3 @@ -368,17 +364,16 @@ main = withPipe $ do ] , testGroup "Clash.FFI.VPI.Port" [ testProperty "direction (vpi_get)" - $ testM $ \(_ :: [Positive Int]) -> - runSimAction $ do - let none = Nothing @Object - portNameRef = pack "top.port" - port <- sendChildRef portNameRef none %% 3 - value <- direction port - sequence - [ inputEQ Direction - , inputEQ port - , outputEQ value - ] + $ testM $ \(_ :: [Positive Int]) -> do + let none = Nothing @Object + portNameRef = pack "top.port" + port <- sendChildRef portNameRef none %% 3 + value <- direction port + sequence + [ inputEQ Direction + , inputEQ port + , outputEQ value + ] ] ]