FSM recurring state technique

R

revkarol

Guest
Let's say I have an FSM such as the example on the Altera website.
This is a typical 4-state FSM in the Moore style.

Now, let's say I want a state where the inputs don't change but I want the outputs to change (say, a counter output). For example, I want to get to state s2 and when I do, I'll stay in state s2 for say 10 cycles and then exit to state s3. I'm not sure how to do this. Using the format of the example below, because the state doesn't change, the output is latched.

This seems like a standard problem, but I don't know the standard solution.



Advance thanks,
Karol.





-- A Moore machine's outputs are dependent only on the current state.
-- The output is written only when the state changes. (State
-- transitions are synchronous.)

library ieee;
use ieee.std_logic_1164.all;

entity moore_4s is

port(
clk : in std_logic;
data_in : in std_logic;
reset : in std_logic;
data_out : out std_logic_vector(1 downto 0)
);

end entity;

architecture rtl of moore_4s is

-- Build an enumerated type for the state machine
type state_type is (s0, s1, s2, s3);

-- Register to hold the current state
signal state : state_type;

begin
-- Logic to advance to the next state
process (clk, reset)
begin
if reset = '1' then
state <= s0;
elsif (rising_edge(clk)) then
case state is
when s0=>
if data_in = '1' then
state <= s1;
else
state <= s0;
end if;
when s1=>
if data_in = '1' then
state <= s2;

else
state <= s1;
end if;
when s2=>
if data_in = '1' then
-- here I would have something like
-- if counter > 10 then
state <= s3;
else
state <= s2;
end if;
when s3 =>
if data_in = '1' then
state <= s0;
else
state <= s3;
end if;
end case;
end if;
end process;

-- Output depends solely on the current state
process (state)
begin

case state is
when s0 =>
data_out <= "00";
when s1 =>
data_out <= "01";
when s2 =>
data_out <= "10";
-- here I would have
data_out <= value depending on counter;
when s3 =>
data_out <= "11";
end case;
end process;

end rtl;
 
On Friday, 30 January 2015 14:12:42 UTC-2, Gabor Sz wrote:
revkarol wrote:
Let's say I have an FSM such as the example on the Altera website.
This is a typical 4-state FSM in the Moore style.

Now, let's say I want a state where the inputs don't change but I want the outputs to change (say, a counter output). For example, I want to get to state s2 and when I do, I'll stay in state s2 for say 10 cycles and then exit to state s3. I'm not sure how to do this. Using the format of the example below, because the state doesn't change, the output is latched.

This seems like a standard problem, but I don't know the standard solution.



Advance thanks,
Karol.





-- A Moore machine's outputs are dependent only on the current state.
-- The output is written only when the state changes. (State
-- transitions are synchronous.)

library ieee;
use ieee.std_logic_1164.all;

entity moore_4s is

port(
clk : in std_logic;
data_in : in std_logic;
reset : in std_logic;
data_out : out std_logic_vector(1 downto 0)
);

end entity;

architecture rtl of moore_4s is

-- Build an enumerated type for the state machine
type state_type is (s0, s1, s2, s3);

-- Register to hold the current state
signal state : state_type;

begin
-- Logic to advance to the next state
process (clk, reset)
begin
if reset = '1' then
state <= s0;
elsif (rising_edge(clk)) then
case state is
when s0=
if data_in = '1' then
state <= s1;
else
state <= s0;
end if;
when s1=
if data_in = '1' then
state <= s2;

else
state <= s1;
end if;
when s2=
if data_in = '1' then
-- here I would have something like
-- if counter > 10 then
state <= s3;
else
state <= s2;
end if;
when s3 =
if data_in = '1' then
state <= s0;
else
state <= s3;
end if;
end case;
end if;
end process;

-- Output depends solely on the current state
process (state)
begin

case state is
when s0 =
data_out <= "00";
when s1 =
data_out <= "01";
when s2 =
data_out <= "10";
-- here I would have
data_out <= value depending on counter;
when s3 =
data_out <= "11";
end case;
end process;

end rtl;

It's not hard to add "counter" as a ranged integer and
then increment it while in state s2. Then the only
other thing you need is to reset the counter before
entering state s2. You can do this in several ways:

