运行 一段 modelsim 上的 verilog 代码时的奇怪行为

Strange behavior when running a piece of verilog code on modelsim

我在ModelSim中运行这段Verilog代码时遇到了问题。我已经建立了一个应该计算毫秒的电路。该电路(模块分子器)由两个块组成:一个接收 5MHz 频率时钟信号的电路(模块计数器),并且应该每通过 5000cct 产生一个高逻辑(相当于 1ms,因此每毫秒产生输出高电平),另一个电路接收相同的时钟信号并计算经过的毫秒数(模块预设)。此外,第二个电路接收负载信号(当有效时,负载产生低逻辑)。我的问题是为什么电路的输出在每毫秒过去后增加两次(而不是像预期的那样只增加一次)。我发布了 Verilog 源代码和图表 (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

我认为这是由触发器 (clk) 和锁存器 (en/st_nxt) 之间的竞争引起的。我确实在计数器模块中观察到了它。

-- vcs 倾向于在时钟边沿之前评估 'en',结果将 '1' (st + 1) 分配给 st_nxt.

-- 问题以不同的顺序进行。结果,在第一次 'en' 更改后,'st' 的值移动了“1”。

分子中发生了类似的事情。那里的闩锁动作更加复杂。此外,由于 races 'out' 计数器的信号出现故障。 vcs 和 questa 中的 'st' 4999 都有一个故障。因此,分子中的锁存器将在此故障 'unexpectedly'.

处改变状态

避免这种情况的方法是使用纯基于触发器的逻辑,不要使用锁存器。或者至少不要以这种方式将它们与人字拖混在一起。

我试图重新编写您的模型以摆脱闩锁。这种方式似乎效果更好:

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