How powerful is Verilog at using parameters to specify designs?

Do you have a question? Post it now! No Registration Necessary

Translate This Thread From English to

Threaded View
I have a design in mind that would fit in this skeleton:
[code]
module xyz ( result, leftOp, rightOp);
parameter  integer nmBits = 1;
localparam integer highBit = nmBits - 1;
output             result;
input [ highBit:0] leftOp;
input [ highBit:0] rightOp;

// ...

endmodule
[/code]
The way (xyz) is designed, this module would work differently for different
 values of (nmBits), but in a way that can be precisely defined. In fact, I
'm seriously thinking of writing a Java program that takes (nmBits) as an i
nput and produces a parameterless verion of (xyz) for that version of (nmBi
ts). So I'm wondering, is it a true statement that, if one can write such a
 Java program to produce the equivalent (parameterless) Verilog code for an
y given set of parameters, one can also write Verilog code with parameters  
to do the same thing?

Re: How powerful is Verilog at using parameters to specify designs?
On Monday, September 21, 2020 at 4:23:25 PM UTC-6, Kevin Simonson wrote:
Quoted text here. Click to load it
nt values of (nmBits), but in a way that can be precisely defined. In fact,
 I'm seriously thinking of writing a Java program that takes (nmBits) as an
 input and produces a parameterless verion of (xyz) for that version of (nm
Bits). So I'm wondering, is it a true statement that, if one can write such
 a Java program to produce the equivalent (parameterless) Verilog code for  
any given set of parameters, one can also write Verilog code with parameter
s to do the same thing?

Your code seems fine, although your port list style is Verilog 1995.   A mo
re modern style would be:

module xyz
#(parameter WIDTH=8) // Width of input operators (default 8 if not overwr
itten at instantiation)
(input [WIDTH-1:0] leftOp,
 input [WIDTH-1:0] rightOp,
 output result);
...
endmodule


Re: How powerful is Verilog at using parameters to specify designs?
On Monday, September 21, 2020 at 6:31:29 PM UTC-7, Kevin Neilson wrote:

Quoted text here. Click to load it

Kevin (Neilson), what if I had more than one parameter? For example, what if I had:
[code]
module queue ( dataOut, clock, shift, dataIn);
parameter integer nmBits = 1;
parameter integer nmElems = 1;
localparam integer highBit = nmBits - 1;
output [highBit:0] dataOut;
input clock;
input shift;
input [highBit:0] dataIn;

// ...

endmodule
[/code]
Could I do the following?
[code]
module queue
  #( parameter integer nmBits = 1, parameter integer nmElems = 1)
  ( output [nmBits-1:0] dataOut
  , input clock
  , input shift
  , input [nmBits-1:0] dataIn);

// ...

endmodule
[/code]
Or am I messing up the syntax? Any info on this would be appreciated.
By the way, everything I know about Verilog has come from _Verilog HDL: A Guide to Digital Design and Synthesis_ by Samir Palnitkar. Is there another book out there that explains how to use System Verilog post 1995?

Re: How powerful is Verilog at using parameters to specify designs?
On Tuesday, September 22, 2020 at 4:16:36 PM UTC-6, Kevin Simonson wrote:
Quoted text here. Click to load it
  
Quoted text here. Click to load it
more modern style would be:  
Quoted text here. Click to load it
erwritten at instantiation)  
Quoted text here. Click to load it
 if I had:  
Quoted text here. Click to load it
 Guide to Digital Design and Synthesis_ by Samir Palnitkar. Is there anothe
r book out there that explains how to use System Verilog post 1995?

That's almost right.  I think you need to leave out the extra "parameter" k
eyword:

module queue  
#( parameter nmBits = 1,  
     nmElems = 1)  
( output [nmBits-1:0] dataOut ,  
input clock,  
input shift,  
input [nmBits-1:0] dataIn);  

Also, the keyword "integer" is superfluous, since it's the default for para
meters, but I think it's allowed.  You should also avoid the name "queue" s
ince that is a keyword in some versions of Verilog.

I do not like the Palnitkar book.  Unless it's been updated, it might use o
utmoded syntax from Verilog-1995.  The port list syntax was streamlined in  
Verilog-2001.  Unfortunately, most Verilog books are bad, so I don't have o
ne to recommend.  You're probably best-off with finding online resources.

Re: How powerful is Verilog at using parameters to specify designs?
Quoted text here. Click to load it

A parameter type is NOT default "integer".  It's default "as big as it
needs to be".  There's weird corner-cases neccesary to be compatible
with Verilog-XL (basically the pseudo-standard).

