Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| language:introduction [2019/04/18 14:26] – [Variables and expressions] rajit | language:introduction [2025/09/04 11:09] (current) – [Loops and conditionals] rajit | ||
|---|---|---|---|
| Line 16: | Line 16: | ||
| To get a feel for how a circuit is described in ACT, we begin with a simple example circuit. The purpose of this circuit is to create a dual rail channel (called '' | To get a feel for how a circuit is described in ACT, we begin with a simple example circuit. The purpose of this circuit is to create a dual rail channel (called '' | ||
| - | < | + | < |
| /* my first act program */ | /* my first act program */ | ||
| defchan a1of2 <: chan(bool) (bool d0,d1,a) | defchan a1of2 <: chan(bool) (bool d0,d1,a) | ||
| Line 72: | Line 72: | ||
| legal: | legal: | ||
| - | < | + | < |
| bitbucket b; | bitbucket b; | ||
| a1of2 x1; | a1of2 x1; | ||
| Line 81: | Line 81: | ||
| On the other hand, the following declaration is incorrect. | On the other hand, the following declaration is incorrect. | ||
| - | < | + | < |
| pbool 5; | pbool 5; | ||
| - | ---> Expecting bnf-item `instance_id', | ||
| </ | </ | ||
| + | < | ||
| + | -[ERROR]-> | ||
| + | </ | ||
| + | |||
| + | |||
| + | Errors use names from the ACT BNF to describe the item that the | ||
| + | parser was expecting. Error messages are accompanied by the file name, | ||
| + | line number, and column number of the item that resulted in the error. | ||
| + | |||
| + | The variable names '' | ||
| + | ACT language features like functions and user-defined types. | ||
| + | |||
| + | The names in the port list of a user-defined type are the only parts of | ||
| + | the type that are visible externally. Other parts of the defined type | ||
| + | cannot be accessed outside the body of the type itself. For example, | ||
| + | consider the following definition of '' | ||
| + | |||
| + | <code act> | ||
| + | defproc bitbucket(a1of2 d) | ||
| + | { | ||
| + | bool p; | ||
| + | prs { | ||
| + | d.d0 | d.d1 -> d.a+ | ||
| + | ~d.d0 & ~d.d1 -> d.a- | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | If we had used this definition, then although '' | ||
| + | within the bit-bucket '' | ||
| + | outside the body. Therefore, a statement such as '' | ||
| + | result in the following message: | ||
| + | |||
| + | <code act> | ||
| + | bitbucket b; | ||
| + | a1of2 c; | ||
| + | b.p = c.d0; | ||
| + | </ | ||
| + | < | ||
| + | -[ERROR]-> | ||
| + | </ | ||
| + | |||
| + | Expressions look very much like C expressions. Expressions can be of two | ||
| + | types: numeric or logical. Numeric expressions can be constructed from | ||
| + | identifiers, | ||
| + | arithmetic operators '' | ||
| + | addition, subtraction, | ||
| + | unary minus operator is also supported. The operator '' | ||
| + | for computing the remainder. Logical expressions can be constructed from | ||
| + | logical variables, the logical constants '' | ||
| + | and the logical operators ''&'', | ||
| + | the and, or, and negation operations respectively. | ||
| + | can be compared using ''<'', | ||
| + | '' | ||
| + | to, greater than, greater than or equal to, equal to, and not equal to | ||
| + | respectively. | ||
| + | |||
| + | ===== Arrays ===== | ||
| + | |||
| + | Most circuits contain a set of components that are replicated a number | ||
| + | of times. This is especially common in datapath circuits. ACT has a | ||
| + | very flexible array mechanism that can be used to construct complex | ||
| + | circuits. The simplest way to create an array is shown below. | ||
| + | |||
| + | <code act> | ||
| + | bool x[10]; | ||
| + | a1of2 y[5][3]; | ||
| + | </ | ||
| + | |||
| + | The first statement creates an array of ten '' | ||
| + | elements are '' | ||
| + | second statement creates a two-dimensional array of '' | ||
| + | variables named '' | ||
| + | '' | ||
| + | be specified as shown below | ||
| + | |||
| + | <code act> | ||
| + | bool w[4..7]; // create Booleans w[4], ..., w[7] | ||
| + | </ | ||
| + | |||
| + | ACT also contains a mechanism for constructing //sparse arrays//. A | ||
| + | sparse array is one that has ' | ||
| + | indices of the array do not form a contiguous, rectangular | ||
| + | block. Consider the following instantiation: | ||
| + | |||
| + | <code act> | ||
| + | bool x[10]; | ||
| + | bool x[12..14]; | ||
| + | </ | ||
| + | |||
| + | The first statement creates '' | ||
| + | second creates '' | ||
| + | sequence of statements, and it makes '' | ||
| + | following, on the other hand, is not valid. | ||
| + | |||
| + | <code act> | ||
| + | bool x[10]; | ||
| + | bool x[9..14]; | ||
| + | -[ERROR]-> | ||
| + | | ||
| + | </ | ||
| + | Each index of an array can only be created once. | ||
| + | |||
| + | Arrays can be connected to others using the '' | ||
| + | arrays have the same size, the same type, and the same number of | ||
| + | dimensions, the connection is valid. Conceptually, | ||
| + | performed by converting each array into an ordered list of individual | ||
| + | elements, where the order is specified by the lexicographic ordering | ||
| + | on their indices (the leftmost index has precedence). Finally, an | ||
| + | element-by-element connection is performed. This is illustrated | ||
| + | below. | ||
| + | |||
| + | <code act> | ||
| + | bool x[10]; | ||
| + | bool x[12..14]; | ||
| + | bool y[2]; | ||
| + | x=y; | ||
| + | -[ERROR]-> | ||
| + | </ | ||
| + | |||
| + | Note the syntax used to report the sparse array type as a combination | ||
| + | of two sub-arrays. | ||
| + | |||
| + | The dimensionality of the two arrays must match for a connection to | ||
| + | succeed, and their shapes also have to be compatible. | ||
| + | |||
| + | <code act> | ||
| + | bool x[12]; | ||
| + | bool w[4][3]; | ||
| + | x=w; | ||
| + | </ | ||
| + | < | ||
| + | -[ERROR]-> | ||
| + | Types `bool[12]' | ||
| + | </ | ||
| + | |||
| + | The following are examples of valid connections: | ||
| + | |||
| + | <code act> | ||
| + | bool x[10]; | ||
| + | bool x[10..12]; | ||
| + | bool y[13]; | ||
| + | x=y; // success! | ||
| + | |||
| + | bool u[4][3]; | ||
| + | bool v[4][3]; | ||
| + | u=v; // success! | ||
| + | </ | ||
| + | |||
| + | ===== Loops and conditionals ===== | ||
| + | |||
| + | Loops and conditionals can be used to describe complex circuit | ||
| + | structures in a compact manner. Loops are useful when creating array | ||
| + | structures, or connecting arrays in a regular manner. For example, | ||
| + | suppose '' | ||
| + | and '' | ||
| + | carry chain for a ten bit ripple-carry adder. | ||
| + | |||
| + | <code act> | ||
| + | fulladder fa[10]; | ||
| + | (i : 9 : fa[i].co=fa[i+1].ci; | ||
| + | </ | ||
| + | |||
| + | The parentheses are used to group the body of the loop. '' | ||
| + | the dummy variable, and it ranges from zero to eight in this | ||
| + | example. In general if only one integer is specified for | ||
| + | the range, the variable ranges from zero to one less than the integer. | ||
| + | This is a special case of a more general [[syntacticreplication|syntactic replication]] construct | ||
| + | that can be used in many contexts. | ||
| + | |||
| + | The conditional statement uses the guarded command notation. They are | ||
| + | used for describing the edge of repetitive structures, during | ||
| + | recursive constructions, | ||
| + | based on parameters. The following is an example where odd-numbered | ||
| + | indices of '' | ||
| + | indices are connected to '' | ||
| + | |||
| + | <code act> | ||
| + | bool x[10], y[10], z[10]; | ||
| + | |||
| + | ( i : 10 : | ||
| + | [ (i%2) = 0 -> x[i] = y[i]; | ||
| + | [] (i%2) = 1 -> x[i] = z[i]; | ||
| + | ] | ||
| + | ) | ||
| + | </ | ||
| + | |||
| + | ===== Scoping ===== | ||
| + | |||
| + | In the second definition of '' | ||
| + | defined within the body of the type definition. Therefore, this variable | ||
| + | is local to the type, and cannot be accessed by any construct outside | ||
| + | the body of the type. Different instances of '' | ||
| + | different copies of '' | ||
| + | created a dualrail channel '' | ||
| + | has no relation to the '' | ||
| + | |||
| + | The ACT language has two scopes: the global scope, and the scope | ||
| + | within the entity being defined. Ports of types have the same scope as | ||
| + | items defined within the body of the type. However, ports are special | ||
| + | in that they can also be accessed from outside the type using | ||
| + | dot-notation. | ||
| + | |||
| + | |||
| + | ACT does not have a special ' | ||
| + | be created by simply defining them in the outer-most scope. For | ||
| + | instance, ACT files will tend to begin with | ||
| + | |||
| + | <code act> | ||
| + | bool Reset, | ||
| + | </ | ||
| + | |||
| + | This permits the names '' | ||
| + | throughout the ACT file. | ||
| + | |||
| + | ===== Namespaces ===== | ||
| + | Complex projects involve a large number of ACT files, including | ||
| + | shared libraries and blocks designed by different people. One could | ||
| + | easily envision a situation where a particular identifier name is used by | ||
| + | multiple designers to describe different processes. | ||
| + | |||
| + | To keep names of processes, channels, and types separate for different | ||
| + | parts of a design, ACT provides the notion of a | ||
| + | // | ||
| + | specific namespace. In all our examples so far, this was the | ||
| + | (implicit) global namespace (named '' | ||
| + | |||
| + | The following example creates a namespace '' | ||
| + | within the namespace. | ||
| + | |||
| + | <code act> | ||
| + | namespace lib { | ||
| + | export defchan a1of2 <: chan(bool) (bool d0,d1,a) { ... } | ||
| + | } | ||
| + | |||
| + | lib::a1of2 d; | ||
| + | </ | ||
| + | |||
| + | We have created a channel definition of type '' | ||
| + | '' | ||
| + | First, the directive '' | ||
| + | type is in fact visible outside the namespace scope. This is why we | ||
| + | can use the notation '' | ||
| + | rationale for this is that one might have created a library, but might | ||
| + | only want a few types exposed (e.g. top-level cells). By default, a | ||
| + | type is not visible outside a namespace unless it is explicitly | ||
| + | '' | ||
| + | global instances of processes. This means that the only legal items | ||
| + | within a namespace are namespace directives, type definitions, | ||
| + | global data/ | ||
| + | |||
| + | Namespaces can be nested. For instance, we could have: | ||
| + | |||
| + | <code act> | ||
| + | namespace processor { | ||
| + | |||
| + | namespace lib { | ||
| + | | ||
| + | } | ||
| + | |||
| + | lib::a1of2 d; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | In this example, '' | ||
| + | within namespace '' | ||
| + | the '' | ||
| + | one level up in the namespace hierarchy. Hence, although the channel | ||
| + | '' | ||
| + | '' | ||
| + | |||
| + | <code act> | ||
| + | namespace processor { | ||
| + | |||
| + | namespace lib { | ||
| + | | ||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | processor:: | ||
| + | </ | ||
| + | < | ||
| + | -[ERROR]-> | ||
| + | | ||
| + | </ | ||
| + | |||
| + | If this channel needs to be visible | ||
| + | outside the '' | ||
| + | exporting the namespace itself. | ||
| + | |||
| + | <code act> | ||
| + | namespace processor { | ||
| + | |||
| + | export namespace lib { | ||
| + | | ||
| + | } | ||
| + | |||
| + | } | ||
| + | processor:: | ||
| + | </ | ||
| + | |||
| + | In this approach, every element that is exported from namespace | ||
| + | '' | ||
| + | |||
| + | Something that is a little unusual is that a type cannot be accessed | ||
| + | within a sub-namespace unless it is exported. For instance, in the | ||
| + | example above, if '' | ||
| + | '' | ||
| + | '' | ||
| + | '' | ||
| + | namespaces apart from the one in which the type was defined. | ||
| + | |||
| + | A namespace can have global variables. The global variables described | ||
| + | earlier were simply a special case corresponding to the namespace | ||
| + | '' | ||
| + | a namespace might have a reset signal that is global to the namespace | ||
| + | only, and which is generated using some logic defined within the | ||
| + | namespace. | ||
| + | |||
| + | ===== Importing ACT files ===== | ||
| + | |||
| + | The keyword '' | ||
| + | ACT file can begin with a sequence of '' | ||
| + | If the same file is imported twice within the same scope, chances are | ||
| + | that some types would be multiply defined. To avoid such problems, | ||
| + | imports of files which have already been imported within the same | ||
| + | scope or an outer scope are ignored. Therefore, always use | ||
| + | '' | ||
| + | |||
| + | <code act> | ||
| + | import " | ||
| + | ... | ||
| + | </ | ||
| + | |||
| + | '' | ||
| + | first, then in the colon-separated path specified by '' | ||
| + | and finally in '' | ||
| + | |||
| + | A typical project would contain multiple files, each possibly having | ||
| + | their own namespace. Namespaces can also have global variables, so | ||
| + | importing a namespace automatically creates an instance of the global | ||
| + | variables from that namespace, and from any sub-namespace that was also | ||
| + | imported. | ||
| + | |||
| + | There are a few things that might create issues in such a situation. | ||
| + | First, duplicate namespaces might exist, especially when re-using old | ||
| + | files. For instance, suppose we have two files: '' | ||
| + | '' | ||
| + | definitions that are useful. Importing both would result in the union of | ||
| + | the namespaces, and could create naming conflicts (e.g. multiple | ||
| + | definition of types having the same name---an error). To solve this | ||
| + | problem, one can do the following: | ||
| + | |||
| + | <code act> | ||
| + | import " | ||
| + | open lib -> lib1; | ||
| + | import " | ||
| + | open lib -> lib2; | ||
| + | </ | ||
| + | |||
| + | The '' | ||
| + | has occured, there cannot be any naming conflicts. This version of | ||
| + | '' | ||
| + | eliminated. | ||
| + | |||
| + | A second issue is one that is more about convenience. Consider a | ||
| + | project that has many different people working on it, each in their | ||
| + | own namespace to avoid naming conflicts. This situation can result in | ||
| + | very long type names. Plus it would be more bookkeeping to have to | ||
| + | create a test environment for the types within, say, | ||
| + | '' | ||
| + | because not all types might be exported! In this case we can say: | ||
| + | |||
| + | <code act> | ||
| + | import " | ||
| + | open processor:: | ||
| + | |||
| + | a1of2 d; | ||
| + | </ | ||
| + | |||
| + | This version of the '' | ||
| + | functions: (i) it adds '' | ||
| + | types, and (ii) it allows one to access all types within the | ||
| + | namespace, not just the ones that are exported (including types within | ||
| + | nested namespaces). Note that this '' | ||
| + | all types cannot be uniquely resolved. | ||
| + | |||
| + | The sequence of '' | ||
| + | at the beginning of an ACT file. | ||
| + | |||
| + | A second version of import uses namespaces directly, but requires that | ||
| + | ACT files be placed in locations that match the namespace | ||
| + | hierarchy. The import statement | ||
| + | |||
| + | <code act> | ||
| + | import processor:: | ||
| + | </ | ||
| + | |||
| + | is equivalent to the following: | ||
| + | |||
| + | <code act> | ||
| + | import " | ||
| + | </ | ||
| + | |||
| + | It assumes that the file '' | ||
| + | '' | ||
| + | '' | ||
| + | |||