FPGARelated.com
Forums

How powerful is Verilog at using parameters to specify designs?

Started by Kevin Simonson September 21, 2020
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 input and produces a parameterless verion of (xyz) for that version of (nmBits). 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 parameters to do the same thing?
On Monday, September 21, 2020 at 4:23:25 PM UTC-6, Kevin Simonson wrote:
> I have a design in mind that would fit in this skeleton:=20 > [code]=20 > module xyz ( result, leftOp, rightOp);=20 > parameter integer nmBits =3D 1;=20 > localparam integer highBit =3D nmBits - 1;=20 > output result;=20 > input [ highBit:0] leftOp;=20 > input [ highBit:0] rightOp;=20 >=20 > // ...=20 >=20 > endmodule=20 > [/code]=20 > The way (xyz) is designed, this module would work differently for differe=
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=3D8) // 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
In article <1bf811ef-9787-46c9-ac5a-81159e8560e0n@googlegroups.com>,
Kevin Simonson  <kvnsmnsn@hotmail.com> wrote:
>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 input and produces a parameterless verion of (xyz) for that version of (nmBits). 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 parameters to do the same thing?
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
On Monday, September 21, 2020 at 6:31:29 PM UTC-7, Kevin Neilson wrote:

> Your code seems fine, although your port list style is Verilog 1995. A more modern style would be: > > module xyz > #(parameter WIDTH=8) // Width of input operators (default 8 if not overwritten at instantiation) > (input [WIDTH-1:0] leftOp, > input [WIDTH-1:0] rightOp, > output result); > ... > endmodule
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?
Gtwrek (Mark?): "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)." Okay. As modified by Kevin Neilson my design has one output bit (result) and two input parameters (leftOp) and (rightOp), each of which has (nmBits) bits. At this point I have three (localparams) integers, 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 } nodeType;
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 few not gates. The design does a lot of connecting elements of (rships) to those multiplexers, nor gates, and not gates, and the precise way it's done requires 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 (rships) to connect to which gates, and then I iterate through (nodes) to actually connect all the wires. Is that an approach
that looks like it should work? Should I be able to do that in System Verilog?
In article <450660fa-8852-4f23-b82a-7d762a760ee0n@googlegroups.com>,
Kevin Simonson  <kvnsmnsn@hotmail.com> wrote:
>Gtwrek (Mark?): "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)." >Okay. As modified by Kevin Neilson my design has one output bit (result) and two input parameters (leftOp) and (rightOp), each of which has (nmBits) bits. At this point I >have three (localparams) integers, 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 } nodeType; >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 few not gates. The design does a lot of connecting elements of (rships) to those multiplexers, nor >gates, and not gates, and the precise way it's done requires 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 (rships) to connect to which gates, and then I iterate through (nodes) to actually connect all the >wires. Is that an approach >that looks like it should work? Should I be able to do that in System Verilog?
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
On Tuesday, September 22, 2020 at 4:16:36 PM UTC-6, Kevin Simonson wrote:
> On Monday, September 21, 2020 at 6:31:29 PM UTC-7, Kevin Neilson wrote: > > > Your code seems fine, although your port list style is Verilog 1995. A more modern style would be: > > > > module xyz > > #(parameter WIDTH=8) // Width of input operators (default 8 if not overwritten at instantiation) > > (input [WIDTH-1:0] leftOp, > > input [WIDTH-1:0] rightOp, > > output result); > > ... > > endmodule > 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?
That's almost right. I think you need to leave out the extra "parameter" keyword: 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 parameters, but I think it's allowed. You should also avoid the name "queue" since that is a keyword in some versions of Verilog. 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 streamlined 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.
In article <0c595586-edfc-41bb-9ab4-3334788e53e1n@googlegroups.com>,
Kevin Neilson  <kevin.neilson@xilinx.com> wrote:
>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 parameters, but I think it's allowed. You should also avoid the name "queue" since that is >a keyword in some versions of Verilog.
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
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.
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?