( ESNUG 334 Item 2 ) --------------------------------------------- [10/28/99]

Subject: ( ESNUG 332 #1 ) full_case/parallel_case, Evil Twins of Synthesis

> The popular myth that exists surrounding "full_case parallel_case" is
> that these Verilog directives always make designs smaller, faster and
> latch-free.  This is false!  Indeed, the "full_case parallel_case"
> switches frequently make designs larger and slower and can obscure the
> fact that latches have been inferred.  These switches can also change
> the functionality of a design causing a mismatch between pre-synthesis
> and post-synthesis simulation, which if not discovered during gate-level
> simulations will cause an ASIC to be taped out with design problems.
>
>     - Cliff Cummings
>       Sunburst Design                            Beaverton, OR


From: Jack Hollins <jhollins@lsil.com>

Hi, John,

I just had to drop you a note of thanks for distributing the 'case' article
among those of us (especially on the west coast) who didn't make it to the
Boston SNUG.  I'd nominate this as one of the most potentially impactful
ESNUG posts of the year.  Keep up the great work.

    - Jack Hollins, Design Engineer
      LSI Logic                                      Milpitas, CA

         ----    ----    ----    ----    ----    ----   ----

From: "Ashutosh Varma" <ashu@axiscorp.com>

Hi John,

Axis Systems has just announced availability of their IEEE compliant XSIM
simulator.  The reason I am mentioning this simulator in relation to this
post is because XSIM, besides being a native compiled code Verilog
simulator, also has a feature called Design Xaminer which does:

  i. Static synthesis subset compliance check for RTL design
  ii. Dynamic RTL vs. post-synthesis gate semantics checking

It is the latter capability which deals with problems like the ones
mentioned by Cliff in his post. The XSIM simulator will point out a
parallel-case or full-case violation in your Verilog RTL simulation using
your actual test-bench and test-vectors.  (This saves you the iteration of
first doing synthesis and then doing *slow* gate-level simulation before you
notice these problems.  Also note that these problems cannot be detected by
static timing analysis tools like PrimeTime, Pearl, or Velocity.)

It also detects other potential cases of mismatches between RTL and
post-synthesis gate simulation, like X propagation (in RTL simulation, X's
are not propagated in Verilog).  www.axiscorp.com

    - Ashutosh Varma
      Axis Systems                                Sunnyvale, CA

         ----    ----    ----    ----    ----    ----   ----

From: Paul Gerlach <paulge@mdhost.cse.tek.com>

John, 

An interesting paper.  I certainly hadn't thought much about it.  At first I
didn't agree with Cliff.  Now I partially do.  To summarize my thoughts (many
of which are just Cliff's...):

1. If synopsys can detect parallel cases and optimize for them, I certainly
   agree that usage of parallel_case is worthless.
   (Except for one-hot machines as Cliff mentions - we use that method)

2. It seems that many places in my own code I have non-full cases that 
   are really fully specified in my mind.  In the past I've used full_case.
   Cliff says don't do that;  I now agree.

   But Cliff also says (in 3.3) that using a default: out = 'bx;
   also isn't good because then your simulation & synthesis don't match.

   Here I disagree.  I think you're trying to optimize the wrong part of 
   the design:  simulation vs. gates match.  In doing so, you're giving up
   some of the power of RTL simulation.  In RTL I'm able to write code
   in such a way that my logic will blow up to X's when a make a false 
   assumption.  (i.e. I *believe* the case to be fully specified, if it's
   not, I want X's introduced into my sims, not some latch function or
   other valid input - X's are easy to detect and trace back)

   In my opinion the power and usefulness of inserting X's to detect designer
   failure in RTL outweighs the fact that synopsys will never "create" an
   X from gates.

   Also, have x's on the right hand side allows synopsys to treat something
   as a don't care, perhaps further optimizing logic.  If I fully specify
   all outputs, even when I don't believe some will happen, I'm 
   constraining the function.  

   Personally I hold the X propogation feature of RTL to be more important
   to me than the don't care optimization.

Editorial comment: in 4.3 Cliff said "this example happens to infer 
priority encoder logic when sythesized."  This confused me for a long time.
It seemed contradictory with statements made in 4.5.   UNTIL I realized
that it created a priority encoder because that's what the truth table
specified, not any code construct or directive.  I thought it was being 
infered that even though synopsys *knew* it was parallel, it was still 
building a priority encoder anyway.  I understand Cliff now.  :-)

    - Paul M Gerlach
      Tektronix, Inc.                              Beaverton, OR

         ----    ----    ----    ----    ----    ----   ----

From: Paul Miranda <paul.miranda@amd.com>

John,

That article on parallel_case and full-case hasn't done anything to convince
me to not use them.  If I really want something to be parallel or full, then
I will put them in.  According to the article, they will do what I expect.
Unless I totally missed something, these constraints will only hurt a design
if you add them when you shouldn't.

    - Paul Miranda
      AMD

         ----    ----    ----    ----    ----    ----   ----

> 4.5 A "parallel" case statement with "parallel_case" directive
>
> The casez statement in Example 7 is parallel! If a "parallel_case"
> directive is added to the casez statement, it will make no difference. The
> design will synthesize the same as without the "parallel_case" directive.
>
> The point is, the "parallel_case" directive is always most dangerous when
> it works! When it does not work, it is just extra characters at the end of
> the case header.
>
>     module intctl2b (int2, int1, int0, irq);
>       output       int2, int1, int0;
>       input  [2:0] irq;
>       reg          int2, int1, int0;
>
>       always @(irq) begin
>         {int2, int1, int0} = 3'b0;
>         casez (irq) // synopsys parallel_case
>           3'b1??: int2 = 1'b1;
>           3'b?1?: int1 = 1'b1;
>           3'b??1: int0 = 1'b1;
>         endcase
>       end
>     endmodule


From: Dave Chapman <dave@goldmountain.com>

John,

This is dangerous and confusing, because it relies on the idea that the
compiler will interpret parallel cases such that, if more than one case is
satisfied, the FIRST ONE ENCOUNTERED will be chosen.  Some stack-algorithm
parsers use the opposite, and will produce unpredictable results.

Much better would be to use explicit if statements.

Case statements are way too ambiguous for stuff like select logic,
arbitration, and interrupts.

    - Dave Chapman
      Goldmountain                             Sebastopol, CA

         ----    ----    ----    ----    ----    ----   ----

From: William Liao <wliao@mmcnet.com>

Hi, John,

Cliff's paper is quite useful, and I think it should be required reading
for all synthesis newbies.  However, I strongly disagree with the idea
that "full_case" and "parallel_case" should be used sparingly.  I try
my best to add "full_case" and "parallel_case" to every case statement.

I don't think Cliff's reasons for avoiding these directives are valid.
He wants to avoid them because when they "work", they cause unexpected
simulation and synthesis mismatches.  But the causes of these mismatches
appear in the synthesis log as the warning messages Cliff mentioned in
his paper.  Shouldn't a good engineer examine the log and fix the
problem instead of avoiding the problem?

Nor do I think the three reasons given in the paper (smaller & faster
design, removing latches, removing priority encoders) are valid.  If
anyone actually believes these are the right reason to use "full_case"
and "parallel_case", they really need to sit down and carefully study
Cliff's paper before they screw up another project (unless they work
for my competitors, in that case they should continue as they are ;-))