1) reset "counter" in all other states.
2) reset "counter" in states that can lead to s2.
3) reset "counter" in the transition terms (when assigning
state <= s2 from another state).

There are other ways to do this including building a timer
"subroutine." I've used this is more complex FSM's where
I needed a variable amount of time spent in a number of
different states. In this case I have a state that just
counts down on a counter until it reaches 0. States that
need the delay will set up their outputs as required, set
the delay value into the counter, store the "return state"
in another signal of the same type as "state" and then
set "state" to the wait state. When the counter is decremented
to zero, the wait state then just assigns state to the return
state.

--
Gabor

Hi Gabor,

I think I follow you. So what I think I should do is create another clocked process that deals with the counting (and the special output based on the counting). When I enter the correct state in the FSM, I set an enable for this clocked process. Without the enable the process does essentially nothing. Then when I'm done I set a "finished" flag, and that in turn triggers the FSM to exit the current state and de-asserts the enable flag.

Many thanks,
Karol.
 
On Fri, 30 Jan 2015 09:22:18 -0800, revkarol wrote:
Hi Gabor,

I think I follow you. So what I think I should do is create another
clocked process that deals with the counting (and the special output
based on the counting). When I enter the correct state in the FSM, I
set an enable for this clocked process. Without the enable the process
does essentially nothing. Then when I'm done I set a "finished" flag,
and that in turn triggers the FSM to exit the current state and
de-asserts the enable flag.

Many thanks,
Karol.
More processes = more problems.

I've frequently done things like this (untested, so beware the syntax).

signal go : boolean;
signal count : integer range 0 to 15;
signal lamp : boolean;
type t_state is (IDLE, OFF, ON);
signal state : t_state;

....

More processes = more problems.

I've frequently done things like below (untested, so beware the syntax).
Note two important things.

1) The entire FSM is one process. The FPGA vendors LOVE to tell you to
use two process state machines, even for simple Moore ones. They're
wrong, it just adds confusion to your world.

2) The states have real people names, not S0, S1, S2.

signal go : boolean;
signal count : integer range 0 to 15;
signal lamp : boolean;
type t_state is (IDLE, LAMPOFF, LAMPON);
signal state : t_state;

....

FSM: process(clk)
begin
if rising_edge(clk) then
lamp <= false;

case state is
when IDLE =>
count <= 0;
if go then
state <= LAMPOFF;
end if;

when LAMPOFF =>
if count = 15 then
count <= 0;
state <= LAMPON;
else
count <= count + 1;
end if;

when LAMPON =>
lamp <= true;
if count = 15 then
count <= 0;
state <= IDLE;
else
count <= count + 1;
end if;
end case;
end if;
end process FSM;

--
Rob Gaddi, Highland Technology -- www.highlandtechnology.com
Email address domain is currently out of order. See above to fix.
 
On Fri, 30 Jan 2015 09:22:18 -0800, revkarol wrote:
Hi Gabor,

I think I follow you. So what I think I should do is create another
clocked process that deals with the counting (and the special output
based on the counting). When I enter the correct state in the FSM, I
set an enable for this clocked process. Without the enable the process
does essentially nothing. Then when I'm done I set a "finished" flag,
and that in turn triggers the FSM to exit the current state and
de-asserts the enable flag.

Many thanks,
Karol.

More processes = more problems.

I've frequently done things like below (untested, so beware the syntax).
Note two important things.

1) The entire FSM is one process. The FPGA vendors LOVE to tell you to
use two process state machines, even for simple Moore ones. They're
wrong, it just adds confusion to your world.

2) The states have real people names, not S0, S1, S2.

signal go : boolean;
signal count : integer range 0 to 15;
signal lamp : boolean;
type t_state is (IDLE, LAMPOFF, LAMPON);
signal state : t_state;

....

FSM: process(clk)
begin
if rising_edge(clk) then
lamp <= false;

case state is
when IDLE =>
count <= 0;
if go then
state <= LAMPOFF;
end if;

when LAMPOFF =>
if count = 15 then
count <= 0;
state <= LAMPON;
else
count <= count + 1;
end if;

