无法使简单的位序列识别器电路正常工作 (FSM)

Can't get simple Bit Sequence Recognizer circuit to work (FSM)

提问人:David 提问时间:1/5/2017 最后编辑:David 更新时间:1/12/2017 访问量:394

问:

这是我正在参加的硬件课程的简单练习。我们需要使用 Altera Quartus II 和 ModelSim 来测试实现;我以前从未使用过的工具,所以我可能遗漏了一些东西,而我的解释也有所欠缺。

该电路有 3 个输入(数据、时钟和复位)和 2 个输出(锁定、错误)。本练习中使用的顺序是 。10001

问题要求设计一个能够识别位序列的电路。当输入正确的序列时,您将被授予访问权限(电路进入“解锁”状态;锁定输出为 0)。否则,如果在任何时候您输入了错误的位,您将触发警报,并且您应该保持“错误”状态,直到电路被手动复位。

“锁定”始终为 1,除非它进入“解锁”状态。“Error”始终为 0,除非它进入“ERROR”状态。

电路应该始终以“RESET”状态启动。一旦它进入“解锁”状态,只要提供的位是 1,它就会保持在那里,或者如果遇到 0,它就会进入“RESET”。

这是我制定的状态图:

enter image description here

欢迎任何帮助或想法!

事实证明,我的实现背后的几乎所有逻辑都是正确的,问题是在人字拖上使用 CLRN 的误解和其中一个作业中的错别字。因此,大多数图像都被删除以消除混乱。

-- 编辑 1

使用以下代码(应该是正确的),波形与预期不符(至少不是)lock

LIBRARY ieee;
USE ieee.std_logic_1164.all; 

entity dlock is 
    port
    (
        DATA  :  IN   STD_LOGIC;
        RESET :  IN   STD_LOGIC;
        CLOCK :  IN   STD_LOGIC;
        LOCK  :  OUT  STD_LOGIC;
        ALARM :  OUT  STD_LOGIC
    );
end dlock;

architecture bdf_type of dlock is 

type STATE_type is (S_RESET, S1, S2, S3, S4, UNLOCK, S_ALARM);
signal state : STATE_type := S_RESET;

begin

process (clock) is
begin
  if (rising_edge(clock)) then
    -- `reset` always puts us back in the reset state
    if (RESET = '1') then
      state <= S_RESET;
    else
      case state is
        when S_RESET =>
          -- Reset; lock active and alarm off
          LOCK  <= '1';
          ALARM <= '0';
          if (DATA = '1') then
            -- Correct bit, proceed to next state
            state <= S1;
          else
            -- Incorrect bit; ALARM
            state <= S_ALARM;
          end if;
        when S1 => 
          if (DATA = '0') then
            state <= S2;
          else
            state <= S_ALARM;
          end if;
        when S2 => 
          if (DATA = '0') then
            state <= S3;
          else
            state <= S_ALARM;
          end if;
        when S3 => 
          if (DATA = '0') then
            state <= S4;
          else
            state <= S_ALARM;
          end if;
        when S4 => 
          if (DATA = '1') then
            state <= UNLOCK;
          else
            state <= S_ALARM;
          end if;
        when UNLOCK =>
          -- Lock inactive!
          LOCK <= '0';
          if (data = '0') then
            state <= S_RESET;
          else
            state <= UNLOCK;
          end if;
        when S_ALARM =>
          -- Alarm active in ALARM state
          ALARM <= '1';
      end case;
    end if;
  end if;
end process;
end bdf_type;
VHDL 硬件 布尔逻辑 状态机 电路

评论

0赞 sonicwave 1/5/2017
它并没有真正回答这个问题,而是一个快速提示 - 除非练习要求您浏览 Karnaugh 映射,否则直接在 VHDL 中实现状态机要容易得多,而且不容易出错(为状态做一个 case 语句,然后查看输入并适当地更新状态和输出)。合成器将负责进行所有优化,您最终会得到更易于编写、读取和调试的代码。
0赞 David 1/5/2017
谢谢,但恐怕我必须遵循这个设计程序,并且自己完成大部分工作。

