Search code examples
verilog

Timing problem between posedge and negedge in a FSM


I have the following module:


module test1(
    input                               en,
    input                               clk,
    input                               burst,
    output                              out_clk
);

reg [1:0] cpt = 0;

localparam burst_beg = 0;
localparam burst_end = 1;
localparam burst_init = 2;

reg [0:1] state = burst_init;

assign out_clk = (cpt <= 1) &&  state != burst_init ? clk : 1'b0;

always @(negedge clk) begin
    if (en) begin
        case (state)
            burst_init: begin
                if (burst) begin
                    state <= burst_end;
                    cpt <= 2;
                end
            end
            burst_beg: begin
                if (!burst) begin
                    state <= burst_end;
                    cpt <= 2;
                end
                else if (cpt == 1) begin
                    cpt <= 2;
                end
                else if (cpt != 2)begin
                    cpt <= 1;
                end
            end 
            burst_end: begin
                if (burst) begin
                    state <= burst_beg; 
                    cpt <= 0;
                end
            end
            default: ;
        endcase
    end
end
endmodule

Its functionality is pretty simple, it lets the clk signal pass only during 2 ticks, after what it just sends a zero on the out_clk wire.

I also have a very simple module:

module test2 (
    input clk,
    output reg [0:3] cpt
);
    initial begin
    cpt = 0;
    end
    always @(posedge clk) begin
        cpt <= cpt + 1;
    end
endmodule

A simple counter that increments on every clock posedge

In sum this gives us the following top module:

module test (
    input clk,
    input en,
    input burst,
    output [0:3] cpt
);

test1 t1(
    .en(en),
    .clk(clk),
    .burst(burst)
);

test2 t2 (
    .clk(t1.out_clk),
    .cpt(cpt)
);

endmodule

Once enabled, on every burst the cpt should normally increment by 2, since only 2 clock cycles of the clk are allowed through to the out_clk. Which is what happens with the current code, here is a GtkWave screenshot to better visualise. GtkWave screen

However here is what happends when I change the negedge in the module test1 to posedge. gtkWave screen bad

Why does my counter increment once more? I do not seem to understand how that is possible since there have only been 2 clock cycles on the out_clk.

This question does seem to give a clue: Timing of Writing at clock edge

Thanks in advance!


Solution

  • This is only possible if clk_out has a glitch. There might be a way to see it on your trace, but in any case it results from using 'clk' in the assign statement. The following scenario could have happened:

    1. clk goes high
    2. cpt and state still have old values
    3. clk_out goes high (because cpt and state are still old)
    4. at posedge cpt and state are evaluated to new values
    5. clk_out goes down in the same simulation tick (because it is shut down by new values of cpt and state).

    As a result you have a glitch not visible in a simple trace. It could be detected by alwayse @* $display... or by a special mode of the tracer (if exists). But nevertheless it produces a clock edge which triggers your counter.

    The way to work around this issue is to make sure that the gating signals are calculated before the clock edge, or make a delay in calculation of out_clk. In your original code you calculated those signal at the negedge, way before the posedge of the clk.