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:expressions [2021/11/21 11:17]
rajit
language:expressions [2024/03/20 07:19] (current)
rajit [External circuit functions]
Line 9: Line 9:
    * The query expression ''(e ? e1 : e2)'' behaves like the C operator. The result is ''e1'' if ''e'' is true, and ''e2'' otherwise.    * The query expression ''(e ? e1 : e2)'' behaves like the C operator. The result is ''e1'' if ''e'' is true, and ''e2'' otherwise.
    * Logical shift operators are ''<<'' (left shift),  ''>>'' (logical right shift), and the arithmetic right shift operator ''>>>''.    * Logical shift operators are ''<<'' (left shift),  ''>>'' (logical right shift), and the arithmetic right shift operator ''>>>''.
-   * Bit operations: For a variable ''x'', the value ''x{b..a}'' extracts bits ''b'' through ''a'' (both included, with ''b'' at least ''a''). Both ''b'' and ''a'' must be constants (or computed by parameters, i.e. constants after expansion). The syntax ''x{a}'' is syntactic sugar for ''x{a..a}'', and extracts one bit from ''x''.+   * Bit operations: For a variable ''x'', the value ''x{b..a}'' extracts bits ''b'' through ''a'' (both included, with ''b'' at least ''a''). Both ''b'' and ''a'' must be constants (or computed by parameters, i.e. constants after expansion). The syntax ''x{a}'' is syntactic sugar for ''x{a..a}'', and extracts one bit from ''x''. Note that ''b'' must be at least ''a''; for example ''x{3..2}'' corresponds to two bits, but ''x{2..3}'' is not a valid reference.
    * Concatenation: Given a set of expressions, ''{e1,e2,e3,...,eN}'' concatenates the bits of the expressions to form a wide result.    * Concatenation: Given a set of expressions, ''{e1,e2,e3,...,eN}'' concatenates the bits of the expressions to form a wide result.
  
Line 18: Line 18:
    * ''int(x,w)'': ''x'' must be an integer expression, and ''w'' must be evaluate to an integer constant after expansion (i.e. it can only be an integer expression that includes parameters). This changes the bit-width of the integer to be ''w''. If the width is reduced, the high order bits are  truncated; if the bit-width is increased, the integer is zero extended.    * ''int(x,w)'': ''x'' must be an integer expression, and ''w'' must be evaluate to an integer constant after expansion (i.e. it can only be an integer expression that includes parameters). This changes the bit-width of the integer to be ''w''. If the width is reduced, the high order bits are  truncated; if the bit-width is increased, the integer is zero extended.
    * ''bool(x)'': ''x'' must be an integer expression. This returns false if the integer is zero, and true otherwise.    * ''bool(x)'': ''x'' must be an integer expression. This returns false if the integer is zero, and true otherwise.
 +
 +Syntactic replication is also supported for the operators ''&'', ''|'', ''^'', ''+'', and ''*''. This means the following expression is valid
 +<code act>
 +  (+ i : 3 : p[i] + 2*i)
 +</code>
 +and is equivalent to
 +<code act>
 +  p[0] + 2*0 + p[1] + 2*1 + p[2] + 2*2
 +</code>
  
 ===== Parameters and constant expressions ===== ===== Parameters and constant expressions =====
  
-Parameter expressions are used to compute parameter values (''pint''/''pbool'') and are evaluated at expansion time. These expressions are signed integers, and use 64-bit integers +Parameter expressions are used to compute parameter values (''pint''/''pbool''/"preal") and are evaluated at expansion time. These expressions are signed integers, and use 64-bit integer arithmeticAll constants are simplified at expansion time using the same 64-bit integer arithmetic as parameters.
- +
-Constants are simplified at expansion time using the same 64-bit integer arithmetic as parameters.+
  
 +In the context of expressions in the ACT language where the entire expression evaluates to a run-time constant, ''int(x)'' can also be specified when ''x'' is a real expression. In this case, the fractional part of the number is discarded. This means, for example, that
 +<code act>
 +pint y = 2;
 +pint x = int(5.4/y)
 +</code>
 +will succeed, while 
 +<code act>
 +int x, y;
 +chp {
 +   y := 2;
 +   x := int(5.4/y)
 + }
 +</code>
 +will report an error.
 ===== Expressions in CHP ===== ===== Expressions in CHP =====
  
Line 32: Line 53:
    * A constant uses the minimum number of bits needed to represent it. Note that a negative constant is assumed to be a two's complement value, an its bit-width is determined in the same way.    * A constant uses the minimum number of bits needed to represent it. Note that a negative constant is assumed to be a two's complement value, an its bit-width is determined in the same way.
    * For unary operators, the bit-width of the result is the same as the bit-width of the argument    * For unary operators, the bit-width of the result is the same as the bit-width of the argument
