FSM(Finite State Machine)是数字设计中经常涉及到的部分。FSM分为两类:Mealy型和Moore型,其主要区别是:Mealy型状态机的输出与当前状态和输入均有关;Moore型状态机的输出仅与当前状态有关,而与输入无关。
如何设计有效的状态机,避免一些不希望出现的输出结果(如锁存、毛刺等),是本文的谈论要点。
以下观点是本人在一些资料的基础上自己总结的,如有不当之处,请大家指出,共同学习!在此感谢所有本文所涉及的文章的作者。文章最后我会将所看资料的名字附上,有兴趣的可以下载查看。
本人在之前设计中,对状态机这块并没有特别留意,只是用最简单的加法器来控制状态,将所有状态机部分写在一个always块中,这样在调试和程序的修改中比较麻烦,稍微不注意就会出现错误或者得不到想要的结果。研究了一些比较权威的资料后,对状态机的设计有了一些自己的体会。
状态机的编码
Binary、gray-code编码使用最少的触发器,较多的组合逻辑。而one-hot 编码反之。由于CPLD更多的提供组合逻辑资源,而FPGA更多的提供触发器资源,所以CPLD 多使用gray-code,而FPGA 多使用one-hot编码。另一方面,对于小型设计使用gray-code和binary编码更有效,而大型状态机使用one-hot更高效。
看synplicity的文档,推荐在24个状态以上会用格雷码,在5~24个状态会用独热码,在4个状态以内用二进制码,肯定独热码比二进制码在实现FSM部分会占更多资源,但是译码输出控制简单,所以如果状态不是太多,独热码较好。状态太少译码不会太复杂,二进制就可以了。状态太多,前面独热码所占资源太多,综合考虑就用格雷码了。
各种类型编码的优缺点
二进制码采用最简单的递增的编码方式对状态进行编码,对于n个状态的状态机,共需要 log2(n)个触发器表示所有的状态。在状态很多的情况下,可以大大减少触发器的数量,对设计的面积有积极的作用。但是在状态跳转过程中,很可能出现多位同时变化的情况,容易在 next state的生成逻辑上产生毛刺。同时,输出也是所有状态位的译码,译码逻辑多数很复杂,往往成为整个设计的关键路径。
格雷码类似二进制编码,但是采用了格雷码的编码方式,每两个相邻的状态只有一位信号变化,避免了next state上毛刺的产生。同时两个相邻状态的输出译码变得简单了,避免了复杂组合逻辑的产生。但是格雷码的这些优点都是建立在状态跳转是顺序执行的基础上的。如果状态机有很多随机跳转和分支,格雷码的实际效果和二进制码相差无几,优势荡然无存。
当然在设计中还可以采用复杂的编码方式,通过对状态跳转的分析,设计一套编码来避免二进制编码的缺点。这种做法工作量很大,而且设计不具有可维护性,实际设计中并不可取。
最后剩下的就是独热码了,也就是onehot。onehot编码方式是当前设计中最常用的状态机编码方式。onehot 编码在一组 0 中只有一个 1,对一个 n 个状态的 FSM 设计,需要 n 个触发器。但是在任意两个状态之间跳转都只有两位状态位变化,不会产生非常复杂的组合逻辑。各个状态之间的译码也相对简单。
如何避免输出锁存?
好的状态机设计时要有错误自动恢复的能力,即当进入错误的状态时,能自动的重新进入循环,避免出现"跑飞“的现象。
消除 FSM中 latches的最简易方法是在执行case语句前对输出值进行初始化赋值。
对应于编码就是对 case,if-else 语句要特别注意,要写完备的条件判断语句。, 使用“case”语句的时候要用“default”建立默认状态,使用“if-else”语句时要将所有需要判断的条件列出后,用“else”建立默认状态。
Verilog设计中可以使用”full-case“ ”parallel-case“指令来实现消除输出锁存。(一般情况下尽量不要使用这两条指令,仅在onehot状态机中使用)
所谓 Full Case 是指:FSM 的所有编码向量都可以与 case 结构的某个分支或 default 默认情况匹配起来。如果一个 FSM 的状态编码是8bit,则对应的256 个状态编码(全状态编码是 2^n个)都可以与case 的某个分支或者default 映射起来。
所谓 Parallel Case 是指:在 case 结构中,每个 case 的判断条件表达式有且仅有唯一的 case 语句的分支与之对应,即两者关系是一一对应关系。
但在使用不当的情况下,这两个指令并不能达到理想的效果。详细分析见Clifford E. Cummings的《"full_case parallel_case", the Evil Twins of VerilogSynthesis》一文。
增加输出寄存器消除毛刺(即将输出延时)
有两个方法实现FSM输出状态寄存,一是将状态变量编码,每个输出是编码后的状态变量中的一位;二是使用三个always模块,一个 always模块采用同步时序的方式描述状态转移,一个采用组合逻辑的方式判断状态转移条件,描述状态转换规律,第三个 always模块使用同步时序电路描述每个状态的输出,又称三段式写法。
1.状态变量编码(摘录自《Coding And Scripting Techniques For FSM Designs WithSynthesis-Optimized, Glitch-Free Outputs》)。注:此方法仅对输出为一位,即输出用高低电平表示时有效。
a.确定状态机中输出(outputs)个数x和状态(state)个数y,并制作一个(y+1)*(x+1)的表格,根据实际的每个状态的输出填表。查找表中每个状态的输出是否相同,若无相同的输出,则直接使用表中的编码方式;如果表中有两个相同的输出,则需要在输出的前边加一位进行区分,三~四个相同的输出则需加两位区分,以此类推。
b.填充所加的位,除了重复的用1来填充外,其余的均用0来填充,使得最终的得到的每个状态的编码值唯一。
状态机编码
有两个输出相同时添加一位重新编码
Example:
module fsm1a_ffo1 (ds, rd, go, ws, clk, rst_n);
output ds, rd;
input go, ws;
input clk, rst_n;
// state bits = x0 _ ds rd
parameter [2:0] IDLE = 3'b0_00,
READ = 3'b0_01,
DLY = 3'b1_01,
DONE = 3'b0_10;
reg [2:0] state, next;
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
always @(state or go or ws) begin
next = 3'bx;
case (state)
IDLE: if (go) next = READ;
else next = IDLE;
READ: next = DLY;
DLY: if (ws) next = READ;
else next = DONE;
DONE: next = IDLE;
endcase
end
assign {ds,rd} = state[1:0];
endmodule
2. 三段式写法
三段式写法是在两段式的基础上加了输出的always模块。上面的example是一个典型的两段式写法。三段式写法相对于两段式,优势在于能实现在不插入额外时钟节拍的前提下实现寄存器输出。
Example:
module fsm_cc4_2r
(output reg gnt,
input dly, done, req, clk, rst_n);
parameter [1:0] IDLE = 2'b00,
BBUSY = 2'b01,
BWAIT = 2'b10,
BFREE = 2'b11;
reg [1:0] state, next;
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
always @(state or dly or done or req) begin
next = 2'bx;
case (state)
IDLE : if (req) next = BBUSY;
else next = IDLE;
BBUSY: if (!done) next = BBUSY;
else if ( dly) next = BWAIT;
else next = BFREE;
BWAIT: if (!dly) next = BFREE;
else next = BWAIT;
BFREE: if (req) next = BBUSY;
else next = IDLE;
endcase
end
always @(posedge clk or negedge rst_n)
if (!rst_n) gnt <= 1'b0;
else begin
gnt <= 1'b0;
case (next)
IDLE, BFREE: ; // default outputs
BBUSY, BWAIT: gnt <= 1'b1;
endcase
end
endmodule
FSM设计中容易疏忽的地方:
1.避免状态机出现跑飞的情况。
2.在两段式和三段式写法中,同时存在时序逻辑always模块和组合逻辑always模块,在时序逻辑模块中要使用非阻塞赋值,而在组合逻辑模块中要使用阻塞赋值。
3.参数定义用 parameter,全局参数定义时才用 `define。
参考资料:
1.foreveryoung的《状态机设计》,这是一个综合性的资料,作者是在看了许多资料的基础上翻译总结出来的,很有代表性。
2.Clifford E. Cummings的 《State Machine Coding Styles for Synthesis》
《The Fundamentals of Efficient Synthesizable Finite State Machine Design using NC-Verilog and BuildGates》
《Coding And Scripting Techniques For FSM Designs With Synthesis-Optimized, Glitch-Free Outputs》
《"full_case parallel_case", the Evil Twins of VerilogSynthesis》
《A Script to Generate RTL Code for State Machines and Synopsys Synthesis Scripts》
Clifford E. Cumming的文章均可在http://www.sunburst-design.com/papers/下载。