3

Just for fun, I wanted to design and simulate D-type flip-flops using only combinational logic in Verilog (or SystemVerilog). I am using using Verilator for the simulation.

My initial attempt, which uses a classical six-NAND design, seems to work fine -- and has passed all tests. My second attempt, based on a four-NAND JK-type, is not working. The output doesn't stay latched during the positive level of the clock, and for some tests the simulation doesn't even converge.

Q: I know it isn't normal or optimal, but is it reasonable to design flip-flops using combinational logic in Verilog? If so, is there something wrong with my second design?

This one works:

module dff( input clk, input D, output Q );

   wire a, sn, rn, b, Qn;

always_comb // captures D @( posedge clk )
   begin
   a = !(b&sn);
   sn = !(a&clk);
   rn = !(sn&b&clk);
   b = !(rn&D);
   Q = !(sn&Qn);
   Qn = !(rn&Q);
   end

endmodule

This one does not work:

module dff( input clk, input D, output Q );

   wire J=D, K=!D;
   wire sn, rn, qn;

always_comb // captures D @( posedge clk ), but fails to hold
   begin
   sn = !(J&clk&qn);
   rn = !(K&clk&Q);
   Q  = !(sn&qn);
   qn = !(rn&Q);
   end

endmodule

In an effort to eliminate blocking assignment and sensitivity lists, I reimplemented the JK-based approach as follows, but the output waveforms were not affected by this difference.

module dff( input clk, input D, output Q );

   wire J=D, K=!D;
   wire sn, rn, qn;

   assign sn = !(J&clk&qn);
   assign rn = !(K&clk&Q);
   assign Q  = !(sn&qn);
   assign qn = !(rn&Q);

endmodule

Note: I based the design of these on the descriptions and diagrams here.

Brent Bradburn
  • 355
  • 3
  • 10
  • 1
    You can use HDLs to do this, but that only makes sense for ASICs. FPGAs don't have the right delay characteristics to be useful in building sequential circuits using combinational feedback paths. – Ben Voigt Jan 09 '15 at 21:06

2 Answers2

2

