Constructors for Types

There are cases when it is useful to be able to build more complex types from existing types. ACT provides a mechanism to do so via interfaces and parameter types.

Interfaces

An interface is defined in the following way:

interface linearchain (bool? in; bool! out);

This defines linearchain to be an interface definition, where the interface has two ports: in and out.

Any process can export a list of interfaces. When a process exports an interface, it means that it has I/O ports that correspond to the names in the interface. The port names within the process need not match the port names in the interface; when an interface is exported, a mapping must be provided as part of the interface export specification.

defproc proc1 (bool? a; bool! b)  :> linearchain { in -> a, out -> b } 
{
 ...
}

In this definition, proc1 exports the linearchain interface, except that the in port of the interface is mapped to a, and the out port of the interface is mapped to b. If multiple interfaces are exported, they are simply separated by commas.

Parameter types

The special ptype meta-parameter type is used to pass in types into a process definition. These types can be used to build a process using other processes as building blocks. The syntax for using a ptype is the following:

ptype(foo) x;

This says that x is a variable that is a type, with the constraint that x must satisfy the type signature specified by interface foo. In other words, x is guaranteed to have the ports corresponding to interface type foo.

ptype parameters can also be used in templates. Consider the following example:

// A constructor for a datapath with W-bit ripple connections, and
// where each component has an additional M inputs and one output
 
// Interface definition
template<pint W, pint M>
interface bitslice (e1of2? rin[W]; e1of2! rout[W]; e1of2? in[M]; e1of2! out);
 
// the constructor
template<pint N, pint M, pint W, ptype(bitslice<W,M>) t>
defproc build_dpath (e1of2? rin[W]; e1of2! rout[W]; 
                     e1of2? in[M*N]; e1of2! out[N])
{
   t x[N];  // array of type "t" that exports a bitslice<W,M> interface
 
 
   // ripple connections
   (i:N-1: x[i].rout=x[i+1].rin;)
   x[0].rin=rin;
   x[N-1].rout=rout;
 
   // i/o connections
   (i:N: x[i].in = in[i*M..(i+1)*M-1];
          x[i].out=out[i]; )
}
 
// A one-bit adder, that exports the bitslice interface
defproc onebit (e1of2? in[2], rin[1]; e1of2! out, rout[1]) 
                 :>  bitslice<1,2> { in -> in, rin -> rin, out -> out, rout -> rout }
{ ... }
 
defproc ripple_adder (e1of2? a[32], b[32], cin; e1of2! out[32], cout)
{
    build_dpath<32,2,1,@onebit> dp;
 
    (i : 32 : dp.in[2*i] = a[i]; dp.in[2*i+1] = b[i];)
    dp.out = out;
    dp.rin[0] = cin;
    dp.rout[0] = cout;
}

Process type (ptype) parameters for templates use the @ character (as in @onebit) in the example above.