Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
language:types [2019/04/18 15:48] – [Direction flags] rajit | language:types [2023/04/09 23:55] (current) – [Parameterized types] rajit | ||
---|---|---|---|
Line 23: | Line 23: | ||
* Parameter types | * Parameter types | ||
- | * '' | + | * '' |
- | * '' | + | |
* '' | * '' | ||
* '' | * '' | ||
Line 51: | Line 50: | ||
name followed by a comma-separated list of identifier names. | name followed by a comma-separated list of identifier names. | ||
- | < | + | < |
bool a, | bool a, | ||
pint x,y,z; | pint x,y,z; | ||
Line 61: | Line 60: | ||
to have more than one instantiation of a variable in the same scope. | to have more than one instantiation of a variable in the same scope. | ||
- | < | + | < |
bool a; | bool a; | ||
pint a; | pint a; | ||
+ | </ | ||
+ | < | ||
-[ERROR]-> | -[ERROR]-> | ||
</ | </ | ||
Line 69: | Line 70: | ||
A parameter instantiation can be accompanied by a single // | A parameter instantiation can be accompanied by a single // | ||
which initializes the value of a variable. | which initializes the value of a variable. | ||
- | < | + | < |
pint a=5, c=8; | pint a=5, c=8; | ||
preal b=8.9; | preal b=8.9; | ||
Line 77: | Line 78: | ||
Using constructs such as | Using constructs such as | ||
+ | <code act> | ||
+ | pint a=c, c=5; | ||
+ | </ | ||
< | < | ||
- | pint a=c, c=5; | ||
-[ERROR]-> | -[ERROR]-> | ||
</ | </ | ||
Line 93: | Line 96: | ||
examples of creating arrays are shown below: | examples of creating arrays are shown below: | ||
- | < | + | < |
int ar1[4] | int ar1[4] | ||
preal ar2[7] | preal ar2[7] | ||
Line 108: | Line 111: | ||
specifications, | specifications, | ||
- | < | + | < |
int ar4[5*3] | int ar4[5*3] | ||
preal ar5[7*x+(y%2)-p] // here x, y, and p must be integer parameter types | preal ar5[7*x+(y%2)-p] // here x, y, and p must be integer parameter types | ||
Line 116: | Line 119: | ||
type. Variables used must always be parameter types (typically '' | type. Variables used must always be parameter types (typically '' | ||
- | < | + | < |
preal a = 4.3; | preal a = 4.3; | ||
bool ar6[7*a+5]; | bool ar6[7*a+5]; | ||
+ | </ | ||
+ | < | ||
-[ERROR]-> | -[ERROR]-> | ||
</ | </ | ||
Line 126: | Line 131: | ||
the example below. | the example below. | ||
- | < | + | < |
bool x[5,3]; | bool x[5,3]; | ||
bool y[1..6][9][2..10]; | bool y[1..6][9][2..10]; | ||
Line 137: | Line 142: | ||
aforementioned array is shown below. | aforementioned array is shown below. | ||
- | < | + | < |
bool n[4..4], n[6..6]; | bool n[4..4], n[6..6]; | ||
</ | </ | ||
Line 145: | Line 150: | ||
dynamically extended in ACT. | dynamically extended in ACT. | ||
- | < | + | < |
bool n[5]; | bool n[5]; | ||
bool n[10..12]; // n is now defined at positions 0 to 4, 10 to 12 | bool n[10..12]; // n is now defined at positions 0 to 4, 10 to 12 | ||
Line 153: | Line 158: | ||
'' | '' | ||
- | < | + | < |
bool m[6..6][5..10] | bool m[6..6][5..10] | ||
</ | </ | ||
Line 159: | Line 164: | ||
Note that this is quite different from the statement | Note that this is quite different from the statement | ||
- | < | + | < |
bool m[6][5..10]; | bool m[6][5..10]; | ||
</ | </ | ||
Line 169: | Line 174: | ||
initializers. | initializers. | ||
- | < | + | < |
bool x[10]; | bool x[10]; | ||
bool y[10] = x; | bool y[10] = x; | ||
+ | </ | ||
+ | < | ||
-[ERROR]-> | -[ERROR]-> | ||
</ | </ | ||
Line 179: | Line 186: | ||
shape of the array in each dimension. | shape of the array in each dimension. | ||
- | ===== User-defined types ===== | ||
- | User-defined type can be used to create complex circuit structures. | ||
- | new user-defined type name is introduced by using '' | ||
- | '' | ||
- | statements. All user-defined types have the same basic structure: | ||
- | - a type signature, that provides information about the interface to the type and the ports that are externally visible; and | ||
- | - a //body//, contained in braces, that specifies the detailed definition of the user-defined type. | ||
- | The type chosen for each port must be the most specific type used by | ||
- | that port in the body (see the implementation relation section). | ||
- | User-defined types can also be parameterized, | ||
- | detail later. | ||
- | ==== Processes and cells ==== | + | ===== Parameterized types ===== |
- | 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 '' | ||
- | a port list consisting of four '' | ||
- | contain any parameter types ('' | ||
- | |||
- | If the body of the user-defined type is replaced by a single | ||
- | semi-colon or is empty, the statement corresponds to a type | ||
- | // | ||
- | mutually recursive types. The declaration corresponding to type | ||
- | '' | ||
- | |||
- | < | ||
- | 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]-> | ||
- | </ | ||
- | |||
- | A type can only have one definition in a given scope. | ||
- | |||
- | < | ||
- | defproc test (bool n) { ... } | ||
- | defproc test (bool n) { ... } | ||
- | -[ERROR]-> | ||
- | </ | ||
- | |||
- | The body of a process specifies its implementation. This can use a | ||
- | combination of instances of other processes, connections, | ||
- | 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 '' | ||
- | 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]-> | ||
- | </ | ||
- | |||
- | 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]-> | ||
- | </ | ||
- | |||
- | 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 '' | ||
- | 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. | ||
- | |||
- | ==== Data types ==== | ||
- | |||
- | A data type is defined using '' | ||
- | to an integer or Boolean value, although it could also be a composite | ||
- | construct like a record or structure (from software programming | ||
- | languages). The syntax is similar to a process, and the constraints | ||
- | about declarations/ | ||
- | |||
- | Data types have some additional structure that is not required for a | ||
- | process. In particular, the body of the data type and its type | ||
- | signature provide information that relates a user-defined data type to | ||
- | a previously defined or built-in data type. When a user-defined data | ||
- | type is specified, a method for setting the value of the data type and | ||
- | reading its value must also be specified. If omitted, certain features | ||
- | of data types will not be enabled for the defined type. | ||
- | |||
- | The following is a simple example of a datatype that creates a dual-rail | ||
- | representation for a Boolean variable. The first line specifies that | ||
- | '' | ||
- | '' | ||
- | |||
- | < | ||
- | deftype d1of2 <: int< | ||
- | { | ||
- | spec { | ||
- | exclhi(d0, | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | The body of the type is similar to a process, except it can only contain | ||
- | connections, | ||
- | following would result in an error: | ||
- | |||
- | < | ||
- | deftype d1of2 <: int< | ||
- | { | ||
- | bool p; | ||
- | spec { | ||
- | exclhi(d0, | ||
- | } | ||
- | } | ||
- | -[ERROR]-> | ||
- | </ | ||
- | |||
- | There are two methods that can be specified for a data type: | ||
- | - a //set method//, used to write a value to the type; | ||
- | - a //get method//, used to read the value of the type. | ||
- | One can think of these as type conversion methods invoked | ||
- | automatically to read or write the data type. | ||
- | |||
- | < | ||
- | deftype d1of2 <: int< | ||
- | { | ||
- | spec { | ||
- | exclhi(d0, | ||
- | } | ||
- | | ||
- | set { | ||
- | | ||
- | } | ||
- | get { | ||
- | | ||
- | } | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | In the example above, the '' | ||
- | '' | ||
- | '' | ||
- | '' | ||
- | the methods specify conversion operations. | ||
- | |||
- | The selection statement in the '' | ||
- | selection operator '' | ||
- | that when the '' | ||
- | cannot both be '' | ||
- | specification body. Also, if both '' | ||
- | (i.e. an illegal state in which to execute a get operation), the | ||
- | variable '' | ||
- | one of '' | ||
- | data type. (This is different in the case of a channel, where the | ||
- | semantics of the channel permit waiting.) | ||
- | |||
- | Port lists for data types can be either built-in data types or | ||
- | user-defined data types. Channels (built-in or user-defined) and | ||
- | processes are not valid types for ports of a data type, since a data | ||
- | type is supposed to represent a circuit structure that is used to | ||
- | represent a data value. | ||
- | |||
- | ==== Channels ==== | ||
- | |||
- | Channels are similar to data types. Instead of relating a user-defined | ||
- | channel to built-in data, we relate them to a built-in channel types | ||
- | instead. The methods required for supporting the full functionality of | ||
- | a channel are operations necessary to send and receive data on the | ||
- | channel, rather than a read and write operation on a data value. | ||
- | |||
- | There are six possible methods that can be defined for a channel type: | ||
- | |||
- | * Methods for sending and receiving values on the channel | ||
- | * '' | ||
- | * '' | ||
- | * Methods for probing a channel to determine if there is synchronization operation being attempted. | ||
- | * '' | ||
- | * '' | ||
- | |||
- | The send operation '' | ||
- | parts: setting the data value, followed by the synchronization | ||
- | operation. Setting the data value also indicates that the sender is | ||
- | ready to communicate. It is illegal to set the data value multiple | ||
- | times without an intervening synchronization operation. Finally, | ||
- | attempting to set the data value might block if the previous channel | ||
- | operation has not completed as yet. Whether or not this could occur | ||
- | depends on the channel protocol. | ||
- | |||
- | The receive operation '' | ||
- | two parts: receiving the data value, followed by the synchronization | ||
- | operation. Attempting to get the data value from the channel will | ||
- | block if the sender has not provided any value. Once a value has been | ||
- | extracted from the channel, the synchronization operation can be | ||
- | executed. Prior to the synchronization, | ||
- | executed; the channel must be designed so that subsequent get | ||
- | operations will return the same value as the first one, and will be | ||
- | guaranteed not to block. The get operation is used to implement a CHP | ||
- | value probe, where the receiver can peek at the value pending in the | ||
- | channel without attempting a synchronization operation. | ||
- | |||
- | An example definition of a Boolean channel where the channel has an | ||
- | lazy-active send and passive receive is below. | ||
- | |||
- | < | ||
- | defchan e1of2 <: chan(bool) (bool d0,d1,e) | ||
- | { | ||
- | spec { | ||
- | exclhi(d0, | ||
- | } | ||
- | | ||
- | set { | ||
- | [e]; | ||
- | } | ||
- | send_rest { | ||
- | [~e]; | ||
- | } | ||
- | get { | ||
- | | ||
- | } | ||
- | recv_rest { | ||
- | | ||
- | } | ||
- | recv_probe = (d0|d1); | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | In the example above, the '' | ||
- | specify the sequence of operations on the channel variables that are | ||
- | invoked for a send action. The '' | ||
- | specify the sequence of operations used to perform a receive. The | ||
- | special variable '' | ||
- | that is being either sent or received on the channel. | ||
- | |||
- | This channel has an active send and passive receive, and hence probes | ||
- | are only supported at the receiver. The '' | ||
- | expression specifies the Boolean expression corresponding to the probe | ||
- | at the receiver end of the channel. A '' | ||
- | specified in a similar way when the sender is passive and receiver is | ||
- | active. | ||
- | |||
- | The '' | ||
- | handshake protocol. If the channel were to correspond to a two-phase | ||
- | protocol, a different sequence of actions can be specified instead. | ||
- | |||
- | Port lists for channel types can be data types (built-in or | ||
- | user-defined) or channels. Processes are not valid types for ports of a | ||
- | channel type. | ||
- | |||
- | ==== Instantiating user-defined types ==== | ||
- | |||
- | User-defined type variables can be instantiated in much the same manner | ||
- | as ordinary type variables. | ||
- | |||
- | < | ||
- | defproc test(bool N, n) { ... } | ||
- | test x; | ||
- | // x.N and x.n refer to the ports of '' | ||
- | </ | ||
- | |||
- | The ACT description above creates an instance of type '' | ||
- | '' | ||
- | ports listed as well as creating whatever is specified by the body of | ||
- | the type definition. The list of ports of a user-defined type can be | ||
- | accessed from the scope outside the type definition by using | ||
- | dot-notation. These externally visible ports are analogous to the | ||
- | //fields// of structures or record types in standard programming | ||
- | languages. | ||
- | |||
- | This analogy to records can be used to build complex data types, | ||
- | albeit with slightly different syntax compared to traditional | ||
- | programming languages. The following is a simple example that | ||
- | illustrates this. | ||
- | |||
- | < | ||
- | deftype mystruct <: int< | ||
- | { | ||
- | methods { | ||
- | set { | ||
- | | ||
- | | ||
- | | ||
- | } | ||
- | get { | ||
- | | ||
- | } | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | |||
- | ===== Parameterized types ===== | ||
Parameterized types give ACT considerable flexibility in type | Parameterized types give ACT considerable flexibility in type | ||
definitions. Parameterized types come in two flavors: built-in types, | definitions. Parameterized types come in two flavors: built-in types, | ||
Line 527: | Line 198: | ||
the definition of another one. | the definition of another one. | ||
- | ==== Built-in integers and channels ==== | ||
Although we have been describing the types '' | Although we have been describing the types '' | ||
- | as simple types, they are in fact paramterized. Omitting the | + | as simple types, they are in fact parameterized. Omitting the |
parameters makes ACT use implicit default parameters for both of | parameters makes ACT use implicit default parameters for both of | ||
them. | them. | ||
Line 538: | Line 208: | ||
brackets, as shown below: | brackets, as shown below: | ||
- | < | + | < |
int< | int< | ||
int< | int< | ||
Line 549: | Line 219: | ||
being sent and received on the channel. | being sent and received on the channel. | ||
- | < | + | < |
chan(bool) x; // x is a Boolean channel | chan(bool) x; // x is a Boolean channel | ||
chan(int< | chan(int< | ||
Line 556: | Line 226: | ||
The default data type for a channel is assumed to be the default | The default data type for a channel is assumed to be the default | ||
'' | '' | ||
+ | |||
+ | Channels are almost always unidirectional, | ||
+ | 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: | ||
+ | <code act> | ||
+ | // a bool is transferred from sender to receiver, and | ||
+ | // an int is transferred from the receiver to the sender | ||
+ | chan(bool, | ||
+ | </ | ||
+ | These are sometimes called //exchange channels//, since data is exchanged between the sender and receiver. | ||
+ | |||
Another built-in data type is the // | Another built-in data type is the // | ||
Line 561: | Line 243: | ||
restricted range. | restricted range. | ||
- | < | + | < |
enum< | enum< | ||
</ | </ | ||
Line 568: | Line 250: | ||
of expressions. Also, enumerations that have power-of-two ranges are | of expressions. Also, enumerations that have power-of-two ranges are | ||
type-equivalent to the approprate '' | type-equivalent to the approprate '' | ||
- | '' | + | '' |
useful when specifying a data value that is a one-hot code. | useful when specifying a data value that is a one-hot code. | ||
- | |||
- | ==== User-defined types ==== | ||
- | Processes, channels, and datatypes created using '' | ||
- | '' | ||
- | parameterization. Parameters are specified using the '' | ||
- | keyword. | ||
- | |||
- | Since the syntax for all three is the same, we use a process | ||
- | definition to illustrate this. To create a parameterized type, the | ||
- | definition of the type is preceeded by a template specifier as shown | ||
- | below. | ||
- | |||
- | < | ||
- | // A generic adder block | ||
- | template< | ||
- | defproc adder (e1of2 a[N], b[N]; e1of2 s[N]) | ||
- | { | ||
- | ... | ||
- | } | ||
- | </ | ||
- | |||
- | This example defines an '' | ||
- | parameter. Note that the value of '' | ||
- | arrays in the port list for the process. Instances of this | ||
- | '' | ||
- | |||
- | < | ||
- | adder< | ||
- | adder< | ||
- | </ | ||
- | |||
- | The value of '' | ||
- | 16. To illustrate how one might define this adder block, assume we | ||
- | have processes '' | ||
- | '' | ||
- | constant source of zeros, and a constant sink respectively. One | ||
- | possible definition of the adder would be: | ||
- | |||
- | < | ||
- | template< | ||
- | defproc adder (e1of2 a[N], b[N]; e1of2 s[N]) | ||
- | { | ||
- | | ||
- | ( i : N-1 : fa[i].a = a[i]; fa[i].b = b[i]; fa[i].s = s[i]; | ||
- | fa[i].co = fa[i+1].ci; ) | ||
- | | ||
- | | ||
- | | ||
- | | ||
- | } | ||
- | </ | ||
- | |||
- | This creates a parameterized ripple-carry adder. Notice the use of | ||
- | loops and arrays to connect the carry chain for the adder, and the | ||
- | inputs and outputs of the process to the '' | ||
- | |||
- | As shown in the example above, the arguments in the template parameter | ||
- | list are specified by listing them next to the type name. Trailing | ||
- | arguments can be omitted from the parameter list attached to the type | ||
- | as shown in the example below. | ||
- | |||
- | < | ||
- | template< | ||
- | defproc test (bool n[N]) { ... } | ||
- | |||
- | test< | ||
- | </ | ||
- | |||
- | Channels and data types can also be parameterized in the same way. For | ||
- | example, the following might be an N-bit dual rail definition. | ||
- | |||
- | < | ||
- | template< | ||
- | deftype d1of2 <: int< | ||
- | </ | ||
- | |||
- | Since the body of the type can use loops and selection statements in | ||
- | arbitrary ways, changing the parameters for the type can completely | ||
- | change the structure of the circuit. It can also change the ports for | ||
- | the type. Hence, when checking for type compatibility, | ||
- | parameters are also taken into account. Hence, the full type for | ||
- | instance '' | ||
- | '' | ||
- | parameters are more completely specified as '' | ||
- | although the angle brackets can be omitted. Arrays can only correspond | ||
- | to instances of the same type---so an array cannot contain a three-bit | ||
- | adder and five-bit adder. | ||
===== Directional types ===== | ===== Directional types ===== | ||
Line 665: | Line 260: | ||
type can be defined, and they are shown below: | type can be defined, and they are shown below: | ||
- | < | + | < |
bool x; // Boolean that may be read or written | bool x; // Boolean that may be read or written | ||
bool! y; // Boolean that must be written, and may be read | bool! y; // Boolean that must be written, and may be read | ||
Line 680: | Line 275: | ||
output on '' | output on '' | ||
- | < | + | < |
defcell nand2 (bool? a, b; bool! c) { ... } | defcell nand2 (bool? a, b; bool! c) { ... } | ||
</ | </ | ||
Line 687: | Line 282: | ||
different. | different. | ||
- | < | + | < |
chan(int) x; // Sends or receives are permitted | chan(int) x; // Sends or receives are permitted | ||
chan!(int) y; // Only sends permitted | chan!(int) y; // Only sends permitted | ||
Line 696: | Line 291: | ||
constructs are useful in libraries for additional error checking, and | constructs are useful in libraries for additional error checking, and | ||
conveying more information to the user of the library. | conveying more information to the user of the library. | ||
- | |||
- | ==== Interaction with user-defined types ==== | ||
- | |||
- | |||
- | ===== The implementation relation ===== | ||
- | |||
- | ===== The ptype ===== | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||