【问题标题】:Is there a better way? Long ELSIF state machine alternatives有没有更好的办法?长 ELSIF 状态机替代方案
【发布时间】:2021-12-29 06:52:17
【问题描述】:

我想知道是否有人可以给我一些建议。我正在尝试设计一个需要 8 个输入(按钮)的模块。 这些按钮确定状态是否处于活动状态。然后在时钟的上升沿,它通过活动状态依次向前或向后移动, 跳过非活动时钟状态。

例如:

如果输入为 11111111,则连续计数 00000001、00000010、00000100、00001000、00010000、00100000、01000000、10000000。

如果输入为 00001111。它会连续计数 00000001、00000010、00000100、00001000。

如果输入是 00000011。它会连续计数 00000001, 00000010。

我是 VHDL 和 fpga 的新手。我尝试了几种不同的方法。他们都有过 问题。但是我写的这个状态机正在按照我想要的方式工作。 它基本上从状态 0 开始,检查它的移动方向,然后检查该方向中的下一个活动状态并移动到它。

例如:

如果处于状态 0 并向前移动,则检查输入 2,如果向后移动,则检查输入 8。如果它们未处于活动状态,则检查输入 3 和输入 7。 如果它们处于活动状态,它会移动到该状态并再次执行该过程。但是这次因为我们处于不同的状态,它从这个新状态开始检查。如果我们处于状态 2。如果向前则检查输入 3,如果向后则检查输入 1。如果处于活动状态,它会移动到该状态。

它有点长,有很多重复的代码,很像 C 代码。如果可以的话,我想摆脱这种情况。但正如我所说,我的知识有限,我想知道是否有更好的方法来实现我需要的功能。任何帮助和指导将不胜感激,我提前感谢您。

这是模块的代码。

library ieee;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

ENTITY state_machine IS
PORT(
  clk      : IN   STD_LOGIC;
  a        : IN   STD_LOGIC;   -- state0 Active '1'
  b        : IN   STD_LOGIC;   -- state1 Active '1'
  c        : IN   STD_LOGIC;   -- state2 Active '1'
  d        : IN   STD_LOGIC;   -- state3 Active '1'
  e        : IN   STD_LOGIC;   -- state4 Active '1'
  f        : IN   STD_LOGIC;   -- state5 Active '1'
  g        : IN   STD_LOGIC;   -- state6 Active '1'
  h        : IN   STD_LOGIC;   -- state7 Active '1'
  dir      : IN   STD_LOGIC;   -- direction
  reset    : IN   STD_LOGIC;
  output   : OUT  STD_LOGIC_VECTOR(2 downto 0));  
END state_machine;

ARCHITECTURE behaviour OF state_machine IS
   TYPE STATE_TYPE IS (s0, s1, s2, s3, s4, s5, s6, s7);
   SIGNAL state : STATE_TYPE;     
