Draft preview. Lessons are being written and reviewed.

Digital Design / Module 3: Verilog that means hardware

17. Parameters and generate

Write one design that scales.

Lecture coming soon

See it move

The idea

Parameters make one design work at many sizes. They are fixed when the design is elaborated, before simulation or synthesis starts, so they aren't runtime variables.

module fifo #(parameter WIDTH = 8, parameter DEPTH = 16) (
  input  [WIDTH-1:0] din,
  output [WIDTH-1:0] dout
  // other ports
);
  localparam AW = $clog2(DEPTH);
  reg [WIDTH-1:0] mem [0:DEPTH-1];
  reg [AW:0] wptr, rptr;
endmodule

fifo #(.WIDTH(32), .DEPTH(64)) u_fifo (/* ports */);

$clog2 gives the address width: 6 for a depth of 64. Use localparam for values derived inside the module, so nobody can override them by mistake.

generate

A generate loop replicates hardware at elaboration. It runs before the circuit exists, and the result is just more hardware.

module add_n #(parameter N = 8)
  (input [N-1:0] a, b, output [N-1:0] s, output cout);
  wire [N:0] c;
  assign c[0] = 1'b0;
  assign cout = c[N];
  genvar i;
  generate
    for (i = 0; i < N; i = i + 1) begin : bit_slice
      full_adder fa(.a(a[i]), .b(b[i]), .cin(c[i]),
                    .s(s[i]), .cout(c[i+1]));
    end
  endgenerate
endmodule

Change the width in the animation above to see what a larger N does to the carry chain.

Loops in always blocks

A for loop inside an always block is also legal, and it is also not sequential at runtime. It unrolls into parallel logic, all of which settles within one clock cycle. A loop with a hundred iterations means a hundred copies of the logic.

Common mistake

Using a parameter to mean something a signal should mean. If you must change a value while the chip is running, it needs to be a register or an input port, because a parameter can't change after elaboration.

Key ideas

  • Parameters set widths and depths at elaboration.
  • $clog2 computes address widths.
  • generate replicates hardware; it is not a runtime loop.

Your exercise

Create a parameterised N-bit ripple adder with generate.

Find the bug

This code has one deliberate bug. Read the symptom, then click the line you think is wrong.

What goes wrong: Low additions are right, but the top bit of s stays stuck at x, and cout is x too.

Interview questions

coreWhat's the address width of a depth-64 memory?

basicWhat does $clog2 return for 16, 17 and 100?

basicWhat is the difference between parameter and localparam?

coreA module is instantiated twice, once with WIDTH = 8 and once with WIDTH = 32. How much hardware is that?

coreFor N = 4 and inputs 3 and 5, the sum reads z000 and cout reads z. What is wrong?

for (i = 0; i < N-1; i = i + 1) begin : bit_slice
  full_adder fa(.a(a[i]), .b(b[i]), .cin(c[i]),
                .s(s[i]), .cout(c[i+1]));
end

coreWhat hardware does this loop describe, and how wide must cnt be?

always @(*) begin
  cnt = 0;
  for (i = 0; i < 8; i = i + 1)
    cnt = cnt + d[i];
end

coreWhy give a generate loop's block a label such as bit_slice?

advancedA FIFO computes AW = $clog2(DEPTH) and declares reg [AW-1:0] addr;. What happens when DEPTH is 1?

advancedHow does this code let one module have either a registered or a combinational output?

generate
  if (REG_OUT) begin : g_reg
    always @(posedge clk) y <= d;
  end else begin : g_comb
    always @(*) y = d;
  end
endgenerate

Practise these 9 questions with animations