Generate flip-flops using only combinational logic

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.

• 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. Jan 9, 2015 at 21:06

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.

• 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, 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? Jan 9, 2015 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. Jan 10, 2015 at 4: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, 2015 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. Jan 10, 2015 at 21:07
• 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. I went crazy and asked more about this distinction here. Jan 11, 2015 at 3:46

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.