A general rule for State Machines?

D

Dave Dean

Guest
Hi all,
I've got a question about some alternate ways to implement a state
machine - in particular, when you may be in a state for several cycles -
I'll use a deserializer as my example here.

First, suppose a boring deserializer that needs to grab just one bit:

entity test is
port (
data_in : in std_logic;
clk : in std_logic
);
end test;

architecture one_bit of test is

type states is (idle, get_data);
signal state : states;
signal data : std_logic;

begin
process(clk)
begin
if (rising_edge(clk)) then
case state is
when idle => if (go='1') then state <= get_data; end if;
when get_data => data <= data_in;
state <= idle;
end case;
end if;
end process;
end one_bit;
---------------------------------
very simple. Now, suppose we need to deserialize TWO bits. Here's two
different changes to the SM that will do this:

architecture two_bits1 of test is

type states is (idle, get_data1, get_data2);
signal state : states;
signal data : std_logic_vector(1 downto 0);

begin
process(clk)
begin
if (rising_edge(clk)) then
case state is
when idle => if (go='1') then state <= get_data1; end if;
when get_data1 => data <= data(0) & data_in;
state <= get_data2;
when get_data2 => data <= data(0) & data_in;
state <= idle;
end case;
end if;
end process;
end imp;
--------------------------------------
architecture two_bits2 of test is

type states is (idle, get_data);
signal state : states;
signal data : std_logic_vector(1 downto 0);
signal counter : std_logic;

begin
process(clk)
begin
if (rising_edge(clk)) then
case state is
when idle => if (go='1') then
state <= get_data;
counter <= '0';
end if;
when get_data => data <= data(0) & data_in;
counter <= counter + 1;
if (counter='1') then
state <= idle;
end if;
end case;
end if;
end process;
end imp;
----------------------------
In the first example, I added a second "get_data" state to allow me to get 2
bits. In the second, I added a counter, so I could stay in the one
"get_data" state for two cycles. In this small example, I'm not sure which
implementation is better.
However, when you start getting significantly larger, like this 16-bit
example:

architecture 16bits of test is

type states is (idle, get_data);
signal state : states;
signal data : std_logic_vector(15 downto 0);
signal counter : std_logic_vector(3 downto 0);

begin
process(clk)
begin
if (rising_edge(clk)) then
case state is
when idle => if (go='1') then
state <= get_data;
counter <= (others => '0');
end if;
when get_data => data <= data(14 downto 0) & data_in;
counter <= counter + 1;
if (counter="1111") then
state <= idle;
end if;
end case;
end if;
end process;
end 16bits;
----------------------------------
Here, I think a counter will be better than adding 16 extra states. If you
use one-hot encoding, you'll be using a lot of extra resources with the
additional states (right?).

Anyway, this is a fairly simple example that I hope illustrates the larger
point. Does anyone have a good rule of thumb of when to use a counter and
when to add extra states? I suppose it can depend on the particular
situation (the extra states will usually be faster) but the counter is
certainly more flexible and elegent.

Thanks,
Dave
 
Dave Dean wrote:

Anyway, this is a fairly simple example that I hope illustrates the
larger
point. Does anyone have a good rule of thumb of when to use a
counter and
when to add extra states?
My opinion: go for the implementation that shows the least code
duplication. This makes it more robust against introduction of bugs
when the implementation is changed. Using a counter certainly would
have my preference. It just makes the code easier to maintain.

--
Paul.
 
Anytime I need to delay more than 3 or 4 clocks, I prefer to use a
counter. Plus, the length of the delay counter can be parameterized.

case state is
when start =>
state := waiting;
delay_cnt := WAIT_DELAY;
when waiting =>
if delay_cnt - 1 < 0 then -- for integer counters only!
state := go;
else
delay_cnt := delay_cnt - 1;
end if;
when ...
end case;

Andy


Paul Uiterlinden wrote:
Dave Dean wrote:

Anyway, this is a fairly simple example that I hope illustrates the
larger
point. Does anyone have a good rule of thumb of when to use a
counter and
when to add extra states?

My opinion: go for the implementation that shows the least code
duplication. This makes it more robust against introduction of bugs
when the implementation is changed. Using a counter certainly would
have my preference. It just makes the code easier to maintain.

--
Paul.
 
If there is no demand for hot encoded states due to speed problems, I
prefer counters, since they can be reused in more than one area of a
complex state machine. Also a counter realy indicates "wait" where
states can do anything (using 2 fsm where actions and clocking are
separated, you will have to check the state transistion part if there
are really nothing else than transions without conditions.
 
Paul Uiterlinden wrote:
Dave Dean wrote:

Anyway, this is a fairly simple example that I hope illustrates the
larger
point. Does anyone have a good rule of thumb of when to use a
counter and
when to add extra states?

My opinion: go for the implementation that shows the least code
duplication. This makes it more robust against introduction of bugs
when the implementation is changed. Using a counter certainly would
have my preference. It just makes the code easier to maintain.
Agreed. As a bonus, you can generalize the code by using a generic to
set the number of bits.

-a
 

Welcome to EDABoard.com

Sponsor

Back
Top