I suggest always explicitly typing parameters IF YOU CAN.  Sometimes,
you need to make use of that "as big as it needs to be" clause.

Regards,
Mark



Re: How powerful is Verilog at using parameters to specify designs?
Kevin Neilson: "I do not like the Palnitkar book. Unless it's been updated,
 it might use outmoded syntax from Verilog-1995. The port list syntax was s
treamlined in Verilog-2001. Unfortunately, most Verilog books are bad, so I
 don't have one to recommend. You're probably best-off with finding online  
resources."

Kevin, do you know of any online resources you can recommend? I tried doing
 a Google search on "on-line resource for learning verilog-2001" and it tur
ned up a bunch of links, and "verilog-2001 tutorial" and it turned up a bun
hc of links, but some of them that said they were for Verilog-2001 used the
 old style for module interfaces, and none of them looked like they were di
rected at people who weren't very experienced with HDLs who wanted to learn
 how to use Verilog-2001.

Re: How powerful is Verilog at using parameters to specify designs?
Quoted text here. Click to load it

Absolutely just stay within Verilog.  IMHO it's the exception, not the
rule to move things out of verilog and use another language (to describe
hardware).  

However many in industry do so.  There's a common reference people use
that uses a web page to generate CRC codes.  While that reference gives
excellant results, its quite puzzling to me in that the equivalent code
in verilog is much more straightforward, clear, and concise.  

The problem with these sorts of things is that it frames everything  
as "instance based" design as opposed to module based design.  Flows within
Vivado, for instance, are unfortunetly based on instance based designs.

Regards,
Mark  


Re: How powerful is Verilog at using parameters to specify designs?
Gtwrek (Mark?): "Absolutely just stay within Verilog. IMHO it's the excepti
on, not the rule to move things out of verilog and use another language (to
 describe hardware)." Okay. As modified by Kevin Neilson my design has one  
output bit (result) and two input parameters (leftOp) and (rightOp), each o
f which has (nmBits) bits. At this point I have three (localparams) integer
s, defined by:
[code]
localparam integer nmNodes  = 2 * nmBits - 1;
localparam integer nmLevels = $clog2( nmBits) + 1;
localparam integer nmRships = 2 * nmNodes - nmLevels;
[/code]
and then I have three arrays, defined by:
[code]
localparam node    nodes  [  nmNodes:1];
localparam integer bases  [ nmLevels:0];
wire               rships [ nmRships:1];
[/code]
Oh, I almost forgot; (node) is defined by:
[code]
typedef enum { CORNER, E_LEAF, N_LEAF, SIDE, E_INTERIOR, N_INTERIOR } nodeT
ype;
typedef struct packed
{ nodeType ndType;
   integer inLow;
   integer inHigh;
   integer out;
} node;
[/code]
Now to build my design I need multiplexers, nor gates, nand gates, and a fe
w not gates. The design does a lot of connecting elements of (rships) to th
ose multiplexers, nor gates, and not gates, and the precise way it's done r
equires some recursive procedure calls. So I built a recursive function:
[code]
function automatic integer fillSubtree ( input integer vrtcl
                                       , input integer hrzntl
                                       , input     bit equal);

// ...

endfunction
[/code]
whose purpose is to build up array (nodes) to show which elements of (rship
s) to connect to which gates, and then I iterate through (nodes) to actuall
y connect all the wires. Is that an approach
that looks like it should work? Should I be able to do that in System Veril
og?

Re: How powerful is Verilog at using parameters to specify designs?
Quoted text here. Click to load it

All of the above looks reasonable.  Recursive functions are
synthesizable as long as the terminating conditions are static (in
software speak, the loop can be unrolled during elaboration)

Some other thoughts - parameters are VERY useful, and all my designs
have loads of them.  But don't discount using constant input wires as
well.  Some things you must utilize with a parameter - anything that
affects an actual size for example.

But other things don't, and you can just use a constant input wire.
Today's synthesizers will produce equilvalent quality of results with
either case.  A common example - some sort of bus decode (like a cpu
register):

module reg
#(
  parameter BUS_SIZE = 32
  // parameter MY_ADDRESS = 32'h1000 // Commonly done, but don't!
)
(
   input [ BUS_SIZE - 1 : 0 ] bus_addr_i,
   // other bus signals...

   input [ BUS_SIZE - 1 : 0 ] my_addr_i
   // other register I/O
);

  wire this_is_my_address = bus_addr_i == my_addr_i;  // Use this!
  //wire this_is_my_address = bus_addr_i == MY_ADDRESS; // Don't use this!

endmodule

Declaring things as wire's are more flexible in designs.  (And arguably)
easier to debug in wave tools.

Regards,
Mark