The main reason I use these two directives is to do sanity checks.
When I add "parallel_case" to a case statement, I am telling Design
Compiler that I have carefully checked my case items, and none of them
overlap.  If Design Compiler generates a warning message, it is telling
me that it disagrees, and I should check again.  I always follow DC's
advice, and it has saved me many times.  Similarly, when I add
"full_case" to a case statement, I am telling Design Compiler that all
possible case items are listed.  Again, if it generates warning messages
because of the "full_case", I check my code.

I have another use for "full_case", and it is more dangerous, but I
think I am covered.  I use "full_case" to avoid "default:", hoping
that Design Compiler understands it should optimize the circuit by
taking advantage of the "don't care".  (Note the word "hoping", but
it is another story.)  When "full_case" is used this way, it is entirely
possible that I miss a valid case item.  My solution is to add a default
case that DC won't translate, like this:

   module mux (in, s, out);

     input [2:1]   in;
     input [1:0]   s;
     output        out;
     reg           out;

   always @(in or s)
     case (s)  // synopsys full_case parallel_case
       2'b01:  out = in[1];
       2'b10:  out = in[2];
       // synopsys translate_off
     default:  
       begin
         $display("******* error");
         $finish;
       end
       // synopsys translate_on
     endcase

   endmodule

Now if I miss something, I will get a message in my simulations,
and still gives DC some room for optimization.  I believe this is no
worse than having a default item in the case.  Why?  Let's assume that
there is a default item that sets "out" to "in[2]".  Let's further
assume that "out" should be "in[1]" when "s" is "2'b00".  If by some
coincidence "in[1]" and "in[2]" are the same when the default item
is triggered, I won't find the bug.  But my code catches this problem
and stops the simulation.

Finally, I believe many if not most case statements can be easily
rewritten to take advantage of "full_case" and "parallel_case".  I'll
use Example 13 in Cliff's paper as an example.

     module code4b (y, a, en);
       output [3:0] y;
       input  [1:0] a;
       input        en;
       reg    [3:0] y;

       always @(a or en) begin
         casez ({en, a}) // synopsys full_case parallel_case
         3'b0_??:  y = 4'b0000;
         3'b1_00:  y = 4'b0001;
         3'b1_01:  y = 4'b0010;
         3'b1_10:  y = 4'b0100;
         3'b1_11:  y = 4'b1000;
         endcase
       end
     endmodule

The statement "y = 4'h0" is now integrated into the case statement, and
"// synopsys full_case parallel_case" are added to make sure all case items
are parallel, and all possible "{en, a}" combinations are covered.

    - William Liao
      MMC Networks



 Sign up for the DeepChip newsletter.
Email
 Read what EDA tool users really think.


Feedback About Wiretaps ESNUGs SIGN UP! Downloads Trip Reports Advertise

"Relax. This is a discussion. Anything said here is just one engineer's opinion. Email in your dissenting letter and it'll be published, too."
This Web Site Is Modified Every 2-3 Days
Copyright 1991-2024 John Cooley.  All Rights Reserved.
| Contact John Cooley | Webmaster | Legal | Feedback Form |

   !!!     "It's not a BUG,
  /o o\  /  it's a FEATURE!"
 (  >  )
  \ - / 
  _] [_     (jcooley 1991)