BEGIN
  sequence: PROCESS (clk, reset)
  BEGIN
  IF reset = '1' THEN
     state <= s0;
  ELSIF (rising_edge (clk)) THEN     
    
  CASE state IS       
       WHEN s0=>                          -- When in state 0
       IF (dir = '1') THEN                -- if direction is forwards
           IF b = '1' THEN  state <= s1;  -- If b is 1 go to state 1...if not
        elsif c = '1' then  state <= s2;  -- if c is 1 go to state 2...if not
        elsif d = '1' then  state <= s3;  -- if d is 1 go to state 3...if not
        elsif e = '1' then  state <= s4;  -- if e is 1 go to state 4...if not
        elsif f = '1' then  state <= s5;  -- if f is 1 go to state 5...if not
        elsif g = '1' then  state <= s6;  -- if g is 1 go to state 6...if not
        elsif h = '1' then  state <= s7;  -- if h is 1 go to state 7...if not
        elsif a = '1' then  state <= s0;  -- if a is 1 go to state 0   
        END IF;                
       ELSE                               -- if direction is backwards
           IF h = '1' THEN  state <= s7;  -- If h is 1 go to state 1...if not
        elsif g = '1' then  state <= s6;  -- if g is 1 go to state 2...if not
        elsif f = '1' then  state <= s5;  -- if f is 1 go to state 3...if not
        elsif e = '1' then  state <= s4;  -- if e is 1 go to state 4...if not
        elsif d = '1' then  state <= s3;  -- if d is 1 go to state 5...if not
        elsif c = '1' then  state <= s2;  -- if c is 1 go to state 6...if not
        elsif b = '1' then  state <= s1;  -- if b is 1 go to state 7...if not
        elsif a = '1' then  state <= s0;  -- if a is 1 go to state 0...if not                                                        
        END IF;                         
       END IF;
           
       WHEN s1=>                          
       IF (dir = '1') THEN 
           IF c = '1' THEN  state <= s2;         
        elsif d = '1' then  state <= s3; 
        elsif e = '1' then  state <= s4;
        elsif f = '1' then  state <= s5;
        elsif g = '1' then  state <= s6;
        elsif h = '1' then  state <= s7;
        elsif a = '1' then  state <= s0;
        elsif b = '1' THEN  state <= s1; 
        END IF;             
       ELSE                  
           IF a = '1' THEN  state <= s0;
        elsif h = '1' then  state <= s7; 
        elsif g = '1' then  state <= s6;
        elsif f = '1' then  state <= s5;
        elsif e = '1' then  state <= s4;
        elsif d = '1' then  state <= s3;
        elsif c = '1' then  state <= s2;
        elsif b = '1' then  state <= s1;                                
        END IF;        
       END IF;
           
       WHEN s2=>
       IF (dir = '1') THEN 
           IF d = '1' THEN  state <= s3;
        elsif e = '1' then  state <= s4;
        elsif f = '1' then  state <= s5;
        elsif g = '1' then  state <= s6;
        elsif h = '1' then  state <= s7;
        elsif a = '1' then  state <= s0;
        elsif b = '1' then  state <= s1;
        elsif c = '1' then  state <= s2;
        END IF; 
       ELSE  
           IF b = '1' THEN  state <= s1;
        elsif a = '1' then  state <= s0; 
        elsif h = '1' then  state <= s7;
        elsif g = '1' then  state <= s6;
        elsif f = '1' then  state <= s5;
        elsif e = '1' then  state <= s4;
        elsif d = '1' then  state <= s3;
        elsif c = '1' then  state <= s2;
        END IF;   
       END IF;
           
       WHEN s3=>
       IF (dir = '1') THEN 
           IF e = '1' THEN  state <= s4;
        elsif f = '1' then  state <= s5; 
        elsif g = '1' then  state <= s6;
        elsif h = '1' then  state <= s7;
        elsif a = '1' then  state <= s0;
        elsif b = '1' then  state <= s1;
        elsif c = '1' then  state <= s2;
        elsif d = '1' then  state <= s3;
        END IF; 
       ELSE
           IF c = '1' THEN  state <= s2;
        elsif b = '1' then  state <= s1; 
        elsif a = '1' then  state <= s0;
        elsif h = '1' then  state <= s7;
        elsif g = '1' then  state <= s6;
        elsif f = '1' then  state <= s5;
        elsif e = '1' then  state <= s4;
        elsif d = '1' then  state <= s3;
        END IF;     
      END IF;
           
       WHEN s4=>
       IF (dir = '1') THEN
           IF f = '1' THEN  state <= s5;
        elsif g = '1' then  state <= s6; 
        elsif h = '1' then  state <= s7;
        elsif a = '1' then  state <= s0;
        elsif b = '1' then  state <= s1;
        elsif c = '1' then  state <= s2;
        elsif d = '1' then  state <= s3;
        elsif e = '1' then  state <= s4;
        END IF; 
       ELSE
           IF d = '1' THEN  state <= s3;
        elsif c = '1' then  state <= s2; 
        elsif b = '1' then  state <= s1;
        elsif a = '1' then  state <= s0;
        elsif h = '1' then  state <= s7;
        elsif g = '1' then  state <= s6;
        elsif f = '1' then  state <= s5;
        elsif e = '1' then  state <= s4;           
        END IF;
      END IF;
           
       WHEN s5=>
       IF (dir = '1') THEN
           IF g = '1' THEN  state <= s6;
        elsif h = '1' then  state <= s7; 
        elsif a = '1' then  state <= s0;
        elsif b = '1' then  state <= s1;
        elsif c = '1' then  state <= s2;
        elsif d = '1' then  state <= s3;
        elsif e = '1' then  state <= s4;
        elsif e = '1' then  state <= s5;
        END IF; 
       ELSE
           IF e = '1' THEN  state <= s4;
        elsif d = '1' then  state <= s3; 
        elsif c = '1' then  state <= s2;
        elsif b = '1' then  state <= s1;
        elsif a = '1' then  state <= s0;
        elsif h = '1' then  state <= s7;
        elsif g = '1' then  state <= s6;
        elsif f = '1' then  state <= s5;     
        END IF;
       END IF;
           
        WHEN s6=>
       IF (dir = '1') THEN         
           IF h = '1' THEN  state <= s7;
        elsif a = '1' then  state <= s0;
        elsif b = '1' then  state <= s1;
        elsif c = '1' then  state <= s2;
        elsif d = '1' then  state <= s3;
        elsif e = '1' then  state <= s4;
        elsif f = '1' then  state <= s5;
        elsif g = '1' then  state <= s6;
        END IF; 
       ELSE
           IF f = '1' THEN  state <= s5;
        elsif e = '1' then  state <= s4; 
        elsif d = '1' then  state <= s3;
        elsif c = '1' then  state <= s2;
        elsif b = '1' then  state <= s1;
        elsif a = '1' then  state <= s0;
        elsif h = '1' then  state <= s7;
        elsif g = '1' then  state <= s6;
        END IF;
       END IF;
           
        WHEN s7=>
       IF (dir = '1') THEN           
           IF a = '1' THEN  state <= s0;
        elsif b = '1' then  state <= s1;
        elsif c = '1' then  state <= s2;
        elsif d = '1' then  state <= s3;
        elsif e = '1' then  state <= s4;
        elsif f = '1' then  state <= s5;
        elsif g = '1' then  state <= s6;
        elsif h = '1' then  state <= s7;
        END IF; 
       ELSE
           IF g = '1' THEN  state <= s6;         
        elsif f = '1' then  state <= s5; 
        elsif e = '1' then  state <= s4;
        elsif d = '1' then  state <= s3;
        elsif c = '1' then  state <= s2;
        elsif b = '1' then  state <= s1;
        elsif a = '1' then  state <= s0;
        elsif h = '1' then  state <= s7;
        END IF;
       END IF;
           
     END CASE;
  END IF;
