状态机的编码 a、状态机的编码。Biary、gray-code 编码使用最少的触发器,较多的组合逻辑。而one-hot编码反之。由于CPLD 更多的提供组合逻辑资源,而FPGA 更多的提供触发器资源,所以CPLD多使用gray-code,而FPGA多使用one-hot 编码。另一方面,对于小型设计使用gray-code和binary 编码更有效,而大型状态机使用one-hot 更高效。
b、在代码中添加综合器的综合约束属性或者在图形界面下设置综合约束属性可以比较方便地改变状态的编码。
VHDL 的示例: Synplicity: attribute syn_encoding : string; attribute syn_encoding of <signal_name> : type is "value "; The syn_encoding attribute has 4 values : sequential, onehot, gray and safe. Exemplar: -- Declare TYPE_ENCODING_style attribute -- Not needed if the exemplar_1164 package is used type encoding_style is (BINARY, ONEHOT, GRAY, RANDOM, AUTO); attribute TYPE_ENCODING_style : encoding_style; ... attribute TYPE_ENCODING_style of <typename> : type is ONEHOT; Verilog 示例: Synplicity: Reg[2:0] state; /* synthesis syn_encoding = "value" */; // The syn_encoding attribute has 4 values : sequential, onehot, gray and safe. Exemplar: Parameter /* exemplar enum <type_name> */ s0 = 0, s1 = 1, s2 = 2, s3 = 3, S4 = 4; Reg [2:0] /* exemplar enum <type_name> */ present_state, next_state 状态机的编码风格 a、关于FSM 的编码方法。FSM 分两大类:米勒型和摩尔型。组成要素有输入(包括复位),状态(包括当前状态的操作),状态转移条件,状态的输出条件。 设计FSM 的方法和技巧多种多样,但是总结起来有两大类:第一种,将状态转移和状态的操作和判断等写到一个模块(process、block)中。另一种是将状态转移单独写成一个模块,将状态的操作和判断等写到另一个模块中(在Verilog 代码中,相当于使用两个“always” block)。其中较好的方式是后者。其原因如下。 首先FSM 和其他设计一样,最好使用同步时序方式设计,好处不再赘述。而状态机实现后,状态转移是用寄存器实现的,是同步时序部分。状态的转移条件的判断是通过组合逻辑判断实现的,之所以第二种比第一种编码方式合理,就在于第二种编码将同步时序和组合逻辑分别放到不同的程序块(process,block)中实现。这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。
b、初始化状态和默认状态。 一个完备的状态机(健壮性强)应该具备初始化状态和默认状态。当芯片加电或者复位后,状态机应该能够自动将所有判断条件复位,并进入初始化状态。需要注明的一点是,大多数FPGA 有GSR(Global Set/Reset)信号,当FPGA 加电后,GSR 信号拉高,对所有的寄存器,RAM 等单元复位/置位,这是配置于FPGA 的逻辑并未生效,所以不能保证正确的进入初始化状态。所以使用GSR 企图进入FPGA 的初始化状态,常常会产生种种不必要的麻烦。一般的方法是采用异步复位信号,当然也可以使用同步复位,但是要注意同步复位的逻辑设计。解决这个问题的另一种方法是将默认的初始状态的编码设为全零,这样GSR 复位后,状态机自动进入初始状态。 令一方面状态机也应该有一个默认(default)状态,当转移条件不满足,或者状态发生了突变时,要能保证逻辑不会陷入“死循环”。这是对状态机健壮性的一个重要要求,也就是常说的要具备“自恢复”功能。对应于编码就是对case,if-else 语句要特别注意,要写完备的条件判断语句。VHDL 中,当使用CASE 语句的时候,要使用“When Others”建立默认状态。使用“IF...THEN...ELSE”语句的时候,要用“ELSE”指定默认状态。Verilog 中,使用“case”语句的时候要用“default”建立默认状态,使用“if...else”语句的注意事项相似。 另外提一个技巧:大多数综合器都支持Verilog 编码状态机的完备状态属性--“full case”。这个属性用于指定将状态机综合成完备的状态,如Synplicity 的综合工具(Synplify/Synplify Pro,Amplify,etc)支持的命令格式如下: case (current_state) // synthesis full_case 2’b00 : next_state <= 2’b01; 2’b01 : next_state <= 2’b11; 2’b11 : next_state <= 2’b00; //这两段代码等效 case (current_state) 2’b00 : next_state <= 2’b01; 2’b01 : next_state <= 2’b11; 2’b11 : next_state <= 2’b00; default : next_state <= 2bx; Synplicity 还有一个关于状态机的综合属性,叫“// synthesis parallel_case”其功能是检查所有的状态是“并行的”(parallel),也就是说在同一时间只有一个状态能够成立。
c、使用完备的“if...else”还有一个重要的好处,就是可以避免生成非目的性的“锁存器”(Latch)。
d、状态机的定义可以用parameter 定义,但是不推荐使用`define 宏定义的方式,因为‘define 宏定义在编译时自动替换整个设计中所定义的宏,而parameter 仅仅定义模块内部的参数,定义的参数不会与模块外的其他状态机混淆。
|