摘要:以离心机定时顺序控制器的设计为例,阐述了用VHDL设计有限状态机的方法,讨论了如何消除状态机输出信号的“毛刺”。
关键词:VHDL 状态机 EDA
离心机 毛刺
现代数字系统的设计一般都采用自顶向下的模块化设计方法。即从整个系统的功能出发,将系统分割成若干功能模块。在自顶向下划分的过程中,最重要垢是将系统或子系统按计算机组成结构那样划分成控制器和若干个受控制的功能模块。受控部分通常是设计者们所熟悉的各种功能的电路,设计较为容易。主要任务是设计控制器,而其控制功能可以用限状态机来实现。因而有必要深入探讨有限状态机的设计方法。
1 状态机设计的一般方法
传统的设计方法是首先绘制出控制器的状态图,并由此列出状态表,再合并消除状态表中的等价状态项。在完成状态寄存器的分配之后,根据状态表求出次态及输出方程,最后画出设计原理图。采用这种方法设计复杂状态机将会十分繁杂。
利用VHDL设计状态机,不需要进行繁琐的状态分配、绘制状态表和化简次态方程。设计者不必使用卡诺图进行逻辑化简,不必画电路原理图,也不必搭试硬件电路进行逻辑功能的测试,所有这些工作都可以通过EDA工具自动完成。应用VHDL设计状态机的具体步骤如下:
(1)根据系统要求确定状态数量、状态转移的条件和各状态输出信号的赋值,并画出状态转移图;
(2)按照状态转移图编写状态机的VHDL设计程序;
(3)利用EDA工具对状态机的功能进行仿真验证。
下面以离心机定时顺序控制器的设计为例,说明状态机的设计方法。
2 定时顺序控制状态机的设计
在化工生产中,离心机用于固、液分离的工艺流程,如图1所示。
从加料至刮刀回程循环N次之后,进入大洗网工序,随后又开始进入新一轮顺序循环工作。该系统控制器的框图如图2所示。
图2中计数模块是由三个带异步复位和并行预置的计数器组成。其中COUNTER1是2位加/减法计数器,用于控制从加料至刮刀回程的各工序时间,其中加料和卸料工序为加计时,其余工序为减计时;COUNTER2是3位减法计数器,用于控制大洗网的时间;COUNTER3是2位加法计数器,用于控制循环工作次数。译码器用于选通与各工序相对应的预置数。图2中K1、K2分别是加料和卸料限位开关信号,R{R1、R2、R3}为计数器减为零(或循环次数等于预置数)时发出的信号,它们都可作为状态转移的控制信号。LD(LD1、LD2、LD3)为并行置数控制信号,CLR(CLR1、CLR2、CLR3)为异步清零信号,ENA(ENA1、ENA2)为计数使能信号,J为加/减计数控制信号,G(G1、A、B、C)为译码器控制信号,FO为各工序电磁阀和指示灯控制信号。
系统工作方式如下:当系统处于初始状态或复位信号reset有效时,系统处于复位状态。按下自动工作键C0,系统进入加料工序并开始自动控制离心机的运行。以加水工序为例,首先为加水预置数状态(water_ld)时,状态机输出信号FO,开启加水电磁阀及指示灯,同时输出信号G控制选通加水时间预置数,在LD1信号的控制下,将预置数送入COUNTER1。此时ENA1=1,J=1,使计数器为减计数状态。然后,在下一个时钟周期进入加水工作状态(water),并开始减计时。当计时为零时,计数器发出借位信号R1作为此工序结束的信号送入状态机,使其转入下一个工作状态。根据系统要求可画出状态转移图如图3所示。其中图3表示各状态转移的顺序和转移条件,表1列出了与各状态相应的输出信号值。
表1 离心机控制器的各状态输出信号值
序号 |
状态 |
Clr1 |
clr2 |
Clr3 |
J |
Ena1 |
Ena2 |
G |
C |
B |
A |
Ld1 |
Ld2 |
Ld3 |
F0 |
S0 |
System- |
1 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
1 |
0000000 |
reset |
S1 |
Load |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0000001 |
S2 |
One_dry_ |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
K1 |
0 |
0 |
0000010 |
S3 |
WQater_ld |
0 |
0 |
0 |
1 |
10 |
0 |
1 |
0 |
0 |
1 |
R1 |
0 |
0 |
0000100 |
S4 |
water |
0 |
0 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0000100 |
S5 |
Two_dry |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
0 |
R1 |
0 |
0 |
0001000 |
ld |
S6 |
Two_dry |
0 |
0 |
0 |
1 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0010000 |
S7 |
Unload |
R1 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0010000 |
reset |
S8 |
unload |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0010000 |
|
S9 |
Return_ |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
1 |
K2 |
0 |
0 |
0100000 |
trip |
S10 |
decision |
1 |
0 |
0 |
1 |
1 |
0 |
R3 |
1 |
0 |
0 |
0 |
R3 |
0 |
0000000 |
S11 |
wash |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1000000 |
S12 |
Cycle-ld |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
0 |
1 |
0000001 |
按照状态转移图可编写状态机的VHDL源程序。采用双进程描述法设计的离心机控制器源程序如下:
library ieee;
use ieee.std_logic_1164.all;
entity controller isport (c0,reset: in std_logic;
clk1,k1,k2,r1,r2,r3: in std_logic;
clr1,clr2,clr3,j,enal,ena2: out std_logic;
ld1,ld2,ld3,g1,a,b,c: out std_logic;
fo: out std_logic_vector(6 downto 0));
end controller;architecture state_machine of
controller is
type statetype is(system_reset,load_dry,water_ld,water,two_dry_ld,
two_dry,unload_reset,unload,return_trip,decision,wash,cycle_ld);
signal present_state,next_state : statetype;
begin
state_comb:process (present_state,c0,reset,k1,k2,r1,r2,r3)begin
if reset='1' then
clr1<='1';clr2<='1';clr3<='1';j<='0';enal<='0';
ena2<='0';
fo<="0000000";ld1<='0';ld2<='0'ld3<='0';
next_state <= system_reset;
else
case present_state is
when system_reset =>
clr1<='1';clr2<='1';clr3<='0';j<='0';
ena1<='0';ena2<='0';
g1<='1';c<='1';b<='0';a<='1';
fo<="0000000";ld1<='0';ld2<='0';ld3<='1';
if (c0='1') then
next_state<=ldad;
else
next_state<=system_reset;
end if;
when load =>
clr1<='0';clr2<='0';clr3<='0';j<='0';
ena1<='1';ena2<='0';
g1<='0';c<='0';b<='0';a<='0';
fo<="0000001";ld1='0';ld2<='0';ld3<='0';
if (k1='1') then
next_state<=one_dry;
else
next_state<=load;
end if;
……
end case;
end if;
end process state_comb;
state_clocked:process(clk1,reset)
begin
if reset='1' then
present_state<=system_reset;
elsif rising_edge(clk1) then
present_state<=next_state;
end if;
end process state_clocked;
end state_machine;
利用EDA软件MAX+plus Ⅱ对该程序进行编译、仿真,其仿真波形见图4。
从图4仿真波形图上可以看出,状态机的输出信号有许多“毛刺”产生。如何消除这些“毛刺”是下面将讨论的问题。
3 在状态机设计中如何消除“毛刺”
在同步电路中,一般情况下,“毛刺”不会产生重大影响。因为“毛刺”仅发生在时钟有效边沿之后的一小段时间内,只要在下一个时钟有效边沿到来之前“毛刺”消失即中。但是,由于状态机的输出信号都是作为其它功能模块的控制信号,如果这些控制信号作为异步控制(如本系统中的CLR)、三态使能控制或时钟信号来使用,则将会使受模块发生误动作,造成系统工作混乱。因此,在这种情况下必须保证状态机的输出没有“毛刺”。
消除状态机输出信号的“毛刺”有以下三种方案:
·在状态机输出信号较少的情况下,直接把状态作为输出信号;
·对于顺序迁移的状态机,选择雷格码作为状态编码;
·在Moore型或Mealy型状态机基础上,用时钟同步输出信号。
本设计采用后两种方案,较简便。雷格码的特点是:当状态机改变状态时,状态向量中仅1位发生变化。与前面用可枚举类型定义的状态变量不同,现在选用常数来指定各状态变量的取值为格雷码。修改部分设计程序如下:
architecture state_machine of controller1 is
signal present_state,next_state:std_logic_vector(3
downto 0);
constant load:std_logic_vector(3 downto 0):="0001";
constant one_dry: std_logic_vector(3 downto
0):="0011";
constant water_ld;std_logic_vector(3 downto
0):="0010";
constant water:std_logic_(3 downto 0):="0110";
constant two_dry_ld:std_logic_vector(3 downto
0):="0111";
constant two_dry:std_logic_vector(3 downto
0);="0101";
constant unload_reset:std_logic_vector(3 downto
0):="0100";
constant unload:std_logic_vector(3 downto 0);="1100";
constant return_trip:std_logic_vector(3 downto
0):="1101";
constant decision:std_logic_vector(3 downto
0):="1111";
constant wash:std_logic_vector(3 downto 0):="1110";
constant cycle_ld:std_logic_vector(3 downto
0):="1010";
……
若要安全消除状态机的“毛刺”,则应采用第三种方案。这时,设计程序要作相应改变,状态的转移和输出信号的赋值要写在同一步时钟进程中。
综上所述,采用VHDL设计状态机,能够大大降低设计难度。使用双进程描述风格编写VHDL设计程序,可以清楚地在一个进程中确定状态的转移和对输出的赋值,而且具有易于建立、理解和维护的优点。特别针对大型或具有大量状态转移和输出信号的状态机设计,将会更加显示出它的优势。对于状态机输出信号所产生的
“毛刺”,可以按照系统的要求,根据不同的情况采用相应的设计方案予以消除。