FSM coding style: one or two processes?

The general form of a Finite State Machine (FSM) is given by this diagram:

An FSM in which the Output Logic block depends on only the current state vector is termed a Moore machine; if the Output Logic also depends on the primary inputs, then it is a Mealy machine.

The Output Logic block is shown as a separate block for convenience only. In practice, for a Moore machine, it is normally incorporated into the sequential State Vector block. This will generally save logic and, more importantly, will ensure that the FSM has only sequential (registered) ouputs. A Mealy machine requires combinatorial outputs but, in practice, these outputs are generally rolled into the Next State logic block.

There are a number of ways in which an FSM may be coded. However, the diagram above immediately suggests two styles: specifically, the one-process, and the two-process, styles.

In the two-process style, a combinatorial process codes the Next State logic (and any required combinatorial Mealy outputs), while a separate sequential process codes the state vector (and the Moore outputs). The combinatorial logic may also be coded as concurrent assignments, rather than as an explicit process, but this is also called the 'two process' style here, for simplicity. In the one-process style, a single sequential process codes both (or all three) blocks.

These 'one-process' and 'two-process' definitions are almost universally used for FSMs, but there are exceptions. The Xilinx XST documentation, for example, shows the 'two-process' style with the next-state logic and the state vector in the same process, and the ouput logic in a second process. A three-process style is shown with a process for each block in the diagram above. It might be argued that a three-process split is suitable for Mealy machines, but it normally makes more sense to place the Mealy output derivation in the single 'next state logic' block, since this already has all the required inputs.

The code below includes examples of both styles. In practice, there is generally little to choose between them, but many engineers have strongly-held views on why one may be "better" than the other. The reasons for using the two-process style are usually given as:

  • This style is required if you need to get any combinatorial signals out of the FSM module; the FSM output itself, for example, or a next-state signal
  • This style is more readable, if only because it more closely follows the FSM diagram above
  • The RMM recommends the two-process style (second edition, p110)
  • It is sometimes claimed that the two-process style is easier to optimise.

However, these arguments are not as straightforward as they may appear:

  • In VHDL, at least, the one-process sequential style can produce combinatorial outputs by assigning to variables, and then assigning these variables to signals after the clock branch. This is not necessarily, of course, a Good Thing.
  • The RMM provides no justification for the two-process style, and refers the reader to the 'Optimising Finite State Machines' chapter of the Design Compiler reference manual. That chapter, however, doesn't cover FSM coding styles. The 'VHDL Compiler' and 'HDL Compiler for Verilog' reference manuals do provide various FSM examples; these are coded in a two-process style, but with no suggestion that this is optimal.
  • The optimisation argument is difficult to back up. While it may historically have been true (for Design Compiler, at least), I personally haven't been able to find anything in the Synopsys manuals to suggest that this is still an issue.

The advantages of the one-process style are generally claimed to be:

  • It should simulate faster
  • It is less verbose than the two-process equivalent
  • It is less error-prone, since it doesn't require a separate combinatorial process to derive the next state (the coder must make sure that this combinatorial process has a correct sensitivity list, and that default assignments are made).

What is indisputable, however, is that in some circumstances, you will need to distribute the 'next state' signal from your FSM. Large designs do not use isolated FSMs, but will break large controllers into two or more communicating FSMs. These FSMs will generally communicate using either the combinatorial next state signal itself, or flags based on the next state signal. The cleanest way to produce these signals is to use the two-process style. If, however, the controller can tolerate an additional cycle of latency in these signals, then the registered state vector may itself be distributed, and a one-process style may be used.