{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, MultiWayIf #-}
module Platform.ClashAccelerator where
--import qualified Prelude as P
import Spec.Machine
import Utility.Utility
import Spec.CSR
import Data.Int
import Data.Word
import Data.Bits
import Data.Char
import Data.Maybe
import qualified Data.List as L
import Control.Applicative
import Control.Monad
import Control.Monad.State
import Control.Monad.Trans
import Control.Monad.Trans.Maybe
import qualified Data.Map as S
import Platform.ExecuteClash
import qualified Spec.Decode as D
import Clash.Prelude
import Platform.GcdExpr

data MMIOClash = MMIOClash {  registers :: Vec 31 Int32, pc :: Int32, nextPC :: Int32 , store:: Maybe (Int32,Int32,(Bool,Bool,Bool,Bool)), gcdI1 :: Int32, gcdI2 :: Int32 , exception :: Bool }
              deriving (Show) -- Beurk, I need to use Word32 instead of Int32 for the addresses.

type MState = State MMIOClash
type MMState = MaybeT MState

instance RiscvMachine MState Int32 where
  getRegister reg = state $ \comp -> (if reg == 0 then 0 else (registers comp) !! (fromIntegral reg-1), comp)
  setRegister reg val = state $ \comp ->((), if reg == 0 then comp else comp { registers = replace (fromIntegral reg-1) (fromIntegral val) (registers comp) })
-- Fake load and stores
  loadWord s a = state $ \comp -> (
                        if (a == 0x4000) 
                         then gcdI1 comp
                         else if (a == 0x4004)
                           then gcdI2 comp
                           else 
                            0                   
     ,comp)
  storeWord s a v = state $ \comp -> ((), comp{store = Just (fromIntegral a, fromIntegral v,(True,True,True,True))})
  getCSRField field = state $ \comp -> (0, comp)
  unsafeSetCSRField field val = state $ \comp -> ((), comp)
  getPC = state $ \comp -> (pc comp, comp)
  setPC val = state $ \comp -> ((), comp { nextPC = fromIntegral val})
  commit = state $ \comp -> ((), comp { pc = nextPC comp })
  getPrivMode = state $ \comp -> (Machine, comp) 
  setPrivMode m = state $ \comp -> ((), comp)

-- GCD program 
oneStep :: Int32 ->  MState ()
oneStep i = do 
  result <- runMaybeT $ do
    pc <- getPC
    setPC (pc + 4)
    let a = D.decode D.RV32I $ fromIntegral i
    execute a
  case result of
    Nothing -> commit >> (state $ \comp -> ((), comp{exception = True})) -- early return
    Just r -> commit >> return r -- this is a ()

wrap :: Int32 -> MMIOClash-> MMIOClash
wrap i s = snd $ runState (oneStep i) s

topEntity:: Clock System -> Reset System -> Enable System ->
    Signal System (Int32,Int32) -> Signal System (Int32,Int32) 
topEntity = exposeClockResetEnable machine
machine = mealy 
     (\(iregister::Vec 31 Int32,pc::Int32) 
                     (gcd1::Int32, gcd2::Int32) ->
                      let i = gcdState pc 
                      in 
                      let newstate = wrap i MMIOClash{registers = reverse iregister,
                                                      store = Nothing,
                                                      pc = pc,
                                                      gcdI1 = gcd1,
                                                      gcdI2 = gcd2,
                                                      exception = False
                                                     }
                      in
                        let storeNext = store newstate
                            npc = nextPC newstate
                        in
                          ((reverse $ registers newstate, npc ), (

--                          ((iregister, pc ), (
                           (\(x,y,z)->x) $ fromMaybe (0,0,(False,False,False,False)) storeNext,
                           (\(x,y,z)->y) $ fromMaybe (0,0,(False,False,False,False)) storeNext
                           ))) (replicate (SNat :: SNat 31) 0, 0)
