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