Copyright | (c) George Ungureanu 2020 |
---|---|
License | BSD-style (see the file LICENSE) |
Maintainer | ugeorge@kth.se |
Stability | experimental |
Portability | portable |
Safe Haskell | Safe |
Language | Haskell2010 |
This is a small experimental library for modeling multi-clock rate SY systems in
the style of Lustre [Halbwachs91]. It is
implemented mainly on top of the ForSyDe.Atom.MoC.SY library, re-using it to the
full extent, and it represents Lustre language constructs as specific
patterns. Instead of developing and representing an inherent clock calculus like
Lustre, this library merely propagates "no-ops" as absent
(i.e. Abst
) events and utilises the algebra of
ForSyDe.Atom.ExB to model clock rates.
Internally, a ForSyDe.Atom.MoC.SY.Clocked process constructor represents implicitly a ForSyDe.Atom.MoC.SY MoC layer wrapping a ForSyDe.Atom.ExB.Absent behavior extension layer. Under these asumptions the ForSyDe.Atom.MoC.SY.Clocked models can be interpreted as follows:
- the host ForSyDe.Atom.MoC.SY processes operate at the basic clock rate. I.e. a signal where all events are present is active in the basic time scale.
- the wrapped ForSyDe.Atom.ExB behaviors operate on and propagate absent/present
events based on the operating clock rate.
Abst
means that an event "did not occur". Combining a present with an absent event in any regular operation (seecomb22
) would violate the clock constraints of well-formed systems (see [Halbwachs91]) and would throw a runtime error. - Although ForSyDe.Atom.MoC.SY.Clocked is hosted on ForSyDe.Atom.MoC.SY, the
Abst
"value" has no true meaning in a pure SY reactive system. Therefore to interface between these two SY domains should always be made through atoDE
/fromDE
interface. OBS: these interfaces are still experimental and higly unsafe especially when considering feedback loops.
Synopsis
- type Signal a = Stream (SY (AbstExt a))
- pre :: Signal a -> Signal a
- (->-) :: Signal a -> Signal a -> Signal a
- when :: Signal a -> Signal Bool -> Signal a
- current :: a -> Signal a -> Signal a
- is :: Signal a -> (a -> Bool) -> Signal Bool
- whenIs2 :: (a -> Bool) -> Signal a -> Signal b1 -> Signal b2 -> (Signal b1, Signal b2)
- whenPres2 :: Signal b -> Signal b1 -> Signal b2 -> (Signal b1, Signal b2)
- filter :: (a -> Bool) -> Signal a -> Signal a
- fill :: a -> Signal a -> Signal a
- delay :: a -> Signal a -> Signal a
- comb22 :: (a1 -> a2 -> (b1, b2)) -> Signal a1 -> Signal a2 -> (Signal b1, Signal b2)
- stated22 :: (b1 -> b2 -> a1 -> a2 -> (b1, b2)) -> (b1, b2) -> Signal a1 -> Signal a2 -> (Signal b1, Signal b2)
- state22 :: (b1 -> b2 -> a1 -> a2 -> (b1, b2)) -> (b1, b2) -> Signal a1 -> Signal a2 -> (Signal b1, Signal b2)
- moore22 :: (st -> a1 -> a2 -> st) -> (st -> (b1, b2)) -> st -> Signal a1 -> Signal a2 -> (Signal b1, Signal b2)
- mealy22 :: (st -> a1 -> a2 -> st) -> (st -> a1 -> a2 -> (b1, b2)) -> st -> Signal a1 -> Signal a2 -> (Signal b1, Signal b2)
Documentation
type Signal a = Stream (SY (AbstExt a)) Source #
Alias showing that in the ForSyDe.Atom.MoC.SY.Clocked domain, a signal is considered a stream of absent-extended SY events.
Lustre constructs
These are process constructors which imitate the behavior of Lustre constructs according to our ForSyDe.Atom.MoC.SY.Clocked definition.
pre :: Signal a -> Signal a Source #
The pre
construct in Lustre. OBS: instead of nil, the first value is an
Abst
. TODO: unclear if this breaks the SY assumption.
>>>
let s1 = signal [Prst 1, Prst 2, Prst 3, Prst 4, Prst 5]
>>>
pre s1
{⟂,1,2,3,4,5}
(->-) :: Signal a -> Signal a -> Signal a infixl 3 Source #
The ->
operator in Lustre.
>>>
let s1 = signal [Prst 1, Prst 2, Prst 3, Prst 4, Prst 5]
>>>
unit (Prst 10) ->- s1
{10,2,3,4,5}
Determines the existence of events in the (left) signal based on the (right) signal of boolean values (conditions). In Lustre this construct is used to generate other clock rates.
>>>
let s1 = (signal . map Prst) [1,2,3,4,5]
>>>
let pred = signal [Prst False, Abst, Prst True, Prst False, Prst True]
>>>
s1 `when` pred
{⟂,⟂,3,⟂,5}
Holds the last non-absent value in a signal, like the current
construct in
Lustre. OBS: instead of nil, the first value is user-defined.
>>>
let s1 = signal [Abst, Abst, Prst 1, Prst 2, Abst, Prst 3]
>>>
current 0 s1
{0,0,1,2,2,3}
Auxiliary constructs
These are utility process constructors used in combination with the main constructs.
is :: Signal a -> (a -> Bool) -> Signal Bool Source #
Applies a predicate function on a signal of absent-extended events.
>>>
let s1 = signal $ map Prst [1,2,3,4,5]
>>>
s1 `is` (>3)
{False,False,False,True,True}
It is useful in combination with when
.
>>>
let s1 = (signal . map Prst) [1,2,3,4,5]
>>>
let s2 = (signal . map Prst) [11,22,33,44,55]
>>>
s2 `when` (s1 `is` (>3))
{⟂,⟂,⟂,44,55}
whenPres2 :: Signal b -> Signal b1 -> Signal b2 -> (Signal b1, Signal b2) Source #
Same as whenIs2
but triggering the output events only based on the presence of
the first input rather than a boolean.
>>>
let s1 = signal $ map Prst [1,2,3,4,5]
>>>
let sp = signal [Prst 1, Prst 1, Abst, Prst 1, Abst]
>>>
whenPres1 sp s1
{1,2,⟂,4,⟂}
Filters out values to Abst
if they do not fulfill a predicate function,
i.e. applies whenIs1
on itself.
>>>
let s1 = (signal . map Prst) [1,2,3,4,5]
>>>
filter (>3) s1
{⟂,⟂,⟂,4,5}
Fills absent events with a user-defined value.
>>>
let s1 = signal [Abst, Abst, Prst 1, Prst 2, Abst, Prst 3]
>>>
fill 0 s1
{0,0,1,2,0,3}
Regular constructors
These are the regular process constructors defined as patterns in the ForSyDe.Atom.MoC module, but adapted for the absent-extended behaviors.
:: (a1 -> a2 -> (b1, b2)) | function on values |
-> Signal a1 | first input signal |
-> Signal a2 | second input signal |
-> (Signal b1, Signal b2) | two output signals |
comb
processes map combinatorial functions on signals on synchronized input
signals. It implements the comb
pattern (see comb22
), and
implicitly applies a resolution between absent-extended events (see
res22
)
Constructors: comb[1-4][1-4]
.
>>>
let s1 = (signal . map Prst) [1..]
>>>
let s2 = signal [Prst 1,Prst 1,Abst,Prst 1,Prst 1]
>>>
comb11 (+1) s2
{2,2,⟂,2,2}>>>
comb22 (\a b-> (a+b,a-b)) s2 s2
({2,2,⟂,2,2},{0,0,⟂,0,0})
Combining two signals at different clock rates throws a runtime error:
comb22 (\a b-> (a+b,a-b)) s1 s2 ({2,3,*** Exception: [ExB.Absent] Illegal occurrence of an absent and present event
:: (b1 -> b2 -> a1 -> a2 -> (b1, b2)) | next stated function |
-> (b1, b2) | initial stated values |
-> Signal a1 | first input signal |
-> Signal a2 | second input signal |
-> (Signal b1, Signal b2) | output signals |
stated
is a state machine without an output decoder. It implements the stated
pattern (see stated22
), and operates on
degen
erated absent-extended values (see
ignore22
) in order to propagate absent events properly and not
raise absent-present resolution errors.
Constructors: stated[1-3][1-3]
.
>>>
let ones = takeS 8 $ SY.constant1 (Prst 1)
>>>
let b = (signal . map Prst) [False,True,False,False,True,True,False,True]
>>>
ones
{1,1,1,1,1,1,1,1}>>>
b
{False,True,False,False,True,True,False,True}>>>
stated11 (+) 1 ones
{1,2,3,4,5,6,7,8}>>>
stated11 (+) 1 (ones `when` b)
{⟂,1,⟂,⟂,2,3,⟂,4}>>>
stated11 (+) 1 ones `when` b
{⟂,2,⟂,⟂,5,6,⟂,8}
:: (b1 -> b2 -> a1 -> a2 -> (b1, b2)) | next state function |
-> (b1, b2) | initial state values |
-> Signal a1 | first input signal |
-> Signal a2 | second input signal |
-> (Signal b1, Signal b2) | output signals |
state
is a state machine without an output decoder, that reacts
instantaneously. It implements the state
pattern (see
state22
), and operates on degen
erated
absent-extended values (see ignore22
) in order to propagate
absent events properly and not raise absent-present resolution errors.
Constructors: state[1-3][1-3]
.
>>>
let ones = takeS 8 $ SY.constant1 (Prst 1)
>>>
let b = (signal . map Prst) [False,True,False,False,True,True,False,True]
>>>
ones
{1,1,1,1,1,1,1,1}>>>
b
{False,True,False,False,True,True,False,True}>>>
state11 (+) 1 ones
{2,3,4,5,6,7,8,9}>>>
state11 (+) 1 (ones `when` b)
{⟂,2,⟂,⟂,3,4,⟂,5}>>>
state11 (+) 1 ones `when` b
{⟂,3,⟂,⟂,6,7,⟂,9}