Augus

12.菜鸟初入FPGA之状态机的设计

0
阅读(2030)

       本次笔记我们使用"电子男特有的方式向我们最亲爱的人说一句"I LOVE YOU"."为例,分别比较一下一段式,两段式,三段式状态机的区别

        有限状态机(finite statemachine,FSM)广泛应用于数字系统的控制器设计中.由于设计方法不同,综合出来的电路结构、速度、面积和时延特性都会有很大的差别, 甚至某些臃肿的电路还会产生难以预料的问题.

    1.“一段式” 

特点:将当前状态向量和输出向量用同一时序always块进行描述。 

缺点:代码冗长,不易修改和调试,可维护性较差且占用资源多;通过case语句对输出向量的赋值应是下一个状态的输出,这点容易出错;状态向量和输出向量都是由寄存器逻辑实现,面积较大;不能实现异步mealy有限状态机。

 优点:寄存器输出,输出向量不会产生毛刺。 

结构:

2016-04-07_154548.jpg

例程:

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:

2016-04-07_155533.jpg

资源使用:

2016-04-07_155617.jpg

仿真效果:

2016-04-07_152654.jpg


2.“两段式” 

特点:(第一段)一个时序always块给当前状态向量赋值,(第二段)一个组合always块给下一个状态向量和输出向量赋值。 

缺点:(1)组合逻辑输出会使输出向量产生毛刺;  

            (2)从速度角度而言,由于这种状态机的输出向量必须由状态向量经译码得到,因此加大了从状态向量到输出向量的延时。  

            (3)从综合角度而言,组合输出消耗了一部分时钟周期,即增加了由它驱动的下一个模块的输入延时。

结构:

2016-04-07_154703.jpg

例程:

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:

2016-04-07_165502.jpg

资源占用:

2016-04-07_165512.jpg

仿真:



3.“三段式” 

特点:两个时序always块,分别产生当前状态向量和输出向量,再用一个组合逻辑always块产生下一个状态向量。   

代码主要包含以下三部分: 

[1] 状态转移部分(时序逻辑) 

[2] 状态转移条件部分(即产生下个状态向量的模块,组合逻辑)

 [3] 输出逻辑部分(时序逻辑)

优点: 三段式描述方法虽然代码结构复杂了一些,但是换来的优势是:使FSM做到了同步寄存器输出,消除了组合逻辑输出的不稳定与毛刺的隐患,而且更利于时序路径分组,一般来说在FPGA/CPLD等可编程逻辑器件上的综合与布局布线效果更佳。

结构:

2016-04-07_155206.jpg

例程:

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:

2016-04-07_172726.jpg

资源占用:

2016-04-07_172732.jpg


以上的仿真代码:

`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 描述方法