Re: How powerful is Verilog at using parameters to specify designs?
Gtwrek (Mark): "All of the above looks reasonable. Recursive functions are synthesizable as long as the terminating conditions are static (in software speak, the loop can be unrolled during elaboration)".

Mark, can I post my Verilog so you can take a look at it, and then post the error messages Icarus is giving me when I try to simulate it? Or e-mail them to you?

Re: How powerful is Verilog at using parameters to specify designs?
Quoted text here. Click to load it

If it's a minimal example, I'll give it a shot. Post here.

--Mark




Re: How powerful is Verilog at using parameters to specify designs?
Gtwrek (Mark): "If it's a minimal example, I'll give it a shot. Post here."

It's kind of complicated, but it's not huge. I'll post it and you can decide whether it's too big or not. It will be my next post, and the post after that will be the error messages Icarus gave me.

Re: How powerful is Verilog at using parameters to specify designs?
module forMark
  #( parameter nmBits = 1)
  ( output              lssThn
  , input [ nmBits-1:0] leftOp
  , input [ nmBits-1:0] rightOp);

typedef enum { CORNER, E_LEAF, N_LEAF, SIDE, E_INTERIOR, N_INTERIOR } nodeType;
typedef struct packed
{ nodeType ndType;
   integer inLow;
   integer inHigh;
   integer out;
} node;

localparam integer nmNodes  = 2 * nmBits - 1;
localparam integer nmLevels = $clog2( nmBits) + 1;
localparam integer nmRships = 2 * nmNodes - nmLevels;

localparam node    nodes  [  nmNodes:1];
localparam integer bases  [ nmLevels:0];
wire               rships [ nmRships:1];
wire               ntRght;
genvar             ix;

function automatic integer fillSubtree ( input integer vrtcl
                                       , input integer hrzntl
                                       , input     bit equal);
    integer rsltLw;
    integer rsltHh;
    integer ndIx;
    integer rlIx;
    integer vr;
    integer hz;
    integer twice;
    integer nxVr;
    bit nxPs;
  begin
    ndIx = (1 << vrtcl) + (hrzntl << vrtcl + 1);
    vr   = vrtcl;
    hz   = hrzntl;
    while (nmNodes < ndIx)
    begin
      vr   = vr - 1;
      hz <<= 1;
      ndIx = (1 << vr) + (hz << vr + 1);
    end
    rlIx        = bases[ vr] + (hz << 1);
    fillSubtree = rlIx;
    if (0 < vr)
    begin
      twice  = hz << 1;
      nxPs   = 0 == hz || ! equal;
      nxVr   = vr - 1;
      rsltLw = fillSubtree( nxVr, twice    , nxPs);
      rsltHh = fillSubtree( nxVr, twice + 1, nxPs);
      nodes[ ndIx].ndType = hz == 0 ? SIDE : equal ? E_INTERIOR : N_INTERIOR;
      nodes[ ndIx].inLow  = rsltLw;
      nodes[ ndIx].inHigh = rsltHh;
      nodes[ ndIx].out    = rlIx;
    end
    else
    begin
      nodes[ ndIx].ndType = hz == 0 ? CORNER : equal ? E_LEAF : N_LEAF;
      nodes[ ndIx].inLow  = hz;
      nodes[ ndIx].out    = rlIx;
    end
  end
endfunction

initial
begin
  integer lvl;
  bases[ 0]   = 1;
  for (lvl = 1; lvl <= nmLevels; lvl = lvl + 1)
  begin
    bases[ lvl] = bases[ lvl - 1] + ((nmNodes + (1 << lvl - 1) >> lvl << 1) - 1);
  end
  fillSubtree( nmLevels - 1, 0, true);
end

for (ix = 1; ix <= nmNodes; ix = ix + 1)
begin
  localparam node     nd     = nodes[ ix];
  localparam integer  highEq = nd.inHigh - 1;
  localparam integer  inLow  = nd.inLow;
  case (nd.ndType)
    E_INTERIOR
  : begin
      nor2 pio( rships[ nd.out - 1], rships[ inLow - 1], rships[ highEq]);
      mplex pim( rships[ nd.out], rships[ highEq], rships[ nd.inHigh], rships[ inLow]);
    end
    N_INTERIOR
  : begin
      nand2 mia( rships[ nd.out - 1], rships[ inLow - 1], rships[ highEq]);
      mplex mim( rships[ nd.out], rships[ highEq], rships[ inLow], rships[ nd.inHigh]);
    end
    SIDE
  : mplex sm( rships[ nd.out], rships[ highEq], rships[ inLow], rships[ nd.inHigh]);
    E_LEAF
  : begin
      equ2 ple( rships[ nd.out], leftOp[ inLow], rightOp[ inLow]);
      assign rships[ nd.out - 1] = rightOp[ inLow];
    end
    N_LEAF
  : begin
      xor2 mlx( rships[ nd.out], leftOp[ inLow], rightOp[ inLow]);
      assign rships[ nd.out - 1] = rightOp[ inLow];
    end
    CORNER
  : begin
      nt1 cn( ntRght, rightOp[ inLow]);
      nor2 co( rships[ nd.out], leftOp[ inLow], ntRght);
    end
  endcase
