{-# 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.Coerce
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)
newtype SimIO a = SimIO {unSimIO :: IO a}
instance Functor SimIO where
fmap = fmapSimIO#
fmapSimIO# :: (a -> b) -> SimIO a -> SimIO b
fmapSimIO# f (SimIO m) = SimIO (fmap f m)
{-# NOINLINE fmapSimIO# #-}
instance Applicative SimIO where
pure = pureSimIO#
(<*>) = apSimIO#
pureSimIO# :: a -> SimIO a
pureSimIO# a = SimIO (pure a)
{-# NOINLINE pureSimIO# #-}
apSimIO# :: SimIO (a -> b) -> SimIO a -> SimIO b
apSimIO# (SimIO f) (SimIO m) = SimIO (f <*> m)
{-# NOINLINE apSimIO# #-}
instance Monad SimIO where
return = pureSimIO#
(>>=) = bindSimIO#
bindSimIO# :: SimIO a -> (a -> SimIO b) -> SimIO b
bindSimIO# (SimIO m) k = SimIO (m >>= (\x -> x `seqX` coerce k x))
{-# NOINLINE bindSimIO# #-}
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 #-}
newtype Reg a = Reg (IORef a)
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 #-}
newtype File = File IO.Handle
openFile
:: FilePath
-> String
-> SimIO File
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)
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 #-}