Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
language:introduction [2019/04/18 10:54]
rajit [Arrays]
language:introduction [2022/05/13 08:31] (current)
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 ''a1of2'' for a one-of-two encoded data channel and an acknowledge) and attach a bit-bucket to it. 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 ''a1of2'' for a one-of-two encoded data channel and an acknowledge) and attach a bit-bucket to it.
  
-<code>+<code act>
 /* 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:
  
-<code>+<code act>
 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.
  
-<code>+<code act>
 pbool 5; pbool 5;
 +</code>
 +<code>
 -[ERROR]-> Expecting bnf-item `instance_id', got `5' -[ERROR]-> Expecting bnf-item `instance_id', got `5'
 </code> </code>
Line 96: Line 98:
 consider the following definition of ''bitbucket''. consider the following definition of ''bitbucket''.
  
-<code>+<code act>
 defproc bitbucket(a1of2 d) defproc bitbucket(a1of2 d)
 { {
Line 112: Line 114:
 result in the following message: result in the following message:
  
-<code>+<code act>
 bitbucket b; bitbucket b;
 a1of2 c; a1of2 c;
 b.p = c.d0; b.p = c.d0;
 +</code>
 +<code>
 -[ERROR]-> `p' is not a port for `bitbucket' -[ERROR]-> `p' is not a port for `bitbucket'
 </code> </code>
Line 137: Line 141:
  
 Most circuits contain a set of components that are replicated a number Most circuits contain a set of components that are replicated a number
-of times. This is especially common in datapath circuits. @sc{act} has a+of times. This is especially common in datapath circuits. ACT has a
 very flexible array mechanism that can be used to construct complex very flexible array mechanism that can be used to construct complex
 circuits. The simplest way to create an array is shown below. circuits. The simplest way to create an array is shown below.
  
-<code>+<code act>
 bool x[10]; bool x[10];
 a1of2 y[5][3]; a1of2 y[5][3];
Line 153: Line 157:
 be specified as shown below be specified as shown below
  
-<code>+<code act>
 bool w[4..7]; // create Booleans w[4], ..., w[7] bool w[4..7]; // create Booleans w[4], ..., w[7]
 </code> </code>
Line 162: Line 166:
 block. Consider the following instantiation: block. Consider the following instantiation:
  
-<code>+<code act>
 bool x[10]; bool x[10];
 bool x[12..14]; bool x[12..14];
Line 172: Line 176:
 following, on the other hand, is not valid. following, on the other hand, is not valid.
  
-<code>+<code act>
 bool x[10]; bool x[10];
 bool x[9..14]; bool x[9..14];
 -[ERROR]-> Sparse array: overlap in range in instantiation -[ERROR]-> Sparse array: overlap in range in instantiation
-           Oiginal: [10]; adding: [9..14]+           Original: [10]; adding: [9..14]
 </code> </code>
 Each index of an array can only be created once. Each index of an array can only be created once.
Line 189: Line 193:
 below. below.
  
-<code>+<code act>
 bool x[10]; bool x[10];
 bool x[12..14]; bool x[12..14];
Line 203: Line 207:
 succeed, and their shapes also have to be compatible. succeed, and their shapes also have to be compatible.
  
-<code>+<code act>
 bool x[12]; bool x[12];
 bool w[4][3]; bool w[4][3];
 x=w; x=w;
 +</code>
 +<code>
 -[ERROR]-> Type-checking failed in connection -[ERROR]-> Type-checking failed in connection
            Types `bool[12]' and `bool[4][3]' are not compatible            Types `bool[12]' and `bool[4][3]' are not compatible
Line 213: Line 219:
 The following are examples of valid connections: The following are examples of valid connections:
  
-<code>+<code act>
 bool x[10]; bool x[10];
 bool x[10..12]; bool x[10..12];
Line 225: Line 231:
  
 ===== Loops and conditionals ===== ===== 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 ''fulladder'' is a process that contains channels ''ci''
 +and ''co'' as its carry-in and carry-out. The following connects the
 +carry chain for a ten bit ripple-carry adder.
 +
 +<code act>
 +fulladder fa[10];
 +(i : 9 : fa[i].co=fa[i+1].ci; )
 +</code>
 +
 +The parentheses are used to group the body of the loop. ''i'' is
 +the dummy variable, and it ranges from zero to eight in this
 +example. The '';'' is a separator, and separates each instance of
 +the body of the loop.  In general if only one integer is specified for
 +the range, the variable ranges from zero to one less than the integer.
 +
 +The conditional statement uses the guarded command notation. They are
 +used for describing the edge of repetitive structures, during
 +recursive constructions, or for creating special versions of processes
 +based on parameters. The following is an example where odd-numbered
 +indices of ''x'' are connected to ''y'', and even-numbered
 +indices are connected to ''z''.
 +
 +<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];
 +   ]
 +)
 +</code>
 +
 +===== Scoping =====
 +
 +In the second definition of ''bitbucket'', the variable ''p'' was
 +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 ''bitbucket'' get
 +different copies of ''p'', since it is a local variable. If we had
 +created a dualrail channel ''p'' after the bitbucket, this ''p''
 +has no relation to the ''p'' in the body of ''bitbucket''.
 +
 +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 'global' keyword. Global nodes can
 +be created by simply defining them in the outer-most scope. For
 +instance, ACT files will tend to begin with
 +
 +<code act>
 +bool Reset,Reset_;
 +</code>
 +
 +This permits the names ''Reset'' and ''Reset_'' to be used
 +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
 +//namespace//. Every instantiation and type definition resides in a
 +specific namespace. In all our examples so far, this was the
 +(implicit) global namespace (named ''Global'').
 +
 +The following example creates a namespace ''lib'' and defines types
 +within the namespace.
 +
 +<code act>
 +namespace lib {
 +  export defchan a1of2 <: chan(bool) (bool d0,d1,a) { ... }
 +}
 +
 +lib::a1of2 d;
 +</code>
 +
 +We have created a channel definition of type ''a1of2'' within the
 +''lib'' namespace. There are a few items that should be noted.
 +First, the directive ''export'' indicates that the ''a1of2''
 +type is in fact visible outside the namespace scope. This is why we
 +can use the notation ''lib::a1of2'' to access this type. The
 +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
 +''export''ed. Second, a user-defined namespace cannot contain any
 +global instances of processes. This means that the only legal items
 +within a namespace are namespace directives, type definitions, and
 +global data/channel types.
 +
 +Namespaces can be nested. For instance, we could have:
 +
 +<code act>
 +namespace processor {
 +
 +  namespace lib {
 +       export defchan a1of2 <: chan(bool) (bool d0,d1,a) { ... } 
 +  }
 +
 +  lib::a1of2 d;
 +}
 +</code>
 +
 +In this example, ''d'' is instantiated from namespace ''lib''
 +within namespace ''processor''. This brings up another subtlety of
 +the ''export'' directive. An exported definition is only exported
 +one level up in the namespace hierarchy. Hence, although the channel
 +''processor::lib::a1of2'' exists, it cannot be accessed outside the
 +''processor'' namespace.
 +
 +<code act>
 +namespace processor {
 +
 +  namespace lib {
 +       export defchan a1of2 <: chan(bool) (bool d0,d1,a) { ... }
 +  }
 +
 +}
 +
 +processor::lib::a1of2 d;
 +</code>
 +<code>
 +-[ERROR]-> Type is not exported up the namespace hierarchy:
 +           processor::lib::a1of2
 +</code>
 +
 +If this channel needs to be visible
 +outside the ''processor'' namespace, this can be accomplished by
 +exporting the namespace itself.
 +
 +<code act>
 +namespace processor {
 +
 +  export namespace lib {
 +       export defchan a1of2 <: chan(bool) (bool d0,d1,a) { ... }
 +  }
 +
 +}
 +processor::lib::a1of2 d;
 +</code>
 +
 +In this approach, every element that is exported from namespace
 +''lib'' is also exported out of namespace ''processor''.
 +
 +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 ''a1of2'' was defined within the
 +''processor'' namespace, then it will not be visible within the
 +''processor::lib'' namespace unless it is exported. An
 +''export'' directive is needed to permit a type to be used in other
 +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
 +''Global''. For instance, a collection of process definitions within
 +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 ''import'' is used to include other design files. An
 +ACT file can begin with a sequence of ''import'' statements.
 +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
 +''import'' to include type definitions defined elsewhere.
 +
 +<code act>
 +import "channel.act";
 +...
 +</code>
 +
 +''import'' searches for the file in the current directory
 +first, then in the colon-separated path specified by ''$ACT_PATH'',
 +and finally in ''$ACT_HOME/act''.
 +
 +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: ''lib1.act'' and
 +''lib2.act'' both containing namespace ''lib'', but having
 +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 "lib1.act";
 +open lib -> lib1;
 +import "lib2.act";
 +open lib -> lib2;
 +</code>
 +
 +The ''open'' construct enables one to rename a namespace. Once this
 +has occured, there cannot be any naming conflicts. This version of
 +''open'' is a renaming construct. The old name for the namespace is
 +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,
 +''processor::lib''---not just because of the long type names, but
 +because not all types might be exported! In this case we can say:
 +
 +<code act>
 +import "lib.act";
 +open processor::lib;
 +
 +a1of2 d;
 +</code>
 +
 +This version of the ''open'' directive has two
 +functions: (i) it adds ''processor::lib'' to the search path for
 +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 ''open'' statement will fail if
 +all types cannot be uniquely resolved.
 +
 +The sequence of ''open'' and ''import'' statements can only be
 +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::lib;
 +</code>
 +
 +is equivalent to the following:
 +
 +<code act>
 +import "processor/lib/_all_.act";
 +</code>
 +
 +It assumes that the file ''_all_.act'' in the directory
 +''processor/lib'' contains all the definitions corresponding to the
 +''processor::lib'' namespace.
 +