{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns, MagicHash, TypeOperators, ScopedTypeVariables, FlexibleContexts #-}
{-# LANGUAGE DataKinds, GADTs, TypeApplications #-}
module Clash.Explicit.SimIO
(
mealyIO
, SimIO
, display
, finish
, Reg
, reg
, readReg
, writeReg
, File
, openFile
, closeFile
, getChar
, putChar
, getLine
, isEOF
, flush
, seek
, rewind
, tell
)
where
import Control.Monad (when)
#if __GLASGOW_HASKELL__ < 900
import Data.Coerce
#endif
import Data.IORef
import GHC.TypeLits
import Prelude hiding (getChar, putChar, getLine)
import qualified System.IO as IO
import System.IO.Unsafe
import Clash.Annotations.Primitive (hasBlackBox)
import Clash.Promoted.Nat
import Clash.Signal.Internal
import Clash.Sized.Unsigned
import Clash.Sized.Vector (Vec (..))
import Clash.XException (seqX)
#if __GLASGOW_HASKELL__ >= 900
data SimIO a = SimIO {unSimIO :: !(IO a)}
#else
newtype SimIO a = SimIO {unSimIO :: IO a}
#endif
{-# ANN unSimIO hasBlackBox #-}
instance Functor SimIO where
fmap = fmapSimIO#
fmapSimIO# :: (a -> b) -> SimIO a -> SimIO b
fmapSimIO# f (SimIO m) = SimIO (fmap f m)
{-# NOINLINE fmapSimIO# #-}
{-# ANN fmapSimIO# hasBlackBox #-}
instance Applicative SimIO where
pure = pureSimIO#
(<*>) = apSimIO#
pureSimIO# :: a -> SimIO a
pureSimIO# a = SimIO (pure a)
{-# NOINLINE pureSimIO# #-}
{-# ANN pureSimIO# hasBlackBox #-}
apSimIO# :: SimIO (a -> b) -> SimIO a -> SimIO b
apSimIO# (SimIO f) (SimIO m) = SimIO (f <*> m)
{-# NOINLINE apSimIO# #-}
{-# ANN apSimIO# hasBlackBox #-}
instance Monad SimIO where
return = pureSimIO#
(>>=) = bindSimIO#
bindSimIO# :: SimIO a -> (a -> SimIO b) -> SimIO b
#if __GLASGOW_HASKELL__ >= 900
bindSimIO# (SimIO m) k = SimIO (m >>= (\x -> x `seqX` unSimIO (k x)))
#else
bindSimIO# (SimIO m) k = SimIO (m >>= (\x -> x `seqX` coerce k x))
#endif
{-# NOINLINE bindSimIO# #-}
{-# ANN bindSimIO# hasBlackBox #-}
display
:: String
-> SimIO ()
display s = SimIO (putStrLn s)
{-# NOINLINE display #-}
{-# ANN display hasBlackBox #-}
finish
:: Integer
-> SimIO a
finish i = return (error (show i))
{-# NOINLINE finish #-}
{-# ANN finish hasBlackBox #-}
#if __GLASGOW_HASKELL__ >= 900
data Reg a = Reg !(IORef a)
#else
newtype Reg a = Reg (IORef a)
#endif
reg
:: a
-> SimIO (Reg a)
reg a = SimIO (Reg <$> newIORef a)
{-# NOINLINE reg #-}
{-# ANN reg hasBlackBox #-}
readReg :: Reg a -> SimIO a
readReg (Reg a) = SimIO (readIORef a)
{-# NOINLINE readReg #-}
{-# ANN readReg hasBlackBox #-}
writeReg
:: Reg a
-> a
-> SimIO ()
writeReg (Reg r) a = SimIO (writeIORef r a)
{-# NOINLINE writeReg #-}
{-# ANN writeReg hasBlackBox #-}
#if __GLASGOW_HASKELL__ >= 900
data File = File !IO.Handle
#else
newtype File = File IO.Handle
#endif
openFile
:: FilePath
-> String
-> SimIO File
#if __GLASGOW_HASKELL__ >= 900
openFile fp "r" = SimIO $ fmap File (IO.openFile fp IO.ReadMode)
openFile fp "w" = SimIO $ fmap File (IO.openFile fp IO.WriteMode)
openFile fp "a" = SimIO $ fmap File (IO.openFile fp IO.AppendMode)
openFile fp "rb" = SimIO $ fmap File (IO.openBinaryFile fp IO.ReadMode)
openFile fp "wb" = SimIO $ fmap File (IO.openBinaryFile fp IO.WriteMode)
openFile fp "ab" = SimIO $ fmap File (IO.openBinaryFile fp IO.AppendMode)
openFile fp "r+" = SimIO $ fmap File (IO.openFile fp IO.ReadWriteMode)
openFile fp "w+" = SimIO $ fmap File (IO.openFile fp IO.WriteMode)
openFile fp "a+" = SimIO $ fmap File (IO.openFile fp IO.AppendMode)
openFile fp "r+b" = SimIO $ fmap File (IO.openBinaryFile fp IO.ReadWriteMode)
openFile fp "w+b" = SimIO $ fmap File (IO.openBinaryFile fp IO.WriteMode)
openFile fp "a+b" = SimIO $ fmap File (IO.openBinaryFile fp IO.AppendMode)
openFile fp "rb+" = SimIO $ fmap File (IO.openBinaryFile fp IO.ReadWriteMode)
openFile fp "wb+" = SimIO $ fmap File (IO.openBinaryFile fp IO.WriteMode)
openFile fp "ab+" = SimIO $ fmap File (IO.openBinaryFile fp IO.AppendMode)
#else
openFile fp "r" = coerce (IO.openFile fp IO.ReadMode)
openFile fp "w" = coerce (IO.openFile fp IO.WriteMode)
openFile fp "a" = coerce (IO.openFile fp IO.AppendMode)
openFile fp "rb" = coerce (IO.openBinaryFile fp IO.ReadMode)
openFile fp "wb" = coerce (IO.openBinaryFile fp IO.WriteMode)
openFile fp "ab" = coerce (IO.openBinaryFile fp IO.AppendMode)
openFile fp "r+" = coerce (IO.openFile fp IO.ReadWriteMode)
openFile fp "w+" = coerce (IO.openFile fp IO.WriteMode)
openFile fp "a+" = coerce (IO.openFile fp IO.AppendMode)
openFile fp "r+b" = coerce (IO.openBinaryFile fp IO.ReadWriteMode)
openFile fp "w+b" = coerce (IO.openBinaryFile fp IO.WriteMode)
openFile fp "a+b" = coerce (IO.openBinaryFile fp IO.AppendMode)
openFile fp "rb+" = coerce (IO.openBinaryFile fp IO.ReadWriteMode)
openFile fp "wb+" = coerce (IO.openBinaryFile fp IO.WriteMode)
openFile fp "ab+" = coerce (IO.openBinaryFile fp IO.AppendMode)
#endif
openFile _ m = error ("openFile unknown mode: " ++ show m)
{-# NOINLINE openFile #-}
{-# ANN openFile hasBlackBox #-}
closeFile
:: File
-> SimIO ()
closeFile (File fp) = SimIO (IO.hClose fp)
{-# NOINLINE closeFile #-}
{-# ANN closeFile hasBlackBox #-}
getChar
:: File
-> SimIO Char
getChar (File fp) = SimIO (IO.hGetChar fp)
{-# NOINLINE getChar #-}
{-# ANN getChar hasBlackBox #-}
putChar
:: Char
-> File
-> SimIO ()
putChar c (File fp) = SimIO (IO.hPutChar fp c)
{-# NOINLINE putChar #-}
{-# ANN putChar hasBlackBox #-}
getLine
:: forall n
. KnownNat n
=> File
-> Reg (Vec n (Unsigned 8))
-> SimIO Int
getLine (File fp) (Reg r) = SimIO $ do
s <- IO.hGetLine fp
let d = snatToNum (SNat @n) - length s
when (d < 0) (IO.hSeek fp IO.RelativeSeek (toInteger d))
modifyIORef r (rep s)
return 0
where
rep :: String -> Vec m (Unsigned 8) -> Vec m (Unsigned 8)
rep [] vs = vs
rep (x:xs) (Cons _ vs) = Cons (toEnum (fromEnum x)) (rep xs vs)
rep _ Nil = Nil
{-# NOINLINE getLine #-}
{-# ANN getLine hasBlackBox #-}
isEOF
:: File
-> SimIO Bool
isEOF (File fp) = SimIO (IO.hIsEOF fp)
{-# NOINLINE isEOF #-}
{-# ANN isEOF hasBlackBox #-}
seek
:: File
-> Integer
-> Int
-> SimIO Int
seek (File fp) pos mode = SimIO (IO.hSeek fp (toEnum mode) pos >> return 0)
{-# NOINLINE seek #-}
{-# ANN seek hasBlackBox #-}
rewind
:: File
-> SimIO Int
rewind (File fp) = SimIO (IO.hSeek fp IO.AbsoluteSeek 0 >> return 0)
{-# NOINLINE rewind #-}
{-# ANN rewind hasBlackBox #-}
tell
:: File
-> SimIO Integer
tell (File fp) = SimIO (IO.hTell fp)
{-# NOINLINE tell #-}
{-# ANN tell hasBlackBox #-}
flush
:: File
-> SimIO ()
flush (File fp) = SimIO (IO.hFlush fp)
{-# NOINLINE flush #-}
{-# ANN flush hasBlackBox #-}
mealyIO
:: KnownDomain dom
=> Clock dom
-> (s -> i -> SimIO o)
-> SimIO s
-> Signal dom i
-> Signal dom o
mealyIO !_ f (SimIO i) inp = unsafePerformIO (i >>= go inp)
where
go q@(~(k :- ks)) s =
(:-) <$> unSimIO (f s k) <*> unsafeInterleaveIO ((q `seq` go ks s))
{-# NOINLINE mealyIO #-}