Altifigence Academy

19 / 37 · Concept

parameter, localparam, $clog2 and boundary configurations

Separate run-time inputs from static configuration and design counter widths and termination for N=1 and non-power-of-two values.

Parameters are fixed when the circuit is elaborated

A port is an input that can change while a circuit operates. A parameter configures an instance before simulation or synthesis builds its structure. localparam names a derived constant that callers do not override. None of these is a software variable you change every cycle.

For a modulo-N counter, valid states are 0 through N−1. Storage width must represent those states. For N≥2, $clog2(N) gives the required unsigned width. N=1 needs special treatment because $clog2(1) is 0; use a minimum storage width of one bit. N=0 is outside this module’s contract.

Define completion by an enabled edge

This module produces a registered pulse when an enabled edge wraps the old count from N−1 to 0. Disabled edges hold count and clear pulse. Reset clears both and wins over enable. N must be a positive integer within the implementation’s supported integer range.

SystemVerilog
module modulo_tick #(
  parameter integer N = 5,
  parameter integer CW = (N <= 1) ? 1 : $clog2(N)
) (
  input  logic          clk, rst, enable,
  output logic [CW-1:0] count,
  output logic          pulse
);
  localparam logic [CW-1:0] LAST = CW'(N-1);
  always_ff @(posedge clk) begin
    if (rst) begin
      count <= '0;
      pulse <= 1'b0;
    end else begin
      pulse <= 1'b0;
      if (enable) begin
        if (count >= LAST) begin
          count <= '0;
          pulse <= 1'b1;
        end else count <= count + 1'b1;
      end
    end
  end
endmodule

CW is exposed because it appears in the port list. Leave its default unchanged, or supply a value at least max(1,$clog2(N)). A smaller override truncates LAST and violates the contract. A larger override adds unused encodings; this implementation recovers from them on an enabled edge because the terminal comparison uses >=.

CW'(N-1) is a constant size cast. It documents the width at the boundary. It does not make an invalid N or CW valid. Validate configuration before elaboration; a run-time assertion cannot repair a malformed declaration.

Test a configuration matrix

NDefault CWCount after enabled edges following resetPulse after those edges
110, 0, 01, 1, 1
211, 0, 10, 1, 0
531, 2, 3, 4, 00, 0, 0, 0, 1
831, 2, 3, 4, 5, 6, 7, 00, 0, 0, 0, 0, 0, 0, 1

N=1 is not a two-state counter just because it has one storage bit. An N=5 counter must not visit 5,6,7 during normal operation. A power-of-two test alone will not expose a missing explicit wrap comparison.

Practice · Verify behavior and reject bad configuration

Build separate instances for N=1,2,5,8. Run at least two full periods, insert disabled edges at the terminal count, and assert reset at the same time as enable. Check each intermediate count and pulse, not just the final state.

Write an integer reference: on reset, count=0 and pulse=0; otherwise pulse=0, and on enable increment modulo N while detecting wrap. Keep it independent of the DUT’s CW and encoded state. Also test the configuration validator’s rejection of N=0 and undersized CW.

A parameterized module is reusable only over the parameter range you specify and verify. Passing one default instance does not establish every possible width or count limit.

Your choice applies to this browser. Change it any time using the footer.