UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Scholar ronnywebers
Scholar
10,675 Views
Registered: ‎10-10-2014

FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

There seem to be 3 kinds of 'templates' for FSM, using 1, 2 or 3 processes. 

 

UG901 - Synthesis, HDL coding techniques (chapter 3), FSM components, shows a 3 process FSM like this :

 

fsm 3 process.png

 

the Verilog and VHDL example a few pages further shows a single process FSM.

 

I've read in some forum posts that 2 and 3 process coding style is inherently more buggy, and that 1 process coding is preferred. But without any further explanation why. Is this a correct statement? As UG901 only shows a single process FSM, I'm tempted to believe this. But many books use 2 or 3 process FSMs.

 

** kudo if the answer was helpful. Accept as solution if your question is answered **
0 Kudos
1 Solution

Accepted Solutions
Historian
Historian
17,092 Views
Registered: ‎01-23-2009

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

First, I want to state that this is all just "style".

 

Done properly, all three types of implementations can be equally correct and (more or less) equally efficient in terms of the final result.

 

So it really is style - which is easier.

 

I (and others) will argue that the one process state machine is the easiest. Some of the reasons have been listed here - no need to deal with complicated sensitivity lists, not having two versions of your state variable (the current and the next), not being able to infer accidental latches (although this is a bug not really specific to FSM coding style). But for me, mostly, it is just less typing (hence less opportunity for bugs)!

 

The one process state machine also has the advantage that it can directly code for the CE of the flip-flop - inside the clocked process an "elseless if" maps directly to the clock enable. if the "next_state" logic is separate you need to explicitly (either at the top or in each if/else branch) explicitly code "next_state <= state" which codes for a multiplexer with feedback. The tool can almost certainly move back and forth between these two (MUX with feedback vs. CE) during optimization, but I find the concept of the "else do nothing" more in line with what we are trying to do in a state machine.

 

Now, as for the question about outputs, we do have two competing coding styles. The 1 process state machine cannot do combinatorial outputs. Thus, it is impossible to have anything in the "output function". This limits the one process state machine to be "Simple Moore" - a Moore machine with the output logic being identically equal to the current state. This meshes well with the concept that "all module outputs come from flip-flops", when the state machine outputs have to leave a module.

 

While this looks like a big restriction, it can be minimized - any Moore machine can essentially be converted to a Simple Moore machine by choosing the state bits carefully (or having more state bits). Furthermore, Mealy machines can be converted to Moore machines, although this can increase the length of the critical path from the inputs.

 

So, we end up with the statement that Mealy machines can't be the outputs of a module - and this is a restriction that I wholeheartedly agree with - you should never have a combinatorial path through a module!

 

As for the idea of putting flip-flops after the "output function",  that would certainly meet the "all outputs come from flip-flops" rule. But this combined thing is a "state machine followed by flip-flops" - it is not "just a state machine". With the extra flip-flops the outputs reflect what happened two clocks ago, not on the previous clock - so, by definition, this is no longer truly a "state machine".

 

