====== Types and Variables ======
Variables are the basic data objects in ACT. Instantiations specify
which variables are created, and state what type they have. The type of
an object completely specifies what the object is and how it can be
used.
Types come in two flavors: parameters and circuit elements. Parameters
are variables whose types are ''pint'', ''preal'',
''pbool'', or arrays thereof. All other types refer to circuit
elements. The basic circuit element is a Boolean value
''bool''. Circuit element types are broken down into three
categories: processes (created with ''defproc''), channels (created
with ''defchan''), and data (created with ''deftype'').
There are some restrictions on variable names. Ordinarily a variable
identifier can be constructed as an arbitrary sequence of underscores,
letters, and digits. Identifier names are case sensitive, so ''case''
and ''Case'' are different identifiers.
===== Basic types =====
The following basic types are supposed by ACT:
* Parameter types
* ''pint'', for integer parameters.
* ''preal'', for real-valued parameters.
* ''pbool'', for Boolean-valued parameters.
* ''ptype'', for type parameters.
* Data types
* ''bool'', for Boolean circuit signals.
* ''int'', for unsigned integer-valued data.
* ''enum'', for enumerations.
* ''chan'', for channels.
The first group of types (and arrays of them) are referred to as
//parameter// types or //meta-language// types, and they begin with
the character ''p''. This is because they do not represent physical
entities in the circuit itself, but rather values that are used to
construct the circuit or specify circuit parameters.
The ''bool'' type corresponds to an electrical node in the
circuit. Eventually all types get implemented using circuit elements and
''bool''s.
The ''int'', ''enum'', and ''chan'' types are used for
higher-level representations of the circuit. These types support
parameters, and are described in more detail later.
Variables of these basic types can be created by specifying the type
name followed by a comma-separated list of identifier names.
bool a,b,c,n1,n1x2;
pint x,y,z;
preal w2,w_3;
The statements above are referred to as //instantiations//, since they
create variables that are instances of the basic types. It is an error
to have more than one instantiation of a variable in the same scope.
bool a;
pint a;
-[ERROR]-> Duplicate instance for name `a'
A parameter instantiation can be accompanied by a single //initializer//
which initializes the value of a variable.
pint a=5, c=8;
preal b=8.9;
The order of initialization of variables is left to right.
Using constructs such as
pint a=c, c=5;
-[ERROR]-> The identifier `c' does not exist in the current scope
should be avoided, as this leads to the error shown above indicating
that ACT does not know about variable ''c'' in the initialization
of ''a''. Constructs where the two instances and initializers are
listed in an order that does not lead to an error are deprecated even
they are well-defined.
===== Array types =====
An array of a basic type or user-defined type can be created using
ACT's array syntax. The syntax is based on C-style arrays, and
examples of creating arrays are shown below:
int ar1[4]
preal ar2[7]
bool ar3[1..6]
The number in square brackets specifies the range of the array. In the
first two examples, valid array indices range from zero to three and
zero to six respectively. The third example specifies the array indices
to range from one to six. In general, if the array index range is
specified by a single integer, the lower bound of the range is zero, and
the upper bound is the specified integer minus one. Instead of simple
integers, arbitrary integer expressions can also be used as array range
specifications, as shown below.
int ar4[5*3]
preal ar5[7*x+(y%2)-p] // here x, y, and p must be integer parameter types
Expressions used to specify array ranges must be of integer
type. Variables used must always be parameter types (typically ''pint'').
preal a = 4.3;
bool ar6[7*a+5];
-[ERROR]-> Expression must be of type int
Multidimensional arrays are specified by additional square brackets. Two
and three-dimensional arrays of ''bool''s are specified as shown in
the example below.
bool x[5,3];
bool y[1..6][9][2..10];
ACT provides a mechanism for constructing //sparse arrays//,
i.e., those whose range need not be a single contiguous block. It is
possible to create an array of nodes whose elements exist only at, say,
positions 4 and 6 of the array. The syntax for creating the
aforementioned array is shown below.
bool n[4..4], n[6..6];
These sparse array instantiations can be mixed with ordinary
instantiations, permitting the definition of arrays which can be
dynamically extended in ACT.
bool n[5];
bool n[10..12]; // n is now defined at positions 0 to 4, 10 to 12
The definition below specifies an instantiation of elements of array
''m'' at positions ''[6][5]'', ''[6][6]'', ..., ''[6][10]''.
bool m[6..6][5..10]
Note that this is quite different from the statement
bool m[6][5..10];
which indicates that array ''m'' is to be instantiated at positions
''[0][5]'', ..., ''[5][10]''.
Unlike ordinary instances, array instantiations cannot be followed by
initializers.
bool x[10];
bool y[10] = x;
-[ERROR]-> Connection can only be specified for non-array instances
For type-checking purposes, an array is defined by its base type
(''bool'' in the example above), number of dimensions, and the
shape of the array in each dimension.
===== Parameterized types =====
Parameterized types give ACT considerable flexibility in type
definitions. Parameterized types come in two flavors: built-in types,
and user-defined types. For user-defined types, ACT guarantees
that the order in which parameters are created and initialized is from
left to right. Therefore, one can use the value of one parameter in
the definition of another one.
Although we have been describing the types ''int'' and ''chan''
as simple types, they are in fact parameterized. Omitting the
parameters makes ACT use implicit default parameters for both of
them.
The ''int'' type is parameterized by the number of bits used to
specify the integer. This bit-width can be specified using angle
brackets, as shown below:
int<1> x; // x is a one bit integer
int<37> y; // y is a thirty-seven bit integer
When interpreting these bits as integers, ACT assumes an unsigned
binary representation. The default bit-width is thirty-two.
The channel type ''chan'' can be parameterized by the type that is
being sent and received on the channel.
chan(bool) x; // x is a Boolean channel
chan(int<16>) y; // y is a 16-bit integer channel
The default data type for a channel is assumed to be the default
''int'', namely ''int<32>''.
Channels are almost always unidirectional, with data being transferred from sender to receiver.
In a few cases, it is useful to be able to transfer data from the sender to the receiver, and from the
receiver to the sender in one channel action. To declare a channel where data are transferred in
both directions, use:
// a bool is transferred from sender to receiver, and
// an int is transferred from the receiver to the sender
chan(bool,int) x;
These are sometimes called //exchange channels//, since data is exchanged between the sender and receiver.
Another built-in data type is the //enumeration// type. An
enumeration type corresponds to integer-valued variables with a
restricted range.
enum<5> x; // x can take on values 0, 1, 2, 3, 4
For convenience, these values are treated as integers for the purposes
of expressions. Also, enumerations that have power-of-two ranges are
type-equivalent to the approprate ''int'' type. For instance,
''enum<2>'' is equivalent to ''int<1>''. Enumerations are
useful when specifying a data value that is a one-hot code.
===== Directional types =====
Data and channel types also support access permissions in terms of valid
operations on the types. To illustrate this, consider the simplest data
type, namely a ''bool''. There are three different ways a ''bool''
type can be defined, and they are shown below:
bool x; // Boolean that may be read or written
bool! y; // Boolean that must be written, and may be read
bool? z; // Boolean that must be read, and cannot be written
The ''!'' and ''?'' suffixes constrain the way in which the type
can be accessed. The primary use of this is in port lists, where one can
specify what variables are read and written by a process. The same
syntax can be used (with the same meaning) for user-defined data types.
The following example shows a possible definition for a two-input nand
gate that takes two inputs ''a'' and ''b'', and produces its
output on ''c''.
defcell nand2 (bool? a, b; bool! c) { ... }
Channels support a similar syntax, but the meaning is slightly
different.
chan(int) x; // Sends or receives are permitted
chan!(int) y; // Only sends permitted
chan?(int) z; // Only receives permitted
Again, the same syntax is valid for user-defined channels. These
constructs are useful in libraries for additional error checking, and
conveying more information to the user of the library.