packages feed

co-feldspar-0.1.0.1: src/Feldspar/Hardware/Primitive/Backend.hs

{-# language GADTs #-}
{-# language QuasiQuotes #-}
{-# language ScopedTypeVariables #-}
{-# language FlexibleContexts #-}

{-# language PolyKinds #-}

module Feldspar.Hardware.Primitive.Backend where

import Feldspar.Hardware.Primitive

import Data.Constraint (Dict(..))
import Data.Proxy

-- syntactic.
import Language.Syntactic
import qualified Language.Syntactic.Traversal as Syn (Args(Nil))

-- hardware-edsl.
import Language.Embedded.Hardware hiding (Sym)
import Language.Embedded.Hardware.Expression.Represent
import Language.Embedded.Hardware.Expression.Represent.Bit (Bits, ni)
import Language.Embedded.Hardware.Expression.Hoist (lift, Kind)
import Language.Embedded.Hardware.Command.CMD (IArray(..))
import qualified Language.Embedded.Hardware.Expression.Hoist as Hoist

-- language-vhdl.
import Language.Embedded.VHDL (VHDL)
import qualified Language.VHDL          as VHDL
import qualified Language.Embedded.VHDL as VHDL

import GHC.TypeLits

--------------------------------------------------------------------------------
-- * Compilation of hardware primitives.
--------------------------------------------------------------------------------

viewLitPrim :: ASTF HardwarePrimDomain a -> Maybe a
viewLitPrim (Sym (Lit a :&: _ )) = Just a
viewLitPrim _                    = Nothing

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

-- todo: should we declare types here as well?
compLiteral :: forall a . PrimType a => (a -> String) -> a -> VHDL VHDL.Expression
compLiteral f = return . exp
  where
    exp :: a -> VHDL.Expression
    exp = lift . VHDL.literal . VHDL.number . f

compNum  :: PrimType a => a -> VHDL VHDL.Expression
compNum  = compLiteral primTypeVal

compBits :: PrimType a => a -> VHDL VHDL.Expression
compBits = compLiteral primTypeBits

instance CompileType HardwarePrimType
  where
    compileType _ (v :: proxy a) =
      compTypeSign (hardwareRep :: HardwarePrimTypeRep a)      
    compileLit _ a = case hardwarePrimTypeOf a of
      BoolHT    -> compNum a
      IntegerHT -> compNum a
      Int8HT    -> compNum a
      Int16HT   -> compNum a
      Int32HT   -> compNum a
      Int64HT   -> compNum a
      Word8HT   -> compNum a
      Word16HT  -> compNum a
      Word32HT  -> compNum a
      Word64HT  -> compNum a
      BitsHT    -> compNum a
    compileBits _ a = case hardwarePrimTypeOf a of
      BoolHT    -> compBits a
      IntegerHT -> compBits a
      Int8HT    -> compBits a
      Int16HT   -> compBits a
      Int32HT   -> compBits a
      Int64HT   -> compBits a
      Word8HT   -> compBits a
      Word16HT  -> compBits a
      Word32HT  -> compBits a
      Word64HT  -> compBits a
      BitsHT    -> compBits a

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

instance CompileExp Prim
  where
    compE = compPrim

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

compSize :: Int -> VHDL.Primary
compSize = VHDL.literal . VHDL.number . primTypeVal

compTypeSize :: forall a . HardwarePrimTypeRep a -> VHDL.Primary
compTypeSize BoolHT    = compSize (1  :: Int)
compTypeSize IntegerHT = compSize (32 :: Int)
compTypeSize Int8HT    = compSize (8  :: Int)
compTypeSize Int16HT   = compSize (16 :: Int)
compTypeSize Int32HT   = compSize (32 :: Int)
compTypeSize Int64HT   = compSize (64 :: Int)
compTypeSize Word8HT   = compSize (8  :: Int)
compTypeSize Word16HT  = compSize (16 :: Int)
compTypeSize Word32HT  = compSize (32 :: Int)
compTypeSize Word64HT  = compSize (64 :: Int)
compTypeSize b@BitsHT  = compSizeBits b
  where
    compSizeBits :: forall n . KnownNat n =>
      HardwarePrimTypeRep (Bits n) -> VHDL.Primary
    compSizeBits rep = compSize (fromInteger (ni (Proxy :: Proxy n)))

compTypeSign :: forall a. HardwarePrimTypeRep a -> VHDL VHDL.Type
compTypeSign BoolHT    = declareType (Proxy :: Proxy a)
compTypeSign IntegerHT = declareType (Proxy :: Proxy a)
compTypeSign Int8HT    = declareType (Proxy :: Proxy a)
compTypeSign Int16HT   = declareType (Proxy :: Proxy a)
compTypeSign Int32HT   = declareType (Proxy :: Proxy a)
compTypeSign Int64HT   = declareType (Proxy :: Proxy a)
compTypeSign Word8HT   = declareType (Proxy :: Proxy a)
compTypeSign Word16HT  = declareType (Proxy :: Proxy a)
compTypeSign Word32HT  = declareType (Proxy :: Proxy a)
compTypeSign Word64HT  = declareType (Proxy :: Proxy a)
compTypeSign BitsHT    = declareType (Proxy :: Proxy a)

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

compExpr   :: [ASTF HardwarePrimDomain a] -> ([VHDL.Relation] -> VHDL.Expression) -> VHDL Kind
compExpr as f = do
  as' <- mapM compKind as
  return $ Hoist.E $ f $ map lift as'

compRel    :: [ASTF HardwarePrimDomain a] -> ([VHDL.ShiftExpression] -> VHDL.Relation) -> VHDL Kind
compRel as f = do
  as' <- mapM compKind as
  return $ Hoist.R $ f $ map lift as'

compShift :: ASTF HardwarePrimDomain a -> ASTF HardwarePrimDomain b -> (VHDL.SimpleExpression -> VHDL.SimpleExpression -> VHDL.ShiftExpression) -> VHDL Kind
compShift a b f = do
  a' <- compKind a
  b' <- compKind b
  tf <- compTypeSign (getDecor b)
  return $ Hoist.Sh $ f (lift a') $ lift $ VHDL.uCast (lift b') tf tt
  where
    tt :: VHDL.Type
    tt = VHDL.integer Nothing

compSimple :: [ASTF HardwarePrimDomain a] -> ([VHDL.Term] -> VHDL.SimpleExpression) -> VHDL Kind
compSimple as f = do
  as' <- mapM compKind as
  return $ Hoist.Si $ f $ map lift as'

compTerm   :: [ASTF HardwarePrimDomain a] -> ([VHDL.Factor] -> VHDL.Term) -> VHDL Kind
compTerm as f = do
  as' <- mapM compKind as
  return $ Hoist.T $ f $ map lift as'

compFactor :: [ASTF HardwarePrimDomain a] -> ([VHDL.Primary] -> VHDL.Factor) -> VHDL Kind
compFactor as f = do
  as' <- mapM compKind as
  return $ Hoist.F $ f $ map lift as'

compCast :: forall a b . HardwarePrimTypeRep a -> ASTF HardwarePrimDomain b -> VHDL Kind
compCast tt a = do
  a'  <- compKind a
  tt' <- compTypeSign tt
  tf' <- compTypeSign tf
  return $ Hoist.E $ VHDL.uCast (lift a') tf' tt'
  where
    tf :: HardwarePrimTypeRep b
    tf = getDecor a

isSigned  :: HardwarePrimTypeRep x -> Maybe Bool
isSigned (Int8HT)   = Just True
isSigned (Int16HT)  = Just True
isSigned (Int32HT)  = Just True
isSigned (Int64HT)  = Just True    
isSigned (Word8HT)  = Just False
isSigned (Word16HT) = Just False
isSigned (Word32HT) = Just False
isSigned (Word64HT) = Just False    
isSigned _          = Nothing

isInteger :: HardwarePrimTypeRep x -> Maybe Bool
isInteger (IntegerHT) = Just True
isInteger _           = Nothing

width :: HardwarePrimTypeRep x -> Int
width (IntegerHT) = 32
width (Int8HT)    = 8
width (Int16HT)   = 16
width (Int32HT)   = 32
width (Int64HT)   = 64
width (Word8HT)   = 8
width (Word16HT)  = 16
width (Word32HT)  = 32
width (Word64HT)  = 64
width _           = 0

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

compKind :: ASTF HardwarePrimDomain a -> VHDL Kind
compKind = simpleMatch (\(s :&: t) -> go t s)
  where
    go :: forall sig
        . HardwarePrimTypeRep (DenResult sig)
        -> HardwarePrimConstructs sig
        -> Args (AST HardwarePrimDomain) sig
        -> VHDL Kind
    go _ (FreeVar v) Syn.Nil =
      return $ Hoist.P $ VHDL.name $ VHDL.NSimple $ VHDL.Ident v
    go t (Lit a)     Syn.Nil | Dict <- hardwarePrimWitType t =
      fmap Hoist.E $ compileLit (Proxy :: Proxy HardwarePrimType) a
      
    go t (Cast f) (a :* Syn.Nil) = compCast t a
    go t I2N (a :* Syn.Nil) = compCast t a

    go _ Neg (a :* Syn.Nil)      = compSimple [a]    (one VHDL.neg)
    go _ Add (a :* b :* Syn.Nil) = compSimple [a, b] VHDL.add
    go _ Sub (a :* b :* Syn.Nil) = compSimple [a, b] VHDL.sub
    go _ Mul (a :* b :* Syn.Nil) = compTerm   [a, b] VHDL.mul
    
    go _ Div (a :* b :* Syn.Nil) = compTerm   [a, b] VHDL.div
    go _ Mod (a :* b :* Syn.Nil) = compTerm   [a, b] VHDL.mod
    
    go _ Not (a :* Syn.Nil)      = compFactor [a]    (one VHDL.not)
    go _ And (a :* b :* Syn.Nil) = compExpr   [a, b] VHDL.and
    go _ Or  (a :* b :* Syn.Nil) = compExpr   [a, b] VHDL.or
    
    go _ Eq  (a :* b :* Syn.Nil) = compRel    [a, b] (two VHDL.eq)
    go _ Lt  (a :* b :* Syn.Nil) = compRel    [a, b] (two VHDL.lt)
    go _ Lte (a :* b :* Syn.Nil) = compRel    [a, b] (two VHDL.lte)
    go _ Gt  (a :* b :* Syn.Nil) = compRel    [a, b] (two VHDL.gt)
    go _ Gte (a :* b :* Syn.Nil) = compRel    [a, b] (two VHDL.gte)

    go _ BitAnd   (a :* b :* Syn.Nil) = compExpr   [a, b] VHDL.and
    go _ BitOr    (a :* b :* Syn.Nil) = compExpr   [a, b] VHDL.or
    go _ BitXor   (a :* b :* Syn.Nil) = compExpr   [a, b] VHDL.xor
    go _ BitCompl (a :* Syn.Nil)      = compFactor [a]    (one VHDL.not)
    
    go _ ShiftL  (a :* b :* Syn.Nil) = compShift a b VHDL.sll
    go _ ShiftR  (a :* b :* Syn.Nil) = compShift a b VHDL.srl
    go _ RotateL (a :* b :* Syn.Nil) = compShift a b VHDL.rol
    go _ RotateR (a :* b :* Syn.Nil) = compShift a b VHDL.ror

    go _ (ArrIx (IArrayC arr)) (i :* Syn.Nil) =
      do i' <- compPrim $ Prim i
         return $ Hoist.P $ VHDL.name $ VHDL.indexed (VHDL.simple arr) (lift i')

    one :: (a -> b) -> ([a] -> b)
    one f = \[a] -> f a

    two :: (a -> a -> b) -> ([a] -> b)
    two f = \[a, b] -> f a b
    
--------------------------------------------------------------------------------

compPrim :: Prim a -> VHDL VHDL.Expression
compPrim = fmap lift . compKind . unPrim

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