本次笔记我们使用"电子男特有的方式向我们最亲爱的人说一句"I LOVE YOU"."为例,分别比较一下一段式,两段式,三段式状态机的区别
有限状态机(finite statemachine,FSM)广泛应用于数字系统的控制器设计中.由于设计方法不同,综合出来的电路结构、速度、面积和时延特性都会有很大的差别, 甚至某些臃肿的电路还会产生难以预料的问题.
1.“一段式”
特点:将当前状态向量和输出向量用同一时序always块进行描述。
缺点:代码冗长,不易修改和调试,可维护性较差且占用资源多;通过case语句对输出向量的赋值应是下一个状态的输出,这点容易出错;状态向量和输出向量都是由寄存器逻辑实现,面积较大;不能实现异步mealy有限状态机。
优点:寄存器输出,输出向量不会产生毛刺。
结构:
例程:
module FSM( clk, rst_n, data_cap,// data_low, out_flow ); input clk,rst_n; input [7:0]data_cap,data_low;//大写,小写字母数据流 output reg [7:0]out_flow; reg [7:0]state; localparam //独热码 CHECK_I = 8'b0000_0001, CHECK_L = 8'b0000_0010, CHECK_o1 = 8'b0000_0100, CHECK_v = 8'b0000_1000, CHECK_e = 8'b0001_0000, CHECK_Y = 8'b0010_0000, CHECK_o2 = 8'b0100_0000, CHECK_u = 8'b1000_0000; always @(posedge clk or negedge rst_n) if(!rst_n)begin out_flow <= 0; state <= CHECK_I; end else begin case(state) CHECK_I : if(data_cap == "I")begin state <= CHECK_L; out_flow <= data_cap; end else state <= CHECK_I; CHECK_L : if(data_cap == "L")begin state <= CHECK_o1; out_flow <= data_cap; end else state <= CHECK_L; CHECK_o1 : if(data_low == "o")begin state <= CHECK_v; out_flow <= data_low; end else state <= CHECK_o1; CHECK_v : if(data_low == "v")begin state <= CHECK_e; out_flow <= data_low; end else state <= CHECK_v; CHECK_e : if(data_low == "e")begin state <= CHECK_Y; out_flow <= data_low; end else state <= CHECK_e; CHECK_Y : if(data_cap == "Y")begin state <= CHECK_o2; out_flow <= data_cap; end else state <= CHECK_Y; CHECK_o2 : if(data_low == "o")begin state <= CHECK_u; out_flow <= data_low; end else state <= CHECK_o2; CHECK_u : if(data_low == "u")begin state <= CHECK_I; out_flow <= data_low; end else state <= CHECK_u; default : begin out_flow <= 0; state <= CHECK_I; end endcase end endmodule
RTL:
资源使用:
仿真效果:
2.“两段式”
特点:(第一段)一个时序always块给当前状态向量赋值,(第二段)一个组合always块给下一个状态向量和输出向量赋值。
缺点:(1)组合逻辑输出会使输出向量产生毛刺;
(2)从速度角度而言,由于这种状态机的输出向量必须由状态向量经译码得到,因此加大了从状态向量到输出向量的延时。
(3)从综合角度而言,组合输出消耗了一部分时钟周期,即增加了由它驱动的下一个模块的输入延时。
结构:
例程:
module FSM( clk, rst_n, data_cap,// data_low, out_flow ); input clk,rst_n; input [7:0]data_cap,data_low;//大写,小写字母数据流 output reg [7:0]out_flow; reg [7:0]state,NS; localparam //独热码 CHECK_I = 8'b0000_0001, CHECK_L = 8'b0000_0010, CHECK_o1 = 8'b0000_0100, CHECK_v = 8'b0000_1000, CHECK_e = 8'b0001_0000, CHECK_Y = 8'b0010_0000, CHECK_o2 = 8'b0100_0000, CHECK_u = 8'b1000_0000; always @(posedge clk or negedge rst_n) if(!rst_n) begin state <= CHECK_I; end else begin state <= NS; end always @(rst_n or state or data_cap or data_low) begin NS = 8'bx; out_flow <= 8'bx; case(state) CHECK_I : if(data_cap == "I") begin NS <= CHECK_L; out_flow <= data_cap; end else NS <= CHECK_I; CHECK_L : if(data_cap == "L")begin NS <= CHECK_o1; out_flow <= data_cap; end else NS <= CHECK_L; CHECK_o1 : if(data_low == "o")begin NS <= CHECK_v; out_flow <= data_low; end else NS <= CHECK_o1; CHECK_v : if(data_low == "v")begin NS <= CHECK_e; out_flow <= data_low; end else NS <= CHECK_v; CHECK_e : if(data_low == "e")begin NS <= CHECK_Y; out_flow <= data_low; end else NS <= CHECK_e; CHECK_Y : if(data_cap == "Y")begin NS <= CHECK_o2; out_flow <= data_cap; end else NS <= CHECK_Y; CHECK_o2 : if(data_low == "o")begin NS <= CHECK_u; out_flow <= data_low; end else NS <= CHECK_o2; CHECK_u : if(data_low == "u")begin NS <= CHECK_I; out_flow <= data_low; end else NS <= CHECK_u; default : begin out_flow <= 8'bx; NS <= CHECK_I; end endcase end endmodule
RTL:
资源占用:
仿真:
3.“三段式”
特点:两个时序always块,分别产生当前状态向量和输出向量,再用一个组合逻辑always块产生下一个状态向量。
代码主要包含以下三部分:
[1] 状态转移部分(时序逻辑)
[2] 状态转移条件部分(即产生下个状态向量的模块,组合逻辑)
[3] 输出逻辑部分(时序逻辑)
优点: 三段式描述方法虽然代码结构复杂了一些,但是换来的优势是:使FSM做到了同步寄存器输出,消除了组合逻辑输出的不稳定与毛刺的隐患,而且更利于时序路径分组,一般来说在FPGA/CPLD等可编程逻辑器件上的综合与布局布线效果更佳。
结构:
例程:
module FSM( clk, rst_n, data_cap,// data_low, out_flow ); input clk,rst_n; input [7:0]data_cap,data_low;//大写,小写字母数据流 output reg [7:0]out_flow; reg [7:0]state,NS; localparam //独热码 CHECK_I = 8'b0000_0001, CHECK_L = 8'b0000_0010, CHECK_o1 = 8'b0000_0100, CHECK_v = 8'b0000_1000, CHECK_e = 8'b0001_0000, CHECK_Y = 8'b0010_0000, CHECK_o2 = 8'b0100_0000, CHECK_u = 8'b1000_0000; always @(posedge clk or negedge rst_n) if(!rst_n) begin state <= CHECK_I; end else begin state <= NS; end always @(rst_n or state or data_cap or data_low) begin NS = 8'bx; case(state) CHECK_I : if(data_cap == "I") NS <= CHECK_L; else NS <= CHECK_I; CHECK_L : if(data_cap == "L") NS <= CHECK_o1; else NS <= CHECK_L; CHECK_o1: if(data_low == "o") NS <= CHECK_v; else NS <= CHECK_o1; CHECK_v : if(data_low == "v") NS <= CHECK_e; else NS <= CHECK_v; CHECK_e : if(data_low == "e") NS <= CHECK_Y; else NS <= CHECK_e; CHECK_Y : if(data_cap == "Y") NS <= CHECK_o2; else NS <= CHECK_Y; CHECK_o2: if(data_low == "o") NS <= CHECK_u; else NS <= CHECK_o2; CHECK_u : if(data_low == "u") NS <= CHECK_I; else NS <= CHECK_u; default : NS <= CHECK_I; endcase end always @(posedge clk or negedge rst_n) if(!rst_n) begin out_flow <= 8'b0; end else begin case(NS) CHECK_I : out_flow <= "I";//I CHECK_L : out_flow <= "L";//L CHECK_o1 : out_flow <= "o";//o CHECK_v : out_flow <= "v";//v CHECK_e : out_flow <= "e";//e CHECK_Y : out_flow <= "Y";//Y CHECK_o2 : out_flow <= "o";//o CHECK_u : out_flow <= "u";//u endcase end endmodule
RTL:
资源占用:
以上的仿真代码:
`timescale 1ns/1ns `define clock_period 20 module FSM_tb; reg clk; reg rst_n; reg [7:0]data_cap,data_low; wire [7:0]out_flow; FSM FSM( .clk(clk), .rst_n(rst_n), .data_cap(data_cap),// .data_low(data_low), .out_flow(out_flow) ); always @(posedge clk) begin #(`clock_period) data_cap = 65 + {$random}%26; #(`clock_period) data_low = 97 + {$random}%26; end initial clk = 1; always #(`clock_period/2)clk = ~clk; initial begin rst_n = 0; //ASCII = 0; #(`clock_period*5); rst_n = 1; #(`clock_period*500 ); //forever begin $stop; end endmodule
一般而言,推荐的FSM 描述方法是后两种。这是因为:FSM和其他设计一样,最好使用同步时序方式设计,以提高设计的稳定性,消除毛刺。状态机实现后,一般来说,状态转移部分是同步时序电路而状态的转移条件的判断是组合逻辑。 第二种描述方法同第一种描述方法相比,将同步时序和组合逻辑分别放到不同的always模块中实现,这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。在第二种方式的描述中,描述当前状态的输出用组合逻辑实现,组合逻辑很容易产生毛刺,而且不利于约束,不利于综合器和布局布线器实现高性能的设计。
为了使FSM 描述清晰简介,易于维护,易于附加时序约束,使综合器和布局布线器更好的优化设计,推荐使用两段式FSM 描述方法。
由于Augus能力有限,笔记如有错误,请大家之处帮忙指出!十分感谢,一起学习!
文章评论(0条评论)
登录后参与讨论