1
\$\begingroup\$

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.

RS flip-flop version 1 produces incorrect output

But the second version works correctly:

RS flip-flop version 2 produces correct output

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
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

The behavior of version 1 is not the same as the behavior of version 2.

For version 1, Q[3] stays high on the cycle after Q is 0xF because of this term:

(Q & ~R)

Q is 1 from the previous cycle. R is 0 in the current cycle. So 1 & ~0 is evaluated as 1 & 1 which results in 1. This is why Q[3] remains 1.

If you want a simpler version which behaves like your version 2, consider:

module RS_FF (input CLK, R, S, output reg Q);
    always @ (posedge CLK) begin
        if (~R && S)
            Q <= 1'b1;
        else if (R)
            Q <= 1'b0;        
    end
endmodule
\$\endgroup\$
1
  • \$\begingroup\$ I was able to fix it by manually setting the special case of R and S to 0. \$\endgroup\$
    – user97662
    Commented Apr 24 at 2:01
0
\$\begingroup\$

I was able to amend the issue by modifying the RS flip flop.

The characteristic equation of RS flip-flop, Q<=S | (~Q & R), is correct. However, since S & R is illegal by definition, it needs to be handled by forcing it to 0. See below.


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)
     begin
        if (R && S)
            Q <= 1'b0;
        else
            Q <= S | (Q & ~R);
     end
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.