when LAMPON =>
lamp <= true;
if count = 15 then
count <= 0;
state <= IDLE;
else
count <= count + 1;
end if;
end case;
end if;
end process FSM;

--
Rob Gaddi, Highland Technology -- www.highlandtechnology.com
Email address domain is currently out of order. See above to fix.
 
revkarol wrote:
Let's say I have an FSM such as the example on the Altera website.
This is a typical 4-state FSM in the Moore style.

Now, let's say I want a state where the inputs don't change but I want the outputs to change (say, a counter output). For example, I want to get to state s2 and when I do, I'll stay in state s2 for say 10 cycles and then exit to state s3. I'm not sure how to do this. Using the format of the example below, because the state doesn't change, the output is latched.

This seems like a standard problem, but I don't know the standard solution.



Advance thanks,
Karol.





-- A Moore machine's outputs are dependent only on the current state.
-- The output is written only when the state changes. (State
-- transitions are synchronous.)

library ieee;
use ieee.std_logic_1164.all;

entity moore_4s is

port(
clk : in std_logic;
data_in : in std_logic;
reset : in std_logic;
data_out : out std_logic_vector(1 downto 0)
);

end entity;

architecture rtl of moore_4s is

-- Build an enumerated type for the state machine
type state_type is (s0, s1, s2, s3);

-- Register to hold the current state
signal state : state_type;

begin
-- Logic to advance to the next state
process (clk, reset)
begin
if reset = '1' then
state <= s0;
elsif (rising_edge(clk)) then
case state is
when s0=
if data_in = '1' then
state <= s1;
else
state <= s0;
end if;
when s1=
if data_in = '1' then
state <= s2;

else
state <= s1;
end if;
when s2=
if data_in = '1' then
-- here I would have something like
-- if counter > 10 then
state <= s3;
else
state <= s2;
end if;
when s3 =
if data_in = '1' then
state <= s0;
else
state <= s3;
end if;
end case;
end if;
end process;

-- Output depends solely on the current state
process (state)
begin

case state is
when s0 =
data_out <= "00";
when s1 =
data_out <= "01";
when s2 =
data_out <= "10";
-- here I would have
data_out <= value depending on counter;
when s3 =
data_out <= "11";
end case;
end process;

end rtl;

It's not hard to add "counter" as a ranged integer and
then increment it while in state s2. Then the only
other thing you need is to reset the counter before
entering state s2. You can do this in several ways:

1) reset "counter" in all other states.
2) reset "counter" in states that can lead to s2.
3) reset "counter" in the transition terms (when assigning
state <= s2 from another state).

There are other ways to do this including building a timer
"subroutine." I've used this is more complex FSM's where
I needed a variable amount of time spent in a number of
different states. In this case I have a state that just
counts down on a counter until it reaches 0. States that
need the delay will set up their outputs as required, set
the delay value into the counter, store the "return state"
in another signal of the same type as "state" and then
set "state" to the wait state. When the counter is decremented
to zero, the wait state then just assigns state to the return
state.

--
Gabor
 
Rob Gaddi wrote:
On Fri, 30 Jan 2015 09:22:18 -0800, revkarol wrote:
Hi Gabor,

I think I follow you. So what I think I should do is create another
clocked process that deals with the counting (and the special output
based on the counting). When I enter the correct state in the FSM, I
set an enable for this clocked process. Without the enable the process
does essentially nothing. Then when I'm done I set a "finished" flag,
and that in turn triggers the FSM to exit the current state and
de-asserts the enable flag.

Many thanks,
Karol.

More processes = more problems.

I've frequently done things like below (untested, so beware the syntax).
Note two important things.

1) The entire FSM is one process. The FPGA vendors LOVE to tell you to
use two process state machines, even for simple Moore ones. They're
wrong, it just adds confusion to your world.

2) The states have real people names, not S0, S1, S2.

signal go : boolean;
signal count : integer range 0 to 15;
signal lamp : boolean;
type t_state is (IDLE, LAMPOFF, LAMPON);
signal state : t_state;

...

FSM: process(clk)
begin
if rising_edge(clk) then
lamp <= false;

case state is
when IDLE =
count <= 0;
if go then
state <= LAMPOFF;
end if;

