More synthesis myths?

J

JimLewis

I've always felt "safer" with '>=' or '<=' comparisons rather than '='
on counters, especially when dealing with non-modulo-2^n counters.

However, it should be noted that the three examples given do not
behave identically. Examples 1 and 3 count from 0 to 4 and repeat.
Example 2 counts from 1 to 4 and repeats!

Small, fast and wrong is still just wrong.

Except in special cases, I almost always load with a value (or
the value from a base register) and down count to zero and
detect zero by watching the carry bit.

Special cases would be like incrementing to a number with a
sparse number of 1's (like 4) as the only thing you need to
check are the 1's.

Cheers,
Jim
 
J

JimLewis

Which is why it is usually better to code it as "counter >= 4".  Then you
don't need to have as smart of a synthesis tool in order to reach the
conclusion that only bit 2 of the counter is needed.

KJ

KJ,
I don't think I agree. While >= 4 seems to produce similar results
to the bit comparison, what about >= 5? If I did my kmaps right -
yikes this is digging back some, decoding the general sense of >= 5
requires:
((Count(2) and Count(0)) or (Count(2) and Count(1))

OTOH, if I count up to 5 and think "=" rather than ">=", due to
properties of counters, I can decode just the bits that are 1
and the resulting logic is:
(Count(2) and Count(0)) ='1'.

For =5 or >=5 to do as good as decoding bits, you need a smart
compiler. OTOH, in a LUT based design, will I notice the
difference of 1 LUT pin? Probably not - unless I have alot
of counters.

Cheers,
Jim
 
K

kennheinrich

KJ,
I don't think I agree.  While >= 4 seems to produce similar results
to the bit comparison, what about >= 5?  If I did my kmaps right -
yikes this is digging back some, decoding the general sense of >= 5
requires:
((Count(2) and Count(0)) or (Count(2) and Count(1))

OTOH, if I count up to 5 and think "=" rather than ">=", due to
properties of counters, I can decode just the bits that are 1
and the resulting logic is:
(Count(2) and Count(0)) ='1'.

For =5 or >=5 to do as good as decoding bits, you need a smart
compiler.  OTOH, in a LUT based design, will I notice the
difference of 1 LUT pin?  Probably not - unless I have alot
of counters.

Cheers,
Jim

I think I agree with KJ, but for different reasons. In many of my
designs, a counter typically indicates how long to remain in one state
of a FSM, or else is used to loop through a shortened (not a full
power of two) sequence, like pixel or line count in video. I've always
felt that decoding (n >= k) instead of (n=k) gives me more of a safety
net to get back more quickly to the restart state or get back into the
main loop in case I reach an unreachable state (defined for this
purpose as n > k). This might happen because I messed up some obscure
corner case in my multiple-interconnected-FSM control logic, in which
I freely admit I'm at fault. But it's been my experience that getting
your chassis hit with 20 kV from an ESD gun during product compliance
testing can do unusual things to your flops, which you should still
recover from ASAP. It seems to me that on the balance of probability,
if I include a "free" check (in the source code sense of free) for
unreachable states (e.g. NTSC_LINE_NUMBER >= 525 in preference to
NTSC_LINE_NUMBER=525) then I have a better chance of not getting stuck
forever when I go into the weeds.

There are a lot of cases where writing stuff in the source (like a
redundant 'when others' case in an otherwise fully covered case stmt
decoding an enumerated state type) has zero semantic meaning in VHDL.
In these cases, one can argue back and forth, and inconclusively, that
a tool ought to or ought not to take extra steps to take the hint to
cover unreachable states, but there's no clear, LRM-traceable
justification for this. But comparing against a counter in a power-of-
two modulus bit vector seems to give a pretty clear mandate to the
synthesizer.

Clearly this design style trick is nowhere near a rigorous proof of
recovery (not like a proper CTL model checking run, by any means), but
it helps. My next statement will probably offend the hard-core gate
bangers, but here goes: I'm too old to care about optimizing the last
p-term out of one comparison -- I tend to be more concerned with
correctness, recoverability and reliability. I'd much rather it never
locks up and recovers quickly when I fuzz test it.

- Kenn
 
K

KJ

KJ,
I don't think I agree.  While >= 4 seems to produce similar results
to the bit comparison, what about >= 5?  If I did my kmaps right -
yikes this is digging back some, decoding the general sense of >= 5
requires:
((Count(2) and Count(0)) or (Count(2) and Count(1))

OTOH, if I count up to 5 and think "=" rather than ">=", due to
properties of counters, I can decode just the bits that are 1
and the resulting logic is:
(Count(2) and Count(0)) ='1'.

I'm not sure that this is a fair comparison, specifically the way
you're creating the logic for "=5" by virtue of your simply counting
the one bits and "due to properties of counters" but not applying that
same thinking to the ">=" case.

The only way you can get the logic that you listed for the "=5" case
is by 'knowing' that count=7 can not occur therefore count(1) is not
needed in the decode. Fair enough. But to be fair on the >=5 case
you should also acknowledge that if one 'knows' that count=7 is
impossible, then count=6 is just as impossible and therefore the "or
(Count(2) and Count(1))" term would not be needed and the exact same
logic would be produced.

A given revision of a given synthesis tool would either have the
capability to detect this condition or not and I think would not
generate the logic that you listed for '>=5' if it was capable of
producing the logic that you listed for '=5'.
For =5 or >=5 to do as good as decoding bits, you need a smart
compiler.

My point wasn't that a smart compiler wouldn't do better than a not so
smart one (the rising tide will lift all boats after all). My point
was that for a given compiler, I believe you'll get equivalent or
better logic by using >=5 than by using =5. I'll accept that there
might be some instance where this is not true, but I've seen far more
instances where it is and have yet to run across the case where it is
not.
OTOH, in a LUT based design, will I notice the
difference of 1 LUT pin?  Probably not - unless I have alot
of counters.
But that's the reason for these discussions huh?

Kevin Jennings
 
K

KJ

I tend to be more concerned with
correctness, recoverability and reliability. I'd much rather it never
locks up and recovers quickly when I fuzz test it.

I'm fully in the same camp as you on this. The primary reason for >=
is to insure recovery of the design, a secondary benefit is
potentially less logic (and most likely but I've never formally proved
it...never produce more logic). From a readability/maintainability/
support perspective either method is equivalent in my opinion.

KJ
 
J

JimLewis

KJ,
From a readability/maintainability/
support perspective either method is equivalent in my opinion.
Agreed.

For me, readability and maintainability are most important.

Recovery is also important. I count down. It recovers also.

From hardware optimization standpoint, counting down and detecting
0 costs one carry output - independent of the size of the
counter and the value to be detected.


signal CountReg : unsigned(2 downto 0) ;

CountProc : process(Clk, nReset)
variable vCountReg : unsigned(CountReg'length downto 0) ; -- 3
downto 0
begin
if nReset = '0' then
CountReg <= "101" ;

elsif rising_edge(Clk) then
vCountReg := ('0' & CountReg) - 1 ;
if vCountReg(vCountReg'left) = '1' then
vCountReg := "0101" ;
end if ;
CountReg <= vCountReg(CountReg'range) ;
end if ;
end process ;

From a readability perspective, I would throw this into a procedure.
procedure Dec (
signal Count : inout unsigned ;
constant ReloadVal : in unsigned ;
constant IncEnable : in std_logic := '1'
) is
variable vCount : unsigned(Count'length downto 0) ;
begin
vCount := ('0' & Count) - IncEnable ;
if vCount(vCount'left) = '1' then
vCount := '0' & ReloadVal ;
end if ;
Count <= vCount(Count'length -1 downto 0) ;
end procedure ;


Then the process is:
CountProc : process(Clk, nReset)
begin
if nReset = '0' then
CountReg <= "101" ;

elsif rising_edge(Clk) then
Dec(CountReg, "101") ;

end if ;
end process ;

In the general implementation, you may prefer
if IncEnable = '1' then
vCount := ('0' & Count) - 1;
end if ;

instead of:
vCount := ('0' & Count) - IncEnable ;

I prefer the latter since it often produces smaller
hardware (although not always).
The primary reason for >=
is to insure recovery of the design, a secondary benefit is
potentially less logic (and most likely but I've never formally proved
it...never produce more logic).

WRT less logic, one counter example proves an assertion false.
Decoding =5 results in:
Count(2) and Count(1) and Count(0)

Decoding >= 5 results in:
((Count(2) and Count(0)) or (Count(2) and Count(1))

Here is a case where ">=" creates more hardware than "=".
So you can't prove your assertion "never produce more logic"
because it isn't true.

Cheers,
Jim
 
J

JimLewis

KJ
My point wasn't that a smart compiler wouldn't do better than a not so
smart one (the rising tide will lift all boats after all).  My point
was that for a given compiler, I believe you'll get equivalent or
better logic by using >=5 than by using =5.  I'll accept that there
might be some instance where this is not true, but I've seen far more
instances where it is and have yet to run across the case where it is
not.

Note that I have always hand coded the =5 trick since
this is only more effective than counting down and detecting 0 where
there are only a few bits (I only do it when there is 1 or 2 bits
decoded).

WRT optimizing >=5, you can't do both recovery and counter bit
optimizations. Recovery in this case requires coverage of the
terms 5, 6, and 7. So the best you will get is:
((Count(2) and Count(0)) or (Count(2) and Count(1))
|------ 5 or 7 --------| |------ 6 or 7 -------|


To me though both of these are a mute point as this discussion has
only
reinforced my thought that counting down and detecting 0 (using the
carry chain) is the better implementation (from both optimization and
recovery).

Cheers,
Jim
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
KetoBurn
Top