end
assign lssThn = rships[ bases[ nmLevels - 1]];

endmodule

Re: How powerful is Verilog at using parameters to specify designs?
On Wednesday, September 23, 2020 at 4:45:55 PM UTC-6, Kevin Simonson wrote:
Quoted text here. Click to load it
eType;  
Quoted text here. Click to load it
IOR;  
Quoted text here. Click to load it
  
Quoted text here. Click to load it
 - 1);  
Quoted text here. Click to load it
inLow]);  
Quoted text here. Click to load it
nHigh]);  
Quoted text here. Click to load it
inHigh]);  
Quoted text here. Click to load it

I have a few comments:
- You probably don't want to instantiate primitives like xor2.  Maybe this  
is some idea you got from the Palnitkar book.  The old books are more geare
d toward circuit modeling, not circuit synthesis.  You want "RTL" code, not
 "structural" code (in which you instantiate gates instead of inferring the
m).  To xor something, use the xor operator:  result = a ^ b;  To NAND so
mething:  result = ~(a & b);

- You are using a lot of "tricky" code, like structures, which are from Ver
ilog-2009 (aka SystemVerilog).  Many synthesizers aren't going to like this
.  I don't know about Icarus, but since it's free, I'd be wary.  Synthesis  
tools are not great.  It's not like a C compiler, where as long as your C i
s legal, it's going to compile.  Your "initial" blocks, functions, and stru
ctures might not synthesize.

- You don't have the "generate" keyword before "for (ix ...".  That might w
ork in some tools, but I think it might be required by a strict interpretat
ion.  But you really shouldn't have a generate block anyway, because you re
ally want RTL code and no gate primitives.

Re: How powerful is Verilog at using parameters to specify designs?
Quoted text here. Click to load it

Going to break feedback down into parts...

A localparam without an assignment makes no sense, and is likely a
syntax error.   Did you mean for these to be variables instead of
localparams?

Regards,
Mark


Re: How powerful is Verilog at using parameters to specify designs?
Quoted text here. Click to load it

Briefly reading through the rest of the code, and how you're using/manipulating
"nodes" and "bases" - I think this is the root cause of your troubles.
Just try and make these variables instead of localparams.  I don't think
there's any reason why they must be localparams.

Regards,
Mark



Re: How powerful is Verilog at using parameters to specify designs?
Okay, I got rid of (struct) type (node) and now just have four arrays, (ndT
ypes), (inLows), (inHighs), and (outs). Then I used Icarus to simulate it,  
and only got one error message. Any idea how to get rid of that one error m
essage? As before, the next post I make will be of my "neilson.sv" Verilog  
file, and the post after that will be the results of my attempt to simulate
 it.

Re: How powerful is Verilog at using parameters to specify designs?
module neilson
  #( parameter nmBits = 1)
  ( output              lssThn
  , input [ nmBits-1:0] leftOp
  , input [ nmBits-1:0] rightOp);

typedef enum { CORNER, E_LEAF, N_LEAF, SIDE, E_INTERIOR, N_INTERIOR } nodeType;

localparam integer nmNodes  = 2 * nmBits - 1;
localparam integer nmLevels = $clog2( nmBits) + 1;
localparam integer nmRships = 2 * nmNodes - nmLevels;

nodeType ndTypes [  nmNodes:1];
integer  inLows  [  nmNodes:1];
integer  inHighs [ nmBits-1:1];
integer  outs    [  nmNodes:1];
integer  bases   [ nmLevels:0];
wire     rships  [ nmRships:1];
wire     ntRght;
genvar   ix;

