Multi dimensional arrays in an always block

L

Lee

Guest
I'm fairly new to verilog having worked in VHDL for many years. I have
a question regarding the usage of a multi dimensional array in the list
of signals after the always @.

If I define a memory structure:

parameter Width = 8;
reg [2:0] mem [Width-1:0];

Then attempt to use that in some code:

always @ (mem) begin
for (i=0, i<Width, i=i+1) begin
sig1 <= | mem;
end
end

If I full specify all the elements of the mem signal in the always line
it compiles fine. However that defeats the purpose of using the
parameter. If I use it like it is written above it errors out.

always @ (mem[0] or mem[1] or mem[2] ....

Any suggestions? Thanks.

Lee.
 
Lee wrote:
I'm fairly new to verilog having worked in VHDL for many years. I have
a question regarding the usage of a multi dimensional array in the list
of signals after the always @.

If I define a memory structure:

parameter Width = 8;
reg [2:0] mem [Width-1:0];

Then attempt to use that in some code:

always @ (mem) begin
for (i=0, i<Width, i=i+1) begin
sig1 <= | mem;
end
end

If I full specify all the elements of the mem signal in the always line
it compiles fine. However that defeats the purpose of using the
parameter. If I use it like it is written above it errors out.

always @ (mem[0] or mem[1] or mem[2] ....

Any suggestions? Thanks.

Lee.

Lee,

Welcome to Verilog-1995. Simple answer you can't do what you want
without verilog-2000. You need the wildcard sensitivity list:

always @*
....

This seems to be one of the more common Verilog-2000 features that
vendors are initially supportting.

Without it, you must explicitly list all array elements as you found out.

One option is to bring the combinational block into the next
sequential block (if it is registered). i.e. after you calculate sig1,
I'm assuming it's registered somewhere later. You could stick the sig1
calculation (ugly, yes) down in that sequential block. Since the
sensitivity list for that procedural block would only be the
posedge clk, you'd be all set.

The only other way around is to not code the combinational block in a
prodedural block, rather use some sort of contious assignent. But this
means no for loops, as you need.

Also, be careful in your assignment. You don't want to use the
non-blocking assignment opperator here '<='. First of all,
you won't get what you expect, second it's a dangerous coding style.

Regards,

Mark



--
Mark Curry
mcurry@ti.wolf.com.invalid Remove the animal from the domain to reply.
 
Lee <EUAQRXPVYYGI@spammotel.com> wrote in message news:<101g3is4ra0nv4b@corp.supernews.com>...
I'm fairly new to verilog having worked in VHDL for many years. I have
a question regarding the usage of a multi dimensional array in the list
of signals after the always @.
An event control in Verilog is not the same thing as a sensitivity list
in VHDL. It is not a wait on an object or objects. It is a wait on an
expression's value. It wakes up if that value changes. This allows
waiting on arbitrarily complex expressions. It does not allow waiting
on an entire array/memory, since that is not a valid expression. And
the simulator mechanisms that support waiting on expressions would have
serious problems if you tried to implement waiting on entire memories.

Historically I think that Phil Moorby intended Verilog memories (i.e.
arrays) to be used for exactly that: memories. In a memory, you don't
have the entire contents of the memory available at the same time; you
have a selected word available (or multiple selected words in the case
of a multi-ported memory). In that case, there would be no need for
combinational logic that can wait on the entire memory. The selection
logic (while technically having the entire memory as input) would rely
on the support for waiting on expressions instead, allowing it to do
things like @(mem[addr]), where addr is the address input. Of course,
many constructs in Verilog have not always been used for what Phil
thought they would be used for. A reg in a design does not necessarily
get turned into a register by synthesis tools.

Now let's look at your example.

If I define a memory structure:

parameter Width = 8;
reg [2:0] mem [Width-1:0];

Then attempt to use that in some code:

always @ (mem) begin
for (i=0, i<Width, i=i+1) begin
sig1 <= | mem;
end
end

The first thing that I would note is that you are trying to treat the
depth of the array as a width (as in vector width, corresponding to
the width of the sig1 vector). This is a clue that you are not matching
your representation to what you are actually trying to do. The width
should be applied to the vector width portion of the array declaration
instead. Meanwhile, the width of your array appears to be fixed at 3
bits instead of being parameterized. So that is the dimension that you
could list explicitly. If you swap the dimensions, you can code it as a
parallel bitwise ORing of vector rows, instead of a sequential OR-reduction
of columns. Assuming that you really wanted sig1 to be a net, and were
just using a reg so you could use procedural code with a for-loop, you
could recode this as

reg [Width-1:0] mem [2:0];

assign sig1 = mem[2] | mem[1] | mem[0];

or if you really wanted a combinational always block and sig1 as a reg

always @(mem[2] or mem[1] or mem[0])
sig1 = mem[2] | mem[1] | mem[0];

Either way, you can eliminate the for-loop, simplifying your code.
Vector operations should also simulate a lot faster because a lot
of bits can be combined in parallel, instead of sequentially. This
is something that probably doesn't matter much in VHDL, because the
standard logic vector representation has been defined in a way that
doesn't allow efficient boolean computation on vectors in parallel.

If you needed to be able to parameterize the depth also, then you
would have a more serious problem. There are ways of dealing with
this too, but the ones I can think of involve Verilog-2001 features
that may not be supported in your tools. For example, you could
use a generate-for around your always block (instead of a procedural
for-loop inside it) to create separate processes to compute each bit
of output, each one sensitive to a single element of the array. E.g.

reg [depth-1:0] mem [Width-1:0];

genvar i;
for (i = 0; i < Width; i = i + 1) begin: blk
always @(mem) sig1 = | mem;
end

or if you wanted sig1 to be a net, replace the always block with

assign sig1 = | mem;

which is legal because i is a constant in each of the resulting
continuous assignments (the LHS of a continuous assignment cannot
be a variable bit select).

The other Verilog-2001 possibility is to use the @* combinational
sensitivity extension. The standard does not actually specify what
happens for array references inside an @*. However, at least some
implementations will treat this as a wait on all of the elements
of the array. Because this behavior is not actually standardized,
this approach could cause portability problems, even if your current
tool handles it the way you want.
 
Steven Sharp wrote:
<snip>

The other Verilog-2001 possibility is to use the @* combinational
sensitivity extension. The standard does not actually specify what
happens for array references inside an @*. However, at least some
implementations will treat this as a wait on all of the elements
of the array. Because this behavior is not actually standardized,
this approach could cause portability problems, even if your current
tool handles it the way you want.
Oh dear, I hope this is not the case. If this is true, it's bad, bad
news for verilog-2001 IMHO.

I'm still not familiar with all the new features in verilog-2001, but
I believe you can declare multi-dimensional arrays more that one way.
i.e. a 2-D array can be declared like Verilog-1995:

reg [ 7 : 0 ] array1 [ 0 : 5 ]; /* 6, 8 bit words */

or

reg [ 7 : 0 ] [ 5 : 0 ] array2;

And these are treated DIFFERENTLY in Verilog 2001, I believe,
although I don't know the details. Probably some ugly exceptions
to keep verilog-1995 code still compatible with 2001.

Steve, can you comment on the second form of a multi-dimensional
array.

Can we say:

always @( array2 )

in verilog 2001?

or can we guarantee, by the standard, that
doing as you suggested for Lee, use the wildcard
always @*
/* do somthing with array2 */

That it'll work by the standard?

If it don't then I'm afraid Verilog-2001 is dead before
it's even arrived....

Regards,

Mark

--
Mark Curry
mcurry@ti.wolf.com.invalid Remove the animal from the domain to reply.
 
Mark,

Your array2 is not valid syntax in Verilog-2001.
You can have only one dimension before the variable name.

Your array1 is, as you write, an array of 6 elements, each of which is an 8-bit
vector.

You could write

reg array3 [7:0] [5:0] ;

which would be an 8 x 6 array of 1-bit elements (i.e., scalars).

Shalom


Mark Curry wrote:

I'm still not familiar with all the new features in verilog-2001, but
I believe you can declare multi-dimensional arrays more that one way.
i.e. a 2-D array can be declared like Verilog-1995:

reg [ 7 : 0 ] array1 [ 0 : 5 ]; /* 6, 8 bit words */

or

reg [ 7 : 0 ] [ 5 : 0 ] array2;

And these are treated DIFFERENTLY in Verilog 2001, I believe,
although I don't know the details. Probably some ugly exceptions
to keep verilog-1995 code still compatible with 2001.

Steve, can you comment on the second form of a multi-dimensional
array.
--
Shalom Bresticker Shalom.Bresticker@motorola.com
Design & Reuse Methodology Tel: +972 9 9522268
Motorola Semiconductor Israel, Ltd. Fax: +972 9 9522890
POB 2208, Herzlia 46120, ISRAEL Cell: +972 50 441478
 
Shalom Bresticker wrote:
Mark,

Your array2 is not valid syntax in Verilog-2001.
You can have only one dimension before the variable name.

Your array1 is, as you write, an array of 6 elements, each of which is an 8-bit
vector.

You could write

reg array3 [7:0] [5:0] ;

which would be an 8 x 6 array of 1-bit elements (i.e., scalars).
Shalom,

Thanks - I knew the syntax was to put all of the dimensions on
one side or the other, but didn't remember which. Stared at it
a while, and convinced myself this looked right, but never looked
it up.

But the question still remains. If I declare array3 as you have
above, can I do:

always @( array3 )
/* Some combinational operation on array3 */

in Verilog 2001? Or replace the array3 sensitivity with
wildcard? You're comment in parens - (i.e. scalars) implies
this is ok by the standard. However's Steve's response really
worried me.

Thanks,

Mark



--
Mark Curry
mcurry@ti.wolf.com.invalid Remove the animal from the domain to reply.
 
Mark Curry <mcurry@ti.wolf.com.invalid> wrote in message news:<bv9rmb$f98$1@home.itg.ti.com>...
Oh dear, I hope this is not the case. If this is true, it's bad, bad
news for verilog-2001 IMHO.
While it is a bad thing, it is not as fatal as you suggest. The Verilog
standard contains quite a few errata, and the standardization group is
actively working on fixing problems and clarifying interpretations. While
some of these changes may not officially become part of the text of the
standard until the next standardization, they are available to any
interested party (e.g. tool implementers).

The standard defines @* by specifying an equivalent event control.
However, when an array reference is involved, the specification would
add the array name to the equivalent event control, with no index. As
has been noted already, this is illegal, so it has no defined behavior.
This leaves the behavior of the @* undefined in that case.

However, it is pretty clear that the desired behavior is to get an
event control that will correctly model combinational logic. The
most straightforward way to get that is to treat this case as if all
elements of the array were added to the sensitivity list (as you have
suggested). NC-Verilog treats it this way, but produces a warning that
this behavior is not standardized. I suspect that VCS treats it the
same way, but probably without warning you. Any synthesis tool
shouldn't care; it just produces combinational logic regardless.

This behavior may become the official one, though there have been
some other suggestions.

array2;

....

Steve, can you comment on the second form of a multi-dimensional
array.
This is not legal in Verilog-2001. You may be confusing this with
packed arrays in SystemVerilog.
 
Mark Curry <mcurry@ti.wolf.com.invalid> wrote in message news:<bvbk85$hbp$1@home.itg.ti.com>...
But the question still remains. If I declare array3 as you have
above, can I do:

always @( array3 )
/* Some combinational operation on array3 */

in Verilog 2001?
No, you have made the problem even worse. Before, you could
have used @(mem[0]) to wait on an element of the array, since
it is a vector, which is a valid expression. But by declaring
it as a two dimensional array of scalars, you now have to provide
both indexes when you reference it. So you can't even use
@(array3[0]), but must do @(array3[0][0]). A vector is a valid
expression, but an array of scalars is not. In the same way, if
you use the Verilog-1995 declarations

reg [7:0] vect;
reg arr [7:0];

then vect is a vector, but arr is an array of scalars. You
can reference vect in an expression, but you cannot reference
arr as a whole in an expression. You must provide an index.
From a pseudo-hardware viewpoint, vect is an 8-bit register that
can be read or loaded broadside, while arr is an 8-word 1-bit-wide
memory that must be read or written one 1-bit word at a time.
 
Steven Sharp wrote:
Mark Curry <mcurry@ti.wolf.com.invalid> wrote in message news:<bvbk85$hbp$1@home.itg.ti.com>...

But the question still remains. If I declare array3 as you have
above, can I do:

always @( array3 )
/* Some combinational operation on array3 */

in Verilog 2001?


No, you have made the problem even worse. Before, you could
have used @(mem[0]) to wait on an element of the array, since
it is a vector, which is a valid expression. But by declaring
it as a two dimensional array of scalars, you now have to provide
both indexes when you reference it. So you can't even use
@(array3[0]), but must do @(array3[0][0]). A vector is a valid
expression, but an array of scalars is not. In the same way, if
you use the Verilog-1995 declarations

reg [7:0] vect;
reg arr [7:0];

then vect is a vector, but arr is an array of scalars. You
can reference vect in an expression, but you cannot reference
arr as a whole in an expression. You must provide an index.
From a pseudo-hardware viewpoint, vect is an 8-bit register that
can be read or loaded broadside, while arr is an 8-word 1-bit-wide
memory that must be read or written one 1-bit word at a time.
Hmm. Can I assume from your response on the other thread that
using the wildcard sensitivity list, with this form of the array
declaration that it'll probably work but is in "the grey area"?

I also do see that I'm confusing some features in verilog-2001
with SystemVerilog.

If you'll allow me to drift a little bit I'd like to show three examples
of why I think these things are very important.

What I'm going to show is a parameterized N to 1 mux, with each
element having M bits.

I show first what I think is the idealized solution - available only
with, I believe, System Verilog. (Warning in advance, I make ALL of
these examples without any tool whatsoever to check syntax. I base
all of my code on the bits and pieces I've picked up here and there
about Verilog-2001 and SystemVerilog)

System verilog EX 1.

module #
(
word_size = 11,
num_elements = 8,
select_size = 3 /* ( 2 ** select_size ) >= num_elements!! */
) mux_ex
(
input [ word_size - 1 : 0 ] all_ins [ num_elements - 1 : 0 ],
input [ select_size - 1 : 0 ] select,

output [ word_size - 1 : 0 ] y
);

wire [ word_size - 1 : 0 ] y = all_ins[ select ];

endmodule


Neat, concise, easy to read, just about anyone will be able
to understand what's going on quickly.

Now, I for one, think that this style will NOT be usable for
MANY years, if at all, even if SystemVerilog becomes the standard.
Mostly I base this on the backend tools not being able
to accept the multi-dimesional port: all_ins. (In the
same vein as all synthesizable VHDL ports basically
boils down to std_logic, and std_logic_vector).

So, I present SystemVerilog version 2:
module #
(
word_size = 11,
num_elements = 8,
select_size = 3
) mux_ex
(
input [ word_size * num_elements - 1 : 0 ] all_ins,
input [ select_size - 1 : 0 ] select,

output [ word_size - 1 : 0 ] y
);

reg [ num_elements - 1 : 0 ] [ word_size - 1 : 0 ] all_ins_array;

always @( all_ins )
all_ins_array = all_ins;

reg [ word_size - 1 : 0 ] y;
always @( all_ins_array )
y = all_ins_array[ select ];
/* Or is it y = all_ins_array[ select ][ word_size - 1 : 0 ]?? */

endmodule

Not quite as neat, but still the behavior is probably easily understood.
From what I can tell the all_ins_array assignment is LEGAL in
SystemVerilog, as this is a packed array. The code could be
smaller in that I could use continous assignments instead of
procedural blocks, but it segues into my next example better:

verilog - 2001 version:
module #
(
word_size = 11,
num_elements = 8,
select_size = 3
) mux_ex
(
input [ word_size * num_elements - 1 : 0 ] all_ins,
input [ select_size - 1 : 0 ] select,

output [ word_size - 1 : 0 ] y
);

reg [ word_size - 1 : 0 ] all_ins_array [ 0 : num_elements - 1 ];

reg [ select_size : 0 ] i; /*yes i size is one greater than select*/
always @( all_ins )
for( i = 0; i < num_elements; i = i + 1'b1 )
all_ins_array[ i ] = all_ins >> ( word_size * i );

reg [ word_size - 1 : 0 ] y;
always @*
y = all_ins_array[ select ];

endmodule

Getting bigger quickly. The for loop is ugly but neccesary,
verilog-2001 doesn't have the notion of "packed arrays". So,
you need to fill the thing up one-by-one. And the shifting / truncation
makes you'll need to think a bit what's going on. And the synthesis
tool would probably need to chug on it a while... (Also easy to make
mistakes in).

Then comes the combinational logic of y, depending on the two-d array
all_ins, which is like the example that started this whole thread.
And, from what I can conclude from this thread, the verilog-2001 version
shown here is in "the grey area" at best for standardization purposes.

These are of course contrived examples, but show the types of things
our group cares about, and was hoping that verilog-2001 would
help solve.

Comments appreciated.

Regards,

Mark

--
Mark Curry
mcurry@ti.cat.com Remove the animal from the domain to reply.
 
Mark,

Thanks for the comments and suggestions. Can you comment more on the
use of the non-blocking operator. I thought that I had read in a book
somewhere that this was the preferred way. Perhaps I have it backwards
thought. I would have thought that either operator would have been fine
in this case. Thanks.

Lee.

Mark Curry wrote:
Also, be careful in your assignment. You don't want to use the
non-blocking assignment opperator here '<='. First of all,
you won't get what you expect, second it's a dangerous coding style.
 
Steven,

Thanks for all the great information. I think I followed most of what
you are saying, though it took me reading it several times to be sure :)

The first thing that I would note is that you are trying to treat the
depth of the array as a width (as in vector width, corresponding to
the width of the sig1 vector). This is a clue that you are not matching
your representation to what you are actually trying to do. The width
should be applied to the vector width portion of the array declaration
instead. Meanwhile, the width of your array appears to be fixed at 3
bits instead of being parameterized. So that is the dimension that you
Your absolutely right. In my haste to put together a quick and dirty
example I definitely used the wrong word here (Width). The code is
indicative of what I wanted though. Depth would have been a much better
name for the parameter.

If you needed to be able to parameterize the depth also, then you
would have a more serious problem. There are ways of dealing with
this too, but the ones I can think of involve Verilog-2001 features
that may not be supported in your tools. For example, you could
use a generate-for around your always block (instead of a procedural
for-loop inside it) to create separate processes to compute each bit
of output, each one sensitive to a single element of the array. E.g.
At this point having a parameter on the actual width is not important
however it may be in the future. If it comes to that I will have to
investigate whether or not our tool set supports verilog-2001. Your
comments on solving this are much appreciated.

reg [depth-1:0] mem [Width-1:0];

genvar i;
for (i = 0; i < Width; i = i + 1) begin: blk
always @(mem) sig1 = | mem;
end

or if you wanted sig1 to be a net, replace the always block with

assign sig1 = | mem;

which is legal because i is a constant in each of the resulting
continuous assignments (the LHS of a continuous assignment cannot
be a variable bit select).

The other Verilog-2001 possibility is to use the @* combinational
sensitivity extension. The standard does not actually specify what
happens for array references inside an @*. However, at least some
implementations will treat this as a wait on all of the elements
of the array. Because this behavior is not actually standardized,
this approach could cause portability problems, even if your current
tool handles it the way you want.
 
Lee wrote:
Mark Curry wrote:

Also, be careful in your assignment. You don't want to use the
non-blocking assignment opperator here '<='. First of all,
you won't get what you expect, second it's a dangerous coding style.

Thanks for the comments and suggestions. Can you comment more on the
use of the non-blocking operator. I thought that I had read in a book
somewhere that this was the preferred way. Perhaps I have it
backwards thought. I would have thought that either operator would
have been fine in this case.
Ok, re-reading you're code, it's not as bad as I thought.
I thought you were assigning the same element multiple times in
the same proedural call. i.e. doing something iteratively through
the loop. Using non-blocking style here would be really bad in
that you'd only get the last iteration's assignment. But you
were not doing this.

But, in general, for combinational logic, you should be using
the blocking assignment: a = b. For sequential logic, use
non-blocking. q <= d.

See:

http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA_rev1_2.pdf

Regards,

Mark



--
Mark Curry
mcurry@ti.wolf.com.invalid Remove the animal from the domain to reply.
 

Welcome to EDABoard.com

Sponsor

Back
Top