And, by the way, there is nothing that says that you always have to do the same thing. While I am a strong proponent of the 1 process state machine, I occasionally (OK, rarely) use a two process state machine - mostly when I need to convert a Mealy machine to a Moore machine (using the "next_state" logic as part of generating the outputs through the "output function".

 

Avrum

11 Replies
Scholar drjohnsmith
Scholar
10,654 Views
Registered: ‎07-09-2009

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

with one big process , our less likely / its impossible to make un intended latches,

 

as everything is under the clock 

 

two and three process state machines tend to have big sensitivity lists in VHDL,

     ( Unless you use the new all option !! ) 

 

company policy is the other driving force.

 

We used to use two process blocks 20 odd years ago, but now all single process.

 

Unless your doing asynchronous state machines, 

     then all bets are off...

 

 

<== If this was helpful, please feel free to give Kudos, and close if it answers your question ==>
Xilinx Employee
Xilinx Employee
10,653 Views
Registered: ‎05-07-2015

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

HI @ronnywebers

 

>>ive read in some forum posts that 2 and 3 process coding style is inherently more buggy,
I don't see why this should be the case. 

Writing a 2 process FSM is just a convenience to separate out the sequential  part  of the FSM  and describe the  next logic  in a  combination-logic process.
the sequential process will simply have curr_state <= next_state statement.

UG901 providing  a single sequential process FSM  example should not be interpreted as a suggestion to prefer the single process approach.





Thanks
Bharath
--------------------------------------------------​--------------------------------------------
Please mark the Answer as "Accept as solution" if information provided addresses your query/concern.
Give Kudos to a post which you think is helpful.
--------------------------------------------------​-------------------------------------------
Scholar ronnywebers
Scholar
10,639 Views
Registered: ‎10-10-2014

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

thanks @nagabhar@drjohnsmith - additional question if I may : if process 3 (output) generates signals going to the outside world, like i.e. SPI interface, wouldn't an output register be necessary? Or a process 3 made a clocked process instead of combinatorial?

** kudo if the answer was helpful. Accept as solution if your question is answered **
0 Kudos
Historian
Historian
17,093 Views
Registered: ‎01-23-2009

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

First, I want to state that this is all just "style".

 

Done properly, all three types of implementations can be equally correct and (more or less) equally efficient in terms of the final result.

 

So it really is style - which is easier.

 

I (and others) will argue that the one process state machine is the easiest. Some of the reasons have been listed here - no need to deal with complicated sensitivity lists, not having two versions of your state variable (the current and the next), not being able to infer accidental latches (although this is a bug not really specific to FSM coding style). But for me, mostly, it is just less typing (hence less opportunity for bugs)!

 

The one process state machine also has the advantage that it can directly code for the CE of the flip-flop - inside the clocked process an "elseless if" maps directly to the clock enable. if the "next_state" logic is separate you need to explicitly (either at the top or in each if/else branch) explicitly code "next_state <= state" which codes for a multiplexer with feedback. The tool can almost certainly move back and forth between these two (MUX with feedback vs. CE) during optimization, but I find the concept of the "else do nothing" more in line with what we are trying to do in a state machine.

 

Now, as for the question about outputs, we do have two competing coding styles. The 1 process state machine cannot do combinatorial outputs. Thus, it is impossible to have anything in the "output function". This limits the one process state machine to be "Simple Moore" - a Moore machine with the output logic being identically equal to the current state. This meshes well with the concept that "all module outputs come from flip-flops", when the state machine outputs have to leave a module.

 

While this looks like a big restriction, it can be minimized - any Moore machine can essentially be converted to a Simple Moore machine by choosing the state bits carefully (or having more state bits). Furthermore, Mealy machines can be converted to Moore machines, although this can increase the length of the critical path from the inputs.

 

So, we end up with the statement that Mealy machines can't be the outputs of a module - and this is a restriction that I wholeheartedly agree with - you should never have a combinatorial path through a module!

 

As for the idea of putting flip-flops after the "output function",  that would certainly meet the "all outputs come from flip-flops" rule. But this combined thing is a "state machine followed by flip-flops" - it is not "just a state machine". With the extra flip-flops the outputs reflect what happened two clocks ago, not on the previous clock - so, by definition, this is no longer truly a "state machine".

 

And, by the way, there is nothing that says that you always have to do the same thing. While I am a strong proponent of the 1 process state machine, I occasionally (OK, rarely) use a two process state machine - mostly when I need to convert a Mealy machine to a Moore machine (using the "next_state" logic as part of generating the outputs through the "output function".

 

Avrum

Scholar drjohnsmith
Scholar
10,617 Views
Registered: ‎07-09-2009

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

output registers,

 

I'd typically re register the output of the FSM , 

     in which case the register might be put into the IOB, 

        

Vivado will just work till it meets your timing constraints, 

    so as to is it in the IOB or not, is not 100 % to do with the FSM.

 

Vivado could register duplicate, have one inside the FPGA , and a duplicate in the IOB,

   

remember , TCL can fix anything.

 

 

<== If this was helpful, please feel free to give Kudos, and close if it answers your question ==>
Scholar ronnywebers
Scholar
10,579 Views
Registered: ‎10-10-2014

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

thanks @avrumw

 

I've rewritten my 3 process FSM to a single process FSM, and it looks indeed much cleaner as you said.

 

I'm wondering if I can get it even more cleaner, so could you please review my questions below. Based on your feedback I will cleanup the code further in a next reply.

 

 

	
	-- signal declarations
	...   
	signal s_fifo_wren      : std_logic := '0';

    constant kTRIGGER_LEVEL : std_logic_vector(13 downto 0) := "00000000100101"; 
    
    type trigger_state_type is (idle, waitingfortrigger, triggered);
    signal trigger_state : trigger_state_type;
    
    signal s_holdoff_count                  : unsigned(7 downto 0);
    signal s_signal_above_triggerlevel      : std_logic;
    signal s_signal_above_triggerlevel_p1   : std_logic;
    signal s_rising_edge_tick               : std_logic;
    
    signal s_log_event_tick                 : std_logic;
	...

begin

	-- *** 2-FF CDC for FSM enable bit (reg_io_0, bit 1) ***
	-- the reg_io_0(1) bit comes from a CPU register in another (at least 2x faster) clock domain
	-- we need to sync this bit into the CLOCK_IO domain first, before using it in the FSM 	
	process(CLOCK_IO)
	begin
		if rising_edge(CLOCK_IO) then
		    if RESET_IO = '1' then						-- synchronously reset the CDC regs
		        s_enable_sync_1 <= '0';
		        s_enable_sync_2 <= '0';
		    else
		        s_enable_sync_1 <= reg_io_0(1);			-- bit coming from CLOCK_CPU domain
		        s_enable_sync_2 <= s_enable_sync_1;		-- 2-FF synchronizer
		    end if;
		end if;
	end process; 

	...

    -- *** oscilloscope 'rising edge trigger' ***
    
    -- first detect if the ADC input signal goes above trigger level (hard-coded constant for this example)
    s_signal_above_triggerlevel <= '1' when reg_adc_data_i > kTRIGGER_LEVEL else '0';
    
    -- generate a 'rising edge tick' when ADC input crosses this trigger level from low to high
    s_signal_above_triggerlevel_p1 <= s_signal_above_triggerlevel when rising_edge(CLOCK_IO);
    s_rising_edge_tick <= s_signal_above_triggerlevel and not s_signal_above_triggerlevel_p1;
    
    -- *** FSM *** 
	-- function : 		
	-- on a s_rising_edge_tick, generate a s_log_event_tick
	-- then wait a specified amount of clock cycles (s_holdoff_count) before accepting a new trigger

    fsm_th_trig : process(CLOCK_IO)
    begin
        if rising_edge(CLOCK_IO) then
            if RESET_IO = '1' then      -- synchronous reset
                trigger_state <= idle;
                s_holdoff_count <= (others => '0');
                s_log_event_tick <= '0';
            else
                case trigger_state is 
                    when idle =>
                        s_log_event_tick <= '0';
                        if s_enable_sync_2 = '1' then
                            trigger_state <= waitingfortrigger;
                        else
                            trigger_state <= idle;
                        end if;
                        
                    when waitingfortrigger =>
                        if s_enable_sync_2 = '0' then
                            trigger_state <= idle;
                            s_log_event_tick <= '0';
                        else
                            if s_rising_edge_tick = '1' then
                                s_holdoff_count <= X"40";       -- preload holdoff_count, hardcoded for this example
                                s_log_event_tick <= '1';        -- generate single log tick on rising edge
                                trigger_state <= triggered;
                            else
                                trigger_state <= waitingfortrigger;
                                s_log_event_tick <= '0';
                            end if;
                        end if; 
                        
                    when triggered =>
                        s_log_event_tick <= '0';                    
                        if s_holdoff_count = X"00" then
                            trigger_state <= waitingfortrigger; -- stay here until holdoff_count expires
                        else
                            s_holdoff_count <= s_holdoff_count - 1;
                            trigger_state <= triggered;
                        end if;
                        
                    when others =>
                        s_log_event_tick <= '0';                           
                        trigger_state <= idle;
                end case;
            end if;
        end if;
    end process fsm_th_trig;
    
	-- connect signals to the event log FIFO
    s_fifo_wren <= s_log_event_tick;	-- s_log_event_tick = output of the FSM
	s_fifo_data <= s_event_time;		-- s_event_time is some counter running on CLOCK_IO	

 

I've put some explanation in comments, I hope this is clear enough

 

Q1 : if I understand your comment 'inside the clocked process an "elseless if" maps directly to the clock enable' means that I can rewrite for example the idle state like this :

 

when idle =>
    s_log_event_tick <= '0';
    if s_enable_sync_2 = '1' then
        trigger_state <= waitingfortrigger;
    end if;

 so the else is not needed, just like in a regular clocked counter where the count <= count + 1 does not need an else case with count <= count

 

Q2 : can I drop the 'when others', or is it safer to leave it there?

 

Q3 : can I put a default value of '0' for s_log_event_tick  before the line 'case trigger state is', such that I only need to write s_log_event_tick <= '1' in the waitingfortrigger state?

 

 

** kudo if the answer was helpful. Accept as solution if your question is answered **
0 Kudos
Historian
Historian
10,562 Views
Registered: ‎01-23-2009

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

I am not a VHDL expert so I am a bit hesitant to answer these (I use Verilog/SystemVerilog), but...

 

Q1: Yes

 

Q2: It is probably a good idea to keep the "when others".

 

In terms of behavior, this doesn't do anything - without it, you are implicitly stating "If I am in a state other than the ones I have listed here, then do nothing". Since you are enumerating all the legal states, then this means "If I am in an illegal state then stay here" - since you shouldn't ever be in an illegal state, then this doesn't matter.

 

However, failures happen (see "Single Event Upsets"), and it is generally preferable for a failed state machine to return to one of the legal states.

 

Of course this all gets far more complex when the synthesis tool starts recoding the state vectors...

 

Q3: This is certainly legal in Verilog/SystemVerilog, and I am pretty certain it is also legal in VHDL.

 

Avrum

Scholar drjohnsmith
Scholar
10,554 Views
Registered: ‎07-09-2009

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

you will need the when others , especially if you simulate.

 

VHDL , has 9 logic levels,

     the state machine 'only' defines 0 or 1 levels, so the tools complain about the missing states unless you include the others.

 

The joys of a tight defined language, I love it, but others hate it.

 

If you have the logic space in the FPGA,

     a style that I see in companies is to deifne a oops state ,

           and the 'others' sate jumps to the oops state

 

 and the oops jumps to the reset state.

 

then you have the option of defining an error flag in the oops state for the run time system,

     

     when oops  =>

          trigger_state <= idle;

   

     when others =>

           trigger_state <= oops;

 

 

again in some companies standards are the constant,  put a B in the front to 110 % guarantee its binary.

 

constant kTRIGGER_LEVEL : std_logic_vector(13 downto 0) := B"00000000100101";

 

              

            

<== If this was helpful, please feel free to give Kudos, and close if it answers your question ==>
Scholar ronnywebers
Scholar
10,475 Views
Registered: ‎10-10-2014

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

thanks @drjohnsmith@avrumw

 

I've updated part of the code with your suggestions, this becomes much more readable - guess more optimal is not possible? (I left out the oops state, but thanks for the great tip @drjohnsmith)

 

Q: about the counter : is this common practice to code it into the FSM, or is it better to leave counters out of the FSM, and use enable signals etcetera?

 

fsm_th_trig : process(CLOCK_IO)
begin
	if rising_edge(CLOCK_IO) then
		if RESET_IO = '1' then      -- synchronous reset
			trigger_state <= idle;
			s_holdoff_count <= (others => '0');
			s_log_event_tick <= '0';
		else
			-- default signal values
			s_log_event_tick <= '0';
			
			-- states
			case trigger_state is 
				when idle =>
					if s_enable_sync_2 = '1' then
						trigger_state <= waitingfortrigger;
					end if;
					
				when waitingfortrigger =>
					if s_enable_sync_2 = '0' then
						trigger_state <= idle;
					else
						if s_rising_edge_tick = '1' then
							s_holdoff_count <= X"40";       -- preload holdoff_count, hardcoded for this example
							s_log_event_tick <= '1';        -- generate single log tick on rising edge
							trigger_state <= triggered;
						end if;
					end if; 
					
				when triggered =>                
					if s_holdoff_count = X"00" then
						trigger_state <= waitingfortrigger; -- stay here until holdoff_count expires
					else
						s_holdoff_count <= s_holdoff_count - 1;
					end if;
					
				when others =>                        
					trigger_state <= idle;
			end case;
		end if;
	end if;
end process fsm_th_trig;

 

** kudo if the answer was helpful. Accept as solution if your question is answered **
0 Kudos
Scholar drjohnsmith
Scholar
6,288 Views
Registered: ‎07-09-2009

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

looks very professional,

 

as for counter in sm or outside with an enable..

 

Easier to read in the sm, 

    and most tools most of the time understand what you want and give a enabled counter,

 

depends how critical / close to the edge you are on speed / size 

 

For this one, I'd probably go the way you have,

 

but , despite what some might say, its down to taste as much as anything

<== If this was helpful, please feel free to give Kudos, and close if it answers your question ==>
Historian
Historian
6,281 Views
Registered: ‎01-23-2009

Re: FSM coding - 1 vs 2 vs 3 process style - which one is preferred

Jump to solution

I highly prefer keeping the counter in the same process as the state machine.

 

Trying to keep the counter separate has some disadvantages...

 

If you try and use Moore outputs for communication between the two, then you end up with one clock of latency. This can be compensated for by reducing the count value by one, but creates corner cases (what if your counter reload value is only 1?)

 

If you try and use Mealy outputs for the communication, then you end up repeating case conditions in both your state machine and counter, For example, your counter increment condition in the separate counter would be (forgive the language change...)

 

 

if ((trigger_state ==  waitingfortrigger) && !s_enable_sync2 && s_rising_edge_tick) 
   s_holdoff_count <= 8'h40
else if ((trigger_state == triggered) && (s_holdoff_count != 0))
   s_holdoff_count <= s_holdoff_count - 1'b1;

which ends up replicating a whole whack of the conditions that already exist (and hence are decoded for) in the state machine. The tool can probably share this logic, but it quickly becomes difficult to maintain this code if it needs to change to fix bugs (as the conditions exist in 2 places).

 

Conversely, I don't see any disadvantage to keeping them in the same process. The tools are capable of separating the "state machine" part of the process from the "counter" part of the process and still do all the optimizations on the state machine.

 

Avrum