本文首先通过一个具体的问题入手,然后对有限状态机的设计进行分析和总结。
一、题目说明
下图是键盘编码和防抖动控制电路,一般采用扫描和轮询的方法来检测对键盘的敲击,这样可以减少键盘与主电路之间的连线数量。该电路每次将outp0、outp1、outp2中的一个置‘0’,然后读入inp1、inp2、inp3、inp4,而其他行输入inp仍保持为1。
设计的电路需要完成:
①编码功能:每个数字必须被编为ASCII码。编码电路通过将new_data置为‘1’,表示电路检测到一次键盘敲击并输出有效编码。这种做法可以避免长期按下一个键而出现一长串相同的数字。
②防抖动电路:机械开关存在开关抖动问题,即开关在形成最终稳定接触之前的不稳定状态,其持续时间一般有几个ms。因此,时钟的频率非常重要,选择一个时钟频率使得5ms内至少进行3次读取操作,只有在5ms内的读数相同时,才会认为该键被触发。
二、题目分析
分析题目可知,我们需要设计的电路应满足以下特征:
1)功能:实现按键防抖动电路编码器,可以将状态分为轮询状态一(ROLL_POLING_1)、轮询状态二(ROLL_POLING_2)、轮询状态三(ROLL_POLING_3)、触发状态(TRIGGER)、输出状态(OUTPUT)。
2)端口:输入时钟CLK、复位信号RST、四位宽按键输入INP,三位宽轮询输出OUTP、按键有效输出信号NEW_DATA、七位宽按键编码信号DATA。
3)触发方式:时钟上升沿触发,RST异步且高电平有效。
4)时序解释说明:
1>触发电路复位后,默认进入轮询状态一(ROLL_POLING_1)。
2>时钟上升时若处于轮询状态一、轮询状态二或轮询状态三(ROLL_POLING_1、ROLL_POLING_2、ROLL_POLING_3),分别输出对应的OUTP。如果INP符合按键触发则进入触发状态(TRIGGER),如果INP不符合按键触发则进入对应的下一轮询状态。
3>时钟上升时若处于触发状态(TRIGGER),判断INP是否为按键触发,若是,则计数累加,若否,则计数清零,且进入轮询状态一(ROLL_POLING_1)。当计数为3时,进入输出状态(OUTPUT)。
4>时钟上升时若处于输出状态(OUTPUT),进入轮询状态一(ROLL_POLING_1)。
5>无论何时,若为输出状态(OUTPUT)则NEW_DATA置‘1’,同时输出编码后的DATA。
状态转移图如下:
三、实现与总结
对于这个问题可以使用有限状态机来描述,一般情况下,我们可以选择使用三进程来描述有限状态机,这样的结构清晰,可读性很高。但是对于本问题来说,这种方法并不适用。
首先,先介绍一下描述有限状态机的三种方法,即单进程描述、双进程描述和三进程描述。
根据功能来分,有限状态机由三部分结构组成,分别是由输入信号和现态决定的次态逻辑、由时钟、次态和复位信号控制的状态寄存器、由现态(或输入)决定的输出逻辑。
大家平时见到的有限状态机通常被分为两部分,即组合逻辑部分和时序逻辑部分。这两者都是对有限状态机的描述,一般来说,次态逻辑和输出逻辑就是组合逻辑电路,状态寄存就是时序逻辑电路。
对于三进程描述,次态逻辑、状态寄存器、输出逻辑各自分别由一个进程块来描述,因此称之为三进程描述。对于双进程描述,将次态逻辑、状态寄存器合并为一个进程块(状态转移块)来描述,因此称之为双进程描述。对于单进程描述,将三部分合并为一个块来描述,这种方法很少见,只有当问题简单时才会使用,当问题复杂时,单进程描述的代码可读性大大下降。
通过上面的描述,我们需要明确双进程和三进程的区别。
三进程描述:次态逻辑和输出逻辑都是组合逻辑,他们不需要时钟和复位信号,他们的进程敏感触发列表是现态和输入。举个例子,在某个现态下,输入突然改变,此时次态逻辑块会被触发,根据新的输入改变次态,然后等到下个时钟沿,现态才发生变化。这会存在状态转移的延迟。这当然是必要的,这可以使得状态的转移是时钟同步的。
以下是三进程描述的代码模板:
---------------三进程描述-----------------
----------------STATE_REG----------------
PROCESS(CLK,RST)BEGIN --根据时钟和复位,决定现态的转移(时序逻辑)
CASE RST IS
WHEN '1'=>
PR_STATE<=INITIAL; --复位时,将现态赋为初始态
WHEN '0'=>
IF CLK'EVENT AND CLK='1'THEN --时钟上升沿时,将次态赋给现态,完成状态的转移
PR_STATE<=NX_STATE;
END IF;
WHEN OTHERS=>NULL;
END CASE;
END PROCESS;
--------------NX_STATE_LOGIC-------------
PROCESS(PR_STATE,INPUT)BEGIN --根据现态和输入,决定次态(组合逻辑)
CASE PR_STATE IS
WHEN INITIAL=> IF INPUT='1'THEN
NX_STATE<=STATE_1;
ELSE
NX_STATE<=INITIAL;
END IF;
WHEN STATE_1=> NX_STATE<=STATE_2;
WHEN STATE_2=> NX_STATE<=STATE_3;
WHEN STATE_3=> NX_STATE<=STATE_4;
WHEN STATE_4=> NX_STATE<=INITIAL;
END CASE;
END PROCESS;
----------------OUTPUT_LOGIC-------------
PROCESS(PR_STATE)BEGIN --根据现态,决定输出(组合逻辑)
CASE PR_STATE IS
WHEN INITIAL=> OUTPUT<=‘1’;
WHEN STATE_1=> OUTPUT<=‘0’;
WHEN STATE_2=> OUTPUT<=‘1’;
WHEN STATE_3=> OUTPUT<=‘0’;
WHEN STATE_4=> OUTPUT<=‘1’;
END CASE;
END PROCESS;
双进程描述:此时没有次态和现态的区分,而统一用状态来描述。每次时钟沿触发时,根据输入和状态来决定下一状态和输出,这里的输出可以只和状态有关(Moore),也可以和输入、状态有关(Mealy)。这种描述相比三进程有十分重要的区别,首先避免了现态和次态的描述,其次可以对于时钟触发的输入信号进行描述,从而使得代码简洁并且符合问题要求。有些问题的输入信号是和时钟有关的(例如序列的检测),这种情况下,使用三进程描述会略微麻烦一些。这是因为我们需要把所有出现的状态划分清楚,以避免次态逻辑因为没有信号发生变化而不会触发执行,导致程序不符合预期。
以下是双进程描述的代码模板:
---------------双进程描述-----------------
----------------STATE_TRANFER----------------
PROCESS(CLK,RST)BEGIN --根据时钟、复位和输入,决定状态的转移(时序逻辑)
CASE RST IS
WHEN '1'=>
STATE<=INITIAL; --复位时,将现态赋为初始态
WHEN '0'=>
IF CLK'EVENT AND CLK='1'THEN --时钟上升沿时,根据状态、输入,完成状态的转移
CASE STATE IS
WHEN INITIAL=> IF INPUT='1'THEN
STATE<=STATE_1;
ELSE
STATE<=INITIAL;
END IF;
WHEN STATE_1=> STATE<=STATE_2;
WHEN STATE_2=> STATE<=STATE_3;
WHEN STATE_3=> STATE<=STATE_4;
WHEN STATE_4=> STATE<=INITIAL;
END CASE;
END IF;
WHEN OTHERS=>NULL;
END CASE;
END PROCESS;
----------------OUTPUT_LOGIC-------------
PROCESS(STATE)BEGIN --根据状态,决定输出(组合逻辑)
CASE STATE IS
WHEN INITIAL=> OUTPUT<=‘1’;
WHEN STATE_1=> OUTPUT<=‘0’;
WHEN STATE_2=> OUTPUT<=‘1’;
WHEN STATE_3=> OUTPUT<=‘0’;
WHEN STATE_4=> OUTPUT<=‘1’;
END CASE;
END PROCESS;
以本题为例,我们将状态分为了轮询状态一(ROLL_POLING_1)、轮询状态二(ROLL_POLING_2)、轮询状态三(ROLL_POLING_3)、触发状态(TRIGGER)、输出状态(OUTPUT)这5个状态,但其实这只是按功能完成的划分,事实上TRIGGER中还可以划分出若干个状态,这些状态的数量取决于需要计数的次数,使用双进程描述时,五个状态足矣,但是如果使用三进程描述,次态逻辑块就比较复杂了。有的同学可能会想,既然输入信号是和时钟有关的,那为什么我们不把次态逻辑块的敏感列表中加入时钟信号,使得每次时钟到来时,都能改变次态呢?这样的话我们避免了因为没有信号发生变化而不会触发执行,也可以使用五个状态来完成,那这样真的可行吗?
答案是否定的。在这里我们忽略了一个重要的问题,那就是VHDL、Verilog的块与块之间都是并行执行的。为了更好的描述,我们分为几个步骤来描述,假设复位后的现态是IDLE。状态转移是IDLE--A--B--IDLE
①首先,因为复位,所以现态是IDLE。然后因为现态发生了变化,所以次态逻辑被触发,次态被赋为了状态A。
②等待直到下一个时钟上升沿,状态寄存块被触发,现态被赋为A,与此同时,次态逻辑因为时钟上升被触发,它会根据现态来决定次态,但是这里用来决定次态的现态是IDLE,而不是A;因为状态寄存和次态逻辑这两个块是同时进行的,因此次态依然被赋为A。
③在这之后因为现态被赋为了A,我们期望的是再次触发次态逻辑块,然后将次态赋为B。但这实际上并不会发生,因为一个块在一个时间点只能触发一次,也就是说,这里次态逻辑块的敏感列表是(PR_STATE,CLK)与(CLK)是一样的效果。因此次态依然是A。
④等到下一个时钟上升沿到来,现态还被赋为A,次态被赋为B,所以在下一个时钟上升到来之前,现态还是A。也就是说,这导致了现态持续了两个时钟周期的A状态,之后才转移。
第一副图中的次态逻辑采用了(PR_STATE,CLK)的敏感列表,第二副图中的次态逻辑采用了(PR_STATE,INPUT)的敏感列表。可以看到第一幅图中的方法使得状态持续了两个时钟周期,这是不期望看到的结果。
综上所述,对于序列的描述、计数累加来说,三进程描述会更加复杂。因为我们更倾向于将状态按功能划分,计数显然是一个高度一致的行为,我们可以使用变量做累加,用if做判断,这样更加简单。如果非要使用三进程描述,我们也还可以通过增加信号(SIGNAL)来描述子状态。
为了简单起见,我们使用双进程描述来解决这个问题。下面是双进程描述代码:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity Anti_shake_circuit_encoder is
Port (INP: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
CLK,RST: IN STD_LOGIC;
OUTP: OUT STD_LOGIC_VECTOR(2 DOWNTO 0);
DATA: OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
NEW_DATA: OUT STD_LOGIC
);
end Anti_shake_circuit_encoder;
architecture Behavioral of Anti_shake_circuit_encoder is
TYPE STATE_TPYE IS (ROLL_POLING_1,ROLL_POLING_2,ROLL_POLING_3,TRIGGER,OUTPUT);
TYPE TRIGGER_POLING_STATE_TYPE IS (IDLE,TRIGGER_POLING_1,TRIGGER_POLING_2,TRIGGER_POLING_3);
SIGNAL STATE:STATE_TPYE;
SIGNAL KEY:INTEGER RANGE 1 TO 4;
SIGNAL TRIGGER_POLING:TRIGGER_POLING_STATE_TYPE;
begin
--------------------------STATE_TRANSFER-------------------------------
STATE_TRANSFER:PROCESS(CLK,RST)
VARIABLE COUNT:INTEGER RANGE 0 TO 3;
BEGIN
CASE RST IS
WHEN '1'=>
STATE<=ROLL_POLING_1;
TRIGGER_POLING<=IDLE;
COUNT:=0;
WHEN '0'=>
IF CLK'EVENT AND CLK='1'THEN
CASE STATE IS
WHEN ROLL_POLING_1=>
COUNT:=1;
CASE INP IS
WHEN "0111"=> STATE<=TRIGGER; KEY <=1; TRIGGER_POLING<=TRIGGER_POLING_1;
WHEN "1011"=> STATE<=TRIGGER; KEY <=2; TRIGGER_POLING<=TRIGGER_POLING_1;
WHEN "1101"=> STATE<=TRIGGER; KEY <=3; TRIGGER_POLING<=TRIGGER_POLING_1;
WHEN "1110"=> STATE<=TRIGGER; KEY <=4; TRIGGER_POLING<=TRIGGER_POLING_1;
WHEN OTHERS => STATE<=ROLL_POLING_2;COUNT:=0;
END CASE;
WHEN ROLL_POLING_2=>
COUNT:=1;
CASE INP IS
WHEN "0111"=> STATE<=TRIGGER; KEY <=1; TRIGGER_POLING<=TRIGGER_POLING_2;
WHEN "1011"=> STATE<=TRIGGER; KEY <=2; TRIGGER_POLING<=TRIGGER_POLING_2;
WHEN "1101"=> STATE<=TRIGGER; KEY <=3; TRIGGER_POLING<=TRIGGER_POLING_2;
WHEN "1110"=> STATE<=TRIGGER; KEY <=4; TRIGGER_POLING<=TRIGGER_POLING_2;
WHEN OTHERS => STATE<=ROLL_POLING_3;COUNT:=0;
END CASE;
WHEN ROLL_POLING_3=>
COUNT:=1;
CASE INP IS
WHEN "0111"=> STATE<=TRIGGER; KEY <=1; TRIGGER_POLING<=TRIGGER_POLING_3;
WHEN "1011"=> STATE<=TRIGGER; KEY <=2; TRIGGER_POLING<=TRIGGER_POLING_3;
WHEN "1101"=> STATE<=TRIGGER; KEY <=3; TRIGGER_POLING<=TRIGGER_POLING_3;
WHEN "1110"=> STATE<=TRIGGER; KEY <=4; TRIGGER_POLING<=TRIGGER_POLING_3;
WHEN OTHERS => STATE<=ROLL_POLING_1;COUNT:=0;
END CASE;
WHEN TRIGGER=>
CASE INP IS
WHEN "0111"=>
IF KEY=1 THEN COUNT:=COUNT+1;ELSE STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;END IF;
IF COUNT=3 THEN STATE<=OUTPUT;END IF;
WHEN "1011"=>
IF KEY=2 THEN COUNT:=COUNT+1;ELSE STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;END IF;
IF COUNT=3 THEN STATE<=OUTPUT;END IF;
WHEN "1101"=>
IF KEY=3 THEN COUNT:=COUNT+1;ELSE STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;END IF;
IF COUNT=3 THEN STATE<=OUTPUT;END IF;
WHEN "1110"=>
IF KEY=4 THEN COUNT:=COUNT+1;ELSE STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;END IF;
IF COUNT=3 THEN STATE<=OUTPUT;END IF;
WHEN OTHERS =>STATE<=ROLL_POLING_1;COUNT:=0;
END CASE;
WHEN OUTPUT=>
STATE<=ROLL_POLING_1;COUNT:=0;TRIGGER_POLING<=IDLE;
END CASE;
END IF;
WHEN OTHERS=>NULL;
END CASE;
END PROCESS;
------------------------------OUTPUT_LOGIC-----------------------------
OUTPUT_LOGIC_OUTP:PROCESS(STATE)
BEGIN
CASE STATE IS
WHEN ROLL_POLING_1=> OUTP<="110";
WHEN ROLL_POLING_2=> OUTP<="101";
WHEN ROLL_POLING_3=> OUTP<="011";
WHEN TRIGGER=>
CASE TRIGGER_POLING IS
WHEN TRIGGER_POLING_1=> OUTP<="110";
WHEN TRIGGER_POLING_2=> OUTP<="101";
WHEN TRIGGER_POLING_3=> OUTP<="011";
WHEN OTHERS => NULL;
END CASE;
WHEN OUTPUT=> NULL;
END CASE;
END PROCESS;
NEW_DATA<='1' WHEN STATE=OUTPUT ELSE '0';
DATA<= B"011_0011" WHEN (TRIGGER_POLING=TRIGGER_POLING_1)AND KEY =1 AND STATE=OUTPUT ELSE--3
B"011_0110" WHEN (TRIGGER_POLING=TRIGGER_POLING_1)AND KEY =2 AND STATE=OUTPUT ELSE--6
B"011_1001" WHEN (TRIGGER_POLING=TRIGGER_POLING_1)AND KEY =3 AND STATE=OUTPUT ELSE--9
B"010_0011" WHEN (TRIGGER_POLING=TRIGGER_POLING_1)AND KEY =4 AND STATE=OUTPUT ELSE--#
B"011_0010" WHEN (TRIGGER_POLING=TRIGGER_POLING_2)AND KEY =1 AND STATE=OUTPUT ELSE--2
B"011_0101" WHEN (TRIGGER_POLING=TRIGGER_POLING_2)AND KEY =2 AND STATE=OUTPUT ELSE--5
B"011_1000" WHEN (TRIGGER_POLING=TRIGGER_POLING_2)AND KEY =3 AND STATE=OUTPUT ELSE--8
B"011_0000" WHEN (TRIGGER_POLING=TRIGGER_POLING_2)AND KEY =4 AND STATE=OUTPUT ELSE--0
B"011_0001" WHEN (TRIGGER_POLING=TRIGGER_POLING_3)AND KEY =1 AND STATE=OUTPUT ELSE--1
B"011_0100" WHEN (TRIGGER_POLING=TRIGGER_POLING_3)AND KEY =2 AND STATE=OUTPUT ELSE--4
B"011_0111" WHEN (TRIGGER_POLING=TRIGGER_POLING_3)AND KEY =3 AND STATE=OUTPUT ELSE--7
B"010_1010" WHEN (TRIGGER_POLING=TRIGGER_POLING_3)AND KEY =4 AND STATE=OUTPUT ELSE--*
B"000_0000";
end Behavioral;
仿真波形图如下
接下来,我们采用三进程描述来完成这个问题。这里次态逻辑的敏感列表是NX_STATE_LOGIC:PROCESS(PR_STATE,INP,COUNT_STATE),其中count_state是用来描述计数状态的,如果没有它也是可以的,只不过需要另外加入状态,否则次态逻辑块可能不会被触发。此外,这里的count_state不是次态逻辑块内部变量count的简单赋值,而是需要将count先赋值给信号count_reg,然后在时钟上升触发时,再将count_reg赋给count_state。这样做是因为,次态逻辑块不会因为自己块中的信号更新而被触发,换句话说,敏感列表可以是外部输入、外部信号,而不能是内部信号。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity Anti_shake_circuit_encoder is
Port (INP: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
CLK,RST: IN STD_LOGIC;
OUTP: OUT STD_LOGIC_VECTOR(2 DOWNTO 0);
DATA: OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
NEW_DATA: OUT STD_LOGIC
);
end Anti_shake_circuit_encoder;
architecture Behavioral of Anti_shake_circuit_encoder is
TYPE STATE_TPYE IS (ROLL_POLING_1,ROLL_POLING_2,ROLL_POLING_3,TRIGGER,OUTPUT);
TYPE TRIGGER_POLING_STATE_TYPE IS (IDLE,TRIGGER_POLING_1,TRIGGER_POLING_2,TRIGGER_POLING_3);
SIGNAL PR_STATE,NX_STATE:STATE_TPYE;
SIGNAL KEY:INTEGER RANGE 1 TO 4;
SIGNAL TRIGGER_POLING:TRIGGER_POLING_STATE_TYPE;
SIGNAL COUNT_REG,COUNT_STATE:INTEGER RANGE 0 TO 3;
SIGNAL DATA_REG:STD_LOGIC_VECTOR(6 DOWNTO 0);
SIGNAL DATA_OUT:STD_LOGIC_VECTOR(6 DOWNTO 0);
begin
---------------------------------STATE_REG-------------------------------
STATE_REG:PROCESS(CLK,RST)
BEGIN
IF(RST='1')THEN
PR_STATE<=ROLL_POLING_1;
COUNT_STATE<=0;
DATA_OUT<="0000000";
ELSIF CLK'EVENT AND CLK='1'THEN
PR_STATE<=NX_STATE;
COUNT_STATE<=COUNT_REG;
DATA_OUT<=DATA_REG;
END IF;
END PROCESS;
------------------------------NX_STATE_LOGIC------------------------------
NX_STATE_LOGIC:PROCESS(PR_STATE,INP,COUNT_STATE)
VARIABLE COUNT: INTEGER RANGE 0 TO 3;
BEGIN
CASE PR_STATE IS
WHEN ROLL_POLING_1=>
COUNT:=1;
CASE INP IS
WHEN "0111"=> NX_STATE<=TRIGGER; KEY <=1; TRIGGER_POLING<=TRIGGER_POLING_1;
WHEN "1011"=> NX_STATE<=TRIGGER; KEY <=2; TRIGGER_POLING<=TRIGGER_POLING_1;
WHEN "1101"=> NX_STATE<=TRIGGER; KEY <=3; TRIGGER_POLING<=TRIGGER_POLING_1;
WHEN "1110"=> NX_STATE<=TRIGGER; KEY <=4; TRIGGER_POLING<=TRIGGER_POLING_1;
WHEN OTHERS => NX_STATE<=ROLL_POLING_2;COUNT:=0;
END CASE;
WHEN ROLL_POLING_2=>
COUNT:=1;
CASE INP IS
WHEN "0111"=> NX_STATE<=TRIGGER; KEY <=1; TRIGGER_POLING<=TRIGGER_POLING_2;
WHEN "1011"=> NX_STATE<=TRIGGER; KEY <=2; TRIGGER_POLING<=TRIGGER_POLING_2;
WHEN "1101"=> NX_STATE<=TRIGGER; KEY <=3; TRIGGER_POLING<=TRIGGER_POLING_2;
WHEN "1110"=> NX_STATE<=TRIGGER; KEY <=4; TRIGGER_POLING<=TRIGGER_POLING_2;
WHEN OTHERS => NX_STATE<=ROLL_POLING_3;COUNT:=0;
END CASE;
WHEN ROLL_POLING_3=>
COUNT:=1;
CASE INP IS
WHEN "0111"=> NX_STATE<=TRIGGER; KEY <=1; TRIGGER_POLING<=TRIGGER_POLING_3;
WHEN "1011"=> NX_STATE<=TRIGGER; KEY <=2; TRIGGER_POLING<=TRIGGER_POLING_3;
WHEN "1101"=> NX_STATE<=TRIGGER; KEY <=3; TRIGGER_POLING<=TRIGGER_POLING_3;
WHEN "1110"=> NX_STATE<=TRIGGER; KEY <=4; TRIGGER_POLING<=TRIGGER_POLING_3;
WHEN OTHERS => NX_STATE<=ROLL_POLING_1;COUNT:=0;
END CASE;
WHEN TRIGGER=>
CASE INP IS
WHEN "0111"=>
IF(KEY=1)THEN
COUNT:=COUNT+1;
IF(COUNT=3)THEN NX_STATE<=OUTPUT;TRIGGER_POLING<=IDLE;ELSE NX_STATE<=TRIGGER; END IF;
ELSE
NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
END IF;
WHEN "1011"=>
IF(KEY=2)THEN
COUNT:=COUNT+1;
IF(COUNT=3)THEN NX_STATE<=OUTPUT;TRIGGER_POLING<=IDLE;ELSE NX_STATE<=TRIGGER; END IF;
ELSE
NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
END IF;
WHEN "1101"=>
IF(KEY=3)THEN
COUNT:=COUNT+1;
IF(COUNT=3)THEN NX_STATE<=OUTPUT;TRIGGER_POLING<=IDLE;ELSE NX_STATE<=TRIGGER; END IF;
ELSE
NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
END IF;
WHEN "1110"=>
IF(KEY=4)THEN
COUNT:=COUNT+1;
IF(COUNT=3)THEN NX_STATE<=OUTPUT;TRIGGER_POLING<=IDLE;ELSE NX_STATE<=TRIGGER; END IF;
ELSE
NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
END IF;
WHEN OTHERS =>
NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
END CASE;
WHEN OUTPUT=> NX_STATE<=ROLL_POLING_1;TRIGGER_POLING<=IDLE;COUNT:=0;
END CASE;
COUNT_REG<= COUNT;
END PROCESS;
--------------------------------ENCODER---------------------------------
PROCESS(TRIGGER_POLING,KEY) BEGIN
CASE TRIGGER_POLING IS
WHEN TRIGGER_POLING_1=>
CASE KEY IS
WHEN 1=> DATA_REG<=B"011_0011"; --3
WHEN 2=> DATA_REG<=B"011_0110"; --6
WHEN 3=> DATA_REG<=B"011_1001"; --9
WHEN 4=> DATA_REG<=B"010_0011"; --#
END CASE;
WHEN TRIGGER_POLING_2=>
CASE KEY IS
WHEN 1=> DATA_REG<=B"011_0010"; --2
WHEN 2=> DATA_REG<=B"011_0101"; --5
WHEN 3=> DATA_REG<=B"011_1000"; --8
WHEN 4=> DATA_REG<=B"011_0000"; --0
END CASE;
WHEN TRIGGER_POLING_3=>
CASE KEY IS
WHEN 1=> DATA_REG<=B"011_0001"; --1
WHEN 2=> DATA_REG<=B"011_0100"; --4
WHEN 3=> DATA_REG<=B"011_0111"; --7
WHEN 4=> DATA_REG<=B"010_1010"; --*
END CASE;
WHEN OTHERS => DATA_REG<=B"000_0000";
END CASE;
END PROCESS;
------------------------------OUTPUT_LOGIC-----------------------------
OUTPUT_LOGIC:PROCESS(PR_STATE)
BEGIN
CASE PR_STATE IS
WHEN ROLL_POLING_1=> OUTP<="110";
WHEN ROLL_POLING_2=> OUTP<="101";
WHEN ROLL_POLING_3=> OUTP<="011";
WHEN TRIGGER=>
CASE TRIGGER_POLING IS
WHEN TRIGGER_POLING_1=> OUTP<="110";
WHEN TRIGGER_POLING_2=> OUTP<="101";
WHEN TRIGGER_POLING_3=> OUTP<="011";
WHEN OTHERS => NULL;
END CASE;
WHEN OUTPUT=> NULL;
END CASE;
END PROCESS;
NEW_DATA<='1' WHEN COUNT_REG=3 ELSE '0';
DATA<=DATA_OUT WHEN COUNT_REG=3 ELSE B"000_0000";
end Behavioral;
仿真波形图为:
这里存在一个小问题,复位信号触发时,改变了现态,从而触发了次态逻辑,导致计数加1,这是一个小问题,如果要更改,可以在次态逻辑的敏感列表中添加RST,并加入复位时的处理。
可以看到,这种题目使用三进程比较复杂和繁琐,不如双进程描述来的简单。