Array proposal for SystemVerilog


Subject: Array proposal for SystemVerilog
From: Paul Graham (pgraham@cadence.com)
Date: Fri Mar 29 2002 - 14:30:35 PST


Better late than never, here's my proposal for arrays in SystemVerilog.
This is basically my March 18 proposal, with a couple of new items (items 8
and 9).

1. Eliminate packed arrays. All arrays are declared with the 'unpacked'
   syntax, with an optional word range before the declaration name, and zero
   or more ranges after the name. Now there is only one type of array
   instead of two types with different rules for each type.

2. Provide for conversion between arrays and integers with the following
   two functions:

   a. $flatten(<array>) returns a bit-vector, the concatenation of the
      words in <array>.

   b. $unflatten(<array>) converts a bit-vector back to an array; can only
      be used in an initialization or as the rhs of an assignment (this
      provides the necessary context for sizing the array).

3. Allow reading and writing of whole arrays.

4. Allow slicing of one-dimensional arrays (for both reading and writing).

5. Allow general array ports of subprograms and modules.

6. Provide functions or attributes to get the bounds of a dimension of an
   array. I would suggest new system functions like $left, $right, etc.,
   corresponding to VHDL attributes for querying array bounds.

7. Allow arithmetic and logical operators on arrays.

   a. A reduction operator combines the words of single array operand and
      returns a single word as a result. The definition is analogous to the
      definition of existing reduction operators on bit-vector types.

   b. A binary operator takes two array operands and returns an array whose
      elements are equal to the binary operator applied to the corresponding
      elements of the input operands.

8. Provide an attribute "packed" to request that the simulator pack the
   elements of an array of struct.

9. Some new rules for typedefs.

I believe these proposals simplify the description of array types, and
extend the capability of arrays.

Details:

Let me be more specific about some of the changes I propose:

