Parameterizable mux

M

mike v.

Guest
Does anyone know why the behavior of the following code isn't defined
by the verilog 2001 standard?

parameter DEPTH = 8;
parameter BITS = 3;

reg [31:0] mem [DEPTH-1:0];
wire [31:0] out;
wire [BITS-1:0] sel;

always @*
out = mem[sel];

I called Cadence and they said that since this is non-standard that
there is some question about when the always block should be evaluated.
Cadence chose to only evaluate the block when the element selected by
sel changes. I was hoping to use the alwasy @* syntax to build a
parameterizable mux. I think I could accomplish the same thing with an
array of instances but a single always block would be simpler and more
readable.

Thanks,
Mike
 
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

mike v. wrote:
Does anyone know why the behavior of the following code isn't defined
by the verilog 2001 standard?

parameter DEPTH = 8;
parameter BITS = 3;

reg [31:0] mem [DEPTH-1:0];
wire [31:0] out;
wire [BITS-1:0] sel;

always @*
out = mem[sel];

I called Cadence and they said that since this is non-standard that
there is some question about when the always block should be evaluated.
Cadence chose to only evaluate the block when the element selected by
sel changes. I was hoping to use the alwasy @* syntax to build a
parameterizable mux. I think I could accomplish the same thing with an
array of instances but a single always block would be simpler and more
readable.
How you are going to send input to your parameterizable mux is
an open question, but never mind that. Cadence is right to point
out the flaw here. Your "mem" is not a net or reg, it is a memory.
You cannot normally include memories in sensitivity lists. For
example, this:

always @(mem) ...

is just plain wrong. So it is reasonable to not include mem in the
sensitivity list with @*. Now what *would* work (at least it works
with Icarus Verilog) is this:

always @(sel, mem[sel]) out = mem[sel];

In this case, you have a proper expression in the sensitivity list
that the @ can be sensitive to.

I've attached a little example program that works with Icarus Verilog.

Note that whether this synthesizes is yet another completely different
story.

- --
Steve Williams "The woods are lovely, dark and deep.
steve at icarus.com But I have promises to keep,
http://www.icarus.com and lines to code before I sleep,
http://www.picturel.com And lines to code before I sleep."
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFEWjOOrPt1Sc2b3ikRAu1MAJ9lZnQIiBSS2QLg445P9F7JlgwZcwCZASSL
2rOtgOBYNXXRbQQw8B2oyoA=
=gIHx
-----END PGP SIGNATURE-----
 
It isn't defined because the people writing the definition didn't think
of this possibility. The way @* is defined in the standard, the @* in
your example is equivalent to @(mem or sel), which is illegal Verilog.

NC-Verilog will give you a warning for this, but will then treat the
implied wait on the memory as if it were a wait on all elements of the
memory. This will give the desired results for your example. It does
not only wait on mem[sel], since that would not satisfy the
requirements for modeling combinational logic.
 
I know that I didn't show the inputs to the mux, let's just assume
there is something to read in the memory. I also understand that
verilog will not accept an array in a sensitivity list. When I first
heard of the always @* syntax, my immediate assumption was that the
compiler would simply examine the variables in the always block and
build its' own sensitivity list, including each element of any memories
and multi-dimensional arrays. Apparently Cadence decided to do
something similar to your Icarus example. My question was to anyone
familiar with the workings of the standards commitee who might know why
this behavior was left undefined by the standard. As it stands now NC
Verilog will do the correct thing with my code and I'm nearly certain
so will synopsys design compiler since DC doesn't even look at
sensitivity lists when building a net list.

Thanks,
Mike


Stephen Williams wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


How you are going to send input to your parameterizable mux is
an open question, but never mind that. Cadence is right to point
out the flaw here. Your "mem" is not a net or reg, it is a memory.
You cannot normally include memories in sensitivity lists. For
example, this:

always @(mem) ...

is just plain wrong. So it is reasonable to not include mem in the
sensitivity list with @*. Now what *would* work (at least it works
with Icarus Verilog) is this:

always @(sel, mem[sel]) out = mem[sel];

In this case, you have a proper expression in the sensitivity list
that the @ can be sensitive to.

I've attached a little example program that works with Icarus Verilog.

Note that whether this synthesizes is yet another completely different
story.
 
On 4 May 2006 10:27:47 -0700, sharp@cadence.com wrote:

It isn't defined because the people writing the definition didn't think
of this possibility. The way @* is defined in the standard, the @* in
your example is equivalent to @(mem or sel), which is illegal Verilog.
I am just curious here: why not @(mem[sel] or sel) which would be ok ?
mem is not a variable but mem[sel] is a variable on the right hand
side. Actually there is no justification to put sel in the list
because it's not mentioned (standard talks about only index variables
on the left hand side) but because you do it I am including it too :)
 
First, there is complete justification to put sel on the list. It is a
variable on the right hand side. The only reason index variables would
be specially mentioned for the left hand side, is that other variables
on the left hand side are not included. For right hand sides, all nets
and variables are included in the list, which includes ones used in
indexes.

For the given example, @(mem[sel] or sel) would work properly. But
there are plenty of cases with variable indexes where that would not
work. As a silly example

integer out, sel, mem[0:1];

always @*
begin
sel = 0;
out = mem[sel];
sel = 1;
end

If you expand this to @(mem[sel] or sel), then it will wait on mem[1],
because sel is 1 when the event control is executed. But since it
actually reads mem[0], this is wrong. If you expand it to @(mem or
sel), and regard the wait on mem as a wait on all elements of mem, then
it works correctly.

For a more realistic example, consider the case

integer i, out, mem[0:7];

always @*
begin
out = 0;
for (i = 0; i < 8; i = i + 1)
out = out | mem;
end

If you expanded the reference to mem into (mem or i), then you
would be waiting on mem[8], since i will be 8 at the time you execute
the event control. That isn't one of the elements you read to compute
out. In fact, mem[8] is out of range, so you will be waiting on a
constant x. Since you actually read all of mem, you need to wait on
mem. So you need to expand it to (mem or i). And since you need a
straightforward rule that does not depend on the arbitrarily complex
code that computed the variable index, and always gives correct
combinational behavior, a reference like mem needs to expand to (mem
or i).
 

Welcome to EDABoard.com

Sponsor

Back
Top