三段式有限状态机

    一段式、二段式、三段式状态机是按照书写FSM时使用的always块数目进行划分的,一般而言对于简单的状态机,可以使用一段式,其代码量以及使用资源都最少,但如果状态机较复杂,一段式状态机会对代码维护产生很大的不便,因此多使用便于维护的三段式状态机。下面对几种状态机进行介绍。

一段式状态机

   一段式FSM在一个时序always块中完成所有的状态转移以及输出工作,使用非阻塞赋值,如以下代码

//一段式状态机
module FSM1(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);

parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;

reg	[3:0]	state;
always @(posedge clk or negedge  rst_n)begin
	if(!rst_n)begin
		state	<= S1;
		o		<= 0;
	end
	else begin
		case(state)
		S1: begin
			state	<= S2;
			o		<= i + 1;
		end
		S2: begin
			state	<= S3;
			o		<= i + 2;
		end
		S3: begin
			state	<= S4;
			o		<= i + 3;
		end
		S4: begin
			state	<= S1;
			o		<= i + 4;
		end
		default: begin
			state	<= S1;
			o		<= 0;
		end
		endcase
	end
end

endmodule

  其仿真结果如下

三段式有限状态机_第1张图片

二段式状态机

  二段式状态机使用两个always块,实现组合逻辑与时序逻辑的分开,其中第一个always块为时序逻辑,控制状态的更新,第二个always块为组合逻辑,产生下一状态next_state,同时产生状态输出。其输出是基于当前状态state的组合逻辑,代码如下

//二段式状态机
module FSM2(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);

parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;

reg		[3:0]	state;
reg		[3:0]	next_state;

always @(posedge clk or negedge  rst_n)begin	//时序逻辑
	if(!rst_n)begin
		state	<= S1;
	end
	else begin
		state	<= next_state;
	end
end

always @(*) begin								//组合逻辑,产生next_state和output
	if(!rst_n) begin
		next_state	<= S1;
		o	<= 0;
	end
	else begin
		case(state)
		S1: begin
			next_state	<= S2;
			o			<= i + 1;
		end
		S2: begin
			next_state	<= S3;
			o			<= i + 2;
		end
		S3: begin
			next_state	<= S4;
			o			<= i + 3;
		end
		S4: begin
			next_state	<= S1;
			o			<= i + 4;
		end
		default: begin
			next_state	<= S1;
			o			<= 0;
		end
		endcase
	end
end

endmodule

  仿真结果如下

三段式有限状态机_第2张图片

  可以看到,其输出与一段式FSM相同,而在状态上,其next_state与一段式FSM的state变化相同

三段式状态机

第三段为基于state的组合逻辑

  我们可以看到,二段式FSM的第二个always块实际上是可以再进行划分的,可以分为生成next_state、产生输出两部分,因此我们直接拆分二段式FSM,获得逻辑输出基于当前状态state的三段式状态机:

//三段式状态机
module FSM3(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);

parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;

reg		[3:0]	state;
reg		[3:0]	next_state;

always @(posedge clk or negedge  rst_n)begin	//时序逻辑
	if(!rst_n)begin
		state	<= S1;
	end
	else begin
		state	<= next_state;
	end
end

always @(*) begin								//组合逻辑,产生next_state
	if(!rst_n) begin
		next_state	<= S1;
	end
	else begin
		case(state)
		S1: begin
			next_state	<= S2;
		end
		S2: begin
			next_state	<= S3;
		end
		S3: begin
			next_state	<= S4;
		end
		S4: begin
			next_state	<= S1;
		end
		default: begin
			next_state	<= S1;
		end
		endcase
	end
end

always @(*) begin								//组合逻辑,基于state产生逻辑输出
	if(!rst_n) begin
		o	<= 0;
	end
	else begin
		case(state)
		S1: begin
			o	<= i + 1;
		end
		S2: begin
			o	<= i + 2;
		end
		S3: begin
			o	<= i + 3;
		end
		S4: begin
			o	<= i + 4;
		end
		default: begin
			o	<= 0;
		end
		endcase
	end
end

endmodule

  由于此三段式FSM仅仅是在写法上将二段式FSM的第二个always块进行了拆分,因此其状态变化以及输出与二段式FSM完全相同,仿真结果如下:

三段式有限状态机_第3张图片

第三段为基于state的时序逻辑

  由于第三段使用的是组合逻辑,因此比较容易出现毛刺,那么很通常的一个想法是将第三个always块变为时序逻辑,通过插入寄存器,实现对毛刺的消除。然而直接将 always(*) 改为 always(posedge clk or negedge rst_n) 就可以了吗?请看下面的代码