when LAMPOFF =
if count = 15 then
count <= 0;
state <= LAMPON;
else
count <= count + 1;
end if;

when LAMPON =
lamp <= true;
if count = 15 then
count <= 0;
state <= IDLE;
else
count <= count + 1;
end if;
end case;
end if;
end process FSM;

That is in fact what I was suggesting. There is no need to count
in a separate process, since your FSM was already a clocked process.

Similarly, here's the "subroutine" approach:

signal go : boolean;
signal count : integer range 0 to 15;
signal lamp : boolean;
type t_state is (IDLE, LAMPOFF, LAMPON, SPIN);
signal state : t_state;
signal rtn_state : t_state;

....

FSM: process(clk)
begin
if rising_edge(clk) then

case state is
when IDLE =>
count <= 0;
if go then
state <= LAMPOFF;
end if;

when LAMPOFF =>
lamp <= false;
count <= 15;
rtn_state <= LAMPON;
state <= SPIN;

when LAMPON =>
lamp <= true;
count <= 15;
rtn_state <= IDLE;
state <= SPIN;

when SPIN =>
if count = 0 then
state <= rtn_state;
else
count <= count - 1;
end if;

end case;
end if;
end process FSM;

--
Gabor
 
On 1/30/2015 2:02 PM, GaborSzakacs wrote:
Rob Gaddi wrote:
On Fri, 30 Jan 2015 09:22:18 -0800, revkarol wrote:
Hi Gabor,

I think I follow you. So what I think I should do is create another
clocked process that deals with the counting (and the special output
based on the counting). When I enter the correct state in the FSM, I
set an enable for this clocked process. Without the enable the process
does essentially nothing. Then when I'm done I set a "finished" flag,
and that in turn triggers the FSM to exit the current state and
de-asserts the enable flag.

Many thanks,
Karol.

More processes = more problems.

I've frequently done things like below (untested, so beware the syntax).
Note two important things.

1) The entire FSM is one process. The FPGA vendors LOVE to tell you to
use two process state machines, even for simple Moore ones. They're
wrong, it just adds confusion to your world.

2) The states have real people names, not S0, S1, S2.

signal go : boolean;
signal count : integer range 0 to 15;
signal lamp : boolean;
type t_state is (IDLE, LAMPOFF, LAMPON);
signal state : t_state;

...

FSM: process(clk)
begin
if rising_edge(clk) then
lamp <= false;
case state is
when IDLE =
count <= 0;
if go then
state <= LAMPOFF;
end if;
when LAMPOFF =
if count = 15 then
count <= 0;
state <= LAMPON;
else
count <= count + 1;
end if;
when LAMPON =
lamp <= true;
if count = 15 then
count <= 0;
state <= IDLE;
else
count <= count + 1;
end if;
end case;
end if;
end process FSM;


That is in fact what I was suggesting. There is no need to count
in a separate process, since your FSM was already a clocked process.

Similarly, here's the "subroutine" approach:

signal go : boolean;
signal count : integer range 0 to 15;
signal lamp : boolean;
type t_state is (IDLE, LAMPOFF, LAMPON, SPIN);
signal state : t_state;
signal rtn_state : t_state;

....

FSM: process(clk)
begin
if rising_edge(clk) then

case state is
when IDLE =
count <= 0;
if go then
state <= LAMPOFF;
end if;

when LAMPOFF =
lamp <= false;
count <= 15;
rtn_state <= LAMPON;
state <= SPIN;

when LAMPON =
lamp <= true;
count <= 15;
rtn_state <= IDLE;
state <= SPIN;

when SPIN =
if count = 0 then
state <= rtn_state;
else
count <= count - 1;
end if;

end case;
end if;
end process FSM;

Just my two cents worth... Rather than assign 0 to count in the idle
state I would simply ignore count which will leave it unchanged at its
present value. In other words the counter would have an enable rather
than a reset. Not sure if it is more complex in the logic or not, but
if the FSM is otherwise not enabled, there should be a "free" enable
input to each of the FFs which likely would be used for this.

