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:58] – [Refinement] rajit | language:types [2025/04/21 14:32] (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 ==== | ||
- | |||
- | 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 built-in types ===== | ||
- | ===== 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 ==== | ||
- | |||
- | |||
- | Direction specifications can be used for built-in data and channel | ||
- | types, as well as user-defined types. Consider the '' | ||
- | user-defined channel type that we saw earlier: | ||
- | |||
- | < | ||
- | defchan e1of2 <: chan(bool) (bool d0,d1,e) | ||
- | { | ||
- | spec { | ||
- | exclhi(d0, | ||
- | } | ||
- | | ||
- | set { | ||
- | [e]; | ||
- | } | ||
- | send_rest { | ||
- | [~e]; | ||
- | } | ||
- | get { | ||
- | | ||
- | } | ||
- | recv_rest { | ||
- | | ||
- | } | ||
- | recv_probe = (d0|d1); | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | When we use '' | ||
- | specify the access permissions for the //port parameters// | ||
- | user-defined type. The convention used is that there are five possible | ||
- | ways to specify any constraints on access to port parameters. For our | ||
- | example, we can use one of the following variations in the port parameter list: | ||
- | |||
- | < | ||
- | bool d0; // No constraints; | ||
- | bool! d0; // Both e1of2? and e1of2! have bool! permissions | ||
- | bool? d0; // Both e1of2? and e1of2! have bool? permissions | ||
- | bool?! d0; // e1of2? has bool? permissions, | ||
- | bool!? d0; // e1of2? has bool! permissions, | ||
- | </ | ||
- | |||
- | Hence, a better definition of an '' | ||
- | completely specify the access permissions for the port parameters in | ||
- | the following way. | ||
- | |||
- | < | ||
- | defchan e1of2 <: chan(bool) (bool?! d0,d1; bool!? e) { ... } | ||
- | </ | ||
- | |||
- | A careful examination of the type signature reveals that the sender | ||
- | and receiver have the appropriate permissions. There is a subtle | ||
- | interaction between connections and directional types in ACT, and this | ||
- | is detailed in the section on connections. | ||
- | |||
- | |||
- | ===== The Implementation relation ===== | ||
- | |||
- | When types are created using either '' | ||
- | they are defined as the implementation of a type. This is also | ||
- | possible for processes defined using '' | ||
- | |||
- | ==== Implementation ==== | ||
- | |||
- | The implementation relation is used to specify the precise | ||
- | implementation of a data or channel type. The most straightforward | ||
- | mechanism to specify a channel is to say that it is the implementation | ||
- | of a built-in data type. In the example above, the channel | ||
- | '' | ||
- | '' | ||
- | and it specifies the communication protocols for a send and receive | ||
- | action. The syntax that is used to say that we have an implementation | ||
- | is the ''<:'' | ||
- | that it is related to. | ||
- | |||
- | When a type implements another one, the new type can be used in places | ||
- | the old type was used. While this is superficially similar to | ||
- | subtyping in programming languages, it is better viewed as a | ||
- | refinement relationship. So, if type '' | ||
- | '' | ||
- | implementation compared to '' | ||
- | //parent type// for type '' | ||
- | or channel type can have a method body that specifies all the | ||
- | operations that can be performed using the type. These method bodies | ||
- | relate operations on the type to operations on the parent type | ||
- | (through '' | ||
- | |||
- | |||
- | Different implementations of the same type are not equivalent or | ||
- | interchangeable. For example, one can imagine two different | ||
- | implementations of an '' | ||
- | one-of-four code. Both are implementations, | ||
- | equivalent to each other. | ||
- | |||
- | A more interesting option is to use refinement between two | ||
- | user-defined types. Refinement is used to create related versions of | ||
- | an existing type. If type '' | ||
- | basic rule is that one can use '' | ||
- | contexts where '' | ||
- | defining one type to be an implementation of another is discussed in | ||
- | detail below. | ||
- | |||
- | ==== Refinement ==== | ||
- | |||
- | When a user defined type implements another, there are several items | ||
- | to consider: (i) the template parameters (if any); (ii) the port | ||
- | parameters; and the body of the new type. Since the new type is | ||
- | supposed to implement the old one, ACT defines the template | ||
- | parameters and port parameters for the new type using the old type as | ||
- | a starting point. | ||
- | |||
- | The port list of the new type consists of the original port list in | ||
- | the base type plus the additional ports specified in the type | ||
- | definition---in other words, the new type //extends// the original | ||
- | port list. As an illustration, | ||
- | |||
- | < | ||
- | defproc type1 (bool a, b) { ... } | ||
- | defproc type2 <: type1 (bool c) { ... } | ||
- | </ | ||
- | |||
- | In this case '' | ||
- | '' | ||
- | |||
- | Template parameters are handled in a similar fashion, except there is | ||
- | a complication. In the example below, '' | ||
- | template parameters '' | ||
- | the port list example above. | ||
- | |||
- | < | ||
- | template< | ||
- | defproc type1 (bool a, b) { ... } | ||
- | |||
- | template< | ||
- | defproc type2 <: type1 (bool c) { ... } | ||
- | </ | ||
- | |||
- | However, we can also define '' | ||
- | |||
- | < | ||
- | template< | ||
- | defproc type1 (bool a, b) { ... } | ||
- | |||
- | template< | ||
- | defproc type2 <: type1< | ||
- | </ | ||
- | |||
- | In this version, the template parameter from '' | ||
- | specified in the type definition for '' | ||
- | feature, template parameters for the new type are defined in the | ||
- | following manner: | ||
- | * Template parameters are categorized as //definable parameters// | ||
- | * Pre-specified parameters are not part of the parameter set that can be specified when a type is created. | ||
- | * The value of the pre-specified parameter is computed based on the type signature. | ||
- | * The list of parameters accessible in the body of the type and outside the type is the combination of the ones defined by the new type and the parent type. | ||
- | * The order of template parameters in the new type corresponds to the new parameters first (in the order specified), followed by any definable parameters left in the parent type. | ||
- | |||
- | So in the example above, we could create an instance of '' | ||
- | as follows: | ||
- | |||
- | < | ||
- | type2< | ||
- | // x.N, x.M are both accessible! | ||
- | </ | ||
- | |||
- | The parameter '' | ||
- | been pre-specified in the type definition for '' | ||
- | we had specified '' | ||
- | the instance | ||
- | |||
- | < | ||
- | type2< | ||
- | // x.M=5, x.N=7 | ||
- | </ | ||
- | |||
- | would set '' | ||
- | '' | ||
- | the following would be an error: | ||
- | |||
- | < | ||
- | template< | ||
- | template< | ||
- | -[ERROR]-> | ||
- | | ||
- | </ | ||
- | since the name of the template parameter is repeated. | ||
- | |||
- | The body of the new type is the union of the original type definition | ||
- | and the new body. As an example to illustrate how this might be used, | ||
- | consider the following example: | ||
- | |||
- | < | ||
- | defproc buffer (e1of2? l, e1of2! r) | ||
- | { | ||
- | bool x; | ||
- | |||
- | chp { | ||
- | *[ l?x;r!x ] | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | '' | ||
- | can be implemented using a variety of circuits, and hence we could | ||
- | define a specific implementation as follows: | ||
- | |||
- | < | ||
- | defproc wchb <: buffer () | ||
- | { | ||
- | prs { | ||
- | ... | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | The process '' | ||
- | (without any extra ports) that contains a particular production-rule | ||
- | implementation of the same buffer. | ||
- | |||
- | ==== Overrides ==== | ||
- | |||
- | Consider the buffer example above. A better and more abstract | ||
- | specification for a buffer at the CHP level of abstraction would be: | ||
- | |||
- | < | ||
- | defproc buffer (chan? | ||
- | { | ||
- | bool x; | ||
- | |||
- | chp { | ||
- | *[ l?x;r!x ] | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | The production rules for this buffer use the fact that the Boolean | ||
- | channel for '' | ||
- | channel. Because an '' | ||
- | '' | ||
- | implementation of a process also replaces existing types with new | ||
- | implementations of the same type. This mechanism is called an | ||
- | // | ||
- | ones that implement them. | ||
- | |||
- | Variables in the port list as well as the body of a type can be | ||
- | overridden. For this to be sound, the new type must be an | ||
- | implementation of the original. The syntax for this is shown below: | ||
- | |||
- | < | ||
- | defproc wchb <: buffer | ||
- | +{ e1of2 l, r; } // override block | ||
- | { | ||
- | prs { | ||
- | ... | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | In this version, the original process implements a buffer with | ||
- | channels, and the '' | ||
- | '' | ||
- | handshake protocols on channels, or with different circuits. | ||
- | |||
- | The '' | ||
- | block uses the instantiation syntax to specify override types. The | ||
- | only syntax permited is of the form within an override block is | ||
- | |||
- | < | ||
- | +{ | ||
- | @var{type} @var{list-of-ids}; | ||
- | ... | ||
- | } | ||
- | </ | ||
- | |||
- | The identifiers cannot contain array specifiers, and the types canot | ||
- | have any direction flags. The identifier names must match names in the | ||
- | parent type (either in the port list or in the body of the type). If | ||
- | the original identifier was declared as an array, the entire array | ||
- | will be overridden. Direction flags from the parent type will be | ||
- | inherited by the overridden type. | ||
- | |||
- | Implemetation relations introduced should be handled with care. For | ||
- | example, suppose one has two definitions of a buffer: one using the | ||
- | CHP language, and the other using the PRS language (as above), and | ||
- | consider the variable '' | ||
- | the CHP language may not be part of the PRS for the WCHB | ||
- | reshuffling. In other words, the '' | ||
- | internal variable that corresponds to '' | ||
- | Similarly, the '' | ||
- | variables that are not present in the CHP language. The relation | ||
- | between such variables is unspecified by the implementation | ||
- | relationship. | ||
- | |||
- | **XXX: test this** | ||
- | |||
- | An additional complication arises if the process '' | ||
- | introducing a state variable '' | ||
- | correspond to the '' | ||
- | should override '' | ||
- | representation of the Boolean variable. | ||
- | |||
- | |||
- | |||
- | |||
- | ===== The ptype ===== | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||