Mixed clocked/combinatorial coding styles

J

Jonathan Bromley

Guest
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.
 
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.
 
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
 
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.
 
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.
 
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
 
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
 
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
 
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
 
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.
 
Hi Jonathan

Jonathan Bromley <jonathan.bromley@MYCOMPANY.com> writes:

Why would you want to do that???

In ASIC technologies, reset is not free.
True in theory. But then, ASIC design rules I am familiar with require
that there be not logic in the reset tree. Put differently, a signal
declared (to the tool) as reset signal must only be connected to the
async reset input of a flip-flop. Deviations from this rule must be
justified and documented. Automatic design rule checkers must be told
to skip individual "violations".

I'd say that *unless* there is a block of significant size in which
registers don't require reset, trying to be clever is not likely to
save a lot.

Regards
Marcus

--
note that "property" can also be used as syntaxtic sugar to reference
a property, breaking the clean design of verilog; [...]

(seen on http://www.veripool.com/verilog-mode_news.html)
 
On Tue, 19 Aug 2008 06:47:14 -0700 (PDT), Tricky wrote:

Haent had time to dig through the long converstation where this
origionated, but myself and a colleague are wondering why not use this
template instead?

process(clock, reset)
begin
if reset = '1' then
count <= (others => '0');

elsif rising_edge(clock) then
count <= count + 1;

end if;
end process
q <= count;
msb <= count(7);
tc <= '1' when (count = X"FF") else '0';

or am I missing the point?
No, I don't think so. Of course you're right that
your code is the same. But it exposes the internal
state of the process ("count") as a signal that is
global to the architecture; and it splits out the
functionality into many processes. For the counter
example, none of that matters; for bigger examples,
it can have an important impact on encapsulation
and readability. I was simply seeking out viable
alternatives - and trying to find what *really*
works and doesn't work, rather than relying on
myth or (in my case) obsolete information.
--
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.
 
Hi.

Haent had time to dig through the long converstation where this
origionated, but myself and a colleague are wondering why not use this
template instead?

process(clock, reset)
begin
if reset = '1' then
count <= (others => '0');

elsif rising_edge(clock) then
count <= count + 1;

end if;
end process


q <= count;
msb <= count(7);
tc <= '1' when (count = X"FF") else '0'; --(or just use the
"and_reduce" function).

or am I missing the point?
 
Jonathan Bromley schrieb:

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;
We had a discussion about this style some months ago. And nobody found
any problems using this style. Unfortunately for Verilog you have to
split this process into two always-blocks.



Additionally it is a pitfall that the following process looks so similar
to your form 3, but creates the unwanted clk-enable for count_pipe:

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

In my company we had a case where this unwanted clk-enable has caused
some trouble. Therefore form 3 should be the recommended "synchronous
template". It is so easy to forget the unwanted clk-enable - especially
if one tries to put everything into one big process.

Ralf
 
On Aug 19, 10:27 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Tue, 19 Aug 2008 06:47:14 -0700 (PDT), Tricky wrote:
Haent had time to dig through the long converstation where this
origionated, but myself and a colleague are wondering why not use this
template instead?

process(clock, reset)
begin
if reset = '1' then
count <= (others => '0');

elsif rising_edge(clock) then
count <= count + 1;

end if;
end process
q <= count;
msb <= count(7);
tc <= '1' when (count = X"FF") else '0';
or am I missing the point?

No, I don't think so. Of course you're right that
your code is the same. But it exposes the internal
state of the process ("count") as a signal that is
global to the architecture; and it splits out the
functionality into many processes. For the counter
example, none of that matters; for bigger examples,
it can have an important impact on encapsulation
and readability. I was simply seeking out viable
alternatives - and trying to find what *really*
works and doesn't work, rather than relying on
myth or (in my case) obsolete information.
--
Jonathan Bromley, Consultant
I can't say I understand your complaints.

"it exposes the internal state of the process ("count") as a signal
that is global to the architecture"

What is that a problem? One of the issues I have with variables is
that they are hard to debug since they can only be viewed when in
context, so I prefer a signal that is always viewable. It was a long
ago that I worked with signals and maybe the tools I used were not
very good. Has that changed?

"it splits out the functionality into many processes"

Is that really an issue? You are indicating that each concurrent
statement is a process. Why would I care about that? I use
concurrent statements freely. Is there a reason to restrict them?

I want to get work done effectively. I don't care much about finer
points of the language or simulation. I want to find ways that make
the most effective use of *my* time. I don't see how any of these
issues achieve that.

I would almost prefer a language that did not offer so much freedom.
I can't help but think this offers limited value to the coders and
makes life a lot harder for the tool writers.

Rick
 
On Aug 19, 11:58 am, Ralf Hildebrandt <Ralf-Hildebra...@gmx.de> wrote:
Jonathan Bromley schrieb:

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;

We had a discussion about this style some months ago. And nobody found
any problems using this style. Unfortunately for Verilog you have to
split this process into two always-blocks.

Additionally it is a pitfall that the following process looks so similar
to your form 3, but creates the unwanted clk-enable for count_pipe:

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

In my company we had a case where this unwanted clk-enable has caused
some trouble. Therefore form 3 should be the recommended "synchronous
template". It is so easy to forget the unwanted clk-enable - especially
if one tries to put everything into one big process.
I don't get it. Where is the unwanted clock enable, do you mean
"reset"?

If the tool is treating reset in this case as a clock enable, I think
you need a new tool vendor. This is the classic inferred register
model I have seen from every tool vendor I have used.

Rick
 
On Tue, 19 Aug 2008 14:29:55 -0700 (PDT), rickman wrote:

"it exposes the internal state of the process ("count") as a signal
that is global to the architecture"

What is that a problem?
Encapsulation, and separation of concerns - all the
usual stuff that anyone needs to think about as their
designs (be they hardware, software or mechanical)
get larger.

One of the issues I have with variables is
that they are hard to debug since they can only be viewed when in
context, so I prefer a signal that is always viewable. It was a long
ago that I worked with signals and maybe the tools I used were not
very good. Has that changed?
There's no difficulty viewing variables (at least, the static
variables that you declare in processes) in any tools I use.
Dynamically-created variables - locals of a function or procedure,
or automatics in Verilog - may require the use of breakpoints.

