packages feed

co-feldspar-0.1.0.1: src/Feldspar/Hardware/Frontend.hs

{-# language TypeFamilies          #-}
{-# language FlexibleInstances     #-}
{-# language FlexibleContexts      #-}
{-# language MultiParamTypeClasses #-}
{-# language UndecidableInstances  #-}
{-# language Rank2Types            #-}
{-# language ScopedTypeVariables   #-}

module Feldspar.Hardware.Frontend where

import Feldspar.Representation
import Feldspar.Frontend
import Feldspar.Sugar
import Feldspar.Array.Vector hiding (reverse)
import Feldspar.Array.Buffered (ArraysSwap(..))
import Feldspar.Hardware.Primitive
import Feldspar.Hardware.Expression
import Feldspar.Hardware.Representation
import Data.Struct

import Control.Monad.Identity (Identity)
import Data.Constraint hiding (Sub)
import Data.Int
import Data.List (genericLength)
import Data.Proxy
import Data.Typeable (Typeable)

-- syntactic.
import Language.Syntactic (Syntactic(..))
import Language.Syntactic.Functional
import qualified Language.Syntactic as Syntactic

-- operational-higher.
import qualified Control.Monad.Operational.Higher as Oper

-- hardware-edsl.
import Language.Embedded.Hardware.Command (Signal, Ident, Mode, ToIdent)
import qualified Language.Embedded.Hardware.Command   as Imp
import qualified Language.Embedded.Hardware.Interface as Imp
import qualified Language.Embedded.Hardware.Expression.Represent as Imp

import Prelude hiding (length, toInteger)
import qualified Prelude as P

--------------------------------------------------------------------------------
-- * Expressions.
--------------------------------------------------------------------------------

instance Value HExp
  where
    value = sugarSymHardware . Lit

instance Share HExp
  where
    share = sugarSymHardware (Let "")

instance Iterate HExp
  where
    loop = sugarSymHardware ForLoop

instance Cond HExp
  where
    cond = sugarSymHardware Cond

instance Equality HExp
  where
    (==) = sugarSymPrimHardware Eq

instance Ordered HExp
  where
    (<)  = sugarSymPrimHardware Lt
    (<=) = sugarSymPrimHardware Lte
    (>)  = sugarSymPrimHardware Gt
    (>=) = sugarSymPrimHardware Gte

instance Logical HExp
  where
    not  = sugarSymPrimHardware Not
    (&&) = sugarSymPrimHardware And
    (||) = sugarSymPrimHardware Or

instance Multiplicative HExp
  where
    mult = sugarSymPrimHardware Mul
    div  = sugarSymPrimHardware Div
    mod  = sugarSymPrimHardware Mod

instance Bitwise HExp
  where
    complement = sugarSymPrimHardware BitCompl
    (.&.) = sugarSymPrimHardware BitAnd
    (.|.) = sugarSymPrimHardware BitOr
    xor   = sugarSymPrimHardware BitXor
    sll   = sugarSymPrimHardware ShiftL
    srl   = sugarSymPrimHardware ShiftR
    rol   = sugarSymPrimHardware RotateL
    ror   = sugarSymPrimHardware RotateR

instance Casting HExp
  where
    i2n = sugarSymPrimHardware I2N
    i2b = error "hardware.todo: i2b"
    b2i = error "hardware.todo: b2i"

cast :: (HardwarePrimType a, HardwarePrimType b) => (a -> b) -> HExp a -> HExp b
cast f = sugarSymPrimHardware (Cast f)

toInteger :: (HardwarePrimType a, Integral a) => HExp a -> HExp Integer
toInteger = cast (fromIntegral)

toUnsigned :: (HardwarePrimType a, Integral a, HardwarePrimType b, Num b) => HExp a -> HExp b
toUnsigned = cast (fromIntegral)

toSigned :: (HardwarePrimType a, Integral a, HardwarePrimType b, Num b) => HExp a -> HExp b
toSigned = cast (fromIntegral)

{-
toBits :: (HardwarePrimType a, Integral a, KnownNat b) => HExp a -> HExp (Bits b)
toBits = cast (bitFrom
-}

toIntegral :: forall a . HardwarePrimType a => Proxy a -> HExp Integer -> HExp a
toIntegral _ = case hardwareRep :: HardwarePrimTypeRep a of
  IntegerHT -> id
  Int8HT    -> toSigned
  Int16HT   -> toSigned
  Int32HT   -> toSigned
  Int64HT   -> toSigned
  Word8HT   -> toUnsigned
  Word16HT  -> toUnsigned
  Word32HT  -> toUnsigned
  Word64HT  -> toUnsigned

--------------------------------------------------------------------------------

instance (Bounded a, HType a) => Bounded (HExp a)
  where
    minBound = value minBound
    maxBound = value maxBound

instance (Num a, HType' a) => Num (HExp a)
  where
    fromInteger = value . fromInteger
    (+)         = sugarSymHardware Add
    (-)         = sugarSymHardware Sub
    (*)         = sugarSymHardware Mul
    negate      = sugarSymHardware Neg
    abs         = error "abs not implemeted for `HExp`"
    signum      = error "signum not implemented for `HExp`"

--------------------------------------------------------------------------------
-- * Instructions.
--------------------------------------------------------------------------------

desugar :: (Syntactic a, Domain a ~ HardwareDomain) => a -> HExp (Internal a)
desugar = HExp . Syntactic.desugar

sugar   :: (Syntactic a, Domain a ~ HardwareDomain) => HExp (Internal a) -> a
sugar   = Syntactic.sugar . unHExp

resugar
  :: ( Syntactic a
     , Syntactic b
     , Internal a ~ Internal b
     , Domain a   ~ HardwareDomain
     , Domain b   ~ HardwareDomain
     )
  => a -> b
resugar = Syntactic.resugar

--------------------------------------------------------------------------------

instance References Hardware
  where
    type Reference Hardware = Ref    
    initRef    = Hardware . fmap Ref . mapStructA (Imp.initVariable) . resugar
    newRef     = Hardware . fmap Ref . mapStructA (const Imp.newVariable) $ typeRep
    getRef     = Hardware . fmap resugar . mapStructA getRef' . unRef
    setRef ref
      = Hardware
      . sequence_
      . zipListStruct setRef' (unRef ref)
      . resugar
    unsafeFreezeRef
      = Hardware
      . fmap resugar
      . mapStructA freezeRef'
      . unRef

-- 'Imp.getRef' specialized hardware.
getRef' :: forall b . HardwarePrimType b => Imp.Variable b -> Oper.Program HardwareCMD (Oper.Param2 HExp HardwarePrimType) (HExp b)
getRef' = withHType (Proxy :: Proxy b) Imp.getVariable

-- 'Imp.setRef' specialized to hardware.
setRef' :: forall b . HardwarePrimType b => Imp.Variable b -> HExp b -> Oper.Program HardwareCMD (Oper.Param2 HExp HardwarePrimType) ()
setRef' = withHType (Proxy :: Proxy b) Imp.setVariable

-- 'Imp.unsafeFreezeRef' specialized to hardware.
freezeRef' :: forall b . HardwarePrimType b => Imp.Variable b -> Oper.Program HardwareCMD (Oper.Param2 HExp HardwarePrimType) (HExp b)
freezeRef' = withHType (Proxy :: Proxy b) Imp.unsafeFreezeVariable

--------------------------------------------------------------------------------

instance Slicable HExp (Arr a)
  where
    slice from len (Arr o l arr) = Arr (o+from) len arr

instance Finite HExp (Arr a)
  where
    length = arrLength

instance Arrays Hardware
  where
    type Array Hardware = Arr
    newArr len
      = Hardware
      $ fmap (Arr 0 len)
      $ mapStructA (const (Imp.newVArray (toInteger len)))
      $ typeRep
    initArr as
      = Hardware
      $ fmap (Arr 0 len . Node)
      $ Imp.initVArray as
      where len = value $ genericLength as
    getArr arr ix
      = Hardware
      $ fmap resugar
      $ mapStructA (flip getArr' (ix + arrOffset arr))
      $ unArr arr
    setArr arr ix a
      = Hardware
      $ sequence_
      $ zipListStruct (\v a -> setArr' a (ix + arrOffset arr) v) (resugar a)
      $ unArr arr
    copyArr arr brr
      = Hardware
      $ sequence_
      $ zipListStruct (\a b ->
          Imp.copyVArray
            (a, toInteger (arrOffset arr))
            (b, toInteger (arrOffset brr))
            (toInteger (length brr)))
        (unArr arr)
        (unArr brr)

-- 'Imp.getVArr' specialized to hardware.
getArr' :: forall b . HardwarePrimType b => Imp.VArray b -> HExp Index -> Oper.Program HardwareCMD (Oper.Param2 HExp HardwarePrimType) (HExp b)
getArr' varr ix = withHType (Proxy :: Proxy b) $ Imp.getVArray varr (toInteger ix)

-- 'Imp.setVArr' specialized to hardware.
setArr' :: forall b . HardwarePrimType b => Imp.VArray b -> HExp Index -> HExp b -> Oper.Program HardwareCMD (Oper.Param2 HExp HardwarePrimType) ()
setArr' varr ix b = withHType (Proxy :: Proxy b) $ Imp.setVArray varr (toInteger ix) b

--------------------------------------------------------------------------------

instance Syntax HExp a => Indexed HExp (IArr a)
  where
    type ArrElem (IArr a) = a
    (!) (IArr off len a) ix = resugar $ mapStruct index a
      where
        index :: HardwarePrimType b => Imp.IArray b -> HExp b
        index arr = sugarSymPrimHardware (ArrIx arr) (toInteger (ix + off))

instance Slicable HExp (IArr a)
  where
    slice from len (IArr o l arr) = IArr (o+from) len arr

instance Finite HExp (IArr a)
  where
    length = iarrLength
      
instance IArrays Hardware
  where
    type IArray Hardware = IArr
    unsafeFreezeArr arr
      = Hardware
      $ fmap (IArr (arrOffset arr) (length arr))
      $ mapStructA (Imp.unsafeFreezeVArray)
      $ unArr arr
    unsafeThawArr iarr
      = Hardware
      $ fmap (Arr (iarrOffset iarr) (length iarr))
      $ mapStructA (Imp.unsafeThawVArray)
      $ unIArr iarr

--------------------------------------------------------------------------------

-- | Short-hand for hardware pull vectors.
type HPull a = Pull HExp a

-- | Short-hand for hardware push vectors.
type HPush a = Push Hardware a

-- | Short-hand for hardware manifest vectors.
type HManifest a = Manifest Hardware a

instance Syntax HExp (HExp a) => Pushy Hardware (IArr (HExp a)) (HExp a)
  where
    toPush iarr = toPush (M iarr :: Manifest Hardware (HExp a))

instance ViewManifest Hardware (IArr (HExp a)) (HExp a)
  where
    viewManifest = Just . M

instance Manifestable Hardware (IArr (HExp a)) (HExp a)    

--------------------------------------------------------------------------------

instance Control Hardware
  where
    iff c t f
      = Hardware
      $ Imp.iff (resugar c)
          (unHardware t)
          (unHardware f)

-- todo: not synthesizable in general, fix to static ranges and add exit.
instance Loop Hardware
  where
    while c body
      = Hardware
      $ Imp.while
          (fmap resugar $ unHardware c)
          (unHardware body)
    for lower _ upper body
      = Hardware
      $ Imp.for
          (resugar (toInteger lower))
          (resugar (toInteger upper))
          (unHardware . body . resugar . toIntegral (proxyE lower))
      where
        proxyE :: forall a . HExp a -> Proxy a
        proxyE _ = Proxy

--------------------------------------------------------------------------------
-- ** Hardware instructions.
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Singals.
--------------------------------------------------------------------------------

-- | Creates a new signal with a given initial value.
initSignal :: HType' a => HExp a -> Hardware (Signal a)
initSignal = Hardware . Imp.initSignal

-- | Creates a new signal.
newSignal :: HType' a => Hardware (Signal a)
newSignal = Hardware $ Imp.newSignal

-- | Get the current value of a signal.
getSignal :: HType' a => Signal a -> Hardware (HExp a)
getSignal = Hardware . Imp.getSignal

-- | Set the current value of a signal.
setSignal :: HType' a => Signal a -> HExp a -> Hardware ()
setSignal s = Hardware . (Imp.setSignal s)

-- | Unsafe version of fetching the contents of a signal.
unsafeFreezeSignal :: HType' a => Signal a -> Hardware (HExp a)
unsafeFreezeSignal = Hardware . Imp.unsafeFreezeSignal

-- | ...
veryUnsafeFreezeSignal :: HType' a => Signal a -> HExp a
veryUnsafeFreezeSignal (Imp.SignalC v) = Imp.varE v
veryUnsafeFreezeSignal _ = error "veryUnsafeFreezeSignal shouldn't be used during evaluation."

--------------------------------------------------------------------------------
-- Arrays.
--------------------------------------------------------------------------------

-- | Arrays based on signals.
type SArr a = Imp.Array a

-- | Creates a empty array of the given length.
newSArr :: HType' a => HExp Index -> Hardware (SArr a)
newSArr = Hardware . Imp.newArray . toInteger

-- | Initialize an array with the given elements.
initSArr :: HType' a => [a] -> Hardware (SArr a)
initSArr = Hardware . Imp.initArray

-- | Fetches the indexed element out of the array.
getSArr :: HType' a => SArr a -> HExp Index -> Hardware (HExp a)
getSArr arr ix = Hardware $ Imp.getArray arr (toInteger ix)

-- | Sets the indexed element of the array to the given value.
setSArr :: HType' a => SArr a -> HExp Index -> HExp a -> Hardware ()
setSArr arr ix val = Hardware $ Imp.setArray arr (toInteger ix) val

-- | ...
veryUnsafeFreezeSArr :: HType' a => Length -> SArr a -> IArr (HExp a)
veryUnsafeFreezeSArr len (Imp.ArrayC v) = IArr 0 (value len) $ Node $ Imp.IArrayC v
veryUnsafeFreezeSArr _ _ = error "veryUnsafeFreezeSArr shouldn't be used during evaluation."

--------------------------------------------------------------------------------
-- Components.
--------------------------------------------------------------------------------
-- todo: the `len` argument isn't strictly needed, the problem is that `IArr`
--       length is `HExp Length` while `Imp.copyArray` expects a pure `Length`.

-- | Hardware signature.
type HSig  = Imp.Sig HardwareCMD HExp HardwarePrimType Identity

--------------------------------------------------------------------------------

-- | Finalize signature with an output of a value.
ret :: forall a . (HType' a, Integral a) => Hardware (HExp a) -> HSig (Signal a -> ())
ret prg = withHType' (Proxy :: Proxy a) $ Imp.output $ \o -> Imp.ret $ Imp.process [] $ do
    v <- unHardware prg
    Imp.setSignal o v

retExpr :: forall a . (HType' a, Integral a) => HExp a -> HSig (Signal a -> ())
retExpr expr = ret (return expr)

-- | Finalize signature with an output of a immutable array.
retIArr :: forall a . (HType' a, Integral a) => Length -> Hardware (IArr (HExp a)) -> HSig (SArr a -> ())
retIArr len prg = withHType' (Proxy :: Proxy a) $
  Imp.outputArray (P.toInteger len) $ \o -> Imp.ret $ Imp.process [] $ do
    (IArr _ _ node) <- unHardware prg
    let iarr = case node of (Node iarr) -> iarr
    arr <- Imp.unsafeThawArray iarr
    Imp.copyArray (o,0) (arr,0) (value (P.toInteger len))

-- | Finalize signature with an output of an array.
retVArr :: forall a . (HType' a, Integral a) => Length -> Hardware (Arr (HExp a)) -> HSig (SArr a -> ())
retVArr len prg = withHType' (Proxy :: Proxy a) $
  Imp.outputArray (P.toInteger len) $ \o -> Imp.ret $ Imp.process [] $ do
    (Arr _ _ node) <- unHardware prg
    let arr = case node of (Node arr) -> arr
    iarr <- Imp.unsafeFreezeVArray arr -- this is so bad.
    brr  <- Imp.unsafeThawArray iarr
    Imp.copyArray (o,0) (brr, 0) (value (P.toInteger len))

--------------------------------------------------------------------------------

-- | Extend signature with an input signal.
input :: forall a b . (HType' a, Integral a) => (HExp a -> HSig b) -> HSig (Signal a -> b)
input f = withHType' (Proxy :: Proxy a) $
  Imp.input $ f . veryUnsafeFreezeSignal

-- | Extend signature with an input array.
inputIArr :: forall a b . (HType' a, Integral a) => Length -> (IArr (HExp a) -> HSig b) -> HSig (SArr a -> b)
inputIArr len f = withHType' (Proxy :: Proxy a) $
  Imp.inputArray (P.toInteger len) $ f . veryUnsafeFreezeSArr len

-- inputVec :: forall a b . (HType' a, Integral a) => Lenght -> () -> HSig (SArr a -> b)


--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------

-- Swap an `Imp.FreePred` constraint with a `HardwarePrimType` one.
withHType :: forall a b . Proxy a
  -> (Imp.PredicateExp HExp a => b)
  -> (HardwarePrimType a => b)
withHType _ f = let rep = hardwareRep :: HardwarePrimTypeRep a in
  case predicateDict rep of
    Dict -> f

withHType' :: forall a b . Proxy a
  -> (Imp.Inhabited a => Imp.Sized a => Imp.PrimType a => b)
  -> (HardwarePrimType a => b)
withHType' _ f = let rep = hardwareRep :: HardwarePrimTypeRep a in
  case (inhabitedDict rep, sizedDict rep, repDict rep) of
    (Dict, Dict, Dict) -> f

-- Proves that a `HardwarePrimTypeRep a` type satisfies `Imp.FreePred`.
predicateDict :: HardwarePrimTypeRep a -> Dict (Imp.PredicateExp HExp a)
predicateDict rep = case rep of
  BoolHT    -> Dict
  IntegerHT -> Dict
  Int8HT    -> Dict
  Int16HT   -> Dict
  Int32HT   -> Dict
  Int64HT   -> Dict
  Word8HT   -> Dict
  Word16HT  -> Dict
  Word32HT  -> Dict
  Word64HT  -> Dict

inhabitedDict :: HardwarePrimTypeRep a -> Dict (Imp.Inhabited a)
inhabitedDict rep = case rep of
  BoolHT    -> Dict
  IntegerHT -> Dict
  Int8HT    -> Dict
  Int16HT   -> Dict
  Int32HT   -> Dict
  Int64HT   -> Dict
  Word8HT   -> Dict
  Word16HT  -> Dict
  Word32HT  -> Dict
  Word64HT  -> Dict

sizedDict :: HardwarePrimTypeRep a -> Dict (Imp.Sized a)
sizedDict rep = case rep of
  BoolHT    -> Dict
  Int8HT    -> Dict
  Int16HT   -> Dict
  Int32HT   -> Dict
  Int64HT   -> Dict
  Word8HT   -> Dict
  Word16HT  -> Dict
  Word32HT  -> Dict
  Word64HT  -> Dict
  t         -> error ("predicateDict failed for " Prelude.++ show t)

repDict :: HardwarePrimTypeRep a -> Dict (Imp.PrimType a)
repDict rep = case rep of
  BoolHT    -> Dict
  IntegerHT -> Dict
  Int8HT    -> Dict
  Int16HT   -> Dict
  Int32HT   -> Dict
  Int64HT   -> Dict
  Word8HT   -> Dict
  Word16HT  -> Dict
  Word32HT  -> Dict
  Word64HT  -> Dict

typeableDict :: HardwarePrimTypeRep a -> Dict (Typeable a)
typeableDict rep = case rep of
  BoolHT    -> Dict
  IntegerHT -> Dict
  Int8HT    -> Dict
  Int16HT   -> Dict
  Int32HT   -> Dict
  Int64HT   -> Dict
  Word8HT   -> Dict
  Word16HT  -> Dict
  Word32HT  -> Dict
  Word64HT  -> Dict

--------------------------------------------------------------------------------