【问题标题】:State machine; why only last state is working?状态机;为什么只有最后一个状态有效?
【发布时间】:2014-06-19 12:42:25
【问题描述】:

我有一个有 6 个状态(3 个主要状态)的状态机。只有最后一个状态有效,但前 2 个没有(共 3 个)。只有最后一个状态有效。我发现了问题,当我移除去抖电路时它可以工作,但我需要去抖电路。我从互联网上得到了去抖电路。如果有人可以提供帮助,我会很高兴。

 type SM_STATES is (state_column_1, scan_col_1, state_column_2, scan_col_2,
                          state_column_3, scan_col_3);
 signal my_state     : SM_STATES                   := state_column_1;

下面是状态机:

scanner_sm : process (clk)
begin  -- process key_scanner
  if clk'event and clk = '1' then

  if state_inc = '1' then -- clock divider finished counting down 100000

    -- reset scan_complete
    scan_complete <= '0';

case my_state is

  when state_column_1 =>
    scanned_val  <= (others => '0');
    original_col   <= "110";
    my_state <= scan_col_1;

  when scan_col_1 =>
    case bcd_val is
      when "1110" => scanned_val <= "1100100";  -- 1 wrong
      when "1101" => scanned_val <= "1100010";  -- 2 wrong
      when others => scanned_val <= "0010000";
    end case;
    my_state <= state_column_2;

  when state_column_2 =>
    original_col   <= "011";
    my_state <= scan_col_2;

  when scan_col_2 =>
    case bcd_val is
      when "1110" => scanned_val <= "1011011";  -- 5 wrong
      when "1101" => scanned_val <= "1011111";  -- 6 wrong
      when others => scanned_val <= "0000000";
    end case;
    my_state <= state_column_3;

  when state_column_3 =>
    original_col   <= "101";
    my_state <= scan_col_3;

  when scan_col_3 => -- Reading S1 // The only working state
    case bcd_val is
      when "1110" => scanned_val <= "1100000";  -- 9/ 1
      when "1101" => scanned_val <= "0111110";  -- X/ 2
      when others => scanned_val <= "0000000";
    end case;
    my_state <= state_column_1; -- ************ Error might be here
    scan_complete <= '1'; -- ********** Error might be here

  when others => scanned_val <= "0000000";
end case;

      end if;
  end if;
end process scanner_sm;

