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