----------------------------------------------------------------------------- -- | -- Module : ForSyDe.Shallow.MoC.SDF -- Copyright : (c) Ingo Sander, KTH/ICT/ES, ForSyDe-Group -- License : BSD-style (see the file LICENSE) -- -- Maintainer : forsyde-dev@ict.kth.se -- Stability : experimental -- Portability : portable -- -- SDFLib.hs, yet to be completed. -- ----------------------------------------------------------------------------- module ForSyDe.Shallow.MoC.SDF ( -- -- * Combinational Process Constructors -- -- | Combinational process constructors are used for processes that -- -- do not have a state. -- mapSDF, zipWithSDF, zipWith3SDF, zipWith4SDF, -- * Sequential Process Constructors -- | Sequential process constructors are used for processes that -- have a state. One of the input parameters is the initial state. delaySDF, -- -- * Processes -- -- | Processes to unzip a signal of tupels into a tuple of signals -- unzipSDF, unzip3SDF, unzip4SDF, -- * Actors -- | Based on the process constructors in the SDF-MoC, the -- SDF-library provides SDF-actors with single or multiple inputs actor11SDF, actor12SDF, actor13SDF, actor14SDF, actor21SDF, actor22SDF, actor23SDF, actor24SDF, actor31SDF, actor32SDF, actor33SDF, actor34SDF, actor41SDF, actor42SDF, actor43SDF, actor44SDF ) where import ForSyDe.Shallow.Core ------------------------------------- -- -- -- SEQUENTIAL PROCESS CONSTRUCTORS -- -- -- ------------------------------------- -- | The process constructor 'delaySDF' delays the signal one event -- cycle by introducing a set of initial values at the beginning of -- the output signal. Note, that this implies that there is a -- prefix at the output signal (the first n events) that has no -- corresponding event at the input signal. This is necessary to -- initialize feedback loops. -- -- >>> delaySDF [0,0,0] $ signal [1,2,3,4] -- {0,0,0,1,2,3,4} delaySDF :: [a] -> Signal a -> Signal a delaySDF initial_tokens xs = signal initial_tokens +-+ xs ------------------------------------------------------------------------ -- -- SDF ACTORS -- ------------------------------------------------------------------------ -- > Actors with one output -- | The process constructor 'actor11SDF' constructs an SDF actor with -- one input and one output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. -- -- >>> let f [a,b] = [a+b,a-b,a*b] -- >>> actor11SDF 2 3 f $ signal [1,2,3,4,5] -- {3,-1,2,7,-1,12} actor11SDF :: Int -> Int -> ([a] -> [b]) -> Signal a -> Signal b actor11SDF = mapSDF -- | The process constructor 'actor21SDF' constructs an SDF actor with -- two input and one output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. -- -- >>> let f [a,b] [c] = [a+b+c,b-c] -- >>> let s1 = signal [1..6] -- >>> let s2 = signal [1..] -- >>> actor21SDF (2,1) 2 f s1 s2 -- {4,1,9,2,14,3} actor21SDF :: (Int, Int) -> Int -> ([a] -> [b] -> [c]) -> Signal a -> Signal b -> Signal c actor21SDF = zipWithSDF -- | The process constructor 'actor31SDF' constructs an SDF actor with -- three input and one output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor31SDF :: (Int, Int, Int) -> Int -> ([a] -> [b] -> [c] -> [d]) -> Signal a -> Signal b -> Signal c -> Signal d actor31SDF = zipWith3SDF -- | The process constructor 'actor41SDF' constructs an SDF actor with -- four input and one output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor41SDF :: (Int, Int, Int, Int) -> Int -> ([a] -> [b] -> [c] -> [d] -> [e]) -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e actor41SDF = zipWith4SDF -- > Actors with two outputs -- | The process constructor 'actor12SDF' constructs an SDF actor with -- one input and two output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor12SDF :: Int -> (Int, Int) -> ([a] -> ([b], [c])) -> Signal a -> (Signal b, Signal c) actor12SDF c (p1,p2) f xs = unzipSDF (p1,p2) $ mapSDF c 1 (wrapF1 f) xs -- | The process constructor 'actor22SDF' constructs an SDF actor with -- two input and two output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor22SDF :: (Int, Int) -> (Int, Int) -> ([a] -> [b] -> ([c], [d])) -> Signal a -> Signal b -> (Signal c, Signal d) actor22SDF (c1,c2) (p1,p2) f xs ys = unzipSDF (p1,p2) $ zipWithSDF (c1,c2) 1 (wrapF2 f) xs ys -- | The process constructor 'actor32SDF' constructs an SDF actor with -- three input and two output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor32SDF :: (Int, Int, Int) -> (Int, Int) -> ([a] -> [b] -> [c] -> ([d], [e])) -> Signal a -> Signal b -> Signal c -> (Signal d, Signal e) actor32SDF (c1,c2,c3) (p1,p2) f as bs cs = unzipSDF (p1,p2) $ zipWith3SDF (c1,c2,c3) 1 (wrapF3 f) as bs cs -- | The process constructor 'actor42SDF' constructs an SDF actor with -- four input and two output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor42SDF :: (Int, Int, Int, Int) -> (Int, Int) -> ([a] -> [b] -> [c] -> [d] -> ([e], [f])) -> Signal a -> Signal b -> Signal c -> Signal d -> (Signal e, Signal f) actor42SDF (c1,c2,c3,c4) (p1,p2) f as bs cs ds = unzipSDF (p1,p2) $ zipWith4SDF (c1,c2,c3,c4) 1 (wrapF4 f) as bs cs ds -- > Actors with three outputs -- | The process constructor 'actor13SDF' constructs an SDF actor with -- one input and three output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor13SDF :: Int -> (Int, Int, Int) -> ([a] -> ([b], [c], [d])) -> Signal a -> (Signal b, Signal c, Signal d) actor13SDF c (p1,p2,p3) f xs = unzip3SDF (p1,p2,p3) $ mapSDF c 1 (wrapF1 f) xs -- | The process constructor 'actor23SDF' constructs an SDF actor with -- two input and three output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor23SDF :: (Int, Int) -> (Int, Int, Int) -> ([a] -> [b] -> ([c], [d], [e])) -> Signal a -> Signal b -> (Signal c, Signal d, Signal e) actor23SDF (c1,c2) (p1,p2,p3) f xs ys = unzip3SDF (p1,p2,p3) $ zipWithSDF (c1,c2) 1 (wrapF2 f) xs ys -- | The process constructor 'actor33SDF' constructs an SDF actor with -- three input and three output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor33SDF :: (Int, Int, Int) -> (Int, Int, Int) -> ([a] -> [b] -> [c] -> ([d], [e], [f])) -> Signal a -> Signal b -> Signal c -> (Signal d, Signal e, Signal f) actor33SDF (c1,c2,c3) (p1,p2,p3) f as bs cs = unzip3SDF (p1,p2,p3) $ zipWith3SDF (c1,c2,c3) 1 (wrapF3 f) as bs cs -- | The process constructor 'actor43SDF' constructs an SDF actor with -- four input and three output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor43SDF :: (Int, Int, Int, Int) -> (Int, Int, Int) -> ([a] -> [b] -> [c] -> [d] -> ([e], [f], [g])) -> Signal a -> Signal b -> Signal c -> Signal d -> (Signal e, Signal f, Signal g) actor43SDF (c1,c2,c3,c4) (p1,p2,p3) f as bs cs ds = unzip3SDF (p1,p2,p3)$ zipWith4SDF (c1,c2,c3,c4) 1 (wrapF4 f) as bs cs ds -- > Actors with four outputs -- | The process constructor 'actor14SDF' constructs an SDF actor with -- one input and four output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor14SDF :: Int -> (Int, Int, Int, Int) -> ([a] -> ([b], [c], [d], [e])) -> Signal a -> (Signal b, Signal c, Signal d, Signal e) actor14SDF c (p1,p2,p3,p4) f xs = unzip4SDF (p1,p2,p3,p4) $ mapSDF c 1 (wrapF1 f) xs -- | The process constructor 'actor24SDF' constructs an SDF actor with -- two input and four output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor24SDF :: (Int, Int) -> (Int, Int, Int, Int) -> ([a] -> [b] -> ([c], [d], [e], [f])) -> Signal a -> Signal b -> (Signal c, Signal d, Signal e, Signal f) actor24SDF (c1,c2) (p1,p2,p3,p4) f xs ys = unzip4SDF (p1,p2,p3,p4) $ zipWithSDF (c1,c2) 1 (wrapF2 f) xs ys -- | The process constructor 'actor34SDF' constructs an SDF actor with -- three input and four output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor34SDF :: (Int, Int, Int) -> (Int, Int, Int, Int) -> ([a] -> [b] -> [c] -> ([d], [e], [f], [g])) -> Signal a -> Signal b -> Signal c -> (Signal d, Signal e, Signal f, Signal g) actor34SDF (c1,c2,c3) (p1,p2,p3,p4) f as bs cs = unzip4SDF (p1,p2,p3,p4) $ zipWith3SDF (c1,c2,c3) 1 (wrapF3 f) as bs cs -- | The process constructor 'actor14SDF' constructs an SDF actor with -- four input and four output signals. For each input or output signal, -- the process constructor takes the number of consumed and produced -- tokens and the function of the actor as arguments. actor44SDF :: (Int, Int, Int, Int) -> (Int, Int, Int, Int) -> ([a] -> [b] -> [c] -> [d] -> ([e], [f], [g], [h])) -> Signal a -> Signal b -> Signal c -> Signal d -> (Signal e, Signal f, Signal g, Signal h) actor44SDF (c1,c2,c3,c4) (p1,p2,p3,p4) f as bs cs ds = unzip4SDF (p1,p2,p3,p4) $ zipWith4SDF (c1,c2,c3,c4) 1 (wrapF4 f) as bs cs ds --------------------------------------------------------------------- -- COMBINATIONAL PROCESS CONSTRUCTORS --------------------------------------------------------------------- -- | The process constructor 'mapSDF' takes the number of consumed -- (@c@) and produced (@p@) tokens and a function @f@ that operates on -- a list, and results in an SDF-process that takes an input signal -- and results in an output signal mapSDF :: Int -> Int -> ([a] -> [b]) -> Signal a -> Signal b mapSDF _ _ _ NullS = NullS mapSDF c p f xs | c <= 0 = error "mapSDF: Number of consumed tokens must be positive integer" | not $ sufficient_tokens c xs = NullS | otherwise = if length produced_tokens == p then signal produced_tokens +-+ mapSDF c p f (dropS c xs) else error "mapSDF: Function does not produce correct number of tokens" where consumed_tokens = fromSignal $ takeS c xs produced_tokens = f consumed_tokens -- | The process constructor 'zipWithSDF' takes a tuple @(c1, c2)@ -- denoting the number of consumed tokens and an integer @p@ denoting -- the number of produced tokens and a function @f@ -- that operates on two lists, and results in an SDF-process that takes two -- input signals and results in an output signal zipWithSDF :: (Int, Int) -> Int -> ([a] -> [b] -> [c]) -> Signal a -> Signal b -> Signal c zipWithSDF (_, _) _ _ NullS _ = NullS zipWithSDF (_, _) _ _ _ NullS = NullS zipWithSDF (c1, c2) p f as bs | c1 <= 0 || c2 <= 0 = error "zipWithSDF: Number of consumed tokens must be positive integer" | (not $ sufficient_tokens c1 as) || (not $ sufficient_tokens c2 bs) = NullS | otherwise = if length produced_tokens == p then signal produced_tokens +-+ zipWithSDF (c1, c2) p f (dropS c1 as) (dropS c2 bs) else error "zipWithSDF: Function does not produce correct number of tokens" where consumed_tokens_as = fromSignal $ takeS c1 as consumed_tokens_bs = fromSignal $ takeS c2 bs produced_tokens = f consumed_tokens_as consumed_tokens_bs -- | The process constructor 'zipWith3SDF' takes a tuple @(c1, c2, c3)@ -- denoting the number of consumed tokens and an integer @p@ denoting -- the number of produced tokens and a function @f@ -- that operates on three lists, and results in an SDF-process that takes three -- input signals and results in an output signal zipWith3SDF :: (Int, Int, Int) -> Int -> ([a] -> [b] -> [c] -> [d]) -> Signal a -> Signal b -> Signal c -> Signal d zipWith3SDF (_, _, _) _ _ NullS _ _= NullS zipWith3SDF (_, _, _) _ _ _ NullS _= NullS zipWith3SDF (_, _, _) _ _ _ _ NullS= NullS zipWith3SDF (c1, c2, c3) p f as bs cs | c1 <= 0 || c2 <= 0 || c3 <= 0 = error "zipWith3SDF: Number of consumed tokens must be positive integer" | (not $ sufficient_tokens c1 as) || (not $ sufficient_tokens c2 bs) || (not $ sufficient_tokens c3 cs) = NullS | otherwise = if length produced_tokens == p then signal produced_tokens +-+ zipWith3SDF (c1, c2, c3) p f (dropS c1 as) (dropS c2 bs) (dropS c3 cs) else error "zipWith3SDF: Function does not produce correct number of tokens" where consumed_tokens_as = fromSignal $ takeS c1 as consumed_tokens_bs = fromSignal $ takeS c2 bs consumed_tokens_cs = fromSignal $ takeS c3 cs produced_tokens = f consumed_tokens_as consumed_tokens_bs consumed_tokens_cs -- | The process constructor 'zipWith4SDF' takes a tuple @(c1, c2, c3,c4)@ -- denoting the number of consumed tokens and an integer @p@ -- denoting the number of produced tokens and a function @f@ that -- operates on three lists, and results in an SDF-process that takes -- three input signals and results in an output signal zipWith4SDF :: (Int, Int, Int, Int) -> Int -> ([a] -> [b] -> [c] -> [d] -> [e]) -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e zipWith4SDF (_, _, _, _) _ _ NullS _ _ _ = NullS zipWith4SDF (_, _, _, _) _ _ _ NullS _ _ = NullS zipWith4SDF (_, _, _, _) _ _ _ _ NullS _ = NullS zipWith4SDF (_, _, _, _) _ _ _ _ _ NullS = NullS zipWith4SDF (c1, c2, c3, c4) p f as bs cs ds | c1 <= 0 || c2 <= 0 || c3 <= 0 || c4 <= 0 = error "zipWith4SDF: Number of consumed tokens must be positive integer" | (not $ sufficient_tokens c1 as) || (not $ sufficient_tokens c2 bs) || (not $ sufficient_tokens c3 cs) || (not $ sufficient_tokens c4 ds) = NullS | otherwise = if length produced_tokens == p then signal produced_tokens +-+ zipWith4SDF (c1, c2, c3, c4) p f (dropS c1 as) (dropS c2 bs) (dropS c3 cs) (dropS c4 ds) else error "zipWith4SDF: Function does not produce correct number of tokens" where consumed_tokens_as = fromSignal $ takeS c1 as consumed_tokens_bs = fromSignal $ takeS c2 bs consumed_tokens_cs = fromSignal $ takeS c3 cs consumed_tokens_ds = fromSignal $ takeS c4 ds produced_tokens = f consumed_tokens_as consumed_tokens_bs consumed_tokens_cs consumed_tokens_ds --------------------------------------------------------------------- -- unzipSDF Processes --------------------------------------------------------------------- unzipSDF :: (Int, Int) -> Signal ([a], [b]) -> (Signal a, Signal b) unzipSDF (p1, p2) xs = (s1, s2) where s1 = signal $ f1 xs s2 = signal $ f2 xs f1 NullS = [] f1 ((as, _):-xs) | length as == p1 = as ++ f1 xs | otherwise = error "unzipSDF: Process does not produce correct number of tokens" f2 NullS = [] f2 ((_, bs):-xs) | length bs == p2 = bs ++ f2 xs | otherwise = error "unzipSDF: Process does not produce correct number of tokens" unzip3SDF :: (Int, Int, Int) -> Signal ([a], [b], [c]) -> (Signal a, Signal b, Signal c) unzip3SDF (p1, p2, p3) xs = (s1, s2, s3) where s1 = signal $ f1 xs s2 = signal $ f2 xs s3 = signal $ f3 xs f1 NullS = [] f1 ((as, _, _):-xs) | length as == p1 = as ++ f1 xs | otherwise = error "unzip3SDF: Process does not produce correct number of tokens" f2 NullS = [] f2 ((_, bs, _):-xs) | length bs == p2 = bs ++ f2 xs | otherwise = error "unzip3SDF: Process does not produce correct number of tokens" f3 NullS = [] f3 ((_, _, cs):-xs) | length cs == p3 = cs ++ f3 xs | otherwise = error "unzip3SDF: Process does not produce correct number of tokens" unzip4SDF :: (Int, Int, Int, Int) -> Signal ([a], [b], [c], [d]) -> (Signal a, Signal b, Signal c, Signal d) unzip4SDF (p1, p2, p3, p4) xs = (s1, s2, s3, s4) where s1 = signal $ f1 xs s2 = signal $ f2 xs s3 = signal $ f3 xs s4 = signal $ f4 xs f1 NullS = [] f1 ((as, _, _, _):-xs) | length as == p1 = as ++ f1 xs | otherwise = error "unzip4SDF: Process does not produce correct number of tokens" f2 NullS = [] f2 ((_, bs, _, _):-xs) | length bs == p2 = bs ++ f2 xs | otherwise = error "unzip4SDF: Process does not produce correct number of tokens" f3 NullS = [] f3 ((_, _, cs, _):-xs) | length cs == p3 = cs ++ f3 xs | otherwise = error "unzip4SDF: Process does not produce correct number of tokens" f4 NullS = [] f4 ((_, _, _, ds):-xs) | length ds == p4 = ds ++ f4 xs | otherwise = error "unzip4SDF: Process does not produce correct number of tokens" ------------------------------------------------------------------------ -- -- Helper functions (not exported!) -- ------------------------------------------------------------------------ sufficient_tokens :: (Num a, Eq a, Ord a) => a -> Signal t -> Bool sufficient_tokens 0 _ = True sufficient_tokens _ NullS = False sufficient_tokens n (_:-xs) = if n < 0 then error "sufficient_tokens: n must not be negative" else sufficient_tokens (n-1) xs -- outputTokens :: [(a, b, c)] -> [b] -- outputTokens [] = [] -- outputTokens ((_, b, _):xs) = b : outputTokens xs wrapF1 :: ([a] -> y) -> [a] -> [y] wrapF1 f = (\a -> [f a]) wrapF2 :: ([a] -> [b] -> y) -> [a] -> [b] -> [y] wrapF2 f = (\a b -> [f a b]) wrapF3 :: ([a] -> [b] -> [c] -> y) -> [a] -> [b] -> [c] -> [y] wrapF3 f = (\a b c -> [f a b c]) wrapF4 :: ([a] -> [b] -> [c] -> [d] -> y) -> [a] -> [b] -> [c] -> [d] -> [y] wrapF4 f = (\a b c d -> [f a b c d]) ------------------------------------------------------------------------ -- -- Test of Library (not exported) -- ------------------------------------------------------------------------ {- s1 = takeS 10 $ signal [1..] s2 = takeS 10 $ signal [10,20..] f1 [x] = [([x,x], [x,x,x])] s3 = unzipSDF (2,3) $ mapSDF 1 1 f1 s1 s4 = actor12SDF 1 (2,3) f1 s1 s5 = signal [1.0,2.0,3.0,4.0,5.0] multiply [x1,x2] [y] = [(x1+x2)* y] multiply _ _ = error "Single list item expected" feedback input = (i1,output) where output = actor21SDF (2,1) 1 multiply input i1 i1 = delaySDF 1 output -}