parameter in Verilog Modules

Parameters in a Simple Adder

In this assignment, you will complete a design that adds four 4-bit numbers in an adder tree configuration:

sum = (a+b) + (c+d)

To compute this sum, we need three additions. Each addition is performed by a module named simple_adder.

Open the file simple_adder.v and examine it line-by-line. In the module declaration, a parameter is declared:

module simple_adder 
  #(                  // Use #( ) to declare parameters before I/O signals
    parameter W=2     // `parameter` keyword, name `W`, default value 2
    )

The parameter W sets the bit-width of the inputs. The default width is set at 2; we will need to override the default value when instantiating the design.

What are parameters?

A parameter is fundamentally different from a signal or variable. In essence, the parameter allows us to modify the module design on a per-instance basis. To see why we need parameters in this design, consider the bit-width of a sum:

We have to allow an extra bit for the carry overflow. For instance, if a=12 and b=7, then the sum is a+b=19, which has binary value 11001. So we need 5 bits for the sum (a+b).

Similarly, we need 5 bits for the sum (c+d). For the next addition, we need to change W to 5 bits in order to add (a+b) with (c+d), and the final sum output has 6 bits. To accomodate the changing bit- width, we could design separate 4-bit and 5-bit adder modules, but it is much more efficient to create a single parameterized module.

In the file src/top.v, notice how the modules are instantiated:

   simple_adder #(.W(4)) SA1
     (
      .clk(clk),
      .a(a),
      .b(b),
      .sum(a_plus_b)
      );

The W parameter for instance SA1 is specified by the syntax #(.W(4)). Without this specification, W would default to 2. Using this syntax, we can customize the bit-width for every single instance of simple_adder.

Assigned Tasks

Complete the top design

Open the file src/top.v and examine the instance definitions for SA1 and SA2. Notice the syntax for overriding the W parameter. Then complete the instance definition for SA3.

Simulate the design

Next, open the file src/testbench.v and note how it has been changed from the previous assignments. We have four 4-bit reg signals:

   reg [3:0] a;  // by the testbench
   reg [3:0] b;
   reg [3:0] c;
   reg [3:0] d;

To test all combinations of these inputs, we would need to run 2^16 test patterns, which amounts to 65,536 unique cases. In our previous testbench, we coded each test case individually – you probably don’t want to individually code over 65k else if cases. Also, we probably don’t need to verify every single case exhaustively. An alternative method is random pattern testing, which is employed in the new testbench:

   // CREATE STIMULI:
   always @(posedge clk) begin
      a <= $random();
      b <= $random();
      c <= $random();
      d <= $random();
   end

Here the $random() function is used to generate test cases. This is a very useful and common strategy for verifying complex prototypes. Be warned, however, that $random is not a synthesizable function. You can use it in testbenches but not in physical designs.

Also notice that both $write and $fwrite lines are used, so the test results will be printed to the console and also written to a text file.

Run make to simulate your design. The first few output lines should look like this:

clk:             0  a:    0(0000)   b:    0(0000)   c:    0(0000)   d:    0(0000)   sum:  x(xxxxxx)
clk:             1  a:    4(0100)   b:    1(0001)   c:    9(1001)   d:    3(0011)   sum:  x(xxxxxx)
clk:             2  a:   13(1101)   b:   13(1101)   c:    5(0101)   d:    2(0010)   sum:  0(000000)
clk:             3  a:    1(0001)   b:   13(1101)   c:    6(0110)   d:   13(1101)   sum: 17(010001)
clk:             4  a:   13(1101)   b:   12(1100)   c:    9(1001)   d:    6(0110)   sum: 33(100001)

Notice the two-cycle delay. Initially, the sum output is undefined, so it shows as x. After two cycles, the initially zero a,b,c,d values propagate to the sum output, which is zero (as expected). In cycle 3, we see the sum of a,b,c,d values from cycle 1: 4 + 1 + 9 + 3 = 17, and so on.

Implement the Design

Open the file simple_adder.xdc and observe that the a, b, c, and d inputs are mapped to the sliding switches in groups of 4. The sum output is mapped to the lower 6 LEDs.

Next, open the file build.tcl and notice differences compared to the previous assignments. The read_xdc command now points to simple_adder.xdc, and the synth_design command now declares top as the top-module name:

synth_design -top top -part xc7a35tcpg236-1

The bitstream is also renamed to simple_adder.bit:

write_bitstream -force simple_adder.bit

Type make implement to run the implementation. After it completes, open the timing and utilization report files. Verify that the WNS is positive, and take note of the value. In the utilization file, note that the adder logic is implemented entirely with slice LUTs, no DSP modules are used.

Program and test the design

Now use the Vivado Hardware Manager to program your design onto the Basys3 board. Test the three cases listed below, and take photos of each case. Place the photos in the same directory as the bitstream, and name them case1.jpg, case2.jpg, and case3.jpg (a different image type, like .png, is acceptable).

Cases to test:

  1. 1 + 2 + 3 + 4 = 10
  2. 3 + 5 + 7 + 11 = 26
  3. 15 + 15 + 15 + 15 = 60

Turn in your work

To turn in your work, run these commands:

git add test_result.txt post_synth.v *.rpt
git add case*
git add *.bit
git commit . -m "Completed"
git push origin main

Then indicate on Canvas that your work is done.