//三段式状态机2
module FSM3_2(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);

parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;

reg		[3:0]	state;
reg		[3:0]	next_state;

always @(posedge clk or negedge  rst_n)begin	//时序逻辑
	if(!rst_n)begin
		state	<= S1;
	end
	else begin
		state	<= next_state;
	end
end

always @(*) begin								//组合逻辑,产生next_state
	if(!rst_n) begin
		next_state	<= S1;
	end
	else begin
		case(state)
		S1: begin
			next_state	<= S2;
		end
		S2: begin
			next_state	<= S3;
		end
		S3: begin
			next_state	<= S4;
		end
		S4: begin
			next_state	<= S1;
		end
		default: begin
			next_state	<= S1;
		end
		endcase
	end
end

always @(posedge clk or negedge rst_n) begin	//时序逻辑
	if(!rst_n) begin
		o	<= 0;
	end
	else begin
        case(state)			//基于当前状态state产生同步时序输出,其输出将出现一拍延迟
		S1: begin
			o	<= i + 1;
		end
		S2: begin
			o	<= i + 2;
		end
		S3: begin
			o	<= i + 3;
		end
		S4: begin
			o	<= i + 4;
		end
		default: begin
			o	<= 0;
		end
		endcase
	end
end

endmodule

三段式有限状态机_第4张图片

  可以看到,其输出将相较于第三段使用组合逻辑的FSM延迟一拍,有些同学在书写三段式FSM时没有注意到这一点,直接将一段式FSM重构为这种三段式FSM,却以为他们的逻辑是一样的,从而导致在逻辑上出现问题。

第三段为基于next_state的时序逻辑

  在二段式FSM的讨论中我们提到过,其next_state的变化才是和一段式FSM相同的,因此在将第三段转换为同步时序逻辑时,不应基于当前状态state,而是应当基于下一状态next_state,代码如下

//三段式状态机3
module FSM3_3(
input				clk,
input				rst_n,
input		[3:0]	i,
output	reg	[3:0]	o
);

parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;

reg		[3:0]	state;
reg		[3:0]	next_state;

always @(posedge clk or negedge  rst_n)begin	//时序逻辑
	if(!rst_n)begin
		state	<= S1;
	end
	else begin
		state	<= next_state;
	end
end

always @(*) begin								//组合逻辑
	if(!rst_n) begin
		next_state	<= S1;
	end
	else begin
		case(state)
		S1: begin
			next_state	<= S2;
		end
		S2: begin
			next_state	<= S3;
		end
		S3: begin
			next_state	<= S4;
		end
		S4: begin
			next_state	<= S1;
		end
		default: begin
			next_state	<= S1;
		end
		endcase
	end
end

always @(posedge clk or negedge rst_n) begin	//同步时序逻辑
	if(!rst_n) begin
		o	<= 0;
	end
	else begin
        case(next_state)	//使用next_state,此时其输出与一段式FSM相同
		S1: begin
			o	<= i + 1;
		end
		S2: begin
			o	<= i + 2;
		end
		S3: begin
			o	<= i + 3;
		end
		S4: begin
			o	<= i + 4;
		end
		default: begin
			o	<= 0;
		end
		endcase
	end
end

endmodule

三段式有限状态机_第5张图片

  可以看到,此时三段式FSM的输出与一段式FSM相同。

  这里我们发现一个有意思的地方,使用next_state产生输出可以输出提前一拍,正是这提前的一拍将同步时序滞后的一拍给抵消了。那如果我们在第三段使用组合逻辑,但也基于next_state进行输出,就可以将输出提前一拍!

关于状态的定义

  有小伙伴可能会疑惑关于状态的定义部分,为什么要定义为独热的形式?

parameter	S1	= 4'b0001;
parameter	S2	= 4'b0010;
parameter	S3	= 4'b0100;
parameter	S4	= 4'b1000;

  因为这样,由任一状态向其他状态转换时,其出现的不稳定状态都是无效的,这样可以提高系统的稳定性。比如state由S1向S2转换时,可能出现0011或者0000这两种情况,而这两种都是无效状态,很容易辨识剔除。而如果我们使用下面的定义方法

parameter	S1	= 4'b0000;
parameter	S2	= 4'b0001;
parameter	S3	= 4'b0010;
parameter	S4	= 4'b0011;

  当从状态S2向S3转换时,可能出现的0011或者0000,分别对应状态S4、S1,此时将可能导致逻辑错误,尤其是输出是关于state的组合逻辑的时候。

你可能感兴趣的:(数字逻辑,fpga开发)