"it splits out the functionality into many processes"

Is that really an issue?
It's not a big deal, except that a process is (to my mind)
a block of stuff that has a life of its own; it's nice for
such blocks to be coherent and decoupled, i.e. to be as
far as possible related to one set of concerns and to
be a complete implementation of that set of concerns.
Inevitably in hardware that ideal is unrealizable, but
it's still worth fighting for.

Speaking of "blocks", of course the (very under-utilised)
VHDL "block" construct allows you to group a bunch of
processes, and the signals that link them, without
requiring a module (entity) to encapsulate them.

I want to get work done effectively. I don't care much about finer
points of the language or simulation. I want to find ways that make
the most effective use of *my* time. I don't see how any of these
issues achieve that.
I am very well aware that you are experienced and productive,
so I think it's best for us to agree to differ here and for
me to note the very interesting fact that different people
think about these issues in very different ways.

I would almost prefer a language that did not offer so much freedom.
I can't help but think this offers limited value to the coders and
makes life a lot harder for the tool writers.
Again, perhaps we should agree to differ.

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.
 
On Tue, 19 Aug 2008 14:32:28 -0700 (PDT), rickman wrote:

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

I don't get it. Where is the unwanted clock enable, do you mean
"reset"?
"reset" is a clock DISABLE for the count_pipe register.
Think about what happens when there's a rising edge on
clock at a time when reset is held asserted. We activate
the process, but we test reset and find it is '1' so we
do the reset branch of the logic; this has no effect on
count_pipe, which therefore holds its value.

If the tool is treating reset in this case as a clock enable, I think
you need a new tool vendor. This is the classic inferred register
model I have seen from every tool vendor I have used.
No, it's not. It's my favourite RTL coding error, in which
I add a register to the clocked logic in a process but then
forget to add it to the reset branch because it doesn't
seem to need resetting from a functional point of view.
If the tool did NOT treat "not reset" as a clock enable on
the count_pipe register, it would be generating logic that
behaved differently from the simulation - and that WOULD be
cause for a bug report.

Naturally, none of this weakens the arguments that you have
already presented for resetting every register in an FPGA
design.
--
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.
 
On Aug 20, 6:00 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Tue, 19 Aug 2008 14:29:55 -0700 (PDT), rickman wrote:
"it exposes the internal state of the process ("count") as a signal
that is global to the architecture"

What is that a problem?

Encapsulation, and separation of concerns - all the
usual stuff that anyone needs to think about as their
designs (be they hardware, software or mechanical)
get larger.
Hierarchy in VHDL is best expressed at the entity level, not process
or procedure. As the design gets larger and it becomes apparent that
a different (or simply finer grained) breakdown is needed, that should
be accomplished by creating new entities. That's where the
encapsulation and separation of concerns should occur.

Entity/architectures can also generally be reused in some new
application without modification, processes and procedures generally
can not since they usually need the context that is provided by the
entity/architecture.

One of the issues I have with variables is
that they are hard to debug since they can only be viewed when in
context, so I prefer a signal that is always viewable.  It was a long
ago that I worked with signals and maybe the tools I used were not
very good.  Has that changed?

There's no difficulty viewing variables (at least, the static
variables that you declare in processes) in any tools I use.
You can't view variables in a wave or list windows after the fact
using Modelsim.

When something fails in any simulation, the reason for the failure
many time is in the past, although sometimes the reason is in the
present. The wave and list windows are the available tools for
investigating what happened prior to the failure in order to discern
what was the root cause. When you use variables extensively you lose
at least some of the ability to use those tools to debug. Given that
handicap you must turn to other methods such as re-running the
simulation to either step through code, or add the variables that
you'd like to see to the wave/list windows at sim start (hoping you've
guessed correctly at all of the ones you need). At best you're only
slightly less productive because of this handicap.

