Friday, July 25, 2008

Try to find the corner case in this verilog code

Hi Everyone, The following is a small verilog code which below models a flip-flop with asynchronous set/reset logic (active low). The model synthesizes correctly, but there is a corner case where simulation results are incorrect. What is the corner case?


always_ff @( posedge clk
or negedge rst_n // active-low reset
or negedge set_n // active-low set
)
if (!rst_n) // reset has priority over set
q_out <= '0; // reset all bits to zero
else if (!set_n)
q_out <= '1; // set all bits to one
else
q_out <= data_in; // d input assignment

Hope this question will tickle the technical senses of all the ASIC verification engineers

1 comment:

Pradeep V said...

This model synthesizes as intended. However, the model does not work correctly for all simulation conditions. Consider rst_n going low, which correctly asynchronously resets the flip-flop. While rst_n is low, set_n goes low. With both rst_n and set_n low at the same time, the flip-flop will correctly remain in reset, because of the priority coding of reset over set in the model. Everything is OK so far. Next, rst_n goes high and set_n stays low. Since the sensitivity list is only sensitive to leading edges (the negedge of rst_n and set_n), the release of rst_n will not trigger the sensitivity list. This means that the flip-flop will be in a reset state when only the set signal is active. Gotcha!

This gotcha only exists with synthesizable asynchronous set/reset flip-flops, and will only be evident until the next clock. The next clock will cause the if...else decisions to be re-evaluated and transition the flip-flop to its set state. The problem is that actual asynchronous set/reset inputs are level sensitive, so when the reset is removed, the active set takes over and drives the flip-flop to its set level. In the model, however, synthesis rules require the sensitivity list trigger on the leading edges of the set/reset inputs, causing the simulation to differ from actual hardware behavior.

This gotcha is a result of the synthesis-imposed coding style for a set/reset flip-flop. This style requires that all signals in a sequential-logic sensitivity list be specified with an edge (posedge or negedge). In order to model accurate simulation behavior, the asynchronous set/reset signals need to be sensitive to all changes on those signals, not just one edge (the leading edge) of those signals.

To avoid this gotcha, a recommended coding style (created by Don Mills of LCDM Engineering) is to:

1) Add a level-sensitive condition to the sensitivity list that triggers on the trailing edge of reset when set is active.

2) Use conditional compilation (`ifdef/`endif) or synthesis pragmas (translate_off/translate_on) to hide the additional sensitivity list condition from synthesis compilers.

Here's the recommended way to code the previous example:

always_ff @( posedge clk
or negedge rst_n // active-low reset
or negedge set_n // active-low set
`ifndef SYNTHESIS // non-synthesizable simulation code
or posedge (rst_n & ~set_n) // trailing edge of reset when set is low
`endif
)
if (!rst_n) // reset has priority over set
q_out <= '0; // reset all bits to zero
else if (!set_n)
q_out <= '1; // set all bits to one
else
q_out <= data_in; // d input assignment



An alternative coding style (invented by Cliff Cummings many years ago) to avoid this gotcha is to use the verification force/release commands to override the simulation behavior of the synthesizable model. This method requires adding an additional always block to the model, that is hidden from synthesis using conditional compilation. Note that this alternate solution must use Verilog's general_purpose "always" procedural block. It cannot take advantage of SystemVerilog's always_comb procedural block, because always_comb does not allow multiple procedural blocks to write to the same variables.

`ifndef SYNTHESIS // start non-synthesizable simulation code
always @*
if (rst_n && !set_n) force q_out = 1'b1;
else release q_out;
`endif // start synthesizable and simulatable code
always_ff @(posedge clk, negedge rst_n, negedge set_n)
if (!rst_n) // reset has priority over set
q_out <= '0; // reset all bits to zero
else if (!set_n)
q_out <= '1; // set all bits to one
else
q_out <= data_in; // d input assignment