This is an old revision of the document!


First-in first-out buffer in CHP

(It is a bit unfortunate that the computer science community uses the term buffer to refer to a fifo, while the circuits community uses the term buffer to refer to an electrical circuit that introduces gain to improve signal drive strength.)

A one-place buffer

ACT supports behavioral descriptions using the CHP language. The CHP language is a message-passing programming language. Its syntax is inspired by Dijkstra's guarded commands language and Hoare's CSP language.

A one-place buffer is a component that repeatedly does the following: it receives a new input; once the input is received, it sends the new value on its output. Suppose we want to describe a CHP component that has an input channel L an output channel R, and has an internal variable x used to hold the received value. The statement

L?x

receives a value from channel L and stores it into variable x. This is a blocking receive: if there is a pending input, the communication action succeeds; otherwise the action waits until an input is available. Once we have successfully received the input, we can follow this operation by sending the value x on the output channel R.

R!x

The send operation is also blocking; if no one is ready to receive the value, the send will block until a receiver arrives. Since these two actions occur in sequence, we write:

L?x; R!x

Finally, the one-place buffer corresponds to an infinite repetition of this sequence of operations. This is written as follows:

*[ L?x; R!x ]

To make this into a valid ACT program, we need to declare all the variables, and define the one-place buffer component.

defproc one_place_buffer (chan?(int) L; chan!(int) R)
{
   int x;
 
   chp {
       *[ L?x; R!x ]
  }
}

This defines a process called one_place_buffer. The name of the process is followed by its port list. Only signals/variables in the port list are accessible from outside the process. We have specified two ports: L, and R. L is an input port, and we must also specify the data type that is communicated on the port (int in this example, corresponding to an unsigned integer). L has type chan?(int): an input (the ?) end of a channel (the chan) that carries integer data (int). Note that since the bit-width of the integer was not specified, it is implicitly 32 bits. R has a similar declaration. Finally, the definition of the process includes the declaration of the variable x, and the behavior of the process specified in the CHP sub-language that is surrounded by chp { … }.

The same process that manipulates 8-bit integers would be written as follows:

defproc one_place_buffer (chan?(int<8>) L; chan!(int<8>) R)
{
   int<8> x;
 
   chp {
       *[ L?x; R!x ]
  }
}

Note the use of angle brackets to specify the bit-width. In ACT, angle brackets are used to specify parameters to types (similar to C++).

A ten-place buffer

A buffer that can hold ten values can be obtained by connecting ten one-place buffers to each other, where each buffer operates in parallel. When we defined a one_place_buffer earlier, we actually defined a new ACT type. This ACT type is a process with the specified port list. We can now use this type to create multiple instances of the process, just like we created x as an instance of an integer earlier.

 one_place_buffer b0;   // create buffer called b0
 one_place_buffer b1;   // create buffer called b1

ACT is a hardware description language; hence all processes operate in parallel. So far we have created two parallel one-place buffers. To convert this into a two-place buffer, we need to connect the output of b0 to the input of b1.

ACT provides a flexible syntax for connecting components/ports/instances to each other. The basic connection syntax is a = b, which connects a and b to each other. We can use this syntax to connect the output R of b0 to the input L of b1 as follows:

b0.R = b1.L;

We can repeat this process to create a ten-place buffer. Since linear arrays of processes are common in circuit design, ACT provides array syntax to simplify this process. We can create ten buffers as follows:

one_place_buffer b[10];

This creates ten concurrent buffers, named b[0], b[1], …, b[9]. We can connect them to each other to create a ten-place buffer like this:

b[0].R = b[1].L;
b[1].R = b[2].L;
...
b[8].R = b[9].L;

Since typing all of these connections gets tedious, ACT provides syntax for repetitive constructions. The same sequence of connections can be written:

(i : 9 : b[i].R = b[i+1].L;)

Here, i is a fresh variable that takes on values 0, 1, …, 8 (one less than 9 specified between the two colons).

The complete ten-place buffer with its primary input and output channels is given by:

defproc tenplace_buffer (chan?(int) L; chan!(int) R)
{
   one_place_buffer b[10];
   (i : 9 : b[i].R = b[i+1].L;)
   b[0].L = L;
   b[9].R = R;
}

Note that we connected the b[0] input to the primary input to the process, and the b[9] output to the primary output R.