Mixed clocked/combinatorial coding styles

This is a discussion on Mixed clocked/combinatorial coding styles within the vhdl forums in Programming Languages category; OK, so I tried the following synthesis example - various declarations omitted to save space. I have been able to try this on five different synthesis tools, spanning the whole range of cost and FPGA-vs-ASIC. The results surprised me a little - it's a long time since I did such a complete survey. ~~~~~~~ Form 1: Clocked process, with combinational output logic outside the usual clocked if..else...endif block. ~~~~~~~ process(clock, reset) variable count: unsigned(7 downto 0); begin -- 8-bit counter, clocked with async reset if reset = '1' then count := (others => '0'); elsif rising_edge(clock) then count := count ...

Go Back   Application Development Forum > Programming Languages > vhdl

Object Mix

Register FAQ Calendar Search Today's Posts Mark Forums Read
  #1  
Old 08-18-2008, 11:53 AM
Jonathan Bromley
Guest
 
Default Mixed clocked/combinatorial coding styles

OK, so I tried the following synthesis example -
various declarations omitted to save space.
I have been able to try this on five different
synthesis tools, spanning the whole range of
cost and FPGA-vs-ASIC. The results surprised
me a little - it's a long time since I did such
a complete survey.

~~~~~~~
Form 1: Clocked process, with combinational output logic
outside the usual clocked if..else...endif block.
~~~~~~~