The JK you have is a latch, not an edge trigger. It is also missing feedback between Q and qn (your first code is also missing this feedback). Combinational always blocks work based on the sensitivity list. The list for auto-sensitivity from always @* and always_comb` is determined by signals used on the right hand side of an expression and not on the left hand side.

If you declare the sensitivity list it should work : always @( J,K,clk, Q,qn ), This way the Q and qn will re-trigger the always block.

Another approach is to use case statement:

always_comb
  if (clk)
    Q = Qpre;
  else
    case({J,K})
    2'b10 : Qpre = 1'b1;
    2'b01 : Qpre = 1'b0;
    2'd11 : Qpre = ~Q;
    default : Qpre = Qpre; // no change
    endcase

The problem with using combinational logic for flops is it can have hold-time violations. Verilog an indeterminate simulator. This means the order that an always block is evaluated are not guaranteed. Using the below code as an example. Verilog can execute b_combff before c_combff therefore c will be assigned the value of a. This is because we are using blocking assignments. The simulator could also run c_combff before b_combff and we will get correct value. Both scenarios are legal.

dff b_combff(.Q(b), .clk(clk), .D(a);
dff c_combff(.Q(c), .clk(clk), .D(b);

A well placed non-blocking assignment (<=) on Q can help. This will separate the evaluation and update into separate regions of the scheduler. This fixes the the hold-time race condition but does not help with synthesize.

Modern synthesizers are smart, but the are not brilliant. They look for coding patterns to determine how to convert RTL to equivalent gates. When they see always @ it will make edge sensitive flip-flops. They do well with very simple SR, RS, & D latches. Biond that they try to make combinational logic. When you add your own custom flops the synthesizer the will attempt to match it to logic it already knows, which will most likely result in asynchronous latching logic.

In short, is possible to design flip-flops using combinational logic, but outside of learning it is generally not a good idea.

Greg
  • 4,270
  • 1
  • 21
  • 32
  • Thanks for the explanations, most of that makes sense to me. However, I don't follow why you say that I have implemented a latch rather than a flip-flip -- I modeled the logic from [here](http://en.wikipedia.org/wiki/Flip-flop_%28electronics%29#JK_flip-flop), where it is indicated as being edge-triggered. Also, I don't see how I'm missing the Q/qn feedback. Shouldn't `always_comb` infer the sensitivity list to be all variables from the contained statements? Maybe you are hinting that the *blocking assignments* are the only problem, and latch/feedback issues are derivative of that? – Brent Bradburn Jan 09 '15 at 23:38
  • I finally get what you were saying about my JK not being edge-triggered (see my answer). I still don't know what you meant by "missing feedback between Q and qn". It seems like have have that feedback in my logic -- and it works as it should. – Brent Bradburn Jan 10 '15 at 04:43
  • Within a begin-end order of operation matters. If you change the order around you will be able to see odd behavior. For example assign 'Q' before 'sn' in the always block. – Greg Jan 10 '15 at 16:03
  • I see what you are getting at, but I don't agree. My testing shows there are no order dependencies (running under Verilator). This is an interesting issue, and I posted a detailed response as an answer to another question: [here](http://electronics.stackexchange.com/a/148551/16776). – Brent Bradburn Jan 10 '15 at 21:07
  • 1
    Ok, I think I get it now -- sorry I'm just coming up to speed on this stuff. Your comments about order-of-operation are assuming software-style or *pre-synthesis* simulation. I hadn't previously considered that possibility because that is not how Verilator (or real hardware) works. This distinction of Verilog semantics is described in section 2.2 of the document [here](http://www.sunburst-design.com/papers/CummingsSNUG1999SJ_SynthMismatch.pdf). I went crazy and asked more about this distinction [here](http://electronics.stackexchange.com/q/148578/16776). – Brent Bradburn Jan 11 '15 at 03:46
  • Ah, Varilator runs as though a *post-synthesis* and therefore it is not strictly adhering to the Verilog/SystemVerilog standards. That explain the behavior. You should have also got errors when assigning `wire` types in an always block. [Cliff](http://www.sunburst-design.com/papers/) writes great papers; I refer to them too when faced with beyond the LRM challenges. The [IEEE Std 1800-2012](http://standards.ieee.org/getieee/1800/download/1800-2012.pdf) is the latest official SystemVerilog spec. And FYI: You can also try out other simulators on [EDAplayground](http://edaplayground.com). – Greg Jan 12 '15 at 19:19
1

Descriptions of the JK flip-flop tend to be severely muddled. In particular, the interaction with the clock rarely seems to be described in a completely coherent way, but this may largely be due to a bigger issue of loose and ambiguous use of terminology.

The basic problem with the question's JK flip-flop is described in this answer. In short, what is modeled is not edge triggered, despite indications to the contrary in the referenced Wikipedia article.

To build an edge-triggered JK flip-flop, you can use a two-stage configuration of cascaded latches (a so called "master-slave") -- such that the two stages are in transparent vs. hold states during opposite clock phases. In this arrangement, the first stage will hold the data steady while the second stage is in its transparent state. This relationship creates the edge-trigger effect, and is implemented below...

module dff( input clk, input D, output Q );
   wire Q1;
   jkff A(!clk,D,!D,Q1);
   jkff B(clk,Q1,!Q1,Q);
endmodule

...where jkff is the same logic as before (but would be better termed a "JK gated latch")...

module jkff( input clk, input J, input K, output Q );

   wire sn, rn, qn;

always_comb
   begin
   sn = !(J&clk&qn);
   rn = !(K&clk&Q);
   Q  = !(sn&qn);
   qn = !(rn&Q);
   end

endmodule

The reported lack of convergence may be explainable by the non-converging toggle mode of the level-triggered design, if the inputs were ever allowed to hold the state (J=K=1).

Of further interest: The book "Verilog HDL", by Samir Palnitkar includes an implementation of a D-type flip-flop which is practically equivalent to the one shown in the question.

So to be explicit: The answer to the initial question is: Yes, this is a reasonable thing to do. However, there is no guarantee that any particular hardware will be able to realize such designs. Different simulators, too, may give different results -- especially with regards to the distinction between pre- and post-synthesis simulation.

Brent Bradburn
  • 355
  • 3
  • 10