Also, and I realize this is not finished code, but an initialization
should be considered for all of this logic including the counter. Since
the counter is only used in the SPIN state and set before entering the
SPIN state, the counter likely doesn't need initialization although it
can make simulations more readable.

--

Rick
 
rickman wrote:
On 1/30/2015 2:02 PM, GaborSzakacs wrote:
Rob Gaddi wrote:
On Fri, 30 Jan 2015 09:22:18 -0800, revkarol wrote:
Hi Gabor,

I think I follow you. So what I think I should do is create another
clocked process that deals with the counting (and the special output
based on the counting). When I enter the correct state in the FSM, I
set an enable for this clocked process. Without the enable the process
does essentially nothing. Then when I'm done I set a "finished" flag,
and that in turn triggers the FSM to exit the current state and
de-asserts the enable flag.

Many thanks,
Karol.

More processes = more problems.

I've frequently done things like below (untested, so beware the syntax).
Note two important things.

1) The entire FSM is one process. The FPGA vendors LOVE to tell you to
use two process state machines, even for simple Moore ones. They're
wrong, it just adds confusion to your world.

2) The states have real people names, not S0, S1, S2.

signal go : boolean;
signal count : integer range 0 to 15;
signal lamp : boolean;
type t_state is (IDLE, LAMPOFF, LAMPON);
signal state : t_state;

...

FSM: process(clk)
begin
if rising_edge(clk) then
lamp <= false;
case state is
when IDLE =
count <= 0;
if go then
state <= LAMPOFF;
end if;
when LAMPOFF =
if count = 15 then
count <= 0;
state <= LAMPON;
else
count <= count + 1;
end if;
when LAMPON =
lamp <= true;
if count = 15 then
count <= 0;
state <= IDLE;
else
count <= count + 1;
end if;
end case;
end if;
end process FSM;


That is in fact what I was suggesting. There is no need to count
in a separate process, since your FSM was already a clocked process.

Similarly, here's the "subroutine" approach:

signal go : boolean;
signal count : integer range 0 to 15;
signal lamp : boolean;
type t_state is (IDLE, LAMPOFF, LAMPON, SPIN);
signal state : t_state;
signal rtn_state : t_state;

....

FSM: process(clk)
begin
if rising_edge(clk) then

case state is
when IDLE =
count <= 0;
if go then
state <= LAMPOFF;
end if;

when LAMPOFF =
lamp <= false;
count <= 15;
rtn_state <= LAMPON;
state <= SPIN;

when LAMPON =
lamp <= true;
count <= 15;
rtn_state <= IDLE;
state <= SPIN;

when SPIN =
if count = 0 then
state <= rtn_state;
else
count <= count - 1;
end if;

end case;
end if;
end process FSM;

Just my two cents worth... Rather than assign 0 to count in the idle
state I would simply ignore count which will leave it unchanged at its
present value. In other words the counter would have an enable rather
than a reset. Not sure if it is more complex in the logic or not, but
if the FSM is otherwise not enabled, there should be a "free" enable
input to each of the FFs which likely would be used for this.

Also, and I realize this is not finished code, but an initialization
should be considered for all of this logic including the counter. Since
the counter is only used in the SPIN state and set before entering the
SPIN state, the counter likely doesn't need initialization although it
can make simulations more readable.

You're right. I think the count <= 0 came from the old code and
I just left it there. If anything it would best be initialized to
15 since no other value is ever loaded in parallel. However the point
of the "subroutine" method is that you could load different values
into the counter depending on the time required by each state. Even
if the count was allowed to start up "U" there would be no issue
with simulation since it is always loaded before it is used. The
"state" variable would need an initial value for simulation, though.

--
Gabor
 
Beware that saving/restoring the state to/from a separate register may prevent the synthesis tool from recognizing and optimizing/augmenting the FSM (many require reading and writing state only from/to one register.) Be sure to check it out before you use this method if that is important to your application.

An alternative method would be to use separate register(s) with flag(s) or a separate enumerated type to determine where to go from the subroutine. An if/then/else tree or case statement in the subroutine state could be used to assign the state register based on the flags/enum, without preventing FSM synthesis optimization/augmentation.

Andy
 

Welcome to EDABoard.com

Sponsor

Back
Top