Reputation: 49
I fairly new to Verilog and learning the ropes. I have some code which generates an 8 bit up-counter (module counter.v), which is then called by a top module (top_module.v). There is a simulation test fixture (test_fixture.v) which calls the top module for testing.
I was trying to define the width of the counter using a parameter (parameter COUNTER_WIDTH) and was having difficulty doing so. A colleague fixed the code for me and it does now indeed work, but I want to understand a few things so I can understand what is actually going on.
Here is the code for the counter module:
module counter
#(parameter COUNTER_WIDTH = 8)
(
input wire CLK,
input wire RST,
input wire CE,
output reg[COUNTER_WIDTH-1:0] out = {COUNTER_WIDTH{1'b0}}
);
always @(posedge CLK) begin
if (RST == 1) begin
out <= {COUNTER_WIDTH{1'b0}};
end else begin
if (CE == 1) begin
out <= out + 1'b1;
end
end
end
endmodule
The top module:
module top_module
#(parameter COUNTER_WIDTH = 8)
(
input wire CLK,
input wire CE,
input wire RST,
output wire[COUNTER_WIDTH-1:0] out
);
counter #(
.COUNTER_WIDTH(COUNTER_WIDTH)
)
counter_inst(
.CLK(CLK),
.RST(RST),
.CE(CE),
.out(out)
);
endmodule
And the test fixture:
module test_fixture();
parameter COUNTER_WIDTH = 8;
// inputs
reg CLK = 0;
reg RST = 0;
reg CE = 0;
// outputs
wire [COUNTER_WIDTH-1:0] Q;
// instance of top module to be tested
top_module #(
.COUNTER_WIDTH(COUNTER_WIDTH)
)
test_inst(
.CLK(CLK),
.RST(RST),
.CE(CE),
.out(Q)
);
endmodule
I think I'm fine with the counter module, but have a question about what is going on in top module/test fixture:
Thanks in advance for your help!
Upvotes: 4
Views: 10013
Reputation: 14007
Think of a parameter as a special kind of constant input whose value is fixed at compile time. Originally, in Verilog, parameters were constants that could be overridden from outside a module (using the now deprecated defparam
statement). So, a Verilog parameter had two roles:
Then, in the 2001 version of the standard (IEEE 1364-2001), the syntax you're using below was introduced (the so-called "ANSI style"). Also IEEE 1364-2001 introduced localparam
s (which fulfill the first of these two roles because they cannot be overridden from outside), so leaving parameters to handle the second of these two roles (the customisation). With the ANSI-style syntax, you override a parameter when you instantiate a module. You can associate the parameter with any static value, for example a parameter of the parent module, a localparam
, a genvar
or a literal (a hard-coded value in your code).
Because of this historical dual-role, in Verilog you must give a parameter a default value, even if it makes no sense. (And this restriction is lifted in SystemVerilog.)
Does giving the parameters different names and default values help your understanding?
// the default value
// |
module counter // V
#(parameter COUNTER_WIDTH = 1)
(
input wire CLK,
input wire RST,
input wire CE,
output reg[COUNTER_WIDTH-1:0] out = {COUNTER_WIDTH{1'b0}}
);
always @(posedge CLK) begin
if (RST == 1) begin
out <= {COUNTER_WIDTH{1'b0}};
end else begin
if (CE == 1) begin
out <= out + 1'b1;
end
end
end
endmodule
Then, in top module, let's give the parameter a different name:
// the default value
// |
module top_module // V
#(parameter COUNTER_NUM_BITS = 2)
(
input wire CLK,
input wire CE,
input wire RST,
output wire[COUNTER_NUM_BITS-1:0] out
);
counter #(
.COUNTER_WIDTH(COUNTER_NUM_BITS)
)
counter_inst(
.CLK(CLK),
.RST(RST),
.CE(CE),
.out(out)
);
endmodule
And again in the test fixture:
module test_fixture();
localparam COUNTER_SIZE = 8; // This is not overridden from outside, so using
// a localparam would be better here.
// (A localparam being a kind of parameter that
// cannot be overridden from outside. Normal
// languages would call it a constant.)
// inputs
reg CLK = 0;
reg RST = 0;
reg CE = 0;
// outputs
wire [COUNTER_SIZE-1:0] Q;
// instance of top module to be tested
top_module #(
.COUNTER_NUM_BITS(COUNTER_SIZE)
)
test_inst(
.CLK(CLK),
.RST(RST),
.CE(CE),
.out(Q)
);
endmodule
Upvotes: 5
Reputation: 6269
why do we have to declare the parameter within a module and connect it to a parameter within another module?
Because, you don't HAVE to give a parameter a value when you instance the module. In which case the tools need to have at least some value to work with.
So where you do:
counter #(
.COUNTER_WIDTH(COUNTER_WIDTH)
)
counter_inst(
.CLK(CLK),
.RST(RST),
.CE(CE),
.out(out)
);
You may also use:
counter
counter_inst(
.CLK(CLK),
.RST(RST),
.CE(CE),
.out(out)
);
In which case the default value is used.
Upvotes: 1