This is an old revision of the document!


The dataflow sublanguage

The dataflow sublanguage provides a convenient short-hand when designing asynchronous circuits using pipelined asynchronous circuits. The dataflow language operates exclusively on channels, and treats channels as variables to specify the dataflow computation.

chan(int) a, b, c;

dataflow {
   a + b -> c
}

This corresponds to an adder, with inputs on channels a and b and the output on channel c. There is an implicit assumption that the design is pipelined, and corresponds to the following CHP program:

*[ a?x,b?y;c!(x+y) ]

More generally, the syntax shown above corresponds to the function dataflow block. A function dataflow element receives one input token on each of its input channels, computes a function of the values received, and produces one output token with the value computed. The example above shows the function syntax. The left hand side of the arrow is a channel expression that corresponds to the function being computed, and the right hand side is the name of the channel on which the output is produced.

The split dataflow element receives a control token and a data token, and uses the value of the control token to route the data token to one of the output channels. If the control token=0, the first channel is used; if it is 1, then the next channel is used; etc. The syntax for this is shown below:

dataflow {
  {c} I -> O0, O1
}

The data input on channel I is routed to either O0 or O1, depending on the token received on input c. In some cases, one might want to discard a token based on a condition. In this case, the special symbol * can be used. The following specifies a circuit where the input is sent to the output O if the condition c is 1, otherwise it is discarded.

dataflow {
  {c} I -> *, O
}

The * indicates a token sink.

The controlled merge dataflow element receives a control token, and then uses the token determine which input channel should be used to accept an input data token. This data token is routed to the output. The syntax is:

dataflow {
  {c} I0, I1 -> O
}

In this example, if a 0 is received on c, then the data token on I0 is sent to the output O.

Copies are implicit, and are automatically introduced when the same channel name is used multiple times on the left hand side. In the example below, an input token is received on a and b and the sum and product are produced on channels sum and prod

dataflow {
  a + b -> sum;
  a * b -> prod
}

The fact that a is used twice on the left hand side implies that there will be a token copy circuit introduced. Note also that semicolon is used as a separator, like in the CHP language.

In pipelined circuits, it is important to be able to introduce slack to optimize performance. The syntax for this is the following:

dataflow {
  a + b -> [4] sum
}

Here, [4] specifies that there are four asynchronous pipeline stages introduced between the input and output. The default (and minimum value) that can be specified is 1.

Finally, we need to be able to introduce initial tokens with pre-specified initial values. The bracket notation is overloaded for this purpose.

dataflow {
  a + b -> [4,2] sum
}

This specifies that not only are there four pipeline stages, but the initial output produced on the sum channel is the integer 2.

The description so far is a complete set of dataflow primitives and can be used to https://csl.yale.edu/~rajit/abstracts/stf.html programs into silicon.