Module 5: Behavioral Verilog for Synthesis ยท 5.6 Modeling Finite State Machines in Verilog

The Three-Process Style

Two processes separated the clocked from the combinational. Why split the combinational half again? Because deciding where the machine goes and deciding what it says are different jobs, and in one block every edit to one risks touching the other.

Three blocks

// the state register
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) state <= IDLE;
    else        state <= next_state;
end

// next state only
always @(*) begin
    next_state = state;
    case (state)
        IDLE:   if (start) next_state = ACTIVE;
        ACTIVE: if (done)  next_state = DONE;
        DONE:              next_state = IDLE;
    endcase
end

// outputs only (Moore)
always @(*) begin
    out = 1'b0;
    case (state)
        ACTIVE: out = 1'b1;
    endcase
end

The first block is the same as ever. The second has no outputs in it. The third has no transitions in it.

What it gives

Read the second block and you know how the machine moves, with nothing else in the way. Read the third and you know what each state produces. Add an output and only the third block changes; add a state and the second changes, and the third only if the state drives something. Formal tools like this separation too: a check on reachability or on transition legality can look at the second block alone.

What it costs

Three blocks to keep in step. For a small machine it is ceremony; the two-state detector does not need it. And the separation cuts both ways: add a state to the transition block and forget the output block, and the new state’s outputs silently take the defaults.

The three styles

one process two processes three processes
blocks 1 2 3
output timing registered, a cycle late combinational, same cycle combinational, same cycle
Mealy outputs no yes yes
latch risk none low, with defaults low, with defaults in each block
best for small machines, outputs to another domain the default large machines, formal work

No row makes one of them right in general; it is a judgement about the size of the machine and what its outputs are for.

The detector once more

`timescale 1ns / 1ps
`default_nettype none

module packet_detector_3proc (
    input  wire clk, rst_n, bit_in,
    output reg  found
);
    localparam S0 = 1'b0, S1 = 1'b1;
    reg state, next_state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) state <= S0;
        else        state <= next_state;
    end

    always @(*) begin
        next_state = state;
        case (state)
            S0: if (bit_in)  next_state = S1;
            S1: if (!bit_in) next_state = S0;
                else         next_state = S1;
        endcase
    end

    always @(*) begin                       // Mealy: state and bit_in
        found = 1'b0;
        if (state == S1 && !bit_in) found = 1'b1;
    end
endmodule

Three versions of one two-state machine. The hardware barely changes between them; what changes is how easily each is read, changed and proved.

Practice Exercise

Sketch a four-state vending machine, IDLE, COIN_RECEIVED, DISPENSE, CHANGE_BACK, in all three styles. Which makes the output logic for dispense and change_out easiest to read without tracing the transitions?

Solution

All three work; the question is which you would rather review. The three-process version wins on this question, because the output block answers “what does each state drive” on its own, with the transitions out of sight. In the one-process version the outputs are interleaved with transitions, and in the two-process version they share a block, so confirming that dispense never asserts outside DISPENSE means reading conditions you do not care about. The full vending machine, with its datapath, is the third worked example in the last article of this section.

The next article is about encodings and what happens when a machine lands in a state you never defined.

โ† The Two-Process Style Encoding and Safe State Machines โ†’