Previous Up Next

1.5  Valued Signals

The language provides a way to manage valued signals. Signals are built and accessed through the construction emit and present. A value signal is a pair made of (1) a boolean stream c indicating when the signal is present and (2) a stream sampled on that clock c 6. In circuit terminology, we get circuits with enable.

1.5.1  Signals as Clock Abstraction

Signals can be built from sampled streams by abstracting their internal clock. Consider again the example given in section 1.2.5. This program can now be accepted if we write:

let node within min max x = o where
  rec clock c = (min <= x) & (x <= max)
  and emit o = true when c

val within :  ’a -> ’a -> ’a => bool sig
val within :: ’a -> ’a -> ’a -> ’a sig

It computes a condition c and a sampled stream true when c. The equation emit o = true when c defines a signal o which is present and equal to true when c is true. The emit construction can be considered as a boxing mechanism which pack a value with its clock condition. The right part of the construction emit must be an expression with some clock type a on c. In that case, it defines a signal with clock type a sig.

1.5.2  Testing the Presence and Signal Matching

It is possible to test for the presence of a signal expression e by writting the boolean expression ? e. The following program, for example, counts the number of instants where x is emitted.

let node count x = cpt where
  rec cpt = if ?x then 1 -> pre cpt + 1 else 0 -> pre cpt

val count :  ’a sig => int
val count :: ’a sig -> ’a

The language provides a more general mechanism to test for the presence of several signals and access their values. It is reminiscent of the pattern-matching construct of ML. This pattern matching mechanisn is safe in the sense that it is possible to access the value of a signal only at the instant where it is emitted. This is in contrast with Esterel, for example, where the value of a signal implicitly holds and can be accessed even when it is not emitted.

The following program takes two signals x and y and returns an integer which equals the sum of x and y when both are emitted, it returns the value of x when x is emitted only and the value 0 when only y is emitted and 0 otherwise.

let node sum x y = o where
  present
    x(v) & y(w) -> do o = v + w done
  | x(v1) -> do o = v1 done
  | y(v2) -> do o = v2 done
  | _ -> do o = 0 done
  end

val sum :  int sig -> int sig => int
val sum :: ’a sig -> ’a sig -> ’a

A present statement is made of several signal conditions and handlers. Each condition is tested sequentially. The signal condition x(v) & y(w) is verified when both signals x and y are present. A condition x(v1) means “x is present and has some value v1”. The variable v1 can in turn be used in the corresponding handler. The last signal condition _ stands for the wildcard signal condition which is always verified.

In the signal pattern x(v) & y(w), x and y are expressions evaluating to signal values whereas v and w stand for patterns. Thus, writting x(42) & y(w) would mean: “await for the presence of signal x evaluating to 42 and the presence of y.

Note that the output of the function sum is a regular flow since the test is exhaustive. Forgetting the default case will raise an error.

let node sum x y = o where
  present
    x(v) & y(w) -> do o = v + w done
  | x(v1) -> do o = v1 done
  | y _ -> do o = 0 done
  end

File "test.ls", line 6-10, characters 2-105:
>..present
>    x(v) & y(w) -> do o = v + w done
>  | x(v1) -> do o = v1 done
>  | y _ -> do o = 0 done
>  end
The identifier o should be defined in every handler or given a last value.

We can easily eliminate this error by giving a last value to o (e.g., adding an equation last o = 0 outside of the present statement). In that case, the default case is implicitly completed with an equation o = last o. The other way is to state that o is now a signal and is thus only partially defined.

let node sum x y = o where
  present
    x(v) & y(w) -> do emit o = v + w done
  | x(v1) -> do emit o = v1 done
  | y _ -> do emit o = 0 done
  end

val sum :  int sig -> int sig => int sig
val sum :: ’a sig -> ’a sig -> ’a sig

A signal pattern may also contain boolean expressions. The following program, sums the values of the two signals x and y provided they arrive at the same time and z >= 0.

let node sum x y z = o where
  present
    x(v) & y(w) & (z >= 0) -> do o = v + w done
  | _ -> do o = 0 done
  end


Remark: Using signals, we can mimic the default construction of the language Signal. default x y emits the value of x when x is present and the value of y when x is absent and y is present.

let node default x y = o where
  present
    x(v) -> do emit o = v done
  | y(v) -> do emit o = v done
  end

This is, of course, only a simulation since all the information about clocks has been lost. In particular, the compiler is unable to state that o is emitted only when x or y are present as the clock calculus of Signal does for the default operator.

In some circumstances, it can be valuable to prefer sampling operators when and merge in order to benefit from clock information.


6
In type notation, a signal is a dependent pair with clock type Σ (c:a).a on c.

Previous Up Next