无法使简单的位序列识别器电路工作(FSM)
Can't get simple Bit Sequence Recognizer circuit to work (FSM)
这是我参加的硬件课程的简单练习。我们需要使用 Altera Quartus II 和 ModelSim 来测试实现;我以前从未使用过的工具,所以我可能会遗漏一些东西,而且我的解释也有所欠缺。
该电路有 3 个输入(数据、时钟和复位)和 2 个输出(锁定、错误)。本练习中使用的序列是 10001
.
这道题要求设计一个能够识别位序列的电路。输入正确的序列后,您将获得访问权限(电路进入 "UNLOCK" 状态;锁定输出为 0)。否则,如果您在任何时候输入了错误的位,就会触发警报,并且您应该保持 "ERROR" 状态,直到电路被手动重置。
"Locked" 始终为 1,除非它进入 "UNLOCK" 状态。 "Error" 始终为 0,除非它进入 "ERROR" 状态。
电路应该总是以 "RESET" 状态开始。一旦进入 "UNLOCK" 状态,只要提供的位为 1,它就会保持在那里,或者如果遇到 0,则进入 "RESET"。
这是我绘制的状态图:
欢迎任何帮助或想法!
事实证明,我的实现背后的几乎所有逻辑都是正确的,问题是在触发器上使用 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 中所写,是低电平有效。这意味着您大部分时间都将电路保持在复位状态。您的数据模式看起来像是您认为您的重置处于高电平状态。
就我在张贴的波形图像中看到的而言,您的错误信号表现正常。每次退出reset一个周期,你的数据都是0,这会让你进入error状态。当然,这只会持续一个周期,因为您会立即再次重置。
这些只是小故障,如果你放大你会看到幻影解锁发生了 0 次(或者非常短的时间段,具体取决于你的门模型)。这是组合逻辑的输出不用于时钟数据的原因之一。通过触发器传递值将消除毛刺。
编辑:
此外,您的状态分配 table 和您的状态输出 table 彼此不一致。一个列出了从 Q2
到 Q0
的 Q
值,另一个列出了从 Q0
到 Q2
,但都将 unlocked
状态列为 "110"
。这不会导致 Error
状态出现问题,因为 "111"
向前和向后读取相同的内容。
编辑2:
至于避免故障...故障是组合逻辑的本质。
你可以让 locked
直接来自翻牌而不增加延迟,方法是让 "locked" 翻牌的输入由解锁状态的相同先决条件决定(即 locked_d = not((state=s4 or state=locked) and data=1)
并使用 locked_q
.
您可以通过将状态机机器编码转换为单热或混合单热(其中 locked/error 状态有专用位)来避免锁定成为多个状态位的函数因为它们驱动输出位,但其他 5 个状态使用 3 个共享状态位)。
想像这样的状态 table:
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
其中 Q4
是您的 error
位,Q3
是您的 locked
位
也就是说,避免毛刺通常并不重要,因为它们在 D 输入或时钟启用时用于时序逻辑时不会引起问题。
我会说你的方法让你的生活不必要地变得更加困难。您根本不需要这些 D
和 Q
信号,只需按照您在问题开头的优秀图表中看到的那样对状态机进行编码即可。我没有写完整的代码,但这应该展示了导致最小的、易于阅读的结果的基本方法:
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
以及其他州。
如果您需要 lock
和 alarm
在 reset
断言后立即更改状态,您应该实施异步复位,而不是上面示例中的同步复位:
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
等然后,您可以通过简单的一行更改来更改所需的模式。
这是我参加的硬件课程的简单练习。我们需要使用 Altera Quartus II 和 ModelSim 来测试实现;我以前从未使用过的工具,所以我可能会遗漏一些东西,而且我的解释也有所欠缺。
该电路有 3 个输入(数据、时钟和复位)和 2 个输出(锁定、错误)。本练习中使用的序列是 10001
.
这道题要求设计一个能够识别位序列的电路。输入正确的序列后,您将获得访问权限(电路进入 "UNLOCK" 状态;锁定输出为 0)。否则,如果您在任何时候输入了错误的位,就会触发警报,并且您应该保持 "ERROR" 状态,直到电路被手动重置。
"Locked" 始终为 1,除非它进入 "UNLOCK" 状态。 "Error" 始终为 0,除非它进入 "ERROR" 状态。
电路应该总是以 "RESET" 状态开始。一旦进入 "UNLOCK" 状态,只要提供的位为 1,它就会保持在那里,或者如果遇到 0,则进入 "RESET"。
这是我绘制的状态图:
欢迎任何帮助或想法!
事实证明,我的实现背后的几乎所有逻辑都是正确的,问题是在触发器上使用 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 中所写,是低电平有效。这意味着您大部分时间都将电路保持在复位状态。您的数据模式看起来像是您认为您的重置处于高电平状态。
就我在张贴的波形图像中看到的而言,您的错误信号表现正常。每次退出reset一个周期,你的数据都是0,这会让你进入error状态。当然,这只会持续一个周期,因为您会立即再次重置。
这些只是小故障,如果你放大你会看到幻影解锁发生了 0 次(或者非常短的时间段,具体取决于你的门模型)。这是组合逻辑的输出不用于时钟数据的原因之一。通过触发器传递值将消除毛刺。
编辑:
此外,您的状态分配 table 和您的状态输出 table 彼此不一致。一个列出了从 Q2
到 Q0
的 Q
值,另一个列出了从 Q0
到 Q2
,但都将 unlocked
状态列为 "110"
。这不会导致 Error
状态出现问题,因为 "111"
向前和向后读取相同的内容。
编辑2: 至于避免故障...故障是组合逻辑的本质。
你可以让 locked
直接来自翻牌而不增加延迟,方法是让 "locked" 翻牌的输入由解锁状态的相同先决条件决定(即 locked_d = not((state=s4 or state=locked) and data=1)
并使用 locked_q
.
您可以通过将状态机机器编码转换为单热或混合单热(其中 locked/error 状态有专用位)来避免锁定成为多个状态位的函数因为它们驱动输出位,但其他 5 个状态使用 3 个共享状态位)。
想像这样的状态 table:
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
其中 Q4
是您的 error
位,Q3
是您的 locked
位
也就是说,避免毛刺通常并不重要,因为它们在 D 输入或时钟启用时用于时序逻辑时不会引起问题。
我会说你的方法让你的生活不必要地变得更加困难。您根本不需要这些 D
和 Q
信号,只需按照您在问题开头的优秀图表中看到的那样对状态机进行编码即可。我没有写完整的代码,但这应该展示了导致最小的、易于阅读的结果的基本方法:
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
以及其他州。
如果您需要 lock
和 alarm
在 reset
断言后立即更改状态,您应该实施异步复位,而不是上面示例中的同步复位:
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
等然后,您可以通过简单的一行更改来更改所需的模式。