process(clock, reset)
variable count: unsigned(7 downto 0);
begin
-- 8-bit counter, clocked with async reset
if reset = '1' then
count := (others => '0');
elsif rising_edge(clock) then
count := count + 1;
end if;
-- Combinational logic
q <= std_logic_vector(count);
msb <= count(7);
if count = (count'range => '1') then
tc <= '1';
else
tc <= '0';
end if;
end process;

This is the form I was objecting to in an earlier post,
on the grounds that the signal assignments don't match
standard synthesis templates. EVERY ONE of the five
synthesis tools I tried gave exactly the results you
might hope for: "msb" is directly connected to count[7],
"q" is directly connected to count[7:0], "tc" is the
output of an AND gate looking at all eight bits of count.


~~~~~~~
Form 2: Clocked process, with outputs assigned based on
next-state value of counter variable.
~~~~~~~

process(clock, reset)
variable count: unsigned(7 downto 0);
begin
if reset = '1' then
count := (others => '0');
q <= std_logic_vector(count);
msb <= count(7);
if count = (count'range => '1') then
tc <= '1';
else
tc <= '0';
end if;
elsif rising_edge(clock) then
count := count + 1;
q <= std_logic_vector(count);
msb <= count(7);
if count = (count'range => '1') then
tc <= '1';
else
tc <= '0';
end if;
end if;
end process;

You'll note that this is quite a bit nastier because
it's necessary to assign to the outputs both in the
reset and in the clocked branch, otherwise the
"q" and "count" registers will have subtly different
behaviour and could not be merged.

All five tools gave correct results, but two tools
failed to merge the duplicate registers they had
created for count and q. Of course, it is entirely
possible that those duplicate registers might be
merged later, during place-and-route, as they were
indeed exact duplicates.

I'm not quite sure what to think now. Form 1 still
does not really appeal. In particular, it cannot be
reproduced in Verilog. Form 2 gives registered outputs
for everything - not even the very simplest combinational
logic between flops and outputs - and maps well to Verilog,
but it's disappointing that some tools didn't merge the
duplicate registers and there is no doubt that it's
clumsier to code, especially if there's an asynchronous
reset in the process.

One final data point: The style of asynch reset
that has been suggested here on several occasions,
allowing you to reset some but not all of a process's
registers without implying unwanted enable logic,
worked as expected in all five tools I tried:

process (clock, reset)
variable count: unsigned(7 downto 0);
begin
if rising_edge(clock) then
count_pipe <= count; -- count_pipe isn't reset
count := count + 1;
end if;
if reset = '1' then
count := (others => '0');
end if;
end process;

This form also maps happily to Verilog.

Comments welcomed.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #2  
Old 08-18-2008, 12:08 PM
Jonathan Bromley
Guest
 
Default Re: Mixed clocked/combinatorial coding styles

With apologies for replying to myself...

On Mon, 18 Aug 2008 16:53:12 +0100, Jonathan Bromley wrote:


> process (clock, reset)
> variable count: unsigned(7 downto 0);
> begin
> if rising_edge(clock) then
> count_pipe <= count; -- count_pipe isn't reset
> count := count + 1;
> end if;
> if reset = '1' then
> count := (others => '0');
> end if;
> end process;
>
>This form also maps happily to Verilog.


Erm, no it doesn't. Since Verilog has no rising_edge
test, you can't sensibly do this in Verilog.

Whoops.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #3  
Old 08-18-2008, 01:46 PM
kennheinrich@sympatico.ca
Guest
 
Default Re: Mixed clocked/combinatorial coding styles

On Aug 18, 11:53 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:

> I have been able to try this on five different
> synthesis tools, spanning the whole range of
> cost and FPGA-vs-ASIC. The results surprised
> me a little - it's a long time since I did such
> a complete survey.

....
> Comments welcomed.
> --



Jonathan,

This is interesting, but I'm not sure exactly what conclusions to
draw. At least, no incorrect logic was generated (sighs of relief from
vendors involved). This smells like it could turn into a hunt for
cases which *do* mess up synthesizers, but that's a lot of
experimenting.

This is also interesting in that you've performed experiments, guided
by some hypotheses, and examined the results in light of those
hypotheses. Smells a bit like the scientific method to me. Clearly,
you don't get this usenet thing at all :-)

- Kenn
Reply With Quote
  #4  
Old 08-18-2008, 02:39 PM
rickman
Guest
 
Default Re: Mixed clocked/combinatorial coding styles

On Aug 18, 11:53 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
> OK, so I tried the following synthesis example -
> various declarations omitted to save space.
> I have been able to try this on five different
> synthesis tools, spanning the whole range of
> cost and FPGA-vs-ASIC. The results surprised
> me a little - it's a long time since I did such
> a complete survey.
>
> ~~~~~~~
> Form 1: Clocked process, with combinational output logic
> outside the usual clocked if..else...endif block.
> ~~~~~~~
>
> process(clock, reset)
> variable count: unsigned(7 downto 0);
> begin
> -- 8-bit counter, clocked with async reset
> if reset = '1' then
> count := (others => '0');
> elsif rising_edge(clock) then
> count := count + 1;
> end if;
> -- Combinational logic
> q <= std_logic_vector(count);
> msb <= count(7);
> if count = (count'range => '1') then
> tc <= '1';
> else
> tc <= '0';
> end if;
> end process;
>
> This is the form I was objecting to in an earlier post,
> on the grounds that the signal assignments don't match
> standard synthesis templates. EVERY ONE of the five
> synthesis tools I tried gave exactly the results you
> might hope for: "msb" is directly connected to count[7],
> "q" is directly connected to count[7:0], "tc" is the
> output of an AND gate looking at all eight bits of count.
>
> ~~~~~~~
> Form 2: Clocked process, with outputs assigned based on
> next-state value of counter variable.
> ~~~~~~~
>
> process(clock, reset)
> variable count: unsigned(7 downto 0);
> begin
> if reset = '1' then
> count := (others => '0');
> q <= std_logic_vector(count);
> msb <= count(7);
> if count = (count'range => '1') then
> tc <= '1';
> else
> tc <= '0';
> end if;
> elsif rising_edge(clock) then
> count := count + 1;
> q <= std_logic_vector(count);
> msb <= count(7);
> if count = (count'range => '1') then
> tc <= '1';
> else
> tc <= '0';
> end if;
> end if;
> end process;
>
> You'll note that this is quite a bit nastier because
> it's necessary to assign to the outputs both in the
> reset and in the clocked branch, otherwise the
> "q" and "count" registers will have subtly different
> behaviour and could not be merged.


I can't say I follow you on this. A reset input by definition defines
the state of all registers, state and output. Why would you want to
assign the outputs to be dependant on the previous state when the
reset is asserted??? I have never done this since it was not the
desired behavior. Oh, I guess it is because you are trying to force
the use of the carry chain. Do you have any reason to believe that
this is needed? Considering that the if (count = 1's) in the reset
code is redundant (at this point count is always the value 0), this
just seems so verbose.


> All five tools gave correct results, but two tools
> failed to merge the duplicate registers they had
> created for count and q. Of course, it is entirely
> possible that those duplicate registers might be
> merged later, during place-and-route, as they were
> indeed exact duplicates.
>
> I'm not quite sure what to think now. Form 1 still
> does not really appeal. In particular, it cannot be
> reproduced in Verilog. Form 2 gives registered outputs
> for everything - not even the very simplest combinational
> logic between flops and outputs - and maps well to Verilog,
> but it's disappointing that some tools didn't merge the
> duplicate registers and there is no doubt that it's
> clumsier to code, especially if there's an asynchronous
> reset in the process.


What about form B, not using a variable since it adds nothing to the
situation and is clearly making things more difficult.

> One final data point: The style of asynch reset
> that has been suggested here on several occasions,
> allowing you to reset some but not all of a process's
> registers without implying unwanted enable logic,
> worked as expected in all five tools I tried:
>
> process (clock, reset)
> variable count: unsigned(7 downto 0);
> begin
> if rising_edge(clock) then
> count_pipe <= count; -- count_pipe isn't reset
> count := count + 1;
> end if;
> if reset = '1' then
> count := (others => '0');
> end if;
> end process;


I personally don't care for this since it is 1) complex and 2) not the
same as your other code that sets the values of the outputs.

Rick
Reply With Quote
  #5  
Old 08-18-2008, 04:25 PM
Jonathan Bromley
Guest
 
Default Re: Mixed clocked/combinatorial coding styles

On Mon, 18 Aug 2008 10:46:14 -0700 (PDT),
kennheinrich@sympatico.ca wrote:

>This is interesting, but I'm not sure exactly what conclusions to
>draw.


Me neither. I should have presented the context a little
better:
"Form 1" was, for all practical purposes, my
preferred way to write a process with internal synchronous
state that has outputs that are a function of that state,
guaranteeing that all outputs come direct from a register
with no intervening logic. There was never any doubt that
tools would generate correct logic from it, but the
description inherently creates duplicate registers and
I wanted to get a bead on whether tools would reliably
merge those duplicated registers; my (poorly documented)
experience in the past has been that it's usually OK.
The answer: some do, some don't.

"Form 2" is something I've tried before and rejected
because it doesn't follow the usual "industry standard"
synthesis coding guidelines, and is too easily abused for
my taste. However, it's come up in discussion here a few
times recently, and I wanted to take a second look at
whether it works. It is potentially a clean and convenient
way of describing the Moore-machine behaviour, but putting
some combinational logic between the internal state and
the ultimate outputs of the process. To my surprise,
all tools did The Right Thing (TM) with it.

"Form 3" was just a variant of Form 2 that I played with
while I had an example in the cooking-pot; the difference
is in the manner of describing asynchronous reset. It has
the useful property, not available with the conventional
clocked process template, that in a single process you can
describe some registers that have asynch reset and some
that don't. Again, tools are OK with it, which I wasn't
necessarily expecting.

Neither Form 2 nor Form 3 can be made to work in Verilog,
which is not good news for those of us who are bilingual.

> This smells like it could turn into a hunt for
>cases which *do* mess up synthesizers, but that's a lot of
>experimenting.


Yeah. Far too much for a non-paying project :-)

>This is also interesting in that you've performed experiments, guided
>by some hypotheses, and examined the results in light of those
>hypotheses. Smells a bit like the scientific method to me. Clearly,
>you don't get this usenet thing at all :-)


Oh, don't worry; I can do unjustified inflammatory assertion
when the need arises :-)

Thanks
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #6  
Old 08-18-2008, 04:38 PM
Jonathan Bromley
Guest
 
Default Re: Mixed clocked/combinatorial coding styles

On Mon, 18 Aug 2008 11:39:42 -0700 (PDT), rickman wrote:

>> it's necessary to assign to the outputs both in the
>> reset and in the clocked branch, otherwise the
>> "q" and "count" registers will have subtly different
>> behaviour and could not be merged.

>
>I can't say I follow you on this. A reset input by definition defines
>the state of all registers, state and output.


Not if I write a description in which some of the registers
are reset and some aren't, surely?

> Why would you want to
>assign the outputs to be dependant on the previous state when the
>reset is asserted??? I have never done this since it was not the
>desired behavior. Oh, I guess it is because you are trying to force
>the use of the carry chain.


No, nothing as smart as that. It's just a simple thing
about the usual synthesisable clocked process:

process(clk)
begin
if reset = '1' then
Q0 <= '0'; -- reset, but never clocked
Q1 <= '0'; -- reset and clocked, OK
elsif rising_edge(clk) then
Q1 <= D1; -- reset and clocked, OK
Q2 <= D2; -- clocked but never reset
end if;
end process;

The usage of Q0 and Q2 is unsatisfactory because...
- Q0 is a latch, enabled by reset, with constant input
- Q2 is a regular flop with (reset = '0') as its clock enable

So, what happens when I wish to describe some kind of pipeline
in which the first stage has an asynch reset but the later
stages do not? That's a sensible design; if you know that
the clock will tick several times during reset, then you
don't need to reset anything but the first stage of the
pipeline because all later stages will flush through
by clocked action during the reset interval. But how
to describe that? You need TWO PROCESSES, one for the
register that HAS a reset and one for the registers
that LACK a reset. Of course, synch reset doesn't suffer
this problem - you can freely apply it to individual
registers, and not to others, in a single process.

All of that was the motivation for my final example,
which you didn't like...

>> begin
>> if rising_edge(clock) then
>> count_pipe <= count; -- count_pipe isn't reset
>> count := count + 1;
>> end if;
>> if reset = '1' then
>> count := (others => '0');
>> end if;
>> end process;


> I personally don't care for this since it is 1) complex and 2) not the
> same as your other code that sets the values of the outputs.


I abbreviated the example; it could easily set outputs just as
the "Form 2" variant does. But I take the point that it is
unfamiliar (surely not really "complex"?) and, as someone else
has pointed out, it's not too nice to have a piece of code that
says Do-Lots-Of-Complicated-Stuff only to have the rug pulled out
from under you by an If-The-World-Is-About-To-End condition
right at the end of the process. It's better to know about
those global things right up front.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #7  
Old 08-18-2008, 05:23 PM
rickman
Guest
 
Default Re: Mixed clocked/combinatorial coding styles

On Aug 18, 4:38 pm, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
> On Mon, 18 Aug 2008 11:39:42 -0700 (PDT), rickman wrote:
> >> it's necessary to assign to the outputs both in the
> >> reset and in the clocked branch, otherwise the
> >> "q" and "count" registers will have subtly different
> >> behaviour and could not be merged.

>
> >I can't say I follow you on this. A reset input by definition defines
> >the state of all registers, state and output.

>
> Not if I write a description in which some of the registers
> are reset and some aren't, surely?


Of course you can define any world you wish to work in. I was
referring to the world of FPGA design. There a global async reset is
nearly always provided and needs to be specified. The standard
template is mapped to the global reset quite well by the tools. In
that context, there is no reason to leave any register uncontrolled on
reset (unless you have some very odd requirements) and failure to do
so can result in unpredictable power up behavior.

I don't have to consider the entire universe of possibilities. I just
have to consider the universe that I choose to work in and I don't
want to make my life difficult by not controlling the power up state
of registers in my design. It also helps with simulation a great
deal!


> > Why would you want to
> >assign the outputs to be dependant on the previous state when the
> >reset is asserted??? I have never done this since it was not the
> >desired behavior. Oh, I guess it is because you are trying to force
> >the use of the carry chain.

>
> No, nothing as smart as that. It's just a simple thing
> about the usual synthesisable clocked process:
>
> process(clk)
> begin
> if reset = '1' then
> Q0 <= '0'; -- reset, but never clocked
> Q1 <= '0'; -- reset and clocked, OK
> elsif rising_edge(clk) then
> Q1 <= D1; -- reset and clocked, OK
> Q2 <= D2; -- clocked but never reset
> end if;
> end process;
>
> The usage of Q0 and Q2 is unsatisfactory because...
> - Q0 is a latch, enabled by reset, with constant input
> - Q2 is a regular flop with (reset = '0') as its clock enable
>
> So, what happens when I wish to describe some kind of pipeline
> in which the first stage has an asynch reset but the later
> stages do not? That's a sensible design; if you know that
> the clock will tick several times during reset, then you
> don't need to reset anything but the first stage of the
> pipeline because all later stages will flush through
> by clocked action during the reset interval. But how
> to describe that? You need TWO PROCESSES, one for the
> register that HAS a reset and one for the registers
> that LACK a reset. Of course, synch reset doesn't suffer
> this problem - you can freely apply it to individual
> registers, and not to others, in a single process.


Again, you don't explain the context of your async reset. If it is
the global reset I see no reason to leave it off of any registers in
your design. A sync reset is one that is typically used at any time
during operation. The async reset is typically mapped to the global
reset. If it does not, IMHO, the async reset does not belong in a
synchronous design. I avoid the problem by avoiding it!


> All of that was the motivation for my final example,
> which you didn't like...
>
> >> begin
> >> if rising_edge(clock) then
> >> count_pipe <= count; -- count_pipe isn't reset
> >> count := count + 1;
> >> end if;
> >> if reset = '1' then
> >> count := (others => '0');
> >> end if;
> >> end process;

> > I personally don't care for this since it is 1) complex and 2) not the
> > same as your other code that sets the values of the outputs.

>
> I abbreviated the example; it could easily set outputs just as
> the "Form 2" variant does. But I take the point that it is
> unfamiliar (surely not really "complex"?) and, as someone else
> has pointed out, it's not too nice to have a piece of code that
> says Do-Lots-Of-Complicated-Stuff only to have the rug pulled out
> from under you by an If-The-World-Is-About-To-End condition
> right at the end of the process. It's better to know about
> those global things right up front.


I will point out that the above example can fail to initialize
count_pipe. The above should produce two registers and only the
first, count, will be reset. Why would you want to do that???

So I still fail to see your motivation, or at least I don't share
it.

Rick
Reply With Quote
  #8  
Old 08-19-2008, 01:45 AM
Mike Treseler
Guest
 
Default Re: Mixed clocked/combinatorial coding styles

rickman wrote:

> I can't say I follow you on this. A reset input by definition defines
> the state of all registers, state and output. Why would you want to
> assign the outputs to be dependant on the previous state when the
> reset is asserted???


Maybe I want to describe a block ram and
a lut shifter in the same process as some registers.
The registers need a reset, but the ram and
shifter can't have one.

-- Mike Treseler
Reply With Quote
  #9  
Old 08-19-2008, 02:18 AM
Mike Treseler
Guest
 
Default Re: Mixed clocked/combinatorial coding styles

Jonathan Bromley wrote:
> OK, so I tried the following synthesis example -
> various declarations omitted to save space.


Thanks for all the unbillable hours.

> All five tools gave correct results, but two tools
> failed to merge the duplicate registers they had
> created for count and q. Of course, it is entirely
> possible that those duplicate registers might be
> merged later, during place-and-route, as they were
> indeed exact duplicates.


I have yet to see a duplicate register
make it all the way through synthesis,
but they do sometimes clutter the RTL
schematic. The "odd" template seems
to tip the algorithm toward doing the
merge on the front end.

-- Mike Treseler
Reply With Quote
  #10  
Old 08-19-2008, 04:12 AM
Jonathan Bromley
Guest
 
Default Re: Mixed clocked/combinatorial coding styles

rickman,

first off, let me say that I broadly agree with what you
say about FPGAs. But...

>I will point out that the above example can fail to initialize
>count_pipe. The above should produce two registers and only the
>first, count, will be reset.


I was careful to qualify the suggestion by noting that
there must be enough clocks during reset for the pipe
to flush. Given the way reset is likely to be managed
on an ASIC design, that is not unreasonable.

>Why would you want to do that???


In ASIC technologies, reset is not free. In the
example I gave, I need only one active clock during the
reset interval to flush the second register with the
async-reset contents of the first. That's an entirely
plausible scenario. If the downstream processing pipe
is long or wide, the area and routing resources saved
by *not* resetting it can be worth having.
>
>So I still fail to see your motivation, or at
>least I don't share it.


There are lots of ASIC designers out there....
most of the time I'm not one of them, so I
absolutely see where you're coming from. But
it's not the only point of view.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
Reply


Thread Tools
Display Modes


All times are GMT -5. The time now is 04:58 AM.


Powered by vBulletin® Version 3.7.2
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
vB Ad Management by =RedTyger=

In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.