![]() |
|
|
|||||||
![]() |
VHDL - A general rule for State Machines? |
|
|
Thread Tools | Search this Thread |
|
|
#1 |
|
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 |
|
|
|
|
#2 |
|
Posts: n/a
|
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. |
|
|
|
#3 |
|
Posts: n/a
|
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. |
|
|
|
#4 |
|
Posts: n/a
|
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. |
|
|
|
#5 |
|
Posts: n/a
|
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 |
|