END PROCESS;

PROCESS (state)
BEGIN
  CASE state IS
     WHEN s0 =>
        output <= "000";
     WHEN s1 =>
        output <= "001";
     WHEN s2 =>
        output <= "010";
     WHEN s3 =>
        output <= "011";
     WHEN s4 =>
        output <= "100";
     WHEN s5 =>
        output <= "101";
     WHEN s6 =>
        output <= "110";
     WHEN s7 =>
        output <= "111";
  END CASE;
END PROCESS;

END behaviour;

这里是测试台:

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;

entity state_machine_tb is
end;

architecture bench of state_machine_tb is

component state_machine
 PORT(
    clk      : IN   STD_LOGIC;
    a        : IN   STD_LOGIC;
    b        : IN   STD_LOGIC;
    c        : IN   STD_LOGIC;
    d        : IN   STD_LOGIC;
    e        : IN   STD_LOGIC;
    f        : IN   STD_LOGIC;
    g        : IN   STD_LOGIC;
    h        : IN   STD_LOGIC;
    dir      : IN   STD_LOGIC;
    reset    : IN   STD_LOGIC;
    output : OUT  STD_LOGIC_VECTOR(2 downto 0));
end component;

signal clk      : STD_LOGIC;
signal a        : STD_LOGIC;
signal b        : STD_LOGIC;
signal c        : STD_LOGIC;
signal d        : STD_LOGIC;
signal e        : STD_LOGIC;
signal f        : STD_LOGIC;
signal g        : STD_LOGIC;
signal h        : STD_LOGIC;
signal dir      : STD_LOGIC;
signal reset    : STD_LOGIC;
signal output :   STD_LOGIC_VECTOR(2 downto 0);

constant clock_period: time := 10 ns;

begin

uut: state_machine port map ( clk    => clk,
                            a      => a,  
                            b      => b,
                            c      => c,
                            d      => d,  
                            e      => e,
                            f      => f,
                            g      => g,
                            h      => h,
                            dir    => dir,
                            reset  => reset,
                            output => output);

