运行 一段 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
我在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