Reputation: 408
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
Upvotes: 1
Views: 367
Reputation: 12344
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
Upvotes: 1