Verilog FSM设计的学习心得(一)

Posted on 2012-04-14 21:54  freeny  阅读(2600)  评论(0编辑  收藏  举报

FSM(Finite State Machine)是数字设计中经常涉及到的部分。FSM分为两类:Mealy型和Moore型,其主要区别是:Mealy型状态机的输出与当前状态和输入均有关;Moore型状态机的输出仅与当前状态有关,而与输入无关。

1_thumb[19]

如何设计有效的状态机,避免一些不希望出现的输出结果(如锁存、毛刺等),是本文的谈论要点。

以下观点是本人在一些资料的基础上自己总结的,如有不当之处,请大家指出,共同学习!在此感谢所有本文所涉及的文章的作者。文章最后我会将所看资料的名字附上,有兴趣的可以下载查看。

本人在之前设计中,对状态机这块并没有特别留意,只是用最简单的加法器来控制状态,将所有状态机部分写在一个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 语句的分支与之对应,即两者关系是一一对应关系。

clip_image004_thumb[2]

但在使用不当的情况下,这两个指令并不能达到理想的效果。详细分析见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来填充,使得最终的得到的每个状态的编码值唯一。

clip_image005_thumb[1]

状态机编码

clip_image006_thumb[1]

有两个输出相同时添加一位重新编码

clip_image007_thumb[1]

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/下载。

Copyright © 2024 freeny
Powered by .NET 8.0 on Kubernetes