-   * For binary operators, let //left// be the bit-width of the left-hand side of the operator, and //right// be the bit-width of the right hand side. There are eight categories of result bit-widths: +   * For binary operators and ternary operators where the result is an integer, let //left// be the bit-width of the left-hand side of the operator, and //right// be the bit-width of the right hand side. There are six categories of result bit-widths: 
-     - max(left,right) : for logical operators (bit-wise and, or, etc.). The smaller operand is zero-extended. For query expressions//left// and //right// are the bit-widths of the two options. +     - max(//left//,//right//; the smaller operand is zero-extended as needed. 
-     - 1+max(left,right) : for addition and subtraction +        * bitwise AND ''&'' 
-     - left+right : for multiplication +        * bitwise OR ''|'' 
-     - left : for division, and both arithmetic and logical right shifts. +        * bitwise XOR ''^'' 
-     - right : for mod +        * Query expressions where the result is an integer. In this case //left// and //right// are the bit-widths of the two options in the query expression, since the first part of the query expression is Boolean
-     - left + 2^right - 1 : for left shift +     - 1+max(//left//,//right//) 
-     - For concatenation, the bit-width is the sum of all the components +        * addition ''+'' 
-     one (1) : for comparison operations where the result is Boolean, the resulting bit-width is 1.+        * subtraction ''-'' 
 +     //left//+//right// 
 +        * multiplication ''*'' 
 +     //left// 
 +        * division ''/'' 
 +        * logical right shift ''>>'' 
 +        * arithmetic right shift ''>>>'' 
 +     //right// 
 +        * mod ''%'' 
 +     //left// + 2^//right// - 1 
 +        * left shift ''<<'' 
 +  For concatenation, the bit-width is the sum of all the components. For the bitfield extraction, the bitwidth is determined by the number of bits extracted. 
 + 
 +While these bit-width rules are nice because you never lose bits, they can have some unexpected consequences. One of the not-so-nice effects of these rules is that, technically, addition is no longer associative in general! For example, consider the following two different assignment statements: 
 + 
 +<code act> 
 +int<2> a; 
 +int<3> b; 
 +int<4> c; 
 +... 
 +chp { 
 +   ... 
 +   x := (a + b+ c; 
 +   := a + (b + c); 
 +   ... 
 +
 +</code> 
 + 
 +Applying the bit-width rules, the expression ''(a+b) + c'' has bit-width 5, whereas ''a + (b + c)'' has bit-width 6. While this does not have any consequences in this particular example, it could become problematic if the bitwise complement operator is used, or in the case where this expression needs to be negated (since subtraction is essentially taking the two's complement and then adding, which includes the bitwise complement operator). 
 + 
 +Another strange example is: 
 + 
 +<code act> 
 +... 
 +chp { 
 +   ... 
 +   x := x - 1
 +   y := y + (-1); 
 +   ... 
 +
 +</code> 
 + 
 +Now the right hand side of the first assignment takes ''x'' and ''1'' and does the subtraction that was expected. The second assignment, on the other hand, takes the bit-pattern for ''-1'' (this turns out to be ''1''), and adds it to ''y''! In other words, ''x-1'' and ''x+(-1)'' are not the same because of the way the bit-width rules operate. If you have any doubts, the ''int(...)'' operator can be used to specify the bit-width. 
  
 ===== Functions ===== ===== Functions =====
  
 ACT provides support for user-defined functions. These functions can be used to make your design more understandable. The syntax of a function is illustrated by the following example: ACT provides support for user-defined functions. These functions can be used to make your design more understandable. The syntax of a function is illustrated by the following example:
-<code>+<code act>
 function f (pint x) : pint function f (pint x) : pint
 { {
Line 60: Line 124:
 Additional parameters that might be helpful as auxilliary variables can be defined within the body of the function in the usual way. Additional parameters that might be helpful as auxilliary variables can be defined within the body of the function in the usual way.
  
-<code>+<code act>
 function sumint (pint x) : pint function sumint (pint x) : pint
 { {
Line 81: Line 145:
  
 The first kind of function is typically used when constructing the circuit, and its value can be statically computed during the circuit construction phase. The second kind of function is viewed as CHP, and can be implemented either by inlining the function or through some other approach (e.g. shared, partially shared, etc). The first kind of function is typically used when constructing the circuit, and its value can be statically computed during the circuit construction phase. The second kind of function is viewed as CHP, and can be implemented either by inlining the function or through some other approach (e.g. shared, partially shared, etc).
 +
 +For example, the following function that takes an ''int<8>'' argument  can be called from the CHP sub-language:
 +<code act>
 +/* look at the MSB to determine if this is a negative 2's complement integer */
 +function isnegative (int<8> x) : bool
 +{
 +  chp {
 +    self := bool(x{7})
 +  }
 +}
 +</code>
 +
 +===== External Functions =====
 +
 +ACT is a hardware description language. However, especially when simulating a design, it is useful to be able to have additional functionality that goes beyond the implemented circuit. For example, a designer may want to test a CHP program by providing it inputs from a test suite that is contained in a file. To do so would require providing an I/O library for ACT. Similarly there may be other additional features one might want for simulation purposes. 
 +
 +Instead of providing specific solutions to a large number of potential use cases, ACT provides a single generic mechanism: the ability to call an //external// function. An external function is one that is not defined within the ACT language itself, but is rather defined in the C programming language. The C function must be compiled into a shared object library that is loaded by ACT at run-time.
 +
 +To define an external function, simply declare the function in ACT without providing a definition.
 +<code act>
 +// an external function with parameter arguments
 +function myextfunc (pint a) : pint;
 +</code>
 +In this example, function ''myextfunc'' is an external function that only involves parameters. Like all parameter-based expressions, the function will be evaluated during the expansion phase and replaced with a constant value.
 +
 +<code act>
 +// an external function with circuit variable arguments
 +function myextfunc2 (int<4> a) : int<5>;
 +</code>
 +In this example, function ''myextfunc2'' is an external function that involves circuit arguments. While this can be used for simulation purposes, any synthesis/circuit generation operations will fail when confronted with such a function. Any CHP program that calls an external circuit function will be marked as //non-synthesizable// by the ACT library. Note that these functions still serve a useful purpose---in particular, they can be useful when modeling the environment of the circuit that is to be implemented, or could provide a reference implementation during hardware development.
 +
 +==== External parameter functions ====
 +
 +To implement the external parameter function, you must provide two things: a C implementation with a specific function prototype, and a ACT configuration file that maps the ACT function name to the C function name.
 +
 +Since ''pint'' types are 64-bit signed integers, the function declaration for an external function would look something like this:
 +
 +<code c>
 +long myexternimpl (int nargs, long *args)
 +{
 +   int i;
 +  /* nargs = # of arguments */
 +   printf ("got %d args\n", nargs);
 +   for (i=0; i < nargs; i++) {
 +      printf ("  arg[%d] = %ld\n", i, args[i]);
 +   }
 +   return 10;
 + }
 +</code>
 +In this example, the function simply prints out the number of arguments and the values passed in, returning a constant value.
 +
 +The second part is specifying the mapping from the ACT function name to the C function name. Since external functions can be organized in multiple libraries, the way this is specified is the following:
 +<code>
 +begin act
 +  begin extern
 +    string_table libs "mylib"
 +    
 +    begin mylib
 +      string path "libpath.so"
 +      string myextfunc "myexternimpl"
 +    end
 +  end
 +end
 +</code>
 +The specification of the mapping is contained within the ''act'' and ''extern'' blocks. The list of libraries is specified as a string table called ''libs''. These library names are then used as the names of library specifier ''begin''..''end'' blocks, one per library.
 +
 +The library specifier block contains the path to the shared object library that contains the external function definitions via the ''path'' string. Finally, a list of mapping strings that map ACT function names to the corresponding C function name must also be included in the library block.
 +
 +Finally, the C file must be compiled as a shared object file, and assembled into a library. For example, on Linux, one might use the following commands to create ''libpath.so'' from a C definition in ''example.c''.
 +<code sh>
 +$  gcc -shared -DPIC -fPIC -c example.c -o example.os
 +$  gcc -shared -Wl,-x -o libpath.so example.os
 +</code>
 +
 +
 +==== External circuit functions ====
 +
 +External circuit functions are implemented in a similar fashion. Currently, only the ACT simulator (''actsim'') uses this functionality, since the rest of the implementation flow simply views such external functions as non-synthesizable components.
 +
 +The C implementation of external functions uses a function prototype that includes bit-width specifiers for all the data types. (Currently there is a limit of 64 bits on the bit-width of arguments/results from external functions.)
 +
 +<code c>
 +/* this includes the simple external interface datatype used */
 +#include <act/actsim_ext.h>
 +
 +struct expr_res myexternimpl2 (int nargs, struct expr_res *args)
 +{
 +   struct expr_res r;
 +   int i;
 +  /* nargs = # of arguments */
 +   printf ("got %d args\n", nargs);
 +   for (i=0; i < nargs; i++) {
 +      printf ("  arg[%d] = %lu (width=%d)\n", i, args[i].v, args[i].width);
 +   }
 +   r.width = 5;
 +   r.v = 4;
 +   return r;
 + }
 +</code>
 +
 +The specification of the mapping configuration file is similar to the case of external parameter functions. The only difference is that the outer most ''begin''/''end''  block is ''begin sim''..''end'' instead of ''begin act''..''end''.
 +
 +An example of file I/O implemented with external functions can be found in the ''actsim'' [[https://github.com/asyncvlsi/actsim|git repository]] in the ''simlib/'' directory.
 +
 +===== Operator Precedence =====
 +
 +The operators have the following precedence, from the highest to lowest:
 +   - ''~'', ''!''
 +   - ''*'', ''/'', ''%''
 +   - ''+'', ''-''
 +   - ''<<'', ''>>'', ''>>>'', ''<'', ''>'', ''<='', ''>='', ''='', ''!=''
 +   - ''&''
 +   - ''^''
 +   - ''|''
 +   - ''?''