在VHDL中实现有限状态机

6

我想知道如果我在VHDL中实现有限状态机,是否需要在每个可能的状态中说明所有输出?即使我知道某些输出不会从一个状态改变到另一个状态,并且我也知道状态的顺序也是相同的?

例如,在这个(强制)示例中:

entity test is
    port (
        clk : in std_logic;
        a : in std_logic;
        b: out std_logic;
        c: out std_logic;
    );
end test;

architecture Behavioral of test is

type executionStage is (s1,s2,s3);
signal currentstate, nextstate: executionStage;

begin
    process (clk)
    begin
          if(rising_edge(clk)) then
                 currentstate <= nextstate;
          else 
                 currentstate <= currentstate;
          end if;
    end process;

    process(currentstate)
    begin
        case currentstate is
            when s1 =>
                if (a = '1') then
                    b <= '1';
                    c <= '0';
                else
                    b <= '1';
                    c <= '1';
                end if;

                nextstate <= s2;

            when s2 =>
                -- b doesnt change state from s1 to here, do I need to define what it is here?
                if (a = '1') then
                    b <= '1';
                    c <= '1';
                else
                    b <= '1';
                    c <= '0';
                end if;

                nextstate <= s3;

            when s3 =>
                if (a = '1') then
                    b <= '0';
                    c <= '0';
                else
                    b <= '1';
                    c <= '1';
                end if;

                nextstate <= s1;
        end case;
    end process;
end Behavioral;

据我理解,如果我不这样做,那么就会创建锁存器?在像那个例子中这并不是什么大问题,但如果我的机器有超过10个输出和超过10个状态,则我的VHDL文件开始看起来非常混乱,而且我确定复制和粘贴相同的内容一定是不好的做法。有更好的方法吗?编辑:我可以为输出定义一个“默认”状态吗?例如,在所有进程之外将b设置为1,然后仅在它为0的情况下定义它在case语句中的值?那样行得通吗?
6个回答

7

如果您只在流程的某些分支中驱动意图为组合的信号,则会推断出锁存器。

然而,您可以通过在同一进程内在case语句之前为信号赋值来定义信号的“默认”状态。例如:

process(currentstate, a)
begin
    b <= '1';
    c <= '1';
    case currentstate is
        when s1 =>
            if (a = '1') then
                c <= '0';
            end if;

            nextstate <= s2;

        when s2 =>
            -- b doesnt change state from s1 to here, do I need to define what it is here?
            if (a /= '1') then
                c <= '0';
            end if;

            nextstate <= s3;

        when s3 =>
            if (a = '1') then
                b <= '0';
                c <= '0';
            end if;

            nextstate <= s1;
    end case;
end process;

谢谢Tomi!所以在我的示例中,我可以在process(currentstate)中设置b <= '1',那么我只需要在s3状态下设置值吗?我还需要在if和else语句中都设置吗或者仅在b等于0的else语句中设置就足够了吗?所以只需在else语句中设置就可以了吗? - JimR
我想知道是否可以在第一个case语句中设置一个默认值,以便所有其他case/if语句都可以“继承”。还是说我需要在所有case/if语句中都设置一个新的默认值? - JimR
您只需要确保在分支的每个分支中都为信号赋值(即在过程中不存在未为信号赋值的路径)。最简单的方法是在case语句外分配一个合理的默认值。在case语句内部无法实现这一点。 - Tomi Junnila
我更新了我的回复,包括一个例子。在这样做的过程中,我意识到我可能误解了你的评论。是的,在when语句内部分配值是可能的,但在其中包含的if语句之外。在when语句中没有要求只有一个语句(这已经在您的代码中显而易见,因为您在if语句之外分配了nextstate)。 - Tomi Junnila

7

你的示例代码存在三个问题:

端口列表中的最后一个端口不应该有分号:

port (
    clk : in std_logic;
    a : in std_logic;
    b: out std_logic;
    c: out std_logic -- no semicolon here!!!
    );

在您的注册过程中,不应该有“else”语句。虽然这可能会被工具接受,但它会让您的VHDL设计师感到困惑。
process (clk)
begin
    if(rising_edge(clk)) then
        currentstate <= nextstate;
    end if;
end process;

