I am trying to make a finite state machine that switches states based on serial input. I need some explanation regarding how my code is executed. I read in a textbook that the section in the process that I have marked "DEFAULT VALUES" is where I should put my default values. However, my signals seem to take on these values whenever I switch states. For example, I set state_next to idle as the default value. Doing this has caused the FSM to continue to jump to idle from other states for no reason.
My other question is clarification in how the whole process for the FSM is executed. When I move from one state to another, is the section before the case statement (the part marked DEFAULT VALUES) supposed to be executed? Or is it only executed if I move from some later state back to idle? When is the DEFAULT VALUES section supposed to be executed?
My code is shown below. Please refer to the "Next-State Logic" section.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity delay_incrementor is
generic ( delay_ports : natural := 3;
width_ports : natural := 3
);
Port ( clk,reset: in STD_LOGIC;
update : in STD_LOGIC;
in_data : in STD_LOGIC_VECTOR (7 downto 0);
led : out STD_LOGIC_VECTOR (2 downto 0);
out_data : out STD_LOGIC_VECTOR (7 downto 0);
d_big,d_mini,d_opo : inout STD_LOGIC_VECTOR (25 downto 0);
w_big,w_mini,w_opo : inout STD_LOGIC_VECTOR (25 downto 0));
end delay_incrementor;
architecture fsm_arch of delay_incrementor is
type state_type is (idle,channel,d_or_w,delay_channel,delay_channel_inc,width_channel,width_channel_inc);
type delay_file_type is array (delay_ports-1 downto 0) of std_logic_vector (25 downto 0);
type width_file_type is array(width_ports-1 downto 0) of std_logic_vector (25 downto 0);
signal d_reg,d_next,d_succ: delay_file_type;
signal w_reg,w_next,w_succ: width_file_type;
signal state_reg,state_next: state_type;
signal which_channel,which_channel_next: natural;
begin
--------------------------------------
--State Register
--------------------------------------
process(clk,reset)
begin
if reset='1' then
state_reg <= idle;
d_reg <= (others => (others => '0'));
w_reg <= (others => (others => '0'));
which_channel <= 0;
elsif (clk='1' and clk'event) then
state_reg <= state_next;
d_reg <= d_next;
w_reg <= w_next;
which_channel <= which_channel_next;
end if;
end process;
--------------------------------------
--Next-State Logic/Output Logic
--------------------------------------
process(state_reg,in_data,d_reg,w_reg,which_channel)
begin
state_next <= idle; --DEFAULT VALUES
d_succ <= d_reg;
w_succ <= w_reg;
which_channel_next <= 0;
case state_reg is
when idle =>
if in_data = "01100011" then --"c"
state_next <= channel;
which_channel_next <= 0;
end if;
when channel =>
if (48 <= unsigned(in_data)) and (unsigned(in_data)<= 57) then
which_channel_next <= (to_integer(unsigned(in_data))-48);
state_next <= d_or_w;
elsif in_data = "00100011" then --"#"
state_next <= idle;
which_channel_next <= which_channel;
end if;
when d_or_w =>
if in_data = "01100100" then --"d"
state_next <= delay_channel;
elsif in_data = "01110111" then --"w"
state_next <= width_channel;
elsif in_data = "00100011" then --"#"
state_next <= idle;
end if;
when delay_channel =>
if in_data = "01101001" then --"i"
state_next <= delay_channel_inc;
elsif in_data = "00100011" then --"#"
state_next <= idle;
end if;
when delay_channel_inc =>
if in_data = "01110101" then --"u"
d_succ(which_channel) <= std_logic_vector(unsigned(d_reg(which_channel))+250);
elsif in_data = "01100100" then --"d"
d_succ(which_channel) <= std_logic_vector(unsigned(d_reg(which_channel))-250);
else
d_succ(which_channel) <= d_reg(which_channel);
end if;
if in_data = "00100011" then --"#"
state_next <= idle;
end if;
when width_channel =>
if in_data = "01101001" then --"i"
state_next <= width_channel_inc;
elsif in_data = "00100011" then --"#"
state_next <= idle;
end if;
when width_channel_inc =>
if in_data = "01110101" then --"u"
w_succ(which_channel) <= std_logic_vector(unsigned(w_reg(which_channel))+250);
elsif in_data = "01100100" then --"d"
w_succ(which_channel) <= std_logic_vector(unsigned(w_reg(which_channel))-250);
else
w_succ(which_channel) <= w_reg(which_channel);
end if;
if in_data = "00100011" then --"#"
state_next <= idle;
end if;
end case;
end process;
process(update,d_reg,w_reg,reset)
begin
if reset='1' then
d_next <= (others => (others =>'0'));
w_next <= (others => (others =>'0'));
elsif (update'event and update='1') then
d_next <= d_succ;
w_next <= w_succ;
else
d_next <= d_reg;
w_next <= w_reg;
end if;
end process;
--------------------------------------
--Output Logic
--------------------------------------
d_big <= d_reg(0);
d_mini <= d_reg(1);
d_opo <= d_reg(2);
w_big <= w_reg(0);
w_mini <= w_reg(1);
w_opo <= w_reg(2);
end fsm_arch;
Here's an alternative version in single process style.
As you surmised, the "default values" were resetting things including State
any time you did not explicitly set values : this is probably unwanted, and I have made some transitions to idle explicit (in *_channel_inc) instead. I've guessed a bit at the semantics here : what happens if a character is present on InData for more than one cycle, or if a different character comes in? but the logic should be easy to change.
A few notes:
x <= std_logic_vector(unsigned(y)+250);
something is probably the wrong type. This means you're fighting the type system instead of using it. I've made d_reg
etc arrays of Unsigned
, and banished the type conversions to the outputs, out of the program flow. X <= Y + 250;
is clearer and simpler.Out
, not InOut
- and I would negotiate to make them Unsigned
, further simplifying and clarifying the design.if in_data = "01101001" then --"i"
versus if in_data = to_slv('i') then
. The latter is easier to read and MUCH easier to write (and get correct). If you don't like the to_slv
helper function (which IS synthesisable), at least use named constants with names reflecting the characters instead. This also makes it easy to tell that this code is processing ASCII (sorry, Latin-1).=
to allow direct comparison between a slv and a character : it's a good idea keep this kind of trick localised to where it is needed. These functions could even be declared in the process itself.rising_edge(clk)
to the obsolete (clk='1' and clk'event)
style.library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity delay_increment is
generic ( delay_ports : natural := 3;
width_ports : natural := 3
);
Port ( clk,reset: in STD_LOGIC;
update : in STD_LOGIC;
in_data : in STD_LOGIC_VECTOR (7 downto 0);
led : out STD_LOGIC_VECTOR (2 downto 0);
out_data : out STD_LOGIC_VECTOR (7 downto 0);
d_big,d_mini,d_opo : out STD_LOGIC_VECTOR (25 downto 0);
w_big,w_mini,w_opo : out STD_LOGIC_VECTOR (25 downto 0));
end delay_increment;
architecture fsm_arch of delay_increment is
type state_type is (idle,channel,d_or_w,delay_channel,delay_channel_inc,width_channel,width_channel_inc);
type delay_file_type is array (delay_ports-1 downto 0) of unsigned (25 downto 0);
type width_file_type is array(width_ports-1 downto 0) of unsigned (25 downto 0);
signal d_reg, d_succ: delay_file_type;
signal w_reg, w_succ: width_file_type;
signal state_reg : state_type;
signal which_channel : natural;
function to_slv(C : Character) return STD_LOGIC_VECTOR is
begin
return STD_LOGIC_VECTOR(to_unsigned(Character'pos(c),8));
end to_slv;
function "=" (A : STD_LOGIC_VECTOR(7 downto 0); B : Character)
return boolean is
begin
return (A = to_slv(B));
end function "+";
begin
--------------------------------------
--State Machine
--------------------------------------
process(clk,reset)
begin
if reset='1' then
state_reg <= idle;
d_reg <= (others => (others => '0'));
w_reg <= (others => (others => '0'));
which_channel <= 0;
elsif rising_edge(clk) then
-- default actions ... update if asked
if update ='1' then
d_reg <= d_succ;
w_reg <= w_succ;
end if;
case state_reg is
when idle =>
if in_data = 'c' then
state_reg <= channel;
which_channel <= 0;
end if;
when channel =>
if (Character'pos('0') <= unsigned(in_data)) and (unsigned(in_data)<= Character'pos('9')) then
which_channel <= (to_integer(unsigned(in_data)) - Character'pos('0'));
state_reg <= d_or_w;
elsif in_data = '#' then
state_reg <= idle;
which_channel <= which_channel;
end if;
when d_or_w =>
if in_data = 'd' then
state_reg <= delay_channel;
elsif in_data = 'w' then
state_reg <= width_channel;
elsif in_data = '#' then
state_reg <= idle;
end if;
when delay_channel =>
if in_data = 'i' then
state_reg <= delay_channel_inc;
elsif in_data = '#' then
state_reg <= idle;
end if;
when delay_channel_inc =>
if in_data = 'u' then
d_succ(which_channel) <= d_reg(which_channel) + 250;
state_reg <= idle;
elsif in_data = 'd' then
d_succ(which_channel) <= d_reg(which_channel) - 250;
state_reg <= idle;
else
d_succ(which_channel) <= d_reg(which_channel); -- wait for any of 'u', 'd', '#'
end if;
if in_data = '#' then
state_reg <= idle;
end if;
when width_channel =>
if in_data = 'i' then
state_reg <= width_channel_inc;
elsif in_data = '#' then
state_reg <= idle;
end if;
when width_channel_inc =>
if in_data = 'u' then
w_succ(which_channel) <= w_reg(which_channel) + 250;
state_reg <= idle;
elsif in_data = 'd' then
w_succ(which_channel) <= w_reg(which_channel) - 250;
state_reg <= idle;
else
w_succ(which_channel) <= w_reg(which_channel); -- wait for any of 'u', 'd', '#'
end if;
if in_data = '#' then
state_reg <= idle;
end if;
end case;
end if;
end process;
--------------------------------------
--Output Logic
--------------------------------------
d_big <= std_logic_vector(d_reg(0));
d_mini <= std_logic_vector(d_reg(1));
d_opo <= std_logic_vector(d_reg(2));
w_big <= std_logic_vector(w_reg(0));
w_mini <= std_logic_vector(w_reg(1));
w_opo <= std_logic_vector(w_reg(2));
end fsm_arch;