stimulus: process
begin
reset <= '1';
dir <= '1'; 
a <= '1';
b <= '1';
c <= '1';
d <= '1';
e <= '1';
f <= '1';
g <= '1';
h <= '1';
wait for clock_period;
reset <= '0';  
wait for clock_period *15;
dir <= '0';
a <= '1';
b <= '1';
c <= '1';
d <= '1';
e <= '1';
f <= '1';
g <= '1';
h <= '1';
wait for clock_period *16;
dir <= '1';
a <= '1';
b <= '1';
c <= '1';
d <= '1';
e <= '0';
f <= '0';
g <= '0';
h <= '0';
wait for clock_period *16;
dir <= '0';
a <= '1';
b <= '1';
c <= '1';
d <= '1';
e <= '0';
f <= '0';
g <= '0';
h <= '0';
wait for clock_period *16;
dir <= '1';
a <= '1';
b <= '0';
c <= '1';
d <= '0';
e <= '1';
f <= '0';
g <= '1';
h <= '0';
wait for clock_period *16;
a <= '1';
b <= '1';
c <= '0';
d <= '0';
e <= '0';
f <= '0';
g <= '0';
h <= '0';
wait for clock_period *16;
a <= '1';
b <= '0';
c <= '0';
d <= '0';
e <= '0';
f <= '0';
g <= '0';
h <= '0';
wait for clock_period *4;
dir <= '0';  
wait;
end process;

clocking: process
begin
  clk <= '0';
  wait for clock_period / 2;
  clk <= '1';
  wait for clock_period / 2;
end process;

end;

【问题讨论】:

  • 从细化规范开始。我们知道“00001111”是做什么的,但是“00001000”呢?还是“00001110”?然后寻找一些可以用来简化问题描述的规律性。正如 Renaud 所建议的,你有一个类型系统;使用它和计数等会容易得多。

标签: if-statement vhdl counter state-machine


【解决方案1】:

如何使用更方便的类型,例如输入按钮的向量和状态的整数?类似于以下内容(未测试):

entity state_machine is
  port(
    clk:  in std_ulogic;
    rst:  in std_ulogic;
    btn:  in std_ulogic_vector(0 to 7);
    dir:  in std_ulogic;
    cnt: out std_ulogic_vector(2 downto 0)
  );
end entity state_machine;

architecture rtl of state_machine is
  signal state: natural range 0 to 7;
begin
  cnt <= std_ulogic_vector(to_unsigned(state, 3));

  process(clk, rst)
    variable v: natural range 0 to 7;
  begin
    if rst = '1' then
      state <= 0;
    elsif rising_edge(clk) then
      for i in 1 to 7 loop
        if dir = '1' then
          v := (state + i) mod 8;
        else
          v := (state - i) mod 8;
        end if;
        if btn(v) = '1' then
          exit;
        end if;
--      exit when btn(v) = '1'; -- VHDL2008 version of above if
      end loop;
      state <= v;
    end if;
  end process;
end architecture rtl;

如果您需要的是 Mealy 状态机,而不是 Moore 状态机(上述内容),您可以稍微修改代码以明确下一个状态并从下一个状态而不是当前状态计算输出状态(未测试):

entity state_machine is
  port(
    clk:  in std_ulogic;
    rst:  in std_ulogic;
    btn:  in std_ulogic_vector(0 to 7);
    dir:  in std_ulogic;
    cnt: out std_ulogic_vector(2 downto 0)
  );
end entity state_machine;

architecture rtl of state_machine is
  signal state, next_state: natural range 0 to 7;
begin
  cnt <= std_ulogic_vector(to_unsigned(next_state, 3));

  process(state, dir, btn)
    variable v: natural range 0 to 7;
  begin
    for i in 1 to 7 loop
      if dir = '1' then
        v := (state + i) mod 8;
      else
        v := (state - i) mod 8;
      end if;
      if btn(v) = '1' then
        exit;
      end if;
--    exit when btn(v) = '1'; -- VHDL2008 version of above if
    end loop;
    next_state <= v;
  end process;

  process(clk, rst)
  begin
    if rst = '1' then
      state <= 0;
    elsif rising_edge(clk) then
      state <= next_state;
    end if;
  end process;
end architecture rtl;

【讨论】:

  • 感谢您的回复。我在本节中遇到语法错误:break when btn(v) = '1';
  • 这是一个 VHDL2008 功能。您使用的是哪个版本的标准?我编辑了我的代码以显示一个适用于旧 VHDL 版本的变体。
  • 我把break这个词改成了exit。现在一切正常。感谢您的所有帮助。
  • 糟糕,我的错,我混淆了几种语言。我应该先测试一下。对不起。
  • 你好。有没有办法立即更新 cnt 输出。目前它会在一个周期后更新。我改变了一些东西,但我所能做的就是将更新扩展到 2 个时钟周期。谢谢。
猜你喜欢
  • 2012-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-25
  • 1970-01-01
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多