在你的组合逻辑中,灵敏度列表应包含你所读取的所有信号:process(a, currentstate)。在这种特殊情况下(再次强调),事情可能会顺利进行,但如果你的灵敏度列表不正确,你就有可能推断出锁存器或引起其他问题。
至于你的问题:
1.是的,在组合过程中,你需要为每个状态的每个信号分配一个值。 2.正如Tomi所提到的,你可以通过在过程开始时分配默认值来轻松完成此操作。 3.但是,你也可以在一个单一的同步过程中编写整个状态机。这样,你就不必为每个状态的每个信号分配一个值。

1
谢谢Philipe!非常感激您提供的信息 :) - JimR

3

提醒一下,这是对菲利普回答的补充(无法直接评论)...

我更喜欢使用双进程编写状态机。这样可以清楚地表明您期望推断的触发器和不推断的触发器的位置。它也更符合描述硬件的方式 - 想象一下使用板级逻辑构建状态机的情况。注册设备匹配状态 <= next_state 进程, 并且 case 语句映射到状态寄存器前面的与/或数组。

话虽如此,我通常会对小型简单任务使用单进程状态机,并转向双进程状态机用于更大的任务。 我甚至有时会使用第三个进程将状态输出组织成不同的“任务”组..但并不经常。一个非常大的状态机告诉我需要处理体系结构问题..


0
process (clk)
begin
  if(rising_edge(clk)) then
    currentstate <= nextstate;
  end if;
end process;

你好

上述过程存在问题,但并非敏感列表的原因。仅声明时钟用于顺序进程是可以的。仿真和综合工具都不会有问题。毕竟,在您的代码中,时钟是变化/转换最快的信号。

然而,您应该使用(最好是)异步复位。当然,现在的供应商说对于FPGA设计,重置甚至不是必要的;它们在引导时发生。或者他们提出同步复位。

尽管如此,异步复位对于基于板的环境仍然很有价值。

简而言之:向您的设计添加复位并正确修复其行为。

此致 Nikolaos Kavvadias


0
以下是基于电平的状态机VHDL代码。 在此示例中,基于电平的过程将使“out1”与“clk”反相,“out2”与“clk”同相。
entity main_code is
    Port ( clk : in  STD_LOGIC;
           in1 : in  STD_LOGIC;
           in2 : in  STD_LOGIC;
           out1 : out  STD_LOGIC;
           out2 : out  STD_LOGIC);
end main_code;

architecture Behavioral of main_code is

-- here are temp signals to associate or assign output (out1 and out2) values indirectly
signal out1_temp : std_logic := '0';  
signal out2_temp : std_logic := '0';

-- counter registers 
signal counter : integer range 0 to 255 := 0;
signal counter_8th_clk : integer range 0 to 255 := 0;
-- state machines definition
type state_machine_type is (s0,s1);
signal state : state_machine_type := s0;
begin
-- concurrent assignments
out1 <= out1_temp;
out2 <= out2_temp;

--half clock generator process
half_clock : process (clk) is
begin
if rising_edge(clk) then
--out1_temp <= not out1_temp;
end if;
end process half_clock;

-- max counter = ndiv -1; here ndiv=4; counter starts from zero;
one_fourth_clock : process (clk)
begin
if rising_edge(clk) then
counter <= counter + 1;
    if (counter >= 3) then 
        counter <= 0;
--      out2_temp <= not out2_temp;
    end if;
end if;
end process one_fourth_clock;


one_eighth_clock : process (clk)
begin
if rising_edge(clk) then
counter_8th_clk <= counter_8th_clk + 1;
    if (counter_8th_clk>=7) then
        counter_8th_clk <= 0;
--      out2_temp <= not out2_temp;
    end if;
end if;
end process one_eighth_clock;

-- state_process creates two half clock (speed) with out1 out of phase with clk
-- and out2 in-phase with clk
-- following process is sensitive to clk level not edge
state_process_level_sensitive : process (clk)
begin
case state  is

    when s0 =>
        out1_temp <= not out1_temp;
        state <= s1;
    when s1 =>
        out2_temp <= not out2_temp;
        state <= s0;
end case;
end process state_process_level_sensitive;



end Behavioral;

这是测试台

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--USE ieee.numeric_std.ALL;

ENTITY my_test_bench IS
END my_test_bench;

