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
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