A Simple Master Reset

from FPGA Resources by GateForge Consulting Ltd.

An FPGA contains a built-in reset network for all the logic registers, which takes effect during configuration. This reset simplifies design and improves Place-And-Route results since you don't have to spend routing resources on a global reset.

However, some hard blocks and some third-party IP blocks may not take full advantage of this configuration reset, and require a separate reset before they begin to function properly, or even simulate properly (due to X propagation).

A simple way to provide this post-configuration reset creates a delayed version of the built-in reset via a small counter. Then you can instantiate one reset module per block that needs it, each with a parameter giving the number of clock cycles to hold the block in reset after configuration. There is no global reset signal, and each reset releases in a fixed sequence as desired.


But given a cycle count parameter, we need a way to figure out how many bits we need to count to that many steps (starting from zero). Verilog-2001 does not have a built-in logarithm function, but we can write one:

// Ceiling of logarithm, base 2
// Taken from Verilog-2001 standard example
// Since $clog2() doesn't exist prior to Verilog-2005 (and thus, SystemVerilog)

function integer clog2;
    input integer value;
    begin
        value = value - 1;
        for (clog2 = 0; value > 0; clog2 = clog2 + 1) begin
            value = value >> 1;
        end
    end
endfunction

Note that this function returns the number of bits needed to hold that many steps, not the absolute value. For example, given 16, it returns 4 (bits), not the expected 5 bits needed to store the value of 16 in binary.

And given a logarithm, we can create a parameterized counter which starts at zero (due to the configuration reset) and counts up until completion, at which point a reset output is deasserted:

// Master reset for IP that needs a reset to enter non-X initial state.
// Simply a delayed release, synchronous to the system clock, after the FPGA
// comes out of configuration reset.

`default_nettype none

module master_reset
#(
    parameter DELAY_CYCLE_COUNT = 0
)
(
    input   wire    clock,
    output  reg     reset
);

    initial begin
        reset = 0;
    end

    `include "clog2_function.vh"

    localparam                      COUNTER_WIDTH   = clog2(DELAY_CYCLE_COUNT);
    localparam                      COUNTER_ZERO    = {COUNTER_WIDTH{1'b0}};
    localparam                      COUNTER_ONE     = {{COUNTER_WIDTH-1{1'b0}},1'b1};
    localparam [COUNTER_WIDTH-1:0]  COUNTER_FINAL   = DELAY_CYCLE_COUNT-1;

    reg [COUNTER_WIDTH-1:0] count = COUNTER_ZERO;

    // Count N-1 times, and reset goes low one cycle later.
    // This keeps everything pipelined, and means reset stays high
    // for DELAY_CYCLE_COUNT cycles exactly.

    always @(posedge clock) begin
        count <= (count != COUNTER_FINAL) ? count + COUNTER_ONE : count;
        reset <= (count != COUNTER_FINAL);
    end

endmodule

Notice the use of localparams and concatenations to create the values we need with exactly the right number of bits, which means the synthesis tool will not find width mismatches (and thus not annoy us with yet another warning), as well as setting initial values for all registers to take advantage of the configuration reset as well as simulate correctly.

Furthermore, the main logic use a ternary conditional operator ?:, which behaves correctly under simulation should something end up with an X or Z value, rather than an if/else statement.

In hindsight, we could have taken the logarithm of DELAY_CYCLE_COUNT-1 instead, which would have reduced the counter width by one bit if the DELAY_CYCLE_COUNT was a power-of-two plus one, but that's not a significant optimization. Better to keep the code clear.


fpgacpu.ca