Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
languages:types2 [2021/11/21 16:52] – rajit | languages:types2 [2021/11/21 16:57] (current) – removed rajit | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== 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/ | ||
- | |||
- | Often 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]-> | ||
- | </ | ||
- | |||
- | 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. | ||
- | |||
- | ==== Channel types ==== | ||
- | |||
- | 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. | ||
- | |||
- | < | ||
- | defchan e1of2 <: chan(bool) (bool d0,d1,e) | ||
- | { | ||
- | spec { | ||
- | exclhi(d0, | ||
- | } | ||
- | } | ||
- | </ | ||
- | |||
- | 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. | ||
- | |||
- | |||
- | ==== Structures ==== | ||
- | |||
- | The '' | ||
- | These types can be used to group data fields or channel fields together. | ||
- | A structure is defined using '' | ||
- | a built-in type as in the examples above. So, for instance: | ||
- | < | ||
- | deftype mystruct (int< | ||
- | </ | ||
- | would declare a structure with two fields: '' | ||
- | |||
- | If all the fields within a structure are data values, this is a special | ||
- | case of a structure consisting purely of data. | ||
- | |||
- | ===== Methods ===== | ||
- | |||
- | Process, channel, and data types can include //methods// that provide mechanisms to | ||
- | manipulate the type or access parts of the type. | ||
- | |||
- | ==== Data type methods ==== | ||
- | |||
- | 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. When a normal | ||
- | data type is used, the special variable '' | ||
- | defined to be the built-in type that is implemented by the | ||
- | user-defined 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.) | ||
- | |||
- | |||
- | ==== Channels ==== | ||
- | |||
- | |||
- | There are eight possible methods that can be defined for a channel type: | ||
- | |||
- | * Methods for sending and receiving values on the channel | ||
- | * '' | ||
- | * '' | ||
- | * Methods for initializing | ||
- | * '' | ||
- | * '' | ||
- | * Methods for probing a channel to determine if there is synchronization operation being attempted. | ||
- | |||
- | For channels, there are two special methods that are used for probe operations with different syntax. Both of these have to be specified via an expression, rather than the normal method syntax. | ||
- | * '' | ||
- | * '' | ||
- | |||
- | The send operation '' | ||
- | parts: setting the data value, followed by the synchronization | ||
- | operation, and possibly the reset phase of the handshake. | ||
- | 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 '' | ||
- | three parts: receiving the data value, followed by the synchronization | ||
- | operation, and finally the rest of the handshake. | ||
- | 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. Note that for a channel, | ||
- | '' | ||
- | on the channel. | ||
- | |||
- | < | ||
- | defchan e1of2 <: chan(bool) (bool d0,d1,e) | ||
- | { | ||
- | spec { | ||
- | exclhi(d0, | ||
- | } | ||
- | | ||
- | set { | ||
- | [e]; | ||
- | } | ||
- | send_up { | ||
- | [~e] | ||
- | } | ||
- | send_rest { | ||
- | d0-,d1- | ||
- | } | ||
- | get { | ||
- | | ||
- | } | ||
- | recv_up { | ||
- | e- | ||
- | } | ||
- | 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. | ||
- | |||
- | |||
- | ==== 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 | ||
- | 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. | ||
- | |||
- | ==== Built-in integers and channels ==== | ||
- | |||
- | Although we have been describing the types '' | ||
- | as simple types, they are in fact paramterized. Omitting the | ||
- | parameters makes ACT use implicit default parameters for both of | ||
- | them. | ||
- | |||
- | The '' | ||
- | specify the integer. This bit-width can be specified using angle | ||
- | brackets, as shown below: | ||
- | |||
- | < | ||
- | int< | ||
- | int< | ||
- | </ | ||
- | |||
- | When interpreting these bits as integers, ACT assumes an unsigned | ||
- | binary representation. The default bit-width is thirty-two. | ||
- | |||
- | The channel type '' | ||
- | being sent and received on the channel. | ||
- | |||
- | < | ||
- | chan(bool) x; // x is a Boolean channel | ||
- | chan(int< | ||
- | </ | ||
- | |||
- | The default data type for a channel is assumed to be the default | ||
- | '' | ||
- | |||
- | Another built-in data type is the // | ||
- | enumeration type corresponds to integer-valued variables with a | ||
- | restricted range. | ||
- | |||
- | < | ||
- | enum< | ||
- | </ | ||
- | |||
- | For convenience, | ||
- | of expressions. Also, enumerations that have power-of-two ranges are | ||
- | type-equivalent to the approprate '' | ||
- | '' | ||
- | 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 ===== | ||
- | |||
- | 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 '' | ||
- | 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 '' | ||
- | 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 '' | ||
- | output on '' | ||
- | |||
- | < | ||
- | 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. | ||
- | |||
- | ==== 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. |