{-# 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)
import Data.IORef
import GHC.TypeLits
hiding (SNat)
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)
data SimIO a = SimIO {unSimIO :: !(IO a)}
{-# ANN unSimIO hasBlackBox #-}
instance Functor SimIO where
fmap = fmapSimIO#
fmapSimIO# :: (a -> b) -> SimIO a -> SimIO b
fmapSimIO# f (SimIO m) = SimIO (fmap f m)
{-# OPAQUE fmapSimIO# #-}
{-# ANN fmapSimIO# hasBlackBox #-}
instance Applicative SimIO where
pure = pureSimIO#
(<*>) = apSimIO#
pureSimIO# :: a -> SimIO a
pureSimIO# a = SimIO (pure a)
{-# OPAQUE pureSimIO# #-}
{-# ANN pureSimIO# hasBlackBox #-}
apSimIO# :: SimIO (a -> b) -> SimIO a -> SimIO b
apSimIO# (SimIO f) (SimIO m) = SimIO (f <*> m)
{-# OPAQUE apSimIO# #-}
{-# ANN apSimIO# hasBlackBox #-}
instance Monad SimIO where
(>>=) = bindSimIO#
bindSimIO# :: SimIO a -> (a -> SimIO b) -> SimIO b
bindSimIO# (SimIO m) k = SimIO (m >>= (\x -> x `seqX` unSimIO (k x)))
{-# OPAQUE bindSimIO# #-}
{-# ANN bindSimIO# hasBlackBox #-}
display
:: String
-> SimIO ()
display s = SimIO (putStrLn s)
{-# OPAQUE display #-}
{-# ANN display hasBlackBox #-}
finish
:: Integer
-> SimIO a
finish i = return (error (show i))
{-# OPAQUE finish #-}
{-# ANN finish hasBlackBox #-}
data Reg a = Reg !(IORef a)
reg
:: a
-> SimIO (Reg a)
reg a = SimIO (Reg <$> newIORef a)
{-# OPAQUE reg #-}
{-# ANN reg hasBlackBox #-}
readReg :: Reg a -> SimIO a
readReg (Reg a) = SimIO (readIORef a)
{-# OPAQUE readReg #-}
{-# ANN readReg hasBlackBox #-}
writeReg
:: Reg a
-> a
-> SimIO ()
writeReg (Reg r) a = SimIO (writeIORef r a)
{-# OPAQUE writeReg #-}
{-# ANN writeReg hasBlackBox #-}
data File = File !IO.Handle
openFile
:: FilePath
-> String
-> SimIO File
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)
openFile _ m = error ("openFile unknown mode: " ++ show m)
fmapSimIO# :: forall a b. (a -> b) -> SimIO a -> SimIO b
{-# OPAQUE openFile #-}
{-# ANN openFile hasBlackBox #-}
closeFile
:: File
-> SimIO ()
closeFile (File fp) = SimIO (IO.hClose fp)
{-# OPAQUE closeFile #-}
{-# ANN closeFile hasBlackBox #-}
getChar
:: File
-> SimIO Char
getChar (File fp) = SimIO (IO.hGetChar fp)
{-# OPAQUE getChar #-}
{-# ANN getChar hasBlackBox #-}
putChar
:: Char
-> File
-> SimIO ()
putChar c (File fp) = SimIO (IO.hPutChar fp c)
{-# OPAQUE putChar #-}
{-# ANN putChar hasBlackBox #-}
String -> SimIO ()
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
{-# OPAQUE getLine #-}
{-# ANN getLine hasBlackBox #-}
isEOF
:: File
-> SimIO Bool
isEOF (File fp) = SimIO (IO.hIsEOF fp)
{-# 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)
{-# OPAQUE seek #-}
{-# ANN seek hasBlackBox #-}
rewind
:: File
-> SimIO Int
rewind (File fp) = SimIO (IO.hSeek fp IO.AbsoluteSeek 0 >> return 0)
{-# OPAQUE rewind #-}
{-# ANN rewind hasBlackBox #-}
tell
:: File
-> SimIO Integer
tell (File fp) = SimIO (IO.hTell fp)
{-# OPAQUE tell #-}
{-# ANN tell hasBlackBox #-}
flush
:: File
-> SimIO ()
flush (File fp) = SimIO (IO.hFlush fp)
{-# OPAQUE flush #-}
{-# ANN flush hasBlackBox #-}
getLine :: forall (n :: Nat).
KnownNat n =>
File -> Reg (Vec n (Unsigned 8)) -> SimIO Int
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))
{-# OPAQUE mealyIO #-}