2 $flatten and $unflatten

   a. The $flatten function takes an array and concatenates the words
      according to a row-order traversal of its indices. $flatten can take
      an argument of any dimensionality.

   b. The $unflatten function takes a bit-vector and returns an array. The
      bit-vector is truncated or zero-extended if necessary to fit the
      destination array. $unflatten is the inverse of $flatten. To provide
      context for the array bounds, $unflatten may only be used as the rhs
      of an assignment (either an assignment statement or the initializer of
      a declaration).

      Example:

         reg [3:0] x[3:0];
         x = $unflatten(16'habcd);

      This is equivalent to:

         x[3] = 4'ha;
         x[2] = 4'hb;
         x[1] = 4'hc;
         x[0] = 4'hd;

      Example:

         reg [31:0] x[1:0];
         reg [63:0] tmp;
         tmp = $flatten(x); // same as tmp = {x[1], x[0]}
         tmp = tmp + 1;
         x = $unflatten(tmp);

       This could also be written:

         x = $unflatten($flatten(x) + 1);

       It is the equivalent of using packed arrays like this:

         reg [31:0][1:0] x;
         x = x + 1;

       Granted, using $flatten and $unflatten is more verbose than relying
       on the conversion between packed arrays and integers.

       On the other hand, we could make $flatten and $unflatten optional,
       defining a rule that whenever an array appears in a bit-vector
       context, it is treated as a flat bit-vector, and whenever a
       bit-vector appears in an array context, it is unflattened into an
       array.

3. Array assignment requires that the source and destination arrays have the
   same lengths in each array dimension. However, the element sizes do not
   have to match. Assignment is done element-by-element, with the source
   element being truncated or zero- or sign-extended before being assigned
   to the target element. Thus existing Verilog rules for expression width
   determination are applied to each word of the source array.

   Example:

       reg signed [3:0] A[3:0];
       reg [7:0] B[3:0];

       B = A;

   This is equivalent to:

       B[3] = A[3];
       B[2] = A[2];
       B[1] = A[1];
       B[0] = A[0];

   Each element of A is sign-extended to 8 bits before being assigned to B.
   This is a consequence of the normal rules for width and sign
   determination.

   Note that these rules for array assignment should govern how arrays are
   passed as module and subprogram ports. A port and its argument must have
   the same length in each array dimension, but word sizes do not have to
   match. Truncation or extension is done as necessary on each word.

4. Slicing a one-dimensional array simply returns a one-dimensional array
   whose array bounds are the bounds of the slice.

5. Subprograms and modules can have array ports.

   a. As a corollary, it seems that a function should be able to return an
      array type. The nice thing about the packed array syntax is that is
      convenient for declaring a function return type:

          function [3:0][7:0] f;

      Unfortunately, the unpacked array syntax makes this awkward. Either
      the packed array syntax can still be used, inconsistently:

          function [3:0][7:0] f;
          -- implicitly declares a result variable:
          -- reg [3:0] result[7:0];
          -- ... return result;

      Or the unpacked array syntax can be used, which is not aesthetically
      appealing when combined with ANSI-style arguments:

          function [3:0] f[7:0](input x, y, z);

6. Provide functions or attributes to the the bounds of a dimension of an
   array. The corresponding vhdl attributes are:

       left, right, high, low, length

   So I would propose system functions:

       $left, $right, $high, $low, $length

   with the syntax:

       $<func>(<array>, <dimension>)

   where <array> is an array object or value, and dimension is an integer
   from 0 to the number of dimensions in the array. Dimension 0 always
   refers to the word size of the array. For an array of bits, e.g.,

       reg x[7:0];

   the left, right, low, and high bounds of dimension 0 are considered to be
   0, and the word length is considered to be 1.

   Dimension 1 is the first dimension to the right of the declared name,
   that is, the slowest changing dimension. The more rapidly changing
   dimensions have sequentially higher numbers.

   I believe that such functions (or any other mechanism to access array
   bounds) are essential if arrays are going to be incorporated into the
   language. They make it easy to write code to traverse arrays without
   accidentally stepping beyond the array bounds.

7. Allow arithmetic and logical operators on arrays.

   a. All existing commutative Verilog binary operators can be used as array
      reduction operators on one-dimensional arrays. Given such an operator
      <op> (non-negating) and a one-dimensional array array A, the expression

          <op> A

      is defined as

          A[i0] <op> A[i1] <op> ... A[iN]

      For the negating operators ~&, ~|, ~^ and ~^, the operation is defined
      as the negation of the corresponding positive reduction operator, that
      is:

          ~<op> A == ~(<op> A)

   b. All Verilog binary operators can be used as binary array operators on
      arrays of any dimensionality. The two operand array must have the
      same number of words in each dimension. However, the element sizes
      may differ. Let A and B be two N-dimensional arrays. Then

          A <op> B

      is an array (call it Z) with the same dimension lengths as A and B,
      and with its elements defined as:

          Z[i1]...[iN] = A[i1]...[iN] <op> B[i1]...[iN]

      Normal Verilog expression width and sign rules apply to each pair of
      elements.

      Example:

          reg signed [7:0] A[3:0];
          reg signed [3:0] B[4:1];
          reg [5:0] Z[0:3];

          Z = A + B;

      This is equivalent to writing:

          Z[0] = A[3] + B[4];
          Z[1] = A[2] + B[3];
          Z[2] = A[1] + B[2];
          Z[3] = A[0] + B[1];

      The normal Verilog width and sign rules imply that each element of A
      is sign-extended to 8 bits before being added to the corresponding
      element of B. Then the result is truncated to 6 bits before being
      assigned to Z.

   c. The conditional operator should be supported. The first operand must
      be a word (i.e., non-array) expression. If either of the second and
      third operands is an array, then both must be. The arrays must have
      the same length in each dimension, though the word sizes may differ.

9. Some new rules for typedefs.

   Typedefs involving arrays have two cases. A typedef can declare a type
   representing a bit-vector or an array.

   A bit-vector typedef looks like:

       typedef [3:0] foo;

   The range comes before the type name, and there are no other types given
   in the typedef.

   An array typedef has at least one range following the type name:

       typedef foo bar [3:0];

   This declares an array of 4 foo objects. In this example foo happens to
   be a bit-vector type, but it could have been an array type as well.

   There is a simulation attribute "packed" which requests that the
   simulator represent a given type as a packed structure or array.

   To get the effect of a packed array like this:

       reg [3:0][3:0] x [7:0];

   you can declare:

       typedef (* simulation, packed = 1 *) [3:0] foo [3:0];
       reg foo x [7:0];

   While x is a two-dimensional array of 4-bit words, its internal
   representation would be that of an array of 16-bit words.

Paul



This archive was generated by hypermail 2b28 : Fri Mar 29 2002 - 14:32:53 PST