Processes and cells

A process is a user-defined type that corresponds to a circuit entity. Other hardware description languages sometimes call it a module or a subcircuit. The basic syntax of a process definition is shown below.

defproc test (bool n, m; bool p, q)
{
 ...
}

The definition above creates a new process, called test, that has a port list consisting of four bools. This port list cannot contain any parameter types (pint, etc).

If the body of the user-defined type is replaced by a single semi-colon or is empty, the statement corresponds to a type declaration. Declarations are typically used when defining mutually recursive types. The declaration corresponding to type test is:

defproc test (bool n, m; bool p, q);

If the process is never defined, ACT assumes that it has an empty body. If a process declaration is followed by a definition, the type signature must match exactly.

defproc test (bool n, m; bool p, q);
defproc test (bool n, m; bool p) { }
-[ERROR]-> Name `test' previously defined as a different process

A type can only have one definition in a given scope.

defproc test (bool n) { ... }
defproc test (bool n) { ... }
-[ERROR]-> Process `test': duplicate definition with the same type signature

The body of a process specifies its implementation. This can use a combination of instances of other processes, connections, and other languages like production rules. Loops and conditional statements can also be used to construct a process.

Port lists have a syntax similar to instantiations. A type specifier can be followed by a list of identifiers rather than just a single identifier, similar to an instantiation. Semicolons are used to separate parameters of differing types, as shown in the example below.

defproc test2(bool n,m; d1of2 p,q) { ... }

In this example we assumed that there was a user-defined type (or channel) called d1of2 that was used in the port list. Any user-defined type in the port list must be either a data or channel type. Processes are supposed to correspond to circuit blocks, and so cannot be port parameters to other circuit blocks.

Square brackets can also be used following the identifier names to specify array ports. The meaning of these square brackets is identical to the ordinary array instantiation. However, the arrays in port lists are restricted to be dense arrays indexed at zero. This restriction is enforced by syntax, and will be reported as a parse error.

defproc test1 (bool a,b,c, d[10]) { }  // success!
defproc test2 (bool a,b,c, d[0..9]) { }
-[ERROR]-> Expecting token `]', got `.'

The ports themselves cannot be converted to sparse arrays within the body of a definition. This means that the following is illegal:

defproc test1 (bool a, b, c, d[10])
{
  bool d[11..12];
  ...
}
-[ERROR]-> Array instance for `d': cannot extend a port array

Type names and variable names do not share the same name space. Creating a type definition with the same name as an instance variable or vice versa is allowed, but deprecated.

Cells follow the same rules for definition as processes, except the keyword defcell is used in place of defproc. The reason for separating cells from processes is that processes are supposed to correspond to logical entities that are meaningful semantic objects. For example, a process ordinarily has its origins in a CHP language description. Cells, on the other hand, can be fragments of logical processes. Examples of cells are standard gates like C-elements, NAND, or NOR gates, or commonly used circuit structures like completion detection logic. Cells are distinguished from processes to make it easier to write automation tools.