function automatic integer fillSubtree ( input integer vrtcl
                                       , input integer hrzntl
                                       , input     bit equal);
    integer rsltLw;
    integer rsltHh;
    integer ndIx;
    integer rlIx;
    integer vr;
    integer hz;
    integer twice;
    integer nxVr;
    bit nxEq;
  begin
    ndIx = (1 << vrtcl) + (hrzntl << vrtcl + 1);
    vr   = vrtcl;
    hz   = hrzntl;
    while (nmNodes < ndIx)
    begin
      vr   = vr - 1;
      hz <<= 1;
      ndIx = (1 << vr) + (hz << vr + 1);
    end
    rlIx        = bases[ vr] + (hz << 1);
    fillSubtree = rlIx;
    if (0 < vr)
    begin
      twice               = hz << 1;
      nxEq                = 0 == hz || ! equal;
      nxVr                = vr - 1;
      rsltLw              = fillSubtree( nxVr, twice    , nxEq);
      rsltHh              = fillSubtree( nxVr, twice + 1, nxEq);
      ndTypes[ ndIx]      = hz == 0 ? SIDE : equal ? E_INTERIOR : N_INTERIOR;
      inLows[ ndIx]       = rsltLw;
      inHighs[ ndIx >> 1] = rsltHh;
      outs[ ndIx]         = rlIx;
    end
    else
    begin
      ndTypes[ ndIx] = hz == 0 ? CORNER : equal ? E_LEAF : N_LEAF;
      inLows[ ndIx]  = hz;
      outs[ ndIx]    = rlIx;
    end
  end
endfunction

initial
begin
  integer lvl;
  bases[ 0]   = 1;
  for (lvl = 1; lvl <= nmLevels; lvl = lvl + 1)
  begin
    bases[ lvl] = bases[ lvl - 1] + ((nmNodes + (1 << lvl - 1) >> lvl << 1) - 1);
  end
  fillSubtree( nmLevels - 1, 0, true);
end

generate
  for (ix = 1; ix <= nmNodes; ix = ix + 1)
  begin
    integer inLow  = inLows[ ix];
    integer inHigh = inHighs[ ix >> 1];
    integer out    = outs[ ix];
    case (ndTypes[ ix])
      E_INTERIOR
    : begin
        nor2 pio( rships[ out - 1], rships[ inLow - 1], rships[ inHigh - 1]);
        mplex pim( rships[ out], rships[ inHigh - 1], rships[ inHigh], rships[ inLow]);
      end
      N_INTERIOR
    : begin
        nand2 mia( rships[ out - 1], rships[ inLow - 1], rships[ inHigh - 1]);
        mplex mim( rships[ out], rships[ inHigh - 1], rships[ inLow], rships[ inHigh]);
      end
      SIDE
    : mplex sm( rships[ out], rships[ inHigh - 1], rships[ inLow], rships[ inHigh]);
      E_LEAF
    : begin
        equ2 ple( rships[ out], leftOp[ inLow], rightOp[ inLow]);
        assign rships[ out - 1] = rightOp[ inLow];
      end
      N_LEAF
    : begin
        xor2 mlx( rships[ out], leftOp[ inLow], rightOp[ inLow]);
        assign rships[ out - 1] = rightOp[ inLow];
      end
      CORNER
    : begin
        nt1 cn( ntRght, rightOp[ inLow]);
        nor2 co( rships[ out], leftOp[ inLow], ntRght);
      end
    endcase
  end
endgenerate
assign lssThn = rships[ bases[ nmLevels - 1]];

endmodule

Re: How powerful is Verilog at using parameters to specify designs?
Quoted text here. Click to load it

Your problem now is the generate case() statements.  Generate case()
statements agruments DO need to be parameters.   (I know I told you to
change it from a localparm to a variable - but I missed the generate case()  
usage) However, I still don't think you should make these parameters.

Like another poster indicated - you're coding at a very low level.
Instatiating "xor2" primitives and the like is quite low level.  Just
inline the expression using the verilog "^", "|", "&", etc operators.
Change your generate case() to a procedural case().

Another comment on style.  Your function should really not be both
sampling, and manipulating non-local variables.  Write your function
with a stable API that passes in the neccesary arguments, and returns
the neccesary outputs.  Manipulating global variables, makes the
function call pretty purposeless.  

There's no reason at all for it to be a function if it's
just manipulating global variables.  Just move it down to the procedural
block if you don't want to solidify the API.  Or put it in it's own
procedural block.  Change the function to:
always @*
begin
  //function contents that manipulate ndTypes[];  
end

Regards,
Mark


Re: How powerful is Verilog at using parameters to specify designs?
Gtwrek (Mark): "Like another poster indicated - you're coding at a very low
 level. Instatiating 'xor2' primitives and the like is quite low level. Jus
t inline the expression using the verilog '^', '|', '&', etc operators." Wh
at's wrong with writing Verilog at a low level? If someone has a feel for h
ow her/his design should work at a low level, is there something wrong with
 coding it at that level? If so, what is wrong with it?

"Change your generate case() to a procedural case()." What exactly is a pro
cedural case, and how can I find out about it?

Site Timeline