For what it's worth, I'm with Mike and company about using a single
process for state machines. In my experience, if a state machine is
so complex that you can't maintain, change, or debug it (and you think
you want to debug it using a separate next_state variable), you have a
very basic problem: that your state machine is too complex. In that
case, you need to look at factoring the state machine into interacting
sub-machines, or taking a step back and rethinking how your design
works, or do something else creative. In some cases, the machine
really *is* a beast from hell, and you might want to generate the
state machine transitions from another higher level of synthesis (a C
program or a script, assuming you know how) where you can abstract and
express the design intent more clearly, or even use one of those
dreaded fancy GUI tools (shudder...). Or whatever gets you to a
simpler, more understandable, easier to maintain design.
Now indulge me while I rant a little bit about the two process vs one
process approaches.
It makes a lot of sense to me, that the two-process machine is a
throwback to a slavish adherence to the Moore/Mealy behaviour
dichotomy that came out of 1950's era mathematics. And I think it was
done wrong, way back in the 1980's, at the beginning of VHDL tutorials
and examples. However, I can see how separating the next state and
output computation from the clocking process makes sense, but *not* in
the naive two-process "digital design 101" style.
Consider a machine with inputs Xin and current state Q, which computes
outputs Yout and next state Q'. In a Moore machine, Yout is a
function of Q, while in a Mealy, it's a function of Q and Xin.
The textbook two-process Moore approach looks like this (taking some
liberties with VHDL syntax)
-- two-process approach
process1 (clk)
Q <= Q'; -- clocked
process2 -- combinatorial
Q' <= f_next(Q,Xin);
Y <= f_out(Q);
-- end two-process approach
To simplify the code, I could simply write it as
-- one process approach
process1 (clk)
Q <= f_next(Q,Xin);
Yout <= f'_out(Q,Xin); -- where f'_out is derived from f_out above
-- end one-process approach
which is the essence of the single-process camp. Here, Xin,Yout, and Q
could be multiple signals, say bundled into a record or an array.
I can move Y from process 1 to process 2 by changing Q to Q', and
fiddling with how I define f_out. The two are more or less isomorphic
modulo some minor timing constraints - half of the arguments on this
thread are about whether they are isomorphic or not, depending on what
assumptions you make about undefined cases.
The key idea here is separating f_next from the process1 assignment.
One reason I want to do this, is to support one critical speed
optimization that I might want to do, which is to handle multiple
inputs on a single clock cycle. In the context of a reactive state
machine, where you can't take liberties with time, this makes little
or no sense. But consider a state machine which decodes MPEG VLC
(Huffman codes) or operates sequentially on an independent input
stream, like a DSP stream processor. If you can handle multiple inputs
per clock, you come out ahead (modulo the act of increased
combinatorial complexity delay affecting minimum clock cycle time).
If I want to handle two symbols per clock, and I have already
separated my design into *functions*, I can almost trivially rewrite
my state machine thus:
process1(clk) -- clocked
Q <= f_next(f_next(Q,Xin1), Xin2);
Yout1 <= f_out(Q,Xin1);
Yout2 <= f_out(f_next(Q), Xin2);
-- all done!
This is a little trickier with Mealy machines, and of course, in some
cases it's not apparent what multiple output events (Y1 and Y2) mean,
but that's a different problem. The idea I'm trying to convey is that
I can string together output functions and next state functions to
modify the behaviour of the machine easily, at a very high level (for
example,doing a one-input per clock to a two-input per clock change).
The key idea is that the computation of f_next and f_out, and the
actual flop assignments, are separated. In my case one in functions
and one in a process. In the two-process approach, by two processes in
which the functions f_next and f_out are still hard-coded.
Clearly, you can't ignore the combinatorial complexity and hence the
time penalty per clock cycle that may come from this. And I'll admit
that this example is a little contrived, but it makes perfect sense
when related to the mathematical viewpoint of automata, from which the
Moore/Mealy dichotomy developed. This is just the VHDL version of
"lifting" the state transition function from a single symbol in an
input alphabet to finite-length strings on the the alphabet.
In my humble opinion, this is how the separation of concerns *ought*
to be presented: as one process, with functions. Then view the single
process approach as writing the function bodies in-line. The two-
process approach is a bastard stepchild of the single-process, more
functional approach. But the ugly stepchild has been allowed to breed
and corrupt the young people of today nonetheless
OK, I'll shut up now
Soapbox mode off.
- Kenn