debounce: process (CLK) is 
begin 
 if (CLK'event and CLK = '1') then  
  Q0 <= scannel_val; 

  Q1 <= Q0; 

  Q2 <= Q1; 

 end if; 

end process; 

Final_val <= Q0 and Q1 and (not Q2);

end Behavioral; 

【问题讨论】:

  • 你为什么在 Stackoverlow 和 Stackexchange 上都发布这个?没有足够的源代码来回答,演示“只有最后一个状态正在工作,但前 2 个没有(共 3 个)”。要么包括一个工作测试台,要么显示一个(希望是高分辨率的)波形图像。
  • 我添加了我的完整流程。我只有一个 top_level,我用互联网上的例子写了它,这是我写的第一个。我无法编写完整的源代码。我知道那没有意义,但我不能......

标签: vhdl state-machine fsm case-when


【解决方案1】:

到目前为止,您的代码是不完整的 - 您在 case 语句中直接分配给状态机评估的信号 my_state。要理解这个问题,我们需要知道模拟器是如何工作的:

与实际硬件相比,模拟器必须按顺序使用顺序 CPU 处理代码。这通过在无限小的时间距离内一遍又一遍地运行代码来工作 - 所谓的Delta delay - 直到所有依赖关系都已解决,即不再发生任何变化。

请注意,在此迭代期间没有经过实际时间。在正确编写的设计中,模拟器现在一直等到下一个事件发生 - 通常是由时钟的滴答声引起的,它再次重新启动顺序迭代。

您的示例基本上类似于一个无限循环:my_state 的更改总是会导致 my_state 的下一次更改,因此模拟器永远不会稳定到一个值 - 直到它偶然达到硬编码的迭代限制在你的情况下是第三种状态。

那么,如何解决这个问题?我们需要引入一个时钟,并且需要根据实际仿真时间进行状态转换,通常是通过等待一个时钟事件。最佳实践是将组合部分和顺序部分分成两个不同的进程,如您的状态机的这个最小示例所示:

library ieee;
use ieee.std_logic_1164.all;

entity foo is

end entity foo;

architecture bar of foo is
  type SM_STATES is (state_column_1, scan_col_1, state_column_2, scan_col_2, state_column_3, scan_col_3);
  signal my_state, my_state_next : SM_STATES;
  signal bcd_val                 : std_logic_vector(3 downto 0) := "1110";
  signal clk                     : std_logic                    := '1';
  signal nRST                    : std_logic                    := '0';
  signal scanned_val             : std_logic_vector(6 downto 0);
  signal original_col            : std_logic_vector(2 downto 0);
  signal scan_complete           : std_logic;
begin  -- architecture bar

  comb : process (my_state, bcd_val) is
  begin  -- process baz
    case my_state is

      when state_column_1 =>
        scanned_val   <= (others => '0');
        original_col  <= "110";
        my_state_next <= scan_col_1;

      when scan_col_1 =>
        case bcd_val is
          when "1110" => scanned_val <= "1100100";  -- 1 wrong
          when "1101" => scanned_val <= "1100010";  -- 2 wrong
          when others => scanned_val <= "0010000";
        end case;
        my_state_next <= state_column_2;

      when state_column_2 =>
        original_col  <= "011";
        my_state_next <= scan_col_2;

      when scan_col_2 =>
        case bcd_val is
          when "1110" => scanned_val <= "1011011";  -- 5 wrong
          when "1101" => scanned_val <= "1011111";  -- 6 wrong
          when others => scanned_val <= "0000000";
        end case;
        my_state_next <= state_column_3;

      when state_column_3 =>
        original_col  <= "101";
        my_state_next <= scan_col_3;

      when scan_col_3 =>                -- Reading S1 // The only working state
        case bcd_val is
          when "1110" => scanned_val <= "1100000";  -- 9/ 1
          when "1101" => scanned_val <= "0111110";  -- X/ 2
          when others => scanned_val <= "0000000";
        end case;
        my_state_next <= state_column_1;  -- ************ Error might be here
        scan_complete <= '1';           -- ********** Error might be here

      when others => scanned_val <= "0000000";
    end case;
  end process comb;

  process (clk, nRST) is
  begin  -- process
    if nRST = '0' then                  -- asynchronous reset (active low)
      my_state <= state_column_1;
    elsif clk'event and clk = '1' then  -- rising clock edge
      my_state <= my_state_next;
    end if;
  end process;

  -- this clock and reset signal would usually be supplied from the outside
  clk  <= not clk after 10 ns;
  nRST <= '1'     after 101 ns;
end architecture bar;

如果我们现在在模拟器中运行这个文件,我们可以看到每个时钟周期的状态切换:

【讨论】:

  • 我的电路上实际上没有重置按钮...我可以没有它吗?我在问题中添加了我的完整过程源代码,我确实有一个时钟和时钟分频器,所以我认为我的时钟应该处理你的意思的状态切换,但我错过了 my_next_state。我将专注于 my_next_state 的实现。我周末没有FPGA,周一我会尝试反馈答案,非常感谢提示!
  • 你能分享一下你的test_bench吗?
  • 经过深思熟虑后,我认为我可以使用 FPGA 按钮之一进行“重置”,但如果有办法在不重置的情况下解决此问题,我想采用这种方式。
【解决方案2】:

您可以将其视为基于迭代绑定的答案。

我实现了你的完整状态机流程:

library ieee;
use ieee.std_logic_1164.all;

entity foo is

end entity foo;

architecture fum of foo is

    type SM_STATES is ( 
        state_column_1, scan_col_1, 
        state_column_2, scan_col_2, 
        state_column_3, scan_col_3
    );

    signal my_state:        SM_STATES;  -- defaults to SM_STATES'LEFT

    signal state_inc:       std_logic := '0';

    signal bcd_val:         std_logic_vector(3 downto 0) := "1110";
    signal clk:             std_logic := '0';

    signal scanned_val:     std_logic_vector(6 downto 0);
    signal original_col:    std_logic_vector(2 downto 0);
    signal scan_complete:   std_logic;

begin

scanner_sm: 
    process (clk)
    begin
        if clk'event and clk = '1' then

            if state_inc = '1' then

                scan_complete <= '0';

                case my_state is

                    when state_column_1 =>
                        scanned_val  <= (others => '0');
                        original_col   <= "110";
                        my_state <= scan_col_1;

                    when scan_col_1 =>
                        case bcd_val is
                            when "1110" => scanned_val <= "1100100";  -- 1 wrong
                            when "1101" => scanned_val <= "1100010";  -- 2 wrong
                            when others => scanned_val <= "0010000";
                        end case;
                        my_state <= state_column_2;

                    when state_column_2 =>
                        original_col   <= "011";
                        my_state <= scan_col_2;

                    when scan_col_2 =>
                        case bcd_val is
                            when "1110" => scanned_val <= "1011011";  -- 5 wrong
                            when "1101" => scanned_val <= "1011111";  -- 6 wrong
                            when others => scanned_val <= "0000000";
                        end case;
                        my_state <= state_column_3;

                    when state_column_3 =>
                        original_col   <= "101";
                        my_state <= scan_col_3;

                    when scan_col_3 => -- Reading S1 // The only working state
                        case bcd_val is
                            when "1110" => scanned_val <= "1100000";  -- 9/ 1
                            when "1101" => scanned_val <= "0111110";  -- X/ 2
                            when others => scanned_val <= "0000000";
                        end case;
                        my_state <= state_column_1; -- ************ Error might be here
                        scan_complete <= '1'; -- ********** Error might be here

                    when others => scanned_val <= "0000000";
                end case;

          end if;
      end if;
    end process scanner_sm;

CLOCK:
    process 
    begin
       wait for 10 ns;
       clk <= not clk;
       if Now > 200 ns then
           wait;
       end if;
    end process;

STIMULUS:
    process
    begin
        wait for 30 ns;
        state_inc <= '1';
        wait;
    end process;

end architecture;

你可以看到没有下一个状态。猜猜它的作用。

我在断言之前将 state_inc 延迟了一个时钟,以说明您应该重置的原因:

您可以看到 scan_complete、scanned_val 和 original_col 的状态在写入之前最初是未知的。你也可以重置 my_state。

您可以从上面的模拟和迭代绑定中看到,您的状态机遍历所有六个状态。如果它卡在某个地方,您会认为 state_inc 无效(也不清楚是什么使它在您的进程中无效)。

除了没有重置之外,您的状态机没有任何明显问题,问题似乎出在其他地方或与状态边界操作有关,阻止了 state_inc 的发生。

虽然您可能需要一个额外的状态来表示扫描完成并开始下一次扫描,否则任何仍然有效的 state_inc 都会导致下一轮状态转换。 (在 scan_col3 和 state_column_1 之间,与您的 cmets “************ 错误可能在这里”保持一致。

添加重置和仅持续一个时钟的附加状态(名称 - 完成?)可以通过以下方式完成:

 if state_inc = '1' or my_state = complete then

现在在哪里

  if state_inc = '1' then

是。

这将使任何有机会在转到 state_column_1 之前对 scan_complete 做出反应。你可能会检查完成是否应该命名为 start_or_complete,并且是第一个状态。

我对您的流程所做的唯一一件事就是更改缩进,以便更容易查看 if 语句中的所有内容。

(我们确实警告过您,我们希望看到更多您的模型)

【讨论】:

  • 您能分享一下您的 test_bench 文件吗?
  • 将上述VHDL源代码复制粘贴到文件中,进行分析、阐述和仿真。 VHDL 测试平台有一个普遍可识别的特征,即在实体声明中缺少端口接口。如果需要,测试台还将包含一个时钟源,以及用于执行 VHDL 设计规范的激励。这个的独特之处在于您只提供了一个流程,因此没有组件实例化,而是您的流程被直接合并。
  • 我终于找到了为什么开关不工作的原因,因为我的去抖电路。当我删除去抖电路时,它可以工作。我从互联网上复制了这个去抖。逻辑对我来说很有意义,但为什么它不起作用我不知道。我在问题中添加了我的去抖电路。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-14
相关资源
最近更新 更多