Comments on packed arrays


Subject: Comments on packed arrays
From: Paul Graham (pgraham@cadence.com)
Date: Mon Mar 18 2002 - 08:17:48 PST


Sorry I couldn't be in San Jose last week. For those of you who don't know
me, I live in Michigan, in the frozen north of the USA.

I would like to make some suggestions about how arrays should be treated in
SystemVerilog. Mainly, I want to simplify and extend the definition of
arrays.

Motivation:

I worked for several years on VHDL tools. Now I support the VHDL and
Verilog front-ends for the Cadence (Ambit) synthesis tool. As a VHDL tool
developer, I see no reason not to generalize the definition of arrays. As a
synthesis developer, I do feel that concerns for simulation efficiency
should not be the limiting factor in defining high-level constructs like
arrays. Ambit (and I suspect Synopsys and every other bilingual synthesis
vendor) uses the same synthesis engine for VHDL and Verilog. So I would not
like to cut off the capability of Verilog array types as a level below that
of VHDL.

VHDL provides for user-defined operators. It is possible in VHDL to define
an array type and then declare operators on that array type. In Verilog,
there is no provision for user-defined operators, so it makes sense to
provide some powerful built-in operators on arrays.

SystemVerilog defines two types of arrays, packed and unpacked arrays.
Packed arrays are first-class types -- they can be read and assigned to, and
they can appear as arguments to functions and tasks (and presumably
modules). A packed array can also be viewed as an integer, so there is an
implicit type conversion between an integer and a packed array.

Unpacked arrays are second-class types. They can be be assigned only one
element at a time. I believe they can be read only one element at a time.
They cannot be used as subprogram ports. It's not clear whether they can be
used as module ports. Can they appear in interfaces? Why restrict
arbitrary array types?

Summary of changes:

My suggestion is to simplify and extend arrays as follows:

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.
   
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.

Thanks for your consideration.

Paul



This archive was generated by hypermail 2b28 : Mon Mar 18 2002 - 08:18:49 PST