如何设计最优化的状态机前言:数字电路通常分为组合逻辑电路和时序电路,组合逻辑电路outputs = F(current inputs)时序电路outputs = F(current inputs,past inputs)有限状态机就是时序电路的数学抽象,一个有限状态机系统包括inputs ,outputs, states .状态机分为同步状态机(synchronous)和异步状态机(asynchronous),异步状态机由于输出信号不稳定,所以不详细讨论,对绝大多数设计来说,用的最广泛的是同步状态机。
下面主要讨论了同步状态机的设计。
一.状态机的基础知识1.1. moore状态机和mealy状态机的区别:2.1.1moore状态机输出只依赖于及其的当前状态,与输入信号无关。
这是moore状态机的优点。
下面是moore状态机的模型:moore状态机比较容易用数学的方式来分析,因此被更广泛的用在代数状态机理论中(algebraic FSM theory)。
Mealy状态机输出依赖于机器现在的状态和输入的值,如果输入改变,输出可以在一个时钟周期中将发生了改变。
其模型如下:图的说明:state memory :保存现在的状态(current state s(t) )state transistion function :根据现态和输入x(t),s(t+1)来决定下一个状态。
Output function :根据s(t)和x(t)来决定最后的输出。
Mealy 状态机通常可以有更少的状态变量,因此在工程领域有更为广阔的应用,状态变量越少,则所需的存储单元就越少。
下面用简单的实例来具体说明两者编程的区别,和综合出来的结果的不同:Mealy状态机的简单例子:1.源程序:demo_process:process(clk,reset)beginif(reset = '1')thenstate <= s0;out1 <= (others=>'0');elsif rising_edge(clk) thencase state iswhen s0 => if(in1 = '1')thenstate <= s1;out1 <= "1000";end if;when s1 => if(in1 = '0')thenstate <= s2;out1 <= "1001";end if;when s2 => if(in1 = '1')thenstate <= s3;out1 <= "1100";end if;when s3 => if(in1 = '0')thenstate <= s0;out1 <= "1111";end if;when others =>null;end case;end if;end process;modelsim仿真结果:synplify综合结果:1.模块表示图:在这张综合图上可以明显得看出红线即input所参与决定的是状态的产生和输出。
而且输出out1为了防止输出的波形不好(因为通过组合电路输出的波形不稳定),所以加了一个触发器。
2.门级综合结果(仅作参考):其中状态机模块编码采用两个触发器,见下图:所以如果在语言中没有对状态进行编码,那么综合器会自动将编码方式设为顺序编码。
Moore状态机的简单例子:1源程序:demo_process:process(clk,reset)beginif(reset = '1')thenstate <= s0;out1 <= (others=>'0');elsif rising_edge(clk) thencase state iswhen s0 => if(in1 = '1')thenstate <= s1;end if;out1 <= "1000";when s1 => if(in1 = '0')thenstate <= s2;end if;out1 <= "1001";when s2 => if(in1 = '1')thenstate <= s3;end if;out1 <= "1100";when s3 => if(in1 = '0')thenstate <= s0;end if;out1 <= "1111";when others =>null;end case;end if;end process;2.Modelsim仿真结果:3.ynplfy综合结果:1.原理图模块:从图中可以看出红线即输入仅仅参与决定了状态机产生状态。
2.门级综合结果:(仅作参考)从以上两种状态机的例子可以看出,同一周期对同样的输入,两种仿真结果一致,只是在综合上有所区别。
1.2 创建状态图或者状态转换表。
设计者通常可以用两种工具来简化状态表的建立过程:状态图和转换表。
状态图提供了一种图形化的,易理解的对FSM操作的描述,但限制于相对较小的设计,当设计太复杂而无法创建状态图时,应使用转换表。
2.2.1 创建状态图状态图应该从复位状态开始,每一个圈表示一个状态,状态图是有向图,圈内部应标上状态的名称和此状态的输出赋值,两个状态之间的转变通过一个有向线段表示,转变条件应标志在有向线段上面,该条件与时钟的上升沿同步。
下面是一个简单的图例:1.2.2 创建转换表状态图是帮助理解状态之间关系的有效一种方法,然而对于许多状态的大电路,状态会变得凌乱,极难画出可用的形式。
这时,普遍采用一种称作转换表的文本描述方式,创建转换表的方法与状态图相同,不同的是转换列在一个表中。
上图的转换表如下:注意:在写状态图或者转换表的时候要坚持互斥原则,离开任何节点的线段上的逻辑表达式必须成对互斥,即没有在离开同一节点不同线段上的两个表达式同时为真的情况,如果两个这样的表达式同时是逻辑1,那么状态机就进入了两个不同的次状态,这是不容许的。
互斥测试的是离开同一节点的两个表达式的逻辑and必须为逻辑0。
1.4 状态的编码方式在vhdl原码中是否对状态机编码作出规定并不影响状态机的功能,综合工具提供状态优化器,他可以在综合过程中确定状态编码。
状态编码主要有5种编码方式:1.顺序编码。
2.格雷码编码。
3.单热编码(one-hot)。
4.随机编码。
5.自动编码(面积最小化)。
状态机在传统上是按二进制编码的。
然而采用Gray编码,相邻状态可减少瞬变的次数。
有时不可能在所有状态中使用Gray编码,则应在状态矢量中增加触发器的数量以减少开关的次数。
另一种方法是使用one-hot编码,虽然该编码使用的触发器较多,即可减少组合逻辑的使用,在带多个输出且每个输出是几个状态的函数的状态机中更是如此。
根据状态机的形式,设计者可在Gray、One-hot或二进制间进行选择。
现在就详细说一下one-hot 状态机:1.ne-hot编码:s0 = “0001” ; s1 = “0010”; s2 = “0100”; s3 = “1000”;每一个状态用掉一个触发器,状态数等于触发器的数目。
这就意味着触发器数目增加,而状态译码组合电路被优化掉了。
对于寄存器资源丰富的xilinx器件来说,是非常适合的。
2.one-hot 状态机的好处:现在的FPGA每一个逻辑块中都包含了一个和多个触发器,对于仅需要触发器的one-hot编码解码来说,提供了很好的条件。
下面列举了一些用one-hot 设计的好处:a)对寄存器资源丰富xilinx fpga 来说更易于适配和布线。
b)one-hot状态机是典型的相当快速的状态机。
他的速度与状态的个数没有任何关系,仅仅决定于状态变迁到一个特殊状态的这种转换的数量(instead depend only on thenumber of transitions into a particular state)。
c)不用担心你会在发现最佳的状态机编码方式。
因为其他设计的状态机如果在加入一些状态或者改变其他一些什么的话,那就可能不再最优了。
One-hot 编码方式在所有状态机中是最佳的,最优的。
d)one-hot 状态机很容易设计。
状态图能够直接被画成原理图或者被直接用vhdl语言写出来,而不用编码成状态表。
e)修改起来简单明了。
增加和删掉一些状态或者改变一些敏感量等式能被综合器很容易的执行,而不会影响余下的状态机。
f)很容易从vhdl 或者verilog 综合。
g)比其他一些高性能的状态机没有任何的布线面积的浪费。
h)能够用静态时序分析的方法很容易的找出危险的不合理的状态机转换路径。
3.举例说明:type STA TE_TYPE is (s0, s1, s2, s3);attribute ENUM_ENCODING: STRING;attribute ENUM_ENCODING of STA TE_TYPE: type is "0001 0010 0100 1000"; signal CS, NS: STA TE_TYPE;begin-- build the state flopsSYNC_PROC: process (clk, reset)beginif (reset='1') thenCS <= s0;elsif rising_edge(clk) thenCS <= NS;end if;end process;-- state machineCOMB_PROC: process (CS,in1)begincase CS iswhen s0 =>out1 <= (others=>'0');if(in1 = '1')thenNS <= s1;out1 <= "1000";end if ;when s1 => if(in1 = '0')thenNS <= s2;out1 <= "1001";end if;when s2 => if(in1 = '1')thenNS <= s3;out1 <= "1100";end if;when s3 => if(in1 = '0')thenNS <= s0;out1 <= "1111";end if;when others =>null;end case;end process;在s0状态里,有一个细节,out1 <= (others=>'0');这句话在if外面,和在if里面的out1 <= "1000";这句话不会自相矛盾。