Modelsim's log -r /* command is a very powerful debug aid...it get
signals, it doesn't pick up variables.

Dynamically-created variables - locals of a function or procedure,
or automatics in Verilog - may require the use of breakpoints.
Again, the use of breakpoints implies that the simulation was
restarted so that one can get visibility into what led up to the bad
thing. This starts to happen when the use of variables and procedures
has clouded things to the point that one can not easily discern what
is going wrong given the available information (which is the entire
'signal' history). Don't get me wrong, I do like variables and
procedures, but whenever possible I'll use a signal instead of a
variable simply for the debug value.

"it splits out the functionality into many processes"

Is that really an issue?

It's not a big deal,
Whew, I was wondering if someone was charging you a fee per process or
something.

except that a process is (to my mind)
a block of stuff that has a life of its own; it's nice for
such blocks to be coherent and decoupled,
Coherent yes, decoupled no. Anything that is decoupled from the rest
of the world is not needed (and a synthesis tool will see to that by
optimizing it away). I realize that this extreme of 'decoupled' is
more decoupled than what you likely had in mind, but my point is that
every process interacts with some input stimulus and provides some
output. It interacts with and is therefore coupled to other things.
What one really wants to have is good interfaces between these hunks
of code (be they multiple processes or multiple entities) rather than
some unachievable decoupling. Personally I prefer Altera's Avalon
specification as a model interface to use, it provides for
handshaking, latency, etc. at a zero (or minimal) logic resource
overhead.

i.e. to be as
far as possible related to one set of concerns and to
be a complete implementation of that set of concerns.
Inevitably in hardware that ideal is unrealizable, but
it's still worth fighting for.
But the reason it's unrealizable is because of what I just mentioned
above regarding interfaces. Given that every useful process will have
some interface to other surrounding code I don't see what the cause is
that you would be 'fighting for'.

Within an entity, I tend to have multiple clocked processes and
concurrent statements. The physical grouping of what signals go in
this clocked process versus that clocked process has to do with how
closely related the logic is that is required to implement that
signal. Things that are unrelated go in separate processes. I'll
also tend to try to limit the physical size of a process so that it
fits on a screen. The end result is a file where you don't have to
scroll back and forth and all around in order to see what is going
on. During the design process where you're still working on what the
actual logic is, things can freely move (by cut/paste) from one
process to another as the realization sets in that the logic for
signal 'abc' is very similar after all to that which I have for
'xyz'. This type of situation is also where the limited use of
variables is a good thing since I can freely move and modify the code
from one process to the other to textually group together the related
stuff because, when the code being moved only uses signals, that cut/
paste can always be safely done without mucking up something else in
the process that it was cut from.

Speaking of "blocks", of course the (very under-utilised)
VHDL "block" construct allows you to group a bunch of
processes, and the signals that link them,  without
requiring a module (entity) to encapsulate them.
I agree, and would add that the same can also be said for the generate
statement...both quite handy ways to create 'local' signals.

I would almost prefer a language that did not offer so much freedom.
I can't help but think this offers limited value to the coders and
makes life a lot harder for the tool writers.

Again, perhaps we should agree to differ.
I agree with you (Jonathon). The more skilled you get with VHDL, the
more you appreciate all the things that they thought to include (but
you still gripe about the odd constraints).

Abel is a simple language to learn and use but I wouldn't use it today
because VHDL provides much more power for creating not only a design
but a self checking testbench of the design within a single language
environment. That design and testbench can also be as fully
parameterized as I would like it so that they can be reused for some
other project down the road.

Kevin Jennings
 
On Aug 18, 5:23 pm, rickman <gnu...@gmail.com> wrote:
On Aug 18, 4:38 pm, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com
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 rarely use async resets at all and wouldn't really use any of these
forms sticking instead with the simple synchronous process form
(putting the "if (reset = '1') then..." at the top for readability).
The one exception for async resets being the shift register(s) that
take as input the external reset pin and from that generate a reset
signal that is synchronous to any clock(s). Using a reset signal that
is not synchronized to the clock is a design failure waiting to
happen.


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.  
I'd turn it around and say that if there is no functional requirement
to reset a register than don't bother spending the time and adding the
extra code to do so.

Most registers (by quantity) in a design do not have such a
requirement since they hold data and do not control anything. Signals
that control other things (like an 'enable' bit that turns on/off some
function, a state machine) do need resets.

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!
You're right when you say "the async reset does not belong in a
synchronous design"...but I would say that the only exceptions to that
being
- The reset synchronizer
- Any output pins of the top level design that require a specific
power up state prior to having a good clock.

Kevin Jennings
 

Welcome to EDABoard.com

Sponsor

Back
Top