I am aware that synthesis tools have not always done
the necessary register merging in this situation,
and I suspect that putting the final signal assignments
outside the if statement is effectively a hack to give
the synthesis tool an appropriate hint. But it means
that Q1 and Q2 no longer follow the proper synthesis
template.
"the proper synthesis template" ???
Templates are useful in describing a typical mapping of code structure
to gates and registers, but should not be used as exclusive examples
of good code/design.
If users (and tools, some of which already have) quit focusing on code
templates, and start understanding (and implementing) clock-cycle
behavior, then there will be less confusion. It also leaves more room
for optimization. For example, if register retiming optimizations are
used, the structure of registers and logic will change from what was
literally described anyway. Only when the structure of the
implementation is functionally important (e.g. in synchronization
boundaries) should we focus on structurally clear coding styles, along
with disabling such optimizations.
Take the following example:
signal s1, s2...
....
process (clk) is
variable v1...
begin
if rising_edge(clk) then
v1 := f1(v1, s2);
end if;
s1 <= f2(v1);
end process;
In the example above, s1 is the combinatorial output of the f2
function of a registered v1, or
s1 = f2(registered(v1));
If the assignment to s1 is moved inside the clocked if clause, then s1
becomes the registered f2 of the combinatorial versions of v1.
s1 = registered(f2(v1));
In either case, without an asynchronous reset, the cycle based
behavior is the same, but the implementation is slightly different. I
have even seen Synplify optimize two signals assigned to the same
function, one inside and the other outside the clocked clause,
optimized into the same register and function, which is still
functionally (on a clock cycle basis) correct.
As Mike T aptly pointed out, asynchronous reset creates a meaningful
difference between the two styles (if s1 is assigned inside the
clocked clause, it must also be reset in the reset clause, whereas if
it is after the clocked clause, it need not be included in the reset
clause.)
Naturally, if you need a combinatorial output from registered logic,
this example is the only way to do it in the same process with the
clocked logic, avoiding the need for "public" signals for the
registers. Jonathan B's use of this style for state machine outputs is
another unique and useful application.
Andy