【问题标题】:VHDL: Default values in a Finite State MachineVHDL:有限状态机中的默认值
【发布时间】:2015-07-21 17:02:05
【问题描述】:

我正在尝试制作一个基于串行输入切换状态的有限状态机。我需要一些关于我的代码是如何执行的解释。我在一本教科书中读到,我在过程中标记为“默认值”的部分是我应该放置默认值的地方。但是,每当我切换状态时,我的信号似乎都会采用这些值。例如,我将 state_next 设置为 idle 作为默认值。这样做会导致 FSM 无缘无故地从其他状态跳转到空闲状态。

我的另一个问题是澄清 FSM 的整个过程是如何执行的。当我从一种状态转移到另一种状态时,是否应该执行 case 语句之前的部分(标记为 DEFAULT VALUES 的部分)?还是仅在我从稍后的状态移回空闲状态时才执行?什么时候应该执行默认值部分?

我的代码如下所示。请参阅“下一状态逻辑”部分。

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;

【问题讨论】:

  • 如果没有声明,您的代码示例是不完整的。

标签: vhdl xilinx-ise


【解决方案1】:

这是单进程样式的替代版本。

正如您所推测的,“默认值”会在您未明确设置值时重置包括 State 在内的内容:这可能是不需要的,我已经进行了一些转换为显式空闲(在 *_channel_inc 中)。我已经猜到了这里的语义:如果一个字符出现在 InData 上超过一个周期,或者如果一个不同的字符出现,会发生什么?但逻辑应该很容易改变。

几点说明:

  1. 任何时候你写x &lt;= std_logic_vector(unsigned(y)+250);的东西都可能是错误的类型。这意味着您正在与类型系统作斗争而不是使用它。我已经制作了d_regUnsigned 数组,并将类型转换排除在程序流之外。 X &lt;= Y + 250; 更清晰、更简单。
  2. 那些端口应该是Out,而不是InOut - 我会协商使它们成为Unsigned,进一步简化和阐明设计。
  3. 空间不消耗门。
  4. 单进程样式的一个优点是,仅用于互连进程的信号较少,在难以确定的时间进行更新。在这里,“更新”与其他所有内容在同一时钟沿使用。
  5. 幻数... if in_data = "01101001" then --"i"if in_data = to_slv('i') then 。后者更容易阅读,更容易编写(并且正确)。如果您不喜欢 to_slv 辅助函数(它是可综合的),至少使用命名常量,其名称可以反映字符。这也很容易看出这段代码正在处理 ASCII(抱歉,Latin-1)。
  6. 编辑:为了让 SM 不那么混乱,我在本地重载了 = 以允许在 slv 和字符之间进行直接比较:最好将这种技巧保持在需要的地方。这些函数甚至可以在进程本身中声明。
  7. 更喜欢rising_edge(clk) 而不是过时的(clk='1' and clk'event) 样式。

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;

【讨论】:

  • 在一个时钟上升沿尝试做太多事情是否有问题?如果我要在流程中再添加 20 个状态会怎样?
  • (1) 取决于。在某些时候,做太多会消耗比你更多的FPGA资源(门等)......但是请注意,状态机一次只能处于一种状态,因此它在任何时钟边沿上可以做的事情是有限的。 (2) 该过程将起作用;然而,它可能太难以理解、维护和调试。我建议不要让状态机发展到你无法理解它们的地步。我见过一些具有许多状态的 SM,它们具有简单的结构(几个简单状态链)并且易于维护和理解 - 所以不会提出任何严格的规则。
【解决方案2】:

每当列出的信号之一发生变化时,都会评估一个进程。因此这个列表被称为“敏感列表”。

有两种类型的进程:
- 顺序的(带有时钟信号)和
- 组合(只是简单的逻辑)。

第一种只需要灵敏度列表中的时钟信号,后一种需要每个右手边的信号,否则仿真会显示出与真实硬件不同的结果。

因此,每次输入信号发生变化('event = true)时,都会从 begin ... end process 评估该过程。

因此,关于您的 DEFAULT 部分,每个信号都有一个默认值,并且不可能使用这种编码风格生成锁存器。通常 state_next 不设置为空闲。它设置为 state_reg

免费翻译:保持当前状态,除非另有说明

您的代码:

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

无法合成。我假设 update 不是真正的时钟信号,所以它不应该用在rising_edge 表达式中。其次,else条件什么时候为真?

解决方案:您的寄存器需要一个启用信号。

附录:

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;
    which_channel <= which_channel_next;
    if update = '1' then
      d_reg <= d_next;
      w_reg <= w_next;
     end if;
  end if;
end process;

【讨论】:

  • 谢谢,这一切都清楚了。关于我的那段代码,我试图使用update 作为启用信号。从我到目前为止的测试来看,它似乎有点工作,并且只要更新信号没有改变,else 条件就为真。如果没有这样的更新信号,我如何更新d_nextw_next
  • @eugenewu 一个使能信号嵌入到上升沿条件的 then 分支中。您可以删除上一个进程。
猜你喜欢
  • 1970-01-01
  • 2015-08-10
  • 2016-08-02
  • 1970-01-01
  • 1970-01-01
  • 2014-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多