I encountered an unusual behavior while simulating flip-flops in Verilog using Vivado.
Take, for instance, a four-bit up counter where I used an RS flip-flop for the most significant bit (Q[3]
).
There are various ways to model this RS flip-flop. For example, using a behavioral model makes the code quite straightforward like so:
// version 1.
module RS_FF(input CLK, R, S, output reg Q);
always @ (posedge CLK)
Q <= S | (Q & ~R);
endmodule
Likewise, it can also be modelled in the following way:
// version 2.
module RS_FF(input CLK, R, S, output reg Q);
initial Q = 1'b0;
always @ (posedge CLK)
begin
if (~R && S)
Q <= 1'b1;
else if (R && ~S)
Q <= 1'b0;
if (R && S)
Q <= 1'b0;
end
endmodule
In the first version, however, the simulated output is incorrect; notice how it doesn't reset to 0 after 0xF.
But the second version works correctly:
My question is, what is the problem with the version 1 of the RS flip-flop code?
Four bits up counter code:
module Four_bits_Up_Counter(input CLK, R, output [3:0] Q);
wire d0;
wire t1;
wire j2, k2;
wire r3, s3;
D_FF D0 (.D(d0), .CLK(CLK), .Q(Q[0]));
T_FF T1 (.T(t1), .CLK(CLK), .Q(Q[1]));
JK_FF JK2 (.J(j2), .K(k2), .CLK(CLK), .Q(Q[2]));
RS_FF RS3 (.R(r3), .S(s3), .CLK(CLK), .Q(Q[3]));
assign d0 = (~R) & (~Q[0]);
assign t1 = (R & Q[1]) | (~R & Q[0]);
assign j2 = (~R & Q[1] & Q[0]);
assign k2 = (R | (Q[1] & Q[0]));
assign r3 = R | (Q[3] & Q[2] & Q[1] & Q[0]);
assign s3 = (~R & Q[2] & Q[1] & Q[0]);
endmodule
module D_FF(input CLK, D, output reg Q);
always @ (posedge CLK)
Q <= D;
endmodule
module T_FF(input CLK, T, output reg Q);
// version 1 works with both up and down counter
initial Q = 1'b0;
always @ (posedge CLK)
begin
if (T)
Q <= ~Q;
end
endmodule
module JK_FF(input CLK, J, K, output reg Q);
initial Q = 1'b0;
always @ (posedge CLK)
begin
if (J && ~K)
Q <= 1'b1;
else if (~J && K)
Q <= 1'b0;
else if (J && K)
Q <= ~Q;
end
endmodule
//module RS_FF(input CLK, R, S, output Q);
module RS_FF(input CLK, R, S, output reg Q);
//version 1, behaivour model
// does not work for up counter
// works for down counter
// always @ (posedge CLK)
// Q <= S | (Q & ~R);
// version 2, behaivour model
// works for up and down counter
initial Q = 1'b0;
always @ (posedge CLK)
begin
if (~R && S)
Q <= 1'b1;
else if (R && ~S)
Q <= 1'b0;
if (R && S)
Q <= 1'b0;
end
endmodule
module clock_gen (
input enable,
output reg clk
);
parameter integer FREQ = 100000; // Frequency in kHz
parameter integer PHASE = 0; // Phase in degrees
parameter integer DUTY = 50; // Duty cycle in percentage
// Real values must be calculated in procedural blocks
real clk_pd, clk_on, clk_off, quarter, start_dly;
// Start clock signal controlled by enable
reg start_clk;
initial begin
// Calculate the timings based on the parameter values
clk_pd = 1.0 / (FREQ * 1e3) * 1e9; // Clock period in ns
clk_on = DUTY / 100.0 * clk_pd; // Clock high duration
clk_off = (100.0 - DUTY) / 100.0 * clk_pd; // Clock low duration
quarter = clk_pd / 4; // Quarter period for phase
start_dly = quarter * PHASE / 90; // Start delay for phase
// Display calculated values
$display("FREQ = %0d kHz", FREQ);
$display("PHASE = %0d deg", PHASE);
$display("DUTY = %0d %%", DUTY);
$display("PERIOD = %0.3f ns", clk_pd);
$display("CLK_ON = %0.3f ns", clk_on);
$display("CLK_OFF = %0.3f ns", clk_off);
$display("QUARTER = %0.3f ns", quarter);
$display("START_DLY = %0.3f ns", start_dly);
end
// Initialize the clock outputs to zero
initial begin
clk = 0;
start_clk = 0;
end
always @ (posedge enable or negedge enable) begin
if (enable) begin
#(start_dly) start_clk = 1;
end else begin
#(start_dly) start_clk = 0;
end
end
// Achieve duty cycle by a skewed clock on/off time and let this
// run as long as the clocks are turned on.
always @(posedge start_clk) begin
if (start_clk) begin
clk = 1;
while (start_clk) begin
#(clk_on) clk = 0;
#(clk_off) clk = 1;
end
clk = 0;
end
end
endmodule
Test bench code:
`timescale 1ns / 1ps
module Four_bits_Up_Counter_TB();
//reg CLK;
reg RESET;
wire [3:0] Q;
reg en;
wire CLK;
clock_gen clk(.enable(en), .clk(CLK));
Four_bits_Up_Counter TB(.CLK(CLK), .R(RESET), .Q(Q));
// initial
// CLK = 0;
// always
// #5 CLK = ~CLK;
initial
begin
en = 1;
RESET = 0;
#20;
RESET = 1;
#10;
RESET = 0;
#297;
RESET = 1;
#100;
RESET = 0;
#100;
$finish;
end
endmodule