答:

1赞 QuantumRipple 1/6/2017 #1
  1. VHDL中写入的复位为低电平有效。这意味着您大部分时间都在复位电路。您的数据模式看起来像您认为您的重置处于高电平有效状态。

  2. 就我在发布的波形图像中看到的而言,您的错误信号表现正常。每次退出一个周期的重置时,您的数据都是 0,这会将您发送到错误状态。当然,这只持续一个周期,因为您立即再次重置。

  3. 这些只是小故障,如果你放大,你会看到幻影解锁发生 0 次(或非常小的时间段,具体取决于您的门型号)。这就是为什么组合逻辑的输出不用于时钟数据的原因之一。通过触发器传递值将消除毛刺。

编辑: 此外,状态分配表和状态输出表彼此不一致。一个列出 downto 的值,另一个列出 to 的值,但两者都将状态列为 。这不会给状态带来问题,因为向前和向后读取相同。QQ2Q0Q0Q2unlocked"110"Error"111"

编辑2: 至于避免故障......毛刺是组合逻辑的本质。

您可以直接从翻牌中获取数据,而不会增加延迟,方法是让“锁定”翻牌的输入由解锁状态的相同前提条件决定(即 并使用 .lockedlocked_d = not((state=s4 or state=locked) and data=1)locked_q

您可以通过将状态机机编码转换为单热或混合单热编码来避免锁定成为多个状态位的函数(其中有一个用于锁定/错误状态的专用位,因为它们驱动输出位,但其他 5 个状态使用 3 个共享状态位)。

可以考虑如下状态表:

Q4 Q3 Q2 Q1 Q0   State
 0  1  0  0  0   Reset
 0  1  0  0  1   S1
 0  1  0  1  0   S2
 0  1  0  1  1   S3
 0  1  1  0  0   S4
 0  0  X  X  X   Unlock
 1  1  X  X  X   Error
 1  0  X  X  X   X
 0  1  1  0  1   X
 0  1  1  1  X   X

你的位在哪里,你的位在哪里Q4errorQ3locked

也就是说,避免毛刺通常并不重要,因为在D输入或时钟使能时序逻辑中使用毛刺时,毛刺不会引起问题。

评论

0赞 David 1/6/2017
嗯,你提出的第一点很有趣。我不知道这是否是我应该处理它的方式,但我反转了来自“重置”的信号,该信号被馈送到触发器的 CLRN 中,现在我得到了这个 imgur.com/a/5h4KT 我认为这是朝着正确方向迈出的一步。但是,锁也会在错误的顺序上打开。起初我以为这可能是由Don't Cares引起的,但是电路应该总是在“重置”状态下启动,所以应该不是问题。似乎在收到 后,它会打开,这不应该发生。1
0赞 QuantumRipple 1/6/2017
将状态信号添加到波形中。最好是某种虚拟阵列,这样它在某种程度上是可读的。由于您的课程要求您编写此内容的方式向后,因此很难阅读和调试。如果确实要管理虚拟阵列,则需要放大其中一个问题区域,才能显示状态向量的字符值。
1赞 QuantumRipple 1/6/2017
不过,仅从检查来看,您的状态转换似乎工作正常,但您的锁定方程可能是错误的。错误输出遵循正确的模式,锁定在状态 S2 中变为低电平。
1赞 QuantumRipple 1/6/2017
状态分配表和状态输出表相互不一致,一个列出从 downto 的值,另一个列出从 到 的值,但两者都将未锁定的状态列为QQ2Q0Q0Q2"110"
0赞 David 1/6/2017
谢谢,我简直不敢相信这是一个多么愚蠢的错误。在更正了状态输出表并使用了 的新方程后,我认为一切都按应有的方式运行: imgur.com/a/G1eN4 误差波看起来也符合预期。关于故障,有没有办法在不添加额外硬件的情况下消除它们?UNLOCK
1赞 scary_jeff 1/6/2017 #2

我想说的是,你的方法让你的生活变得不必要地变得更加困难。你根本不需要这些和信号,只需完全按照你在问题开头的优秀图表中看到的那样对状态机进行编码。我还没有编写完整的代码,但这应该显示了导致最小、易于阅读结果的基本方法:DQ

type STATE_type is (S_RESET, S1, UNLOCK, ERROR);
signal state : STATE_type := S_RESET;

...

process (clock) is
begin
  if (rising_edge(clock)) then
    -- `reset` always puts us back in the reset state
    if (reset = '1') then
      state <= S_RESET;
    else
      case state is
        when S_RESET =>
          -- Reset; lock active and alarm off
          lock <= '1';
          alarm <= '0';
          if (data = '1') then
            -- Correct bit, proceed to next state
            state <= S1;
          else
            -- Incorrect bit; error
            state <= ERROR;
          end if;
        when S1 => 
          if (data = '0') then
            state <= UNLOCK;
          else
            state <= ERROR;
          end if;
        when UNLOCK =>
          -- Lock inactive!
          lock <= '0';
          if (data = '0') then
            state <= RESET;
          end if;
        when ERROR =>
          -- Alarm active in error state
          alarm <= '1';
      end case;
    end if;
  end if;
end process;

您应该能够轻松地添加其他状态并继续添加其他状态。S2


如果需要 和 在断言后立即更改状态,则应实现异步重置,而不是上面示例中的同步重置:lockalarmreset

  if (reset = '1') then
    state <= S_RESET;
    alarm <= '0';
    lock <= '1';
  elsif (rising_edge(clock)) then
    case state is
      -- `when` statements
    end case;
  end if;

以这种方式编写它的另一个优点是,您可以轻松地将所需的模式设置为常量:

constant PATTERN : std_logic_vector(0 to 4) := "10001";

然后,不同状态下的数据比较将如下所示:

when S_RESET =>
  if (data = PATTERN(0)) then

...

when S1 =>
  if (data = PATTERN(1)) then

等。然后,您可以通过简单的单行更改来更改所需的模式。

评论

0赞 David 1/6/2017
谢谢。这个解决方案超出了本练习的范围,我以前从未编写过 VHDL 代码,但它看起来是一个优雅的解决方案,所以我想我会试一试。请参阅我编辑的帖子,其中包含一些其他问题。
0赞 scary_jeff 1/6/2017
@David 你断言得太快了,看不到发布。一旦你提供了正确的序列,状态机将移动到该状态,一旦到达这里,下一个时钟将释放;你在这一点上断言,所以还没有被释放。如果要立即释放,则应将 移动到 旁边。您也不需要该州的子句。resetlockUNLOCKlockresetlocklocklock <= '0';state <= UNLOCK;elseUNLOCK
0赞 David 1/7/2017
谢谢,我现在能够看到解锁正在发生。现在它基本上是正确的,但另一个问题是警报波延迟了一个时钟周期(应该确切地转到 是 )。在相同的情况下,应该有一个类似的问题。alarm0reset1lock1
0赞 scary_jeff 1/7/2017
@David如果您需要提前一个时钟周期,请在您设置的同一点分配它。我已经编辑了您评论的另一部分的答案。alarmstate <= ERROR;
0赞 David 1/7/2017
这样就可以了,谢谢。不过,还有一件小事困扰着我。考虑以下波形 imgur.com/a/tfIoY 您可以看到,在重置(现在可以正常工作)之后,即使输入不正确,也需要再一个时钟周期来设置闹钟。这可以修改吗?(也很抱歉有这么多问题,因为我说过我以前从未使用过这些应用程序或在 VHDL 中编程,所以让它工作对我来说有点命中注定)