{-# 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
#if MIN_VERSION_base(4,18,0)
hiding (SNat)
#endif
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)
{-# CLASH_OPAQUE fmapSimIO# #-}
{-# ANN fmapSimIO# hasBlackBox #-}
instance Applicative SimIO where
pure = pureSimIO#
(<*>) = apSimIO#
pureSimIO# :: a -> SimIO a
pureSimIO# a = SimIO (pure a)
{-# CLASH_OPAQUE pureSimIO# #-}
{-# ANN pureSimIO# hasBlackBox #-}
apSimIO# :: SimIO (a -> b) -> SimIO a -> SimIO b
apSimIO# (SimIO f) (SimIO m) = SimIO (f <*> m)
{-# CLASH_OPAQUE apSimIO# #-}
{-# ANN apSimIO# hasBlackBox #-}
instance Monad SimIO where
#if !MIN_VERSION_base(4,16,0)
return = pureSimIO#
#endif
(>>=) = 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
{-# CLASH_OPAQUE bindSimIO# #-}
{-# ANN bindSimIO# hasBlackBox #-}
display
:: String
-> SimIO ()
display s = SimIO (putStrLn s)
{-# CLASH_OPAQUE display #-}
{-# ANN display hasBlackBox #-}
finish
:: Integer
-> SimIO a
finish i = return (error (show i))
{-# CLASH_OPAQUE 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)
{-# CLASH_OPAQUE reg #-}
{-# ANN reg hasBlackBox #-}
readReg :: Reg a -> SimIO a
readReg (Reg a) = SimIO (readIORef a)
{-# CLASH_OPAQUE readReg #-}
{-# ANN readReg hasBlackBox #-}
writeReg
:: Reg a
-> a
-> SimIO ()
writeReg (Reg r) a = SimIO (writeIORef r a)
{-# CLASH_OPAQUE 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)
{-# CLASH_OPAQUE openFile #-}
{-# ANN openFile hasBlackBox #-}
closeFile
:: File
-> SimIO ()
closeFile (File fp) = SimIO (IO.hClose fp)
{-# CLASH_OPAQUE closeFile #-}
{-# ANN closeFile hasBlackBox #-}
getChar
:: File
-> SimIO Char
getChar (File fp) = SimIO (IO.hGetChar fp)
{-# CLASH_OPAQUE getChar #-}
{-# ANN getChar hasBlackBox #-}
putChar
:: Char
-> File
-> SimIO ()
putChar c (File fp) = SimIO (IO.hPutChar fp c)
{-# CLASH_OPAQUE 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
{-# CLASH_OPAQUE getLine #-}
{-# ANN getLine hasBlackBox #-}
isEOF
:: File
-> SimIO Bool
isEOF (File fp) = SimIO (IO.hIsEOF fp)
{-# CLASH_OPAQUE isEOF #-}
{-# ANN isEOF hasBlackBox #-}
seek
:: File
-> Integer
-> Int
-> SimIO Int
seek (File fp) pos mode = SimIO (IO.hSeek fp (toEnum mode) pos >> return 0)
{-# CLASH_OPAQUE seek #-}
{-# ANN seek hasBlackBox #-}
rewind
:: File
-> SimIO Int
rewind (File fp) = SimIO (IO.hSeek fp IO.AbsoluteSeek 0 >> return 0)
{-# CLASH_OPAQUE rewind #-}
{-# ANN rewind hasBlackBox #-}
tell
:: File
-> SimIO Integer
tell (File fp) = SimIO (IO.hTell fp)
{-# CLASH_OPAQUE tell #-}
{-# ANN tell hasBlackBox #-}
flush
:: File
-> SimIO ()
flush (File fp) = SimIO (IO.hFlush fp)
{-# CLASH_OPAQUE 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))
{-# CLASH_OPAQUE mealyIO #-}