packages feed

signals-0.2.0.1: src/Signal/Compiler.hs

{-# LANGUAGE GADTs               #-}
{-# LANGUAGE Rank2Types          #-}
{-# LANGUAGE TypeOperators       #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE FlexibleInstances   #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Signal.Compiler (compiler, compile) where

import Signal.Core               (S(..), Symbol(..), Sig, E(..))
import Signal.Core.Stream
import Signal.Core.Reify
import Signal.Core.Witness

import Signal.Compiler.Interface
import Signal.Compiler.Cycles
import Signal.Compiler.Sorter
import Signal.Compiler.Linker
import Signal.Compiler.Channels

import Control.Arrow             (first)
import Control.Monad.Identity    (Identity)
import Control.Monad.Reader      (ReaderT)
import Control.Monad.State       (State)
import Control.Monad.Operational.Higher
import qualified Control.Monad.Identity as CMI
import qualified Control.Monad.Reader   as CMR
import qualified Control.Monad.State    as CMS

import Data.Either  (partitionEithers)
import Data.Maybe   (fromJust)
import Data.Typeable
import Data.IntMap  (IntMap)
import Data.Ref
import Data.Ref.Map (Name, Map)
import qualified Data.IntMap  as IMap
import qualified Data.Ref.Map as RMap

import Language.VHDL          (Identifier)
import Language.Embedded.VHDL ( Kind
                              , Mode
                              , PredicateExp
                              , CompileExp
                              , SequentialCMD
                              , ConcurrentCMD
                              , HeaderCMD)
import qualified Language.VHDL          as VHDL
import qualified Language.Embedded.VHDL as HDL

import Prelude           hiding (read, Left, Right)
import qualified Prelude as P

--------------------------------------------------------------------------------
-- * Compilation
--------------------------------------------------------------------------------

-- | Monad used for compilation
type Gen i = ReaderT (Channels i) (Program i)

read :: forall i a. CompileExp (IExp i) => Ident i a -> E i a
read (Ident i _ _) = dist (witness :: Wit i a) i
  where
    dist :: Wit i x -> Identifiers (S Symbol i x) -> E i x
    dist (WE) (Identified i) = HDL.varE i
    dist (WP l r) (u, v)     = (dist l u, dist r v)

write :: forall i a. (SequentialCMD (IExp i) :<: i) => Ident i a -> E i a -> Gen i ()
write (Ident i kind _) = dist (witness :: Wit i a) i
  where
    dist :: Wit i x -> Identifiers (S Symbol i x) -> E i x -> Gen i ()
    dist (WE) (Identified i) exp = CMS.lift $ case kind of
      HDL.Variable -> i HDL.==: exp
      HDL.Signal   -> i HDL.<== exp
    dist (WP l r) (u, v) (x, y) = dist l u x >> dist r v y

--------------------------------------------------------------------------------
-- **

comp'
  :: forall i.
     ( Compile       (IExp i)
     , CompileExp    (IExp i)
     , SequentialCMD (IExp i) :<: i
     , ConcurrentCMD (IExp i) :<: i
     , HeaderCMD     (IExp i) :<: i)
  => Ordered i
  -> Gen     i ()
comp' (Ordered (Key name)) =
  do env <- CMR.asks (RMap.lookup name)
     case env of
       Nothing                 -> return ()
       Just (Channel node out) -> case node of
         Repeat c -> do
           declare out Nothing
           write out c
         Map f s -> do
           declare out Nothing
           write out (f $ read s)
         Delay d s -> do
           declare (swap out)     (Just d)
           declare (global $ out) (Nothing)
           write (swap out) (read s)
         Mux s cs -> do
           declare out Nothing
           env <- CMR.ask
           let (l, r)  = first (fmap literal) $ unzip cs
               choices = fmap (run env . write out . read) r
           CMS.lift $ HDL.switch (read s) (zip l choices) (Nothing)
         Var d -> do
            declare out Nothing
  where
    declare :: forall a. Ident i a -> Maybe (E i a) -> Gen i ()
    declare (Ident i k s) me = dist (witness :: Wit i a) i me
      where
        dist :: Wit i x -> Identifiers (S Symbol i x) -> Maybe (E i x) -> Gen i ()
        dist (WE) (Identified i) me = decl i me
        dist (WP l r) (u, v)     me = dist l u (fmap fst me) >> dist r v (fmap snd me)
        
        decl :: PredicateExp (IExp i) x => Identifier -> Maybe (IExp i x) -> Gen i ()
        decl ident exp = decl' ident k s exp

    global :: Ident i (Identity a) -> Ident i (Identity a)
    global (Ident is k _) = (Ident is k Global)

    run :: Channels i -> Gen i x -> Program i x
    run = flip CMR.runReaderT

decl' 
  :: ( PredicateExp  (IExp i) a
     , SequentialCMD (IExp i) :<: i
     , ConcurrentCMD (IExp i) :<: i
     , HeaderCMD     (IExp i) :<: i)
  => Identifier
  -> Kind
  -> Scope
  -> Maybe (IExp i a)
  -> Gen i ()
decl' ident kind scope exp = CMS.lift $ case kind of
  HDL.Variable -> case scope of
    Header -> void $ HDL.signalPort ident HDL.In exp
    Local  ->        HDL.variableL  ident exp
  HDL.Signal   -> case scope of
    Header -> void $ HDL.signalPort ident HDL.Out exp
    Global ->        HDL.signalG    ident exp

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

-- | Swap a delay's identifier back from its `opposite` to the original
swap :: Ident i (Identity a) -> Ident i (Identity a)
swap (Ident (Identified i)            k s) =
     (Ident (Identified (opposite i)) k s)

--------------------------------------------------------------------------------
-- **

type Order i = [Ordered i]

compile'
  :: forall i a.
     ( Compile       (IExp i)
     , CompileExp    (IExp i)
     , PredicateExp  (IExp i) Bool
     , SequentialCMD (IExp i) :<: i
     , ConcurrentCMD (IExp i) :<: i
     , HeaderCMD     (IExp i) :<: i)
  => Key      i (Identity a)  -- root
  -> Channels i               -- nodes
  -> Order    i               -- ordering
  -> Str      i a             -- output
compile' out channels order = Stream $ inArchitecture "arch" $
  do let inp@(delays, nodes) = split channels order
     clk <- HDL.clock
     run $ do
       inProcess "combinatorial" (sens inp) $
         do mapM_ comp' nodes
            mapM_ comp' delays
       unless (null delays) $
         inProcess "sequential" [clk] $
           when (rising clk) $
             mapM_ update delays
       exit out
  where
    run :: Gen i x -> Program i (Program i x)
    run = return . flip CMR.runReaderT (markRoot out channels)

    sens :: (Order i, Order i) -> [Identifier]
    sens (delays, nodes) =
        inputs channels nodes ++ concatMap ids delays
      where
        ids :: Ordered i -> [Identifier]
        ids (Ordered (Key name)) = case RMap.lookup name channels of
          Just (Channel (Delay {}) i) -> collect i

    when :: IExp i Bool -> Gen i () -> Gen i ()
    when exp = CMR.mapReaderT (HDL.when exp)

    update :: Ordered i -> Gen i ()
    update (Ordered (Key name)) = do
      (Channel (Delay {}) ident) <- CMR.asks (fromJust . RMap.lookup name)
      write ident (read (swap ident))

    exit :: Key i (Identity a) -> Gen i (IExp i a)
    exit (Key name) = do
      (Channel _ ident) <- CMR.asks (fromJust . RMap.lookup name)
      return $ read ident

    -- *** this is very hacky, as it assumes `IExp i` to be `HDL.Exp`
    rising :: CompileExp (IExp i) => Identifier -> IExp i Bool
    rising (VHDL.Ident i) = HDL.varE $ VHDL.Ident $ "rising_edge(" ++ i ++ ")"

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

-- *** I don't like how it needs to lookup every ordered name
split :: Channels i -> Order i -> (Order i, Order i)
split c = partitionEithers . fmap sort
  where
    sort :: Ordered i -> Either (Ordered i) (Ordered i)
    sort ord@(Ordered (Key name)) = case RMap.lookup name c of
      Just (Channel (Delay {}) _) -> P.Left  ord
      _                           -> P.Right ord

inputs :: Channels i -> Order i -> [Identifier]
inputs c = concatMap vars
  where
    vars :: Ordered i -> [Identifier]
    vars ord@(Ordered (Key name)) = case RMap.lookup name c of
      Just (Channel (Var {}) i) -> collect i
      _                         -> []

-- *** This could easily be improved by not using lists internally
collect :: forall i a. Ident i a -> [Identifier]
collect (Ident is _ _) = dist (witness :: Wit i a) is
  where
    dist :: Wit i x -> Identifiers (S Symbol i x) -> [Identifier]
    dist (WE) (Identified i) = [i]
    dist (WP l r) (u, v)     = dist l u ++ dist r v

-- | Mark a key as root, giving it a signal kind and marking it as a port
markRoot :: Key i a -> Channels i -> Channels i
markRoot (Key name) = RMap.adjust update name
  where
    update :: Channel i a -> Channel i a
    update c = case c of
      Channel node (Ident i _ _) -> Channel node (Ident i HDL.Signal Header)

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

inProcess :: (ConcurrentCMD (IExp i) :<: i) => String -> [Identifier] -> Gen i () -> Gen i ()
inProcess name = CMR.mapReaderT . HDL.process name

inArchitecture :: (HeaderCMD (IExp i) :<: i) => String -> Program i (Program i a) -> Program i (Program i a)
inArchitecture name = fmap (HDL.architecture name)

--------------------------------------------------------------------------------
-- **

-- | Compile signal functions into stream functions
compiler
  :: ( Compile       (IExp i)
     , CompileExp    (IExp i)
     , PredicateExp  (IExp i) Bool
     , PredicateExp  (IExp i) a
     , SequentialCMD (IExp i) :<: i
     , ConcurrentCMD (IExp i) :<: i
     , HeaderCMD     (IExp i) :<: i
     , Typeable a
     , Typeable b
     , Typeable i)
  => (Sig i a -> Sig i b) -> IO (Str i a -> Str i b)
compiler f =
  do (root, nodes) <- freify f
     let order = sorter root  nodes
         cycle = cycles root  nodes
         links = linker order nodes
     return $ case cycle of
       True  -> error "signal compiler: found cycle"
       False ->
         let filtered = RMap.filter useful links
             channels = fromLinks filtered
          in const $ compile' root channels order

--------------------------------------------------------------------------------
-- | Compile signals into streams

compile
  :: ( Compile       (IExp i)
     , CompileExp    (IExp i)
     , PredicateExp  (IExp i) Bool
     , PredicateExp  (IExp i) a
     , SequentialCMD (IExp i) :<: i
     , ConcurrentCMD (IExp i) :<: i
     , HeaderCMD     (IExp i) :<: i
     , Typeable a)
  => Sig i a -> IO (Str i a)
compile f =
  do (root, nodes) <- reify f
     let order = sorter root  nodes
         cycle = cycles root  nodes
         links = linker order nodes
     return $ case cycle of
       True  -> error "signal compiler: found cycle"
       False ->
         let filtered = RMap.filter useful links
             channels = fromLinks filtered
          in compile' root channels order

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

-- | Usefulness refers to whether we should generate code for the node or not
useful :: Linked i a -> Bool
useful (Linked node _) =
  case node of
    Var    {} -> True
    Repeat {} -> True
    Map    {} -> True
    Delay  {} -> True
    Mux    {} -> True
    _         -> False

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