ARCHITECTURE behavior OF my_test_bench IS 

    -- Component Declaration for the Unit Under Test (UUT)

    COMPONENT main_code
    PORT(
         clk : IN  std_logic;
         in1 : IN  std_logic;
         in2 : IN  std_logic;
         out1 : OUT  std_logic;
         out2 : OUT  std_logic
        );
    END COMPONENT;


   --Inputs
   signal clk : std_logic := '0';
   signal in1 : std_logic := '0';
   signal in2 : std_logic := '0';

    --Outputs
   signal out1 : std_logic;
   signal out2 : std_logic;

   -- Clock period definitions
   constant clk_period : time := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: main_code PORT MAP (
          clk => clk,
          in1 => in1,
          in2 => in2,
          out1 => out1,
          out2 => out2
        );

   -- Clock process definitions
   clk_process :process
   begin
        clk <= '0';
        wait for clk_period/2;
        clk <= '1';
        wait for clk_period/2;
   end process;


   -- Stimulus process
   stim_proc: process
   begin        
      -- hold reset state for 100 ns.
--      wait for 100 ns;    
--
--      wait for clk_period*10;

      -- insert stimulus here 

      wait;
   end process;

END;

0
以下是与边缘敏感相关的状态机VHDL代码。 在此示例中,边缘敏感进程将使“out1”和“out2”与“clk”同步。
entity main_code is
    Port ( clk : in  STD_LOGIC;
           in1 : in  STD_LOGIC;
           in2 : in  STD_LOGIC;
           out1 : out  STD_LOGIC;
           out2 : out  STD_LOGIC);
end main_code;

architecture Behavioral of main_code is

-- here are temp signals to associate or assign output (out1 and out2) values indirectly
signal out1_temp : std_logic := '0';  
signal out2_temp : std_logic := '0';

-- counter registers 
signal counter : integer range 0 to 255 := 0;
signal counter_8th_clk : integer range 0 to 255 := 0;
-- state machines definition
type state_machine_type is (s0,s1);
signal state : state_machine_type := s0;
begin
-- concurrent assignments
out1 <= out1_temp;
out2 <= out2_temp;

--half clock generator process
half_clock : process (clk) is
begin
if rising_edge(clk) then
--out1_temp <= not out1_temp;
end if;
end process half_clock;

-- max counter = ndiv -1; here ndiv=4; counter starts from zero;
one_fourth_clock : process (clk)
begin
if rising_edge(clk) then
counter <= counter + 1;
    if (counter >= 3) then 
        counter <= 0;
--      out2_temp <= not out2_temp;
    end if;
end if;
end process one_fourth_clock;


one_eighth_clock : process (clk)
begin
if rising_edge(clk) then
counter_8th_clk <= counter_8th_clk + 1;
    if (counter_8th_clk>=7) then
        counter_8th_clk <= 0;
--      out2_temp <= not out2_temp;
    end if;
end if;
end process one_eighth_clock;

-- state_process creates two half clock (speed) with out1 out of phase with clk
-- and out2 in-phase with clk
-- following process is sensitive to clk level not edge
state_process_edge_sensitive : process (clk)
begin
if rising_edge (clk) then
case state  is

    when s0 =>
        out1_temp <= not out1_temp;
        state <= s1;
    when s1 =>
        out2_temp <= not out2_temp;
        state <= s0;
end case;
end if;

end process state_process_edge_sensitive;



end Behavioral;

这里是测试台
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--USE ieee.numeric_std.ALL;

ENTITY my_test_bench IS
END my_test_bench;

ARCHITECTURE behavior OF my_test_bench IS 

    -- Component Declaration for the Unit Under Test (UUT)

    COMPONENT main_code
    PORT(
         clk : IN  std_logic;
         in1 : IN  std_logic;
         in2 : IN  std_logic;
         out1 : OUT  std_logic;
         out2 : OUT  std_logic
        );
    END COMPONENT;


   --Inputs
   signal clk : std_logic := '0';
   signal in1 : std_logic := '0';
   signal in2 : std_logic := '0';

    --Outputs
   signal out1 : std_logic;
   signal out2 : std_logic;

   -- Clock period definitions
   constant clk_period : time := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: main_code PORT MAP (
          clk => clk,
          in1 => in1,
          in2 => in2,
          out1 => out1,
          out2 => out2
        );

   -- Clock process definitions
   clk_process :process
   begin
        clk <= '0';
        wait for clk_period/2;
        clk <= '1';
        wait for clk_period/2;
   end process;


   -- Stimulus process
   stim_proc: process
   begin        
      -- hold reset state for 100 ns.
--      wait for 100 ns;    
--
--      wait for clk_period*10;

      -- insert stimulus here 

      wait;
   end process;

END;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接