Search code examples
verilogcountermodelsimdigitalpreset

Strange behavior when running a piece of verilog code on modelsim


I have a problem when running this piece of Verilog code in ModelSim. I have built a circuit that is supposed to count the milliseconds. The circuit(module numarator) consists of two blocks: a circuit which receives a 5MHz frequency clock signal(module counter), and is supposed to produce a high-logic every 5000cct passed(the equivalent of 1ms, thus producing output high after every millisecond), and another circuit which receives the same clock signal and counts the number of elapsed milliseconds(module preset). Besides, the second circuit receives a load signal(when active, the load produces a low-logic). My question is why the output of the circuit is incremented twice after each millisecond elapsed(and not only once, as expected). I posted the Verilog source code and the diagram ( diagram ).

`timescale 1ns/1ns
//1ms pulse generator
module counter(
    input clk,rst,en,
    output reg out
);

reg [12:0] st,st_nxt;

always @(posedge clk) begin
    if(rst) st <= 13'd0;//requires rst
    else st <= st_nxt;//for initialization
end

always @ * begin
    out = 1'b0;
    if(en) begin
      st_nxt = st + 1;
      if(st == 13'd4999) begin
          st_nxt = 13'd0;
          out = 1'b1;
      end
    end
end

endmodule
//tb for the first circuit
module counter_tb(
    output reg clk,rst,en,
    output out
);

counter cut(.clk(clk),.rst(rst),.en(en),.out(out));

initial en = 1'd1;

initial begin
    rst = 1'd1;
    #50 rst = 1'd0;
end

initial begin
    clk = 1'd1;
    forever
    #100 clk = ~clk;
end

endmodule
//preset counter
module preset(
    input clk,rst,en,ld,
    output reg [12:0] out
);

reg [12:0] st,st_nxt;

always @(posedge clk) begin
    if(rst) st <= 13'd0;//requires rst
    else st <= st_nxt;//for initialization
end

always @ * begin
    if(ld) st_nxt = 13'd0;//the circuit is designed
    else if(en) st_nxt = st + 1;//to load only 0
    out = st;
end

endmodule
//tb preset counter
module preset_tb(
    output reg clk,rst,en,ld,
    output [12:0]out 
);

preset cut(.clk(clk),.rst(rst),.en(en),.ld(ld),.out(out));

initial begin
    rst = 1'd1;
    #50 rst = 1'd0;
end

initial begin
    clk = 1'd1;
    forever
    #100 clk = ~clk;
end

initial begin
    en = 1'd1;
    #1000 en = 1'd0;
    #200 en = 1'd1;
end

initial begin
    ld = 1'd0;
    #2000 ld = 1'd1;
    #400 ld = 1'd0;
end

endmodule
//ms couonter
module numarator(
    input clk,rst,en,ld,
    output [12:0] out
);

wire f;

counter i0(.clk(clk),.rst(rst),.en(en),.out(f));
preset i1(.clk(clk),.rst(rst),.en(f),.ld(ld),.out(out));

endmodule
//tb ms counter
module numarator_tb(
    output reg clk,rst,en,ld,
    output [12:0] out
);

numarator cut(.clk(clk),.rst(rst),.en(en),.ld(ld),.out(out));

initial begin
    rst = 1'd1;
    #50 rst = 1'd0;
end

initial begin
    clk = 1'd1;
    forever
    #100 clk = ~clk;
end

initial begin
    en = 1'd1;
    #2000000 en = 1'd0;
    #1000000 en = 1'd1;
end

initial begin
    ld = 1'd0;
    #4000000 ld = 1'd1;
    #1000000 ld = 1'd0;
end

endmodule

Solution

  • I believe that this is caused by the race between flop (clk) and a latch(en/st_nxt). I do observe it in the counter module.

    -- vcs tends to evaluate 'en' before the clock edge and as a result assigns '1' (st + 1) to the st_nxt.

    -- questa does it in a different order. As a result after first 'en' change the values of the 'st' become shifted by '1'.

    Something similar happens in numerator. The latch action there is even more complicated. Also, due to the races 'out' signal of the counter is glitchy. There is a glitch at 'st' 4999 in both, vcs and questa. So, the latch in the numerator will change the state at this glitch 'unexpectedly'.

    The way to avoid this situation is to use pure flop-based logic, do not use latches. Or at least do not mix them with flops this way.

    I tried to re-write your model to get rid of latches. This way and it seems to work better:

    module counter(
               input      clk,rst,en,
               output reg out
               );
       reg [12:0]         st;
    
       always @(posedge clk) begin
         if(rst) begin
            st <= 13'd0;//requires rst
            out <= 0;
         end
         else if (en) begin
            if (st == 13'd4999) begin
                st <= 13'd0;
                out <= 1'b1;
            end
            else
               st <= st + 1;     
          end
       end
    endmodule // counter
    
    //preset counter
    module preset(
              input         clk,rst,en,ld,
              output reg [12:0] out
              );
    
       reg [12:0]           st;
    
       always @(posedge clk) begin
          if(rst)
            st <= 13'd0;//requires rst
          else if (ld)
            st = 13'd0;
          else if (en)  
            st <= st + 1;//for initialization
       end
    
       always @ * begin
          out = st;
       end
    
    endmodule // preset