写在前面

全部答案汇总:刷完这套题,我才发现Verilog原来如此简单----HDLBits答案汇总

今天更新Circuits章节中Verification:Reading Simulation的1个小节:Finite State Machine(FSM,有限状态机)。

HDLBits上的状态机部分是精华,是整个网站的核心部分。

有限状态机是FPGA用来实现控制的一个重要手段(差不多可以说是唯一手段了),基本上写复杂的时序都绕不开状态机,大家要一定要好好学习状态机的写法及应用。

有关状态机的部分,可以参考我的这篇博文:状态机(一段式、二段式、三段式)、摩尔型(Moore)和米勒型(Mealy)


Simple FSM 1(asynchronous reset)

【题目】:

        实现下面这个2状态、1输入、1输出的摩尔型状态机(异步复位、复位状态为B)。

 

【个人思路】:

        状态机的实现首推三段式状态机,可能一开始觉得三段式状态机写起来太麻烦,但是相信我当你的状态机越来越复杂的时候,三段式状态机一定是写起来最简单的那种。

module top_module(
    input 	clk		,					//输入时钟
    input 	areset	,    				//高电平有效的复位信号
    input 	in		,					//输入信号
    output 	out							//输出信号
);
//------------<状态机参数定义>------------------------------------------
//使用独热码进行状态定义
parameter 	A=2'b01, 
			B=2'b10; 

//------------<reg定义>-------------------------------------------------			
reg	[1:0]	cur_state	,				//定义现态寄存器
			next_state	;				//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk or posedge areset) begin    
	if(areset)
		cur_state <= B;					//现态的默认状态
	else 
		cur_state <= next_state;		//将次态赋值给现态
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
always @(*) begin
	if(areset)
		next_state = B;					//次态的默认状态
	else
		case(cur_state)					//基于现态的状态转移
				A:	
					if(!in)	
						next_state = B;	//转移条件发生
					else
						next_state = A;	//转移条件未发生
				B:	
					if(!in)
						next_state = A;	//转移条件发生
					else
						next_state = B;	//转移条件未发生				
				default:;
		endcase
end

//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk or posedge areset) begin    
    if(areset)
		out <= 1'b1;					//复位默认输出
	else 
		case(next_state)				//基于次态比基于现态输出提前一个时钟周期
			A:	out <= 1'b0;
			B:	out <= 1'b1;
			default:out <= 1'b1;
		endcase
end

endmodule

Simple FSM 1(synchronous reset)

【题目】:

        实现下面这个2状态、1输入、1输出的摩尔型状态机(异步复位、复位状态为B)。

【个人思路 】:

        和上题只是复位方式不同。

module top_module(
    input 	clk		,					//输入时钟
    input 	reset	,    				//高电平有效的复位信号
    input 	in		,					//输入信号
    output 	out							//输出信号
);
//------------<状态机参数定义>------------------------------------------
//使用独热码进行状态定义
parameter 	A=2'b01, 
			B=2'b10; 

//------------<reg定义>-------------------------------------------------			
reg	[1:0]	cur_state	,				//定义现态寄存器
			next_state	;				//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk) begin    
	if(reset)
		cur_state <= B;					//现态的默认状态
	else 
		cur_state <= next_state;		//将次态赋值给现态
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
always @(*) begin
	if(reset)
		next_state = B;					//次态的默认状态
	else
		case(cur_state)					//基于现态的状态转移
				A:	
					if(!in)	
						next_state = B;	//转移条件发生
					else
						next_state = A;	//转移条件未发生
				B:	
					if(!in)
						next_state = A;	//转移条件发生
					else
						next_state = B;	//转移条件未发生				
				default:;
		endcase
end

//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk) begin    
    if(reset)
		out <= 1'b1;					//复位默认输出
	else 
		case(next_state)				//基于次态比基于现态输出提前一个时钟周期
			A:	out <= 1'b0;
			B:	out <= 1'b1;
			default:out <= 1'b1;
		endcase
end

endmodule

Simple FSM 2(asynchronous reset)

【题目】:

        实现下面这个2状态、2输入、1输出的摩尔型状态机(同步复位、复位状态为B)。

【个人思路】: 

        参考第一题实现即可,不过输入变多了一个。

module top_module(
    input 	clk		,			//输入时钟
    input 	areset	,           //高电平有效的复位信号
    input 	j		,           //输入信号
    input 	k		,           //输入信号	
    output 	out					//输出信号
);
//------------<状态机参数定义>------------------------------------------
//使用独热码进行状态定义
parameter	OFF = 2'b01	, 
			ON  = 2'b10	; 

//------------<reg定义>-------------------------------------------------
reg	[1:0]	cur_state	,		//定义现态寄存器 
			next_state	;		//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk or posedge areset) begin    
	if(areset)
		cur_state <= OFF;
	else 
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
always @(*) begin
	if(areset)
		next_state = OFF;
	else
		case(cur_state)
				ON:	
					if(k)
						next_state = OFF;
					else
						next_state = ON;
				OFF:	
					if(j)
						next_state = ON;
					else
						next_state = OFF;					
				default:;
		endcase
end

//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk or posedge areset) begin    
    if(areset)
		out <= 1'b0;
	else 
		case(next_state)
			ON:		out <= 1'b1;
			OFF:	out <= 1'b0;
			default:;
		endcase
end

endmodule

Simple FSM 2(synchronous reset)

【题目】:

        实现下面这个2状态、1输入、1输出的摩尔型状态机(同步复位、复位状态为B)。

【个人思路 】:

        和上题只是复位方式不同。

module top_module(
    input 	clk		,			//输入时钟
    input 	reset	,           //高电平有效的复位信号
    input 	j		,           //输入信号
    input 	k		,           //输入信号	
    output 	out					//输出信号
);
//------------<状态机参数定义>------------------------------------------
//使用独热码进行状态定义
parameter	OFF = 2'b01	, 
			ON  = 2'b10	; 

//------------<reg定义>-------------------------------------------------
reg	[1:0]	cur_state	,		//定义现态寄存器 
			next_state	;		//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk) begin    
	if(reset)
		cur_state <= OFF;
	else 
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
always @(*) begin
	if(reset)
		next_state = OFF;
	else
		case(cur_state)
				ON:	
					if(k)
						next_state = OFF;
					else
						next_state = ON;
				OFF:	
					if(j)
						next_state = ON;
					else
						next_state = OFF;					
				default:;
		endcase
end

//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk) begin    
    if(reset)
		out <= 1'b0;
	else 
		case(next_state)
			ON:		out <= 1'b1;
			OFF:	out <= 1'b0;
			default:;
		endcase
end

endmodule

Simple state transitions 3

【题目】:

        实现下面的摩尔状态机,下表是状态转移图,1输入1输出4状态。使用如下的编码方式:A=2'b00, B=2'b01, C=2'b10, D=2'b11。

        这个状态机只实现状态跳转逻辑和输出逻辑。已经给出了现态state,根据状态转移图计算出次态和输出。

【个人思路】:

        这里使用2段式的状态机就行(题目要求),一段实现状态转移;另一端实现输出。

module top_module(
    input 			in			,
    input 	[1:0] 	state		,
    output 	[1:0] 	next_state	,
    output 			out
); 
//------------<状态机参数定义>------------------------------------------
//使用2进制码进行状态定义
parameter 	A = 2'b00, 
			B = 2'b01, 
			C = 2'b10, 
			D = 2'b11;
			
//状态机第一段:描述状态转移
always@(*)begin
	case(state)
		A:
			if(in)
				next_state = B;
			else
				next_state = A;
		B:
			if(in)
				next_state = B;
			else
				next_state = C;			
		C:
			if(in)
				next_state = D;
			else
				next_state = A;			
		D:
			if(in)
				next_state = B;
			else
				next_state = C;			
		default:;
	endcase
end

//状态机第二段:组合逻辑实现输出
always@(*)begin
	case(state)
		A,B,C:
			out = 1'b0;
		D:	
			out = 1'b1;
		default:;
	endcase
end

endmodule

Simple one-hot state transitions 3

【题目】:

        实现下面的摩尔状态机,下表是状态转移图,1输入1输出4状态。使用如下的独热码编码方式:A=4'b0001, B=4'b0010, C=4'b0100, D=4'b1000。

        这个状态机只实现状态跳转逻辑和输出逻辑。已经给出了现态state,根据状态转移图计算出次态和输出。

【个人思路】:

        基本和上题一致,只是编码方式要求改成独热码方式。独热码是使用一位有效的编码方式,这意味着使用独热码编码,有几个状态,那么编码寄存器的位宽就有几位。这无疑多消耗了寄存器资源。

        独热码:和格雷码相比,虽然独热码多用了触发器,但所用组合电路可以省一些(相当于进行了一次译码操作),因而使电路的速度和可靠性有显著提高,而总的单元数并无显著增加。因为独热码只有一位的变化,所以更适用于高速系统。

module top_module(
    input 			in			,
    input 	[3:0] 	state		,
    output 	[3:0] 	next_state	,
    output 			out
); 
//------------<状态机参数定义>------------------------------------------
parameter 	A = 0, 
			B = 1, 
			C = 2, 
			D = 3;
			
//描述状态转移
assign next_state[A] = (state[A]&~in) | (state[C] & ~in);
assign next_state[B] = (state[A]&in) | (state[D]&in) | (state[B]&in);
assign next_state[C] = (state[B]&~in) | (state[D]&~in);
assign next_state[D] = (state[C]&in);

//组合逻辑实现输出
assign out = (state[D]);

endmodule

Simple FSM 3(asynchronous reset)

【题目】:

        题目与上题相同,区别为异步复位,复位至状态A。

【个人思路】:

        本题的输入、输出不再是现态和次态,所以写起来用三段式状态机会比较方便。

module top_module(
    input 	clk		,			//输入时钟
    input 	areset	,           //高电平有效的复位信号
    input 	in		,           //输入信号	
    output 	out					//输出信号
);
//------------<状态机参数定义>------------------------------------------
//使用独热码进行状态定义
parameter	A = 4'b0001	, 
			B = 4'b0010	, 
			C = 4'b0100	, 
			D = 4'b1000	; 

//------------<reg定义>-------------------------------------------------
reg	[3:0]	cur_state	,		//定义现态寄存器 
			next_state	;		//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk or posedge areset) begin    
	if(areset)
		cur_state <= A;
	else 
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
always @(*) begin
	if(areset)
		next_state = A;
	else
		case(cur_state)
				A:	
					if(in)
						next_state = B;
					else
						next_state = A;
				B:	
					if(in)
						next_state = B;
					else
						next_state = C;	
				C:	
					if(in)
						next_state = D;
					else
						next_state = A;
				D:	
					if(in)
						next_state = B;
					else
						next_state = C;						
				default:next_state = A;
		endcase
end

//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk or posedge areset) begin   
    if(areset)
		out <= 1'b0;
	else 
		case(next_state)
			A,B,C:	out <= 1'b0;
			D:		out <= 1'b1;
			default:out <= 1'b0;
		endcase
end

endmodule

Simple FSM 3(asynchronous reset)

【题目】:

        题目与上题相同,区别为同步复位,复位至状态A。

【个人思路】:

        本题的输入、输出不再是现态和次态,所以写起来用三段式状态机会比较方便。

module top_module(
    input 	clk		,			//输入时钟
    input 	reset	,           //高电平有效的复位信号
    input 	in		,           //输入信号	
    output 	out					//输出信号
);
//------------<状态机参数定义>------------------------------------------
//使用独热码进行状态定义
parameter	A = 4'b0001	, 
			B = 4'b0010	, 
			C = 4'b0100	, 
			D = 4'b1000	; 

//------------<reg定义>-------------------------------------------------
reg	[3:0]	cur_state	,		//定义现态寄存器 
			next_state	;		//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk) begin    
	if(reset)
		cur_state <= A;
	else 
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
always @(*) begin
	if(reset)
		next_state = A;
	else
		case(cur_state)
				A:	
					if(in)
						next_state = B;
					else
						next_state = A;
				B:	
					if(in)
						next_state = B;
					else
						next_state = C;	
				C:	
					if(in)
						next_state = D;
					else
						next_state = A;
				D:	
					if(in)
						next_state = B;
					else
						next_state = C;						
				default:next_state = A;
		endcase
end

//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk) begin   
    if(reset)
		out <= 1'b0;
	else 
		case(next_state)
			A,B,C:	out <= 1'b0;
			D:		out <= 1'b1;
			default:out <= 1'b0;
		endcase
end

endmodule

Design a Moore FSM 

【题目】:

        根据水位变化情况输出不同状态,有三个传感器S1,S2,S3,可以将水库分成四部分,低于S1时,输出FR1,FR2,FR3;水位介于S1~S2时,输出输出FR1,FR2;水位介于S2~S3时,输出输出FR1;水位高于S3时,可以认为FR1,FR2,FR3全为零;同时还会将上一刻水位状态和现在的水位状态进行比较,如果高于现在水位,需要打开(三角)FR,否则不需要打开。

【个人思路】:

题目首先建立在所有传感器能正常工作的基础上,所以请别关注诸如S1传感器无效但S2传感器有效这种情况(别杠)。

首先应该设计4个状态,状态的变化应该都发生在临近状态之间:

  • S0:水位低于S1。当检测到S1传感器有效时,说明水位超过了S1,状态跳转到S1;其他情况则保持水位不变。
  • S1:水位在S1~S2。当检测到S2传感器有效时,说明水位超过了S2,状态跳转到S2;当检测到S1传感器无效时,说明水位在S1之下,状态跳转到S0;其他情况则保持水位不变。
  • S2:水位在S2~S3。当检测到S3传感器有效时,说明水位超过了S3,状态跳转到S3;当检测到S2传感器无效时,说明水位在S2之下,状态跳转到S1;其他情况则保持水位不变。
  • S3:水位超过S3。当检测到S3传感器无效时,说明水位在S3之下,状态跳转到S2;其他情况则保持水位不变。

输出FR1、FR2、FR3根据所在状态对照上图进行输出即可。

dfr的输出原则是水位发生了下降则dfr输出1,所以需要比较次态和现态的关系。举例,若现态为S1,次态为S0,则说明水位下降,dfr输出1;若现态为S1,次态为S2,则说明水位上升,dfr输出0;若现态为S1,次态为S1,则说明水位不变,dfr输出不变(dfr <= dfr)。

module top_module(
    input 		clk		,			//输入时钟
    input 		reset	,           //高电平有效的复位信号
    input [3:1]	s		,           //输入信号	
	output 		fr3		,			//输出信号
    output 		fr2		,			//输出信号
    output 		fr1		,           //输出信号
    output 		dfr					//输出信号
);

//------------<状态机参数定义>------------------------------------------
//使用独热码进行状态定义
parameter	S0 = 4'b0001	, 		//低于S1
			S1 = 4'b0010	, 		//S1~S2
			S2 = 4'b0100	, 		//S2~S3
			S3 = 4'b1000	; 		//超过S3

//------------<reg定义>-------------------------------------------------
reg	[3:0]	cur_state	,			//定义现态寄存器 
			next_state	;			//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk) begin    
	if(reset)
		cur_state <= S0;
	else 
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
always @(*) begin
	if(reset)
		next_state = S0;
	else
		case(cur_state)
				S0:	
					if(s == 3'b001)			//检测到S1传感器,代表水位上升,水位在S1-S2
						next_state = S1;
					else					//水位无变化
						next_state = S0;
				S1:	
					if(s == 3'b011)			//检测到S1、S2传感器,代表水位上升,水位超过了S2-S3
						next_state = S2;
					else if(s == 3'b000)	//没有检测到传感器,代表水位下降,水位在S1之下
						next_state = S0;	
					else					//水位无变化
						next_state = S1;
				S2:	
					if(s == 3'b111)			//检测到S1、S2、S3传感器,代表水位上升,水位超过了S3
						next_state = S3;
					else if(s == 3'b001)	//检测到S1传感器,代表水位下降,水位在S1-S2
						next_state = S1;	
					else					//水位无变化
						next_state = S2;
				S3:	
					if(s == 3'b011)			//检测到S1、S2传感器,代表水位下降,水位超过了S2-S3
						next_state = S2;
					else					//水位无变化
						next_state = S3;					
				default:next_state = S0;
		endcase
end

//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk) begin   
    if(reset)begin
		fr1 <= 1'b1;
		fr2 <= 1'b1;
		fr3 <= 1'b1;
		dfr <= 1'b1;
	end
	else 
		case(next_state)
			S0:begin
				fr1 <= 1'b1;
				fr2 <= 1'b1;
				fr3 <= 1'b1;
				dfr <= 1'b1;				
			end	
			S1:begin
				fr1 <= 1'b1;
				fr2 <= 1'b1;
				fr3 <= 1'b0;
				if (cur_state == S0)			//上一个状态是S0,说明水位升高了
					dfr <= 1'b0;				//不打开开关
				else if (cur_state == S1)		//上一个状态是S1,说明水位没变化
					dfr <= dfr;					//开关保持不变
				else 							//上一个状态是S2,说明水位降低了
					dfr <= 1'b1;				//打开开关					
			end		
			S2:begin
				fr1 <= 1'b1;
				fr2 <= 1'b0;
				fr3 <= 1'b0;
				if (cur_state == S1)			//上一个状态是S1,说明水位升高了
					dfr <= 1'b0;				//不打开开关
				else if (cur_state == S2)		//上一个状态是S2,说明水位没变化
					dfr <= dfr;					//开关保持不变
				else 							//上一个状态是S3,说明水位降低了
					dfr <= 1'b1;				//打开开关					
			end	
			S3:begin
				fr1 <= 1'b0;
				fr2 <= 1'b0;
				fr3 <= 1'b0;
				if (cur_state == S2)			//上一个状态是S2,说明水位升高了
					dfr <= 1'b0;				//不打开开关				
				else 							//上一个状态是S3,说明水位没变化
					dfr <= dfr;					//开关保持不变					
			end		
			default:begin
				fr1 <= 1'b1;
				fr2 <= 1'b1;
				fr3 <= 1'b1;
				dfr <= 1'b1;			
			end	
		endcase
end

endmodule

Lemmings 1

【题目】:

        旅鼠有以下两个状态:向左走向右走。遇到障碍物它就会调转方向。如果旅鼠向左遇到障碍物就会向右走;旅鼠向右遇到障碍物就会向左走;如果两边都有障碍物,就会调转方向。

        用摩尔型状态机实现这个游戏模型。

【个人思路】

        2个状态(复位到向左状态):

                向左LEFT:向左遇到障碍物则跳转到状态RIGHT;不然没有遇到障碍物则保持状态。

                向右RIGHT:向右遇到障碍物则跳转到状态LEFT;不然没有遇到障碍物则保持状态。

module top_module(
    input		clk			,
    input		areset		,    
    input		bump_left	,
    input		bump_right	,
    output reg	walk_left	,
    output reg	walk_right
);   
//------------<状态机参数定义>------------------------------------------
//使用独热码进行状态定义
parameter 	LEFT  = 2'b01, 		//向左状态
			RIGHT = 2'b10;		//向右状态
//------------<reg定义>----------------------------------------------
reg	[1:0]	cur_state	, 		//定义现态寄存器 
			next_state	;		//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk, posedge areset) begin
	if(areset)
		cur_state <= LEFT;				//复位状态向左
	else
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always @(*) begin
	next_state = LEFT;					//复位状态向左
	case(cur_state)
		LEFT:begin
			if(bump_left)				//向左遇到障碍物
				next_state = RIGHT;
			else						//没有障碍物
				next_state = LEFT;
		end
		RIGHT:begin
			if(bump_right)				//向右遇到障碍物
				next_state = LEFT;
			else
				next_state = RIGHT;		//没有障碍物
		end
		default:next_state = LEFT;
	endcase
end

//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk, posedge areset) begin
	if(areset)begin
		walk_left <= 1'b1;				//复位向左走
		walk_right <= 1'b0;		
	end
	else begin
		case(next_state)
			LEFT:begin					//向左状态向左走
				walk_left <= 1'b1;
				walk_right <= 1'b0;
			end
			RIGHT:begin					//向右状态向左走
				walk_left <= 1'b0;
				walk_right <= 1'b1;
			end	
			default:begin				//默认状态向左走
				walk_left <= 1'b1;
				walk_right <= 1'b0;
			end
		endcase
	end
end

endmodule

Lemmings 2

【题目】:

        在上题的基础上增加了输入ground和输出ahhh,当ground为1,旅鼠的行动和上题一致,当ground为0,代表旅鼠处于掉落状态,此时不再移动,同时ahhh为1。

        ground的优先级最高。

【个人思路】:

          

        4个状态(复位到向左状态):

                向左LEFT:ground为0则跳转到状态FALL_L;不然向左遇到障碍物则跳转到状态RIGHT;不然没有遇到障碍物则保持状态。

                向右RIGHT:ground为0则跳转到状态FALL_R;不然向右遇到障碍物则跳转到状态LEFT;不然没有遇到障碍物则保持状态。

                向左走的时候掉落FALL_L:ground为1则跳转到状态LEFT;ground为0则保持状态。

                向右走的时候掉落FALL_R:ground为1则跳转到状态RIGHT;ground为0则保持状态。

module top_module(
    input 		clk			,
    input 		areset		,    		//复位向左状态
    input 		bump_left	,
    input 		bump_right	,
	input		ground		,
    output 	reg	walk_left	,
    output 	reg	walk_right	,
	output	reg	aaah
	);
	
//使用独热码进行状态定义
parameter 	LEFT	= 4'b0001, 			//向左状态
			RIGHT	= 4'b0010,			//向右状态
			FALL_L	= 4'b0100,			//向左时掉落状态
			FALL_R	= 4'b1000;			//向右时掉落状态
	
reg [3:0]	cur_state	,				//定义现态寄存器 
			next_state	;				//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk, posedge areset) begin
	if(areset)
		cur_state <= LEFT;				//复位向左状态
	else
		cur_state <= next_state;
end
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always @(*) begin
	next_state = LEFT;					//复位向左状态
	case(cur_state)
		LEFT:begin
			if(!ground)					//掉落
				next_state = FALL_L;	
			else if(bump_left)			//向左遇到障碍物
				next_state = RIGHT;
			else
				next_state = LEFT;
		end
		RIGHT:begin
			if(!ground)					//掉落
				next_state = FALL_R;
			else if(bump_right)			//向右遇到障碍物
				next_state = LEFT;
			else
				next_state = RIGHT;
		end
		FALL_L:begin
			if(ground)					//回到地面,方向向左走(掉落前方向)
				next_state = LEFT;
			else
				next_state = FALL_L;
		end
		FALL_R:begin
			if(ground)					//回到地面,方向向右走(掉落前方向)
				next_state = RIGHT;
			else
				next_state = FALL_R;
		end			
		default:next_state = LEFT;
	endcase
end
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk, posedge areset) begin
	if(areset)begin						//初始向左状态
		walk_left <= 1'b1;
		walk_right <= 1'b0;	
		aaah <= 1'b0;	
	end
	else begin
		case(next_state)
			LEFT:begin					//向左走
				walk_left <= 1'b1;
				walk_right <= 1'b0;
				aaah <= 1'b0;
			end
			RIGHT:begin					//向右走
				walk_left <= 1'b0;
				walk_right <= 1'b1;
				aaah <= 1'b0;
			end	
			FALL_L:begin				//停止移动,大叫(aaah输出1)
				walk_left <= 1'b0;
				walk_right <= 1'b0;
				aaah <= 1'b1;
			end
			FALL_R:begin				//停止移动,大叫(aaah输出1)
				walk_left <= 1'b0;
				walk_right <= 1'b0;
				aaah <= 1'b1;
			end				
			default:begin				//默认向左状态
				walk_left <= 1'b1;
				walk_right <= 1'b0;
				aaah <= 1'b0;
			end
		endcase
	end
end

endmodule

Lemmings 3

【题目】:

        在上题的基础上增加了输入dig以及输出digging。当旅鼠处于地面(ground为1)走动时,一旦dig为1则旅鼠处于挖掘状态,digging输出1;直到ground为0则旅鼠处于掉落状态(aaah为1)

【个人思路】:

6个状态(复位到向左状态):

  • 向左LEFT:ground为0则跳转到状态FALL_L;不然挖掘就跳转到DIG_L状态;不然向左遇到障碍物则跳转到状态RIGHT;不然没有遇到障碍物则保持状态。
  • 向右RIGHT:ground为0则跳转到状态FALL_R;不然挖掘就跳转到DIG_R状态;不然向右遇到障碍物则跳转到状态LEFT;不然没有遇到障碍物则保持状态。
  • 向左走的时候掉落挖掘DIG_L:ground为0(挖空了)则跳转到FALL_L状态;不然则保持状态。
  • 向右走的时候掉落挖掘DIG_R:ground为0(挖空了)则跳转到FALL_R状态;不然则保持状态。
  • 向左走的时候掉落FALL_L:ground为1则跳转到状态LEFT;ground为0则保持状态。
  • 向右走的时候掉落FALL_R:ground为1则跳转到状态RIGHT;ground为0则保持状态。

module top_module(
    input 		clk			,
    input 		areset		,			//复位向左状态
    input 		bump_left	,
    input 		bump_right	,
    input 		ground		,
    input 		dig			,
	
    output reg	walk_left	,
    output reg	walk_right	,
    output reg	aaah		,
    output reg	digging 
); 

//使用独热码进行状态定义
parameter 	LEFT	= 6'b000001, 		//向左状态
			RIGHT	= 6'b000010,		//向右状态
			FALL_L	= 6'b000100,		//向左时掉落状态
			FALL_R	= 6'b001000,        //向右时掉落状态
			DIG_L	= 6'b010000,		//向左时挖掘状态
			DIG_R	= 6'b100000;		//向右时挖掘状态
			
reg [5:0]	cur_state	, 				//定义现态寄存器 
			next_state	;               //定义次态寄存器
		
//三段式状态机第一段:同步时序描述状态转移			
always @(posedge clk, posedge areset) begin
	if(areset)
		cur_state <= LEFT;				//复位向左状态
	else
		cur_state <= next_state;
end	
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always @(*) begin
	next_state = LEFT;					//复位向左状态
	case(cur_state)	
		LEFT:begin
			if(!ground)					//掉落
				next_state = FALL_L;
			else if(dig)				//挖掘
				next_state = DIG_L;				
			else if(bump_left)			//向左遇到障碍物	
				next_state = RIGHT;
			else
				next_state = LEFT;
		end
		RIGHT:begin
			if(!ground)					//掉落
				next_state = FALL_R;    
			else if(dig)                //挖掘
				next_state = DIG_R;						
			else if(bump_right)         //向右遇到障碍物	
				next_state = LEFT;
			else
				next_state = RIGHT;
		end
		FALL_L:begin
			if(ground)					//回到地面,方向向左走(掉落前方向)
				next_state = LEFT;
			else
				next_state = FALL_L;
		end
		FALL_R:begin
			if(ground)					//回到地面,方向向右走(掉落前方向)
				next_state = RIGHT;
			else
				next_state = FALL_R;
		end
		DIG_L:begin						
			if(!ground)					//挖空了,跳转到掉落状态
				next_state = FALL_L;
			else
				next_state = DIG_L;
		end
		DIG_R:begin
			if(!ground)					//挖空了,跳转到掉落状态
				next_state = FALL_R;
			else
				next_state = DIG_R;
		end		
		default:next_state = LEFT;
	endcase
end
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk, posedge areset) begin
	if(areset)begin						//初始向左状态
		walk_left <= 1'b1;
		walk_right <= 1'b0;	
		aaah <= 1'b0;
		digging <= 1'b0;
	end
	else begin
		case(next_state)
			LEFT:begin					//向左走
				walk_left <= 1'b1;
				walk_right <= 1'b0;
				aaah <= 1'b0;
				digging <= 1'b0;
			end
			RIGHT:begin					//向右走
				walk_left <= 1'b0;
				walk_right <= 1'b1;
				aaah <= 1'b0;
				digging <= 1'b0;
			end	
			FALL_L:begin				//停止移动,大叫(aaah输出1)
				walk_left <= 1'b0;
				walk_right <= 1'b0;
				aaah <= 1'b1;
				digging <= 1'b0;
			end
			FALL_R:begin				//停止移动,大叫(aaah输出1)
				walk_left <= 1'b0;
				walk_right <= 1'b0;
				aaah <= 1'b1;
				digging <= 1'b0;
			end	
			DIG_L:begin					//停止移动,挖掘,digging输出1
				walk_left <= 1'b0;
				walk_right <= 1'b0;
				aaah <= 1'b0;
				digging <= 1'b1;
			end
			DIG_R:begin					//停止移动,挖掘,digging输出1
				walk_left <= 1'b0;
				walk_right <= 1'b0;
				aaah <= 1'b0;
				digging <= 1'b1;
			end			
			default:begin				//默认向左状态
				walk_left <= 1'b1;
				walk_right <= 1'b0;	
				aaah <= 1'b0;
				digging <= 1'b0;
			end
		endcase
	end
end

endmodule		

Lemmings 4

【题目】:

        在上题的基础上增加了一个掉落时间的判断,当旅鼠处于ground==0的状态超过20个时钟周期则判定为”死亡“(splatter),此时所有输出都为0.

【个人思路】:

        在上题的基础上增加一个状态:SPLAT。同时设计一个计数器,该计数器只在处于掉落状态(FALL_L和FALL_R)时计数(每个时钟周期+1),其他状态均保持0。这样在旅鼠先处于掉落状态后回到地面后(ground==1)判断计数器是否大于20,若大于20则旅鼠”死亡“。

7个状态(复位到向左状态):

  • 向左LEFT:ground为0则跳转到状态FALL_L;不然挖掘就跳转到DIG_L状态;不然向左遇到障碍物则跳转到状态RIGHT;不然没有遇到障碍物则保持状态。
  • 向右RIGHT:ground为0则跳转到状态FALL_R;不然挖掘就跳转到DIG_R状态;不然向右遇到障碍物则跳转到状态LEFT;不然没有遇到障碍物则保持状态。
  • 向左走的时候掉落挖掘DIG_L:ground为0(挖空了)则跳转到FALL_L状态;不然则保持状态。
  • 向右走的时候掉落挖掘DIG_R:ground为0(挖空了)则跳转到FALL_R状态;不然则保持状态。
  • 向左走的时候掉落FALL_L:ground为1且计数器小于等于20则跳转到状态LEFT;ground为1且计数器大于20则跳转到状态SPLAT;不然ground为0则保持状态。
  • 向右走的时候掉落FALL_R:ground为1且计数器小于等于20则跳转到状态RIGHT;ground为1且计数器大于20则跳转到状态SPLAT;不然ground为0则保持状态。
  • SPLAT:一直保持这个状态(直到复位)。
module top_module(
    input 		clk			,
    input 		areset		,    		//复位向左状态
    input 		bump_left	,
    input 		bump_right	,
    input 		ground		,
    input 		dig			,
	
    output reg	walk_left	,
    output reg	walk_right	,
    output reg	aaah		,
    output reg	digging 
); 
//使用独热码进行状态定义
parameter 	LEFT	= 7'b0000001, 		//向左状态
			RIGHT	= 7'b0000010,       //向右状态
			FALL_L	= 7'b0000100,       //向左时掉落状态
			FALL_R	= 7'b0001000,       //向右时掉落状态
			DIG_L	= 7'b0010000,       //向左时挖掘状态
			DIG_R	= 7'b0100000,       //向右时挖掘状态
			SPLAT	= 7'b1000000;		//死亡状态
				
reg [6:0]	cur_state	,				//定义现态寄存器 
			next_state	;				//定义次态寄存器
			
reg	[9:0]	cnt			;				//最大计数1024个周期,用来计数旅鼠掉落时间
		
//三段式状态机第一段:同步时序描述状态转移			
always @(posedge clk, posedge areset) begin
	if(areset)
		cur_state <= LEFT;
	else
		cur_state <= next_state;
end	
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always @(*) begin
	next_state = LEFT;					//复位向左状态
	case(cur_state)                     
		LEFT:begin                      
			if(!ground)                 //掉落
				next_state = FALL_L;     
			else if(dig)                //挖掘
				next_state = DIG_L;						
			else if(bump_left)          //向左遇到障碍物	
				next_state = RIGHT;
			else
				next_state = LEFT;
		end
		RIGHT:begin
			if(!ground)					//掉落
				next_state = FALL_R;    
			else if(dig)                //挖掘
				next_state = DIG_R;								
			else if(bump_right)         //向左遇到障碍物	
				next_state = LEFT;
			else
				next_state = RIGHT;
		end
		FALL_L:begin
			if(ground)begin				
				if(cnt <= 10'd20)
					next_state = LEFT;	//计数器20以内,回到地面,方向向左走(掉落前方向)	
				else
					next_state = SPLAT;	//计数器大于20,旅鼠死亡
			end
			else
				next_state = FALL_L;
		end
		FALL_R:begin
			if(ground)begin				
				if(cnt <= 10'd20)
					next_state = RIGHT;	//计数器20以内,回到地面,方向向右走(掉落前方向)
				else
					next_state = SPLAT;	//计数器大于20,旅鼠死亡
			end
			else
				next_state = FALL_R;
		end
		DIG_L:begin
			if(!ground)
				next_state = FALL_L;	//挖空了,跳转到掉落状态
			else
				next_state = DIG_L;
		end
		DIG_R:begin
			if(!ground)
				next_state = FALL_R;	//挖空了,跳转到掉落状态
			else
				next_state = DIG_R;
		end
		SPLAT:begin
			next_state = SPLAT;			//保持该状态
		end
		default:next_state = LEFT;
	endcase
end
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk, posedge areset) begin
	if(areset)begin						//初始向左状态
		walk_left <= 1'b1;
		walk_right <= 1'b0;	
		aaah <= 1'b0;
		digging <= 1'b0;
		cnt <= 10'd0;
	end
	else begin
		case(next_state)
			LEFT:begin					//向左走
				walk_left <= 1'b1;
				walk_right <= 1'b0;
				aaah <= 1'b0;
				digging <= 1'b0;
				cnt <= 10'd0;			//计数器不计数
			end	
			RIGHT:begin					//向右走	
				walk_left <= 1'b0;
				walk_right <= 1'b1;
				aaah <= 1'b0;
				digging <= 1'b0;
				cnt <= 10'd0;			//计数器不计数
			end	
			FALL_L:begin				//大叫(aaah输出1)
				walk_left <= 1'b0;
				walk_right <= 1'b0;
				aaah <= 1'b1;
				digging <= 1'b0;
				cnt <= cnt + 1'b1;		//计数器开始累加1
			end
			FALL_R:begin				//大叫(aaah输出1)
				walk_left <= 1'b0;      
				walk_right <= 1'b0;     
				aaah <= 1'b1;           
				digging <= 1'b0;        
				cnt <= cnt + 1'b1;		//计数器开始累加1		
			end	
			DIG_L:begin					//挖掘,digging输出1
				walk_left <= 1'b0;
				walk_right <= 1'b0;
				aaah <= 1'b0;
				digging <= 1'b1;
				cnt <= 10'd0;			//计数器不计数
			end
			DIG_R:begin					//挖掘,digging输出1
				walk_left <= 1'b0;      
				walk_right <= 1'b0;     
				aaah <= 1'b0;           
				digging <= 1'b1;        
				cnt <= 10'd0;			//计数器不计数	
			end	
			SPLAT:begin					//死亡状态,所有输出为0
				walk_left <= 1'b0;
				walk_right <= 1'b0;
				aaah <= 1'b0;
				digging <= 1'b0;
				cnt <= 10'd0;
			end			
			default:begin				//默认向左走状态
				walk_left <= 1'b1;
				walk_right <= 1'b0;	
				aaah <= 1'b0;
				digging <= 1'b0;
				cnt <= 10'd0;	
			end
		endcase
	end
end

endmodule		

One-hot FSM

【题目】:

        使用独热码进行输出。

【个人思路】:

        之前有这类型的题,不详细说了,根据状态转移图写就行。

module top_module(
    input 			in			,
    input 	[9:0] 	state		,
    output 	[9:0] 	next_state	,
    output 			out1		,
    output 			out2
);

parameter	S0 = 4'd0,
			S1 = 4'd1,
			S2 = 4'd2,
			S3 = 4'd3,
			S4 = 4'd4,
			S5 = 4'd5,
			S6 = 4'd6,
			S7 = 4'd7,
			S8 = 4'd8,
			S9 = 4'd9;

assign next_state[S0] = ~in & (state[S0] | state[S1] | state[S2] | state[S3] | state[S4] | state[S7] | state[S8] | state[S9]);
assign next_state[S1] = in & (state[S0] | state[S8] | state[S9]);
assign next_state[S2] = in & state[S1];
assign next_state[S3] = in & state[S2];
assign next_state[S4] = in & state[S3];
assign next_state[S5] = in & state[S4];
assign next_state[S6] = in & state[S5];
assign next_state[S7] = in & (state[S6] | state[S7]);
assign next_state[S8] = ~in & state[S5];
assign next_state[S9] = ~in & state[S6];

assign out1 = state[S8] | state[S9];
assign out2 = state[S7] | state[S9];
	
endmodule

PS/2 packet parser

【题目】:

        本质上是个序列检测。检测到in[3]为高电平后,认定该字节为因为接收的第一个字节,共接收三个字节后,将指示位done拉高一个周期。

【个人思路】:

        状态转移图如下:

                

  • S0:初始状态,一直检测in[3]是否为高电平,若是,则代表接收了第一个字节跳转到状态S1,则不是则继续检测
  • S1:接收第二个字节,仅维持一个时钟周期后跳转到状态S2
  • S2:接收第二个字节,仅维持一个时钟周期后跳转到状态S3
  • S3:检测in[3]是否为高电平,若是则接收了第一个字节跳转到状态S1,若不是则跳转到状态S0。在这个字节输出done信号,表示3个字节接收完成
module top_module(
    input 			clk		,
    input 	[7:0] 	in		,
    input 			reset	,    
    output 			done	
); 

//使用独热码进行状态定义
parameter	S0 = 4'b0001,				//判断状态及接收第一个字节
			S1 = 4'b0010,				//接收第二个字节
			S2 = 4'b0100,				//接收第三个字节
			S3 = 4'b1000;				//判断状态及接收第一个字节
						
reg	[3:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk)begin
	if(reset)
		cur_state <= S0;
	else
		cur_state <= next_state;
end
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	next_state = S0;
	case(cur_state)
		S0:
			if(in[3])				//检测到第一个字节则跳转到S1
				next_state = S1;
			else
				next_state = S0;	//没有检测到第一个字节则继续检测
        S1: 
			next_state = S2;		//接收第二个字节
		S2: 
			next_state = S3;		//接收第三个字节
        S3: 
			if(in[3])				//检测到第一个字节则跳转到S1
				next_state = S1;
			else
				next_state = S0;	//没有检测到第一个字节则跳转到S0		
		default:next_state = S0;
	endcase
end
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk)begin
	if(reset)
		done <= 1'b0;
	else
		case(next_state)
			S0,S1,S2:	
				done <= 1'b0; 
			S3: 					//三个字节检测完成输出done
				done <= 1'b1;
			default:done <= 1'b0;
		endcase
end

endmodule

PS/2 packet parser and datapath

【题目】:

        和上题基本相同,增加了一个输出out_bytes,out_bytes就是接收到的三个数据,当三个数据输出完成后,与done信号同时输出,其他时候不关心。

【个人思路】:

        状态转移图与上题一致。可以看到接收到的数据是高位在前,所以可以使用到的8位数据去覆盖out_bytes的低8数据,这样就将in的值赋给了out_bytes。

module top_module(
    input 			clk			,
    input 	[7:0] 	in			,
    input 			reset		, 
	output 	[23:0] 	out_bytes	,    	
    output 			done	
); 

//使用独热码进行状态定义
parameter	S0 = 4'b0001,				//判断状态及接收第一个字节
			S1 = 4'b0010,				//接收第二个字节
			S2 = 4'b0100,				//接收第三个字节
			S3 = 4'b1000;				//判断状态及接收第一个字节
						
reg	[3:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk)begin
	if(reset)
		cur_state <= S0;
	else
		cur_state <= next_state;
end
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	next_state = S0;
	case(cur_state)
		S0:
			if(in[3])				//检测到第一个字节则跳转到S1
				next_state = S1;
			else
				next_state = S0;	//没有检测到第一个字节则继续检测
        S1: 
			next_state = S2;		//接收第二个字节
		S2: 
			next_state = S3;		//接收第三个字节
        S3: 
			if(in[3])				//检测到第一个字节则跳转到S1
				next_state = S1;
			else
				next_state = S0;	//没有检测到第一个字节则跳转到S0		
		default:next_state = S0;
	endcase
end
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk)begin
	if(reset)
		done <= 1'b0;
	else
		case(next_state)
			S0,S1,S2:begin	
				done <= 1'b0;
				out_bytes <= {out_bytes[15:0],in[7:0]};	
			end
			S3:begin
				done <= 1'b1;
				out_bytes <= {out_bytes[15:0],in[7:0]};
			end
			default:done <= 1'b0;
		endcase
end

endmodule

Serial Receiver

【题目】:

        很多串行通信协议发送过程中会在数据字节前后增加标记来标识字节首尾,即包含 start 位和 stop 位,比如 UART 协议就是这样。该题就模拟了这种类型协议的接收端行为。这里开始的标记是低电平,结束的标记是高电平,在开始和结束之间传输的 bit 位为8即一个字节,这是正确接收的条件,当满足该条件后会在 stop 位后面的时钟沿给出 done 的输出标记。如果不满足该条件,比如起始 start 条件不满足,则会一直等待知道接收到低电平信号才开始;又如 start 开始后,中间的位数超过了8 bit,即8 bit 后一直是低电平信号,不出现高电平的 stop 信号,则 receiver 继续等待知道出现高电平位才结束,但这是一个错误接收,不会给出 done 的输出标记。

        波形图如下所示:

【个人思路】:

        这个题目其实和UART(异步串口)很像,只不过这题目的时钟是同步的。可以参考我的这篇博文做个初步的了解:串口(UART)的实现

        根据题意,绘制了如下的时序图:

  • 系统本来处于初始状态,输入in为高电平,等待开始信号(低电平) ;
  • 阶段1:输入变为低电平,意味者一次数据传输开始,在此阶段,接收8-bit数据,然后判断接下来的数据是否是高电平(终止位),图中1阶段不是高电平,意味着传输发生了错误,直到终止信号来临后系统再度进入初始状态,同时这次输出的数据被舍弃(没有done信号输出)
  • 阶段2:系统正确的接收到了起始信号、8-bit数据、终止信号,所以拉高done信号,意味着这次传输有效
  • 阶段3:由于在一次终止信号后可能马上就跟着一个起始信号,所以阶段3系统不会进入初始状态,而是直接进入数据传输状态
  • 设计了一个中间信号cnt_data,该信号在接收数据阶段从0-7计数(8是停止位),用来统计接收的数据个数,可以减少状态数
  • done在成功接收到一个终止信号后(代表成功接收8-bit数据)输出一个周期的高电平

状态转移图:

        

  • IDLE:      初始状态,检测到输入in为高电平后跳转到DATA状态,否则保持该状态
  • DATA:     在这个状态有个从0开始的计数器cnt_data,当计数器计数到7时,代表接收到了8-bit数据,计数到8时输入信号若为1,则代表该信号为终止信号,一次传输结束,跳转到状态ERROR;若输入信号为0,则代表传输错误,跳转到状态ERROR,继续等待终止信号的到来;若计数器小于8,则保持该状态(传输未结束)
  • ERROR: 检测到输入in为0后代表传输依然错误,保持在该状态,继续检测终止信号;检测到输入in为1后代表传输结束,由于该次传输出现了错误,所以直接跳转到初始状态
  • STOP:    检测到输入in为0后代表检测到了一个新的起始信号,接下来就可以接收数据,所以跳转到状态DATA;若检测到输入in为1(空闲状态)则跳转到IDLE状态
module top_module(
    input 	clk,
    input 	in,
    input 	reset,    
    output 	done
); 
//定义状态机状态
parameter	IDLE  = 4'b0001,	
			DATA  = 4'b0010,	
			STOP  = 4'b0100,	
			ERROR = 4'b1000;

reg	[3:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器

reg	[3:0]	cnt_data	;				//数据接收计数器			

//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk)begin
	if(reset)
		cur_state <= IDLE;
	else
		cur_state <= next_state;
end	
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	next_state = IDLE;
	case(cur_state)
		IDLE    : 
			if(!in)						//检测到起始信号
				next_state = DATA;
			else						//空闲状态
				next_state = IDLE;
		DATA   :
			if(cnt_data == 4'd8)begin	//数据接收完毕,准备接收终止位
				if(in)					//数据线拉高,说明接收到了停止位
					next_state = STOP;
				else					//数据线拉低,输出传输错误
					next_state = ERROR;
			end
			else
				next_state = DATA;		//数据接收未完成
		STOP  : 
			if(in)						//数据线仍然为高,说明处于空闲状态
				next_state = IDLE;
			else						//数据线拉低,接收到了新的起始位
				next_state = DATA;		
		ERROR :
			if(in)						//数据线拉高,说明接收到了停止位
				next_state = IDLE;
			else
				next_state = ERROR;		//数据线拉低,输出传输依然错误	
		default:	next_state = IDLE;
	endcase
end	
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk)begin
	if(reset)
		done <= 1'b0;
	else
		case(next_state)
			STOP: 					
				done <= 1'b1;
			default:
				done <= 1'b0;
		endcase
end

//数据计数器,从0-8,共九位,8位数据+1位停止位
always @(posedge clk)begin
	if(reset)
		cnt_data <= 4'd0;
	else if(cur_state == DATA)begin
		if(cnt_data == 4'd8)
			cnt_data <= 4'd0;
		else
			cnt_data <= cnt_data + 1'b1;		
	end
	else
		cnt_data <= cnt_data;
end

endmodule

 Serial Receiver and Datapath

【题目】:

        现在,您已经有了一个有限状态机,可以识别在串行位流中正确接收字节的时间,添加一个输出正确接收数据字节的数据路径。当done为1时,Out_byte需要有效,否则不关心。注意,串行协议先发送最低有效位。

        波形图如下所示:

【个人思路】:

        这个题目就是在上题的基础上增加一个将接收到的8-bit有效信号输出。状体图同上题。

        因为码流上的输出是低位在前,高位在后,所以可以设计一个reg变量out_byte_temp去接收数据(移位寄存就可以接收数据,也是串并转换的一种方法),因为out_byte_temp在接收过程中一直在变化,所以不能作为输出给其他模块,将out_byte_temp寄存一拍并在done为高电平时输出就行了。

    根据题意,绘制了如下的时序图:

  • 在DATA状态, out_byte_temp通过移位操作完成串并转换,也就是接收了8-bit数据
  • 在STOP状态,将out_byte_temp赋值给out_byte输出
 module top_module(
    input 			clk,
    input 			in,
    input 			reset,    // Synchronous reset
	output [7:0] 	out_byte,
    output 			done
); 
//定义状态机状态
parameter	IDLE  = 4'b0001,	
			DATA  = 4'b0010,	
			STOP  = 4'b0100,	
			ERROR = 4'b1000;

reg	[3:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器

reg	[3:0]	cnt_data	;				//数据接收计数器
reg	[7:0]	out_byte_temp;				//接收数据寄存		

//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk)begin
	if(reset)
		cur_state <= IDLE;
	else
		cur_state <= next_state;
end	
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	next_state = IDLE;
	case(cur_state)
		IDLE    : 
			if(!in)						//检测到起始信号
				next_state = DATA;
			else						//空闲状态
				next_state = IDLE;
		DATA   :
			if(cnt_data == 4'd8)begin	//数据接收完毕,准备接收终止位
				if(in)					//数据线拉高,说明接收到了停止位
					next_state = STOP;
				else					//数据线拉低,输出传输错误
					next_state = ERROR;
			end
			else
				next_state = DATA;		//数据接收未完成
		STOP  : 
			if(in)						//数据线仍然为高,说明处于空闲状态
				next_state = IDLE;
			else						//数据线拉低,接收到了新的起始位
				next_state = DATA;		
		ERROR :
			if(in)						//数据线拉高,说明接收到了停止位
				next_state = IDLE;
			else
				next_state = ERROR;		//数据线拉低,输出传输依然错误	
		default:	next_state = IDLE;
	endcase
end	
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk)begin
	if(reset)
		done <= 1'b0;
	else
		case(next_state)				
			STOP:begin 					
				done <= 1'b1;
                out_byte <= out_byte_temp;	
			end	
			default:begin
				done <= 1'b0;
                out_byte <= 8'd0;	
			end
		endcase
end

//数据计数器,从0-8,共九位,8位数据+1位停止位
always @(posedge clk)begin
	if(reset)
		cnt_data <= 4'd0;
	else if(cur_state == DATA)begin
		if(cnt_data == 4'd8)
			cnt_data <= 4'd0;
		else
			cnt_data <= cnt_data + 1'b1;		
	end
	else
		cnt_data <= cnt_data;
end

//接收数据
always @(posedge clk)begin
	if(reset)
		out_byte_temp <= 8'd0;
	else if(next_state == DATA)
		out_byte_temp <= {in,out_byte_temp[7:1]};		
	else
		out_byte_temp <= out_byte_temp;
end

endmodule

Serial Receiver with Parity Checking

【题目】:

这题仍然是在前面的基础上进行进化,增添了奇偶校验位。关于奇偶校验可以参考我的这篇博文:Verilgo实现的FPGA奇偶校验

该题采用的是奇校验的方式,并且提供了奇偶校验模块。原本 start 和 stop 位之间的8 bit 变为了9 bit,新增的1 bit 为奇校验位,从而使得这9 bit 中“1”的数量为奇数个,即题目中提供的奇偶校验模块输出为1时表面数据正确,否则数据错误不予接收。波形图如下所示:

【个人思路】

        本题和上题的区别在于增加要一个奇校验位,只有发送过来的数据符合奇校验的规则时,才拉高done并输出out_byte。

        这个题目设计的奇校验比较奇怪,先看他的源码:

module parity (
    input clk,
    input reset,
    input in,
    output reg odd);

    always @(posedge clk)
        if (reset) odd <= 0;
        else if (in) odd <= ~odd;

endmodule

        不难看出,这是在码流中没检测到一个1,则输出odd翻转一次,而数据位+奇校验位=9位数据中若满足要求则有奇数个1,那么odd输出则为1,若不满足校验要求,则是偶数个1使得校验位odd输出为0。也就是说这个奇偶校验模块实际上是一个判断位,用于对9位数据做奇校验判断。

        而我们一般的奇校验模块则是给定8位数据,输出最后一位校验位。这一点不同大家需要注意。

        同时,因为只能使用该模块对9位数据做校验,所以在不接收9位数据的其他时态需要一定对该模块复位。

        时序图与上题基本一致,如下:

        在上题的基础上,需要在STOP状态拉高done信号,在本题增加了奇校验后,需要在此模块同时对odd信号做判断,若为真,需说明校验成功,同时输出数据和拉高done信号;若为0,则说明数据接收时序没问题,但接收的数据不符合校验要求(传输过程出现错误),不输出数据、不拉高done信号。

module top_module(
    input 				clk,
    input 				in,
    input 				reset,    // Synchronous reset
	output reg[7:0] 	out_byte,
    output reg			done
); 
//定义状态机状态
parameter	IDLE  = 4'b0001,	
			DATA  = 4'b0010,	
			STOP  = 4'b0100,	
			ERROR = 4'b1000;

reg	[3:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器

reg	[3:0]	cnt_data	;				//数据接收计数器
reg	[7:0]	out_byte_temp;				//接收数据寄存		

wire 	odd			;
wire	reset_odd	;

//复位信号无效或者在接收数据时,不对奇偶校验模块复位,其它时间一直对该模块复位
assign	reset_odd = reset | (!(cur_state == DATA));	

//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk)begin
	if(reset)
		cur_state <= IDLE;
	else
		cur_state <= next_state;
end	
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	next_state = IDLE;
	case(cur_state)
		IDLE    : 
			if(!in)						//检测到起始信号
				next_state = DATA;
			else						//空闲状态
				next_state = IDLE;
		DATA   :
			if(cnt_data == 4'd9)begin	//数据接收完毕,准备接收终止位
				if(in)					//数据线拉高,说明接收到了停止位
					next_state = STOP;
				else					//数据线拉低,输出传输错误
					next_state = ERROR;
			end
			else
				next_state = DATA;		//数据接收未完成
		STOP  : 
			if(in)						//数据线仍然为高,说明处于空闲状态
				next_state = IDLE;
			else						//数据线拉低,接收到了新的起始位
				next_state = DATA;		
		ERROR :
			if(in)						//数据线拉高,说明接收到了停止位
				next_state = IDLE;
			else
				next_state = ERROR;		//数据线拉低,输出传输依然错误	
		default:	next_state = IDLE;
	endcase
end	
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk)begin
	if(reset)
		done <= 1'b0;
	else
		case(next_state)				
			STOP:begin
				if(odd)
					done <= 1'b1;
				else
					done <= 1'b0;
				out_byte <= out_byte_temp;	
			end	
			default:begin
				done <= 1'b0;
                out_byte <= 8'd0;	
			end
		endcase
end

//数据计数器,从0-8,共九位,8位数据+1位停止位
always @(posedge clk)begin
	if(reset)
		cnt_data <= 4'd0;
	else if(cur_state == DATA)begin
		/*if(cnt_data == 4'd8)
			cnt_data <= 4'd0;
		else*/
			cnt_data <= cnt_data + 1'b1;		
	end
	else
		cnt_data <= 4'd0;
end

//接收数据
always @(posedge clk)begin
	if(reset)
		out_byte_temp <= 8'd0;
	//else if((next_state == DATA) && (cnt_data <= 4'd7))
	else if((cur_state == DATA) && (cnt_data <= 4'd7))
		out_byte_temp <= {in,out_byte_temp[7:1]};		
	else
		out_byte_temp <= out_byte_temp;
end

parity	parity_inst(
    .clk	(clk),
    .reset	(reset_odd),
    .in		(in),
    .odd	(odd)
);

endmodule		

Sequence recongnition

        这题目的大概意思是,检测到连续的5个1就拉高disc一个周期(一定要是111_110这种结构,不能是11111_1这种结构,这种属于检测到了连续的6个1,下同);检测到连续的6个1就拉高flag一个周期;检测到连续的7个1就拉高flag一个周期;检测到连续的8个1就拉高flag2个周期(从第7个开始,1越多flag高电平的时间越长)。

        这题目比较简单的,注意检测到连续的6个1不能拉高disc,7个以上不能拉高disc、flag。

module top_module(
    input 	clk,
    input 	reset,    // Synchronous reset
    input 	in,
    output 	disc,
    output 	flag,
    output 	err
);
//定义状态机状态
parameter	S    = 4'd0,	
			S1   = 4'd1,	
			S2   = 4'd2,	
			S3   = 4'd3,	
			S4   = 4'd4,	
			S5   = 4'd5,	
			DISC = 4'd6,	
			S6   = 4'd7,	
			FALG = 4'd8,	
			S7   = 4'd9,
			ERRO = 4'd10;

reg	[3:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk)begin
	if(reset)
		cur_state <= S;
	else
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	next_state = S;
	case(cur_state)
		S		: 	next_state = in ? S1 : S;
		S1		: 	next_state = in ? S2 : S;
		S2		: 	next_state = in ? S3 : S;
		S3		: 	next_state = in ? S4 : S;
		S4		: 	next_state = in ? S5 : S;
		S5		: 	next_state = in ? S6 : DISC;
		DISC	: 	next_state = in ? S1 : S;
		S6		: 	next_state = in ? ERRO : FALG;
		FALG	: 	next_state = in ? S1 : S;	
		ERRO	: 	next_state = in ? ERRO : S;	
		default	:	next_state = S;
	endcase
end	
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk)begin
	if(reset)begin
		disc <= 1'b0;
		flag <= 1'b0;
		err  <= 1'b0;
	end
	else
		case(next_state)				
			DISC:begin
				disc <= 1'b1;			
				flag <= 1'b0;
				err  <= 1'b0;
			end
			FALG:begin
				disc <= 1'b0;
				flag <= 1'b1;
				err  <= 1'b0;
			end
			ERRO:begin
				disc <= 1'b0;
				flag <= 1'b0;
				err  <= 1'b1;
			end			
			default:begin
				disc <= 1'b0;
				flag <= 1'b0;
				err  <= 1'b0;
			end
		endcase
end

endmodule

Q8:Design a Mealy FSM

【题目】:

        实现一个mealy型有限状态机,它可以识别名为x的输入信号上的序列“101”。FSM应该有一个输出信号z,当检测到“101”序列时,该输出信号z被置位为逻辑1。FSM还应该有一个低电平有效的异步复位。您的状态机中可能只有3个状态。你的FSM应该能识别重叠序列。

【个人思路】:
        mealy型有限状态机的与moore型状态机最大的区别在于其输出不光与当前状态有关,而且与输入有关,这就决定了其状态数要少。

        状态转移图如下:

        

  • S  :初始状态,检测到1后跳转到状态S1,不然保持在该状态,输出均为0
  • S1:检测到0后跳转到S2,检测到1维持在该状态
  • S2 :此时已经检测到了“10”,若继续检测到1,则“101”检测成功,状态跳转到S1(重复检测),且输出1;若检测到0,则跳转到S
module top_module (
    input clk,
    input aresetn,    
    input x,
    output z ); 


parameter	S    = 3'b001,	
			S1   = 3'b010,	
			S2   = 3'b100;

reg	[2:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器
						
always @(posedge clk or negedge aresetn)begin
    if(!aresetn)
		cur_state <= S;
	else
		cur_state <= next_state;
end
   
always@(*)begin
	next_state = S;
	case(cur_state)
		S		: 	next_state = x ? S1 : S;
		S1		: 	next_state = x ? S1 : S2;
		S2		: 	next_state = x ? S1 : S;
		default	:	next_state = S;
	endcase
end	  
    
assign z = ((cur_state == S2) && (x == 1'b1)) ? 1'b1 : 1'b0;

endmodule

 Serial two's complementer(Moore FSM)

【题目】:

        您要设计一个1输入、1输出串行2的补码摩尔状态机。输入(x)是从数字的最低有效位开始的一系列位(每个时钟周期一个位),输出(Z)是输入的2的补码。机器可以接受任意长度的输入数字。电路需要异步复位。转换在Reset被释放时开始,在断言Reset时停止。

【个人思路】:
        这题目大概意思就是从低位到高位,一个一个输入一个数,然后输出就是这个数的补码。这里的默认条件是输入的数是负数(因为正数的补码是本身,这样设计没意义)。

        负数的补码是取反加1。加入输入的前几个数据都是0,

                举例1输入0-0-0-1,那么输入的数据就是1000(求它的补码),1000取反=0111,+1后是1000

                举例2输入0-0-0-1-1,那么输入的数据就是11000(求它的补码),1000取反=00111,+1后是0_1000

                举例3输入0-0-0-1-1-0-1,那么输入的数据就是1011000(求它的补码),1011000取反=01000111,+1后是010_1000

        根据上面的规律,可以推导出,在出现第一个1之前(包括这个1,也就是进位),它的补码等于输入本身;在出现第一个1后,后面的补码等于输入取反。

状态转移图如下:

  • A  :初始状态,检测到1后跳转到状态B,不然保持在该状态
  • B  :该状态首先需要需要输出A的1,和从C检测到0跳转到B状态后需要输出的1
  • C :检测到0后跳转到状态B,不然保持在该状态

MOORE型状态机输出与输入无关,仅仅于状态有关,所以相比于下一题的MEALY型状态机状态多了,而且输出也会落后一个时钟周期。

module top_module (
    input clk,
    input areset,
    input x,
    output z
); 

//定义状态机状态
parameter	A = 2'd0,	
			B = 2'd1,
			C = 2'd2;
    
reg[1:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器    
//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk or posedge areset)begin
	if(areset)
		cur_state <= A;
	else
		cur_state <= next_state;
end
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	//next_state = A;
	case(cur_state)
		A 		: 	next_state = x ? B : A;
		B 		: 	next_state = x ? C : B;
		C 		: 	next_state = x ? C : B;
		default	:	next_state = A;
	endcase
end	
//三段式状态机第三段
assign z = (cur_state ==B);
	
endmodule

Serial two's complementer(Mealy FSM)

【个人思路】:

        这题在上题的基础上要求使用MEALY型状态机求解。

        状态转移图:

  • A:检测到1,立马输出1,且跳转到下一个状态B;检测到0则继续保持该状态,说明没有检测到第一个1
  • B: 检测到0,则输出相反的1,状态不变;检测到1,则输出相反的0,状态不变
module top_module (
    input 	clk,
    input 	areset,
    input 	x,
    output 	z
); 

//定义状态机状态
parameter	A = 1'd0,	
			B = 1'd1;	

reg			cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk or posedge areset)begin
	if(areset)
		cur_state <= A;
	else
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	next_state = A;
	case(cur_state)
		A 		: 	next_state = x ? B : A;
		B 		: 	next_state = B;
		default	:	next_state = A;
	endcase
end	
//三段式状态机第三段
    assign z = (cur_state == A && x) ||  (cur_state == B && ~x);

endmodule

Q3a: FSM

【题目】:

        一个输入s和w的有限状态机。假设FSM从一个名为a的重置状态开始,如下所示。当s = 0时,状态为A,当s = 1时,状态为B。一旦进入状态B, FSM就会检查接下来三个时钟周期中输入w的值。如果w在这两个时钟周期中恰好为1,则FSM必须在下一个时钟周期中将输出z设置为1。否则z等于0。FSM继续检查w是否有接下来的三个时钟周期,以此类推。下面的时序图说明了不同的w值所需的z值。使用尽可能少的状态。注意,s输入只在状态A中使用,所以您需要只考虑w输入。

【个人思路】:

        大概意思是状态机复位状态在A,检测到s为1后进入状态B,在状态B每隔三个时钟周期就检查一次这三个时钟周期内w为高的时钟周期个数是否为2,若是则拉高输出z一个周期。

        状态机设计两个状态A、B。在状态B设计一个计数器1从0-2循环计数,这样就把B状态分隔成了每3个周期的小段。再设计一个计数器2,来计数输入w为1的个数。这样就可以根据w为高的次数来对应的拉高输出z。

        时序图如下:

module top_module (
    input 	clk,
    input 	reset,   			
    input 	s,
    input 	w,
    output 	reg	z
);

parameter	A = 2'b01,	
			B = 2'b10;

reg	[1:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器
			
reg		[2:0]	cnt1	;
reg		[2:0]	cnt2	;
					
always @(posedge clk)begin
	if(reset)
		cur_state <= A;
	else
		cur_state <= next_state;
end
  
always@(*)begin
	next_state = A;
	case(cur_state)
		A 		: 	next_state = s ? B : A;
		B		: 	next_state = B;
		default	:	next_state = A;
	endcase
end
always @(posedge clk)begin
	if(reset)
		z <= 1'b0;
	else
		case(cur_state)
			A	: 	
				z <= 1'b0;
			B	: 	
				if(cnt1 == 3'd2)begin
                    	//检测到1个1且当前输入w为1(2给1)或者检测到2个1且当前输入w为0(2个1)
                    if((cnt2 == 3'd1 && w) || (cnt2 == 3'd2 && !w)) 
						z <= 1'b1;
					else
						z <= 1'b0;
				end
				else
					z <= 1'b0;
			default	:	
				z <= 1'b0;
		endcase
end
	
//计数器1:计数0-2 3个周期循环计数
always @(posedge clk)begin
	if(reset)
		cnt1 <= 3'd0;
	else if(cur_state == B)begin
		if(cnt1 == 3'd2)
			cnt1 <= 3'd0;
		else
			cnt1 <= cnt1 + 3'd1;
	end
	else
		cnt1 <= 3'd0;	
end	
//计数器2:每找到一个1就加一
always @(posedge clk)begin
	if(reset)
		cnt2 <= 3'd0;
	else if(cur_state == B)begin
		if(cnt1 == 3'd2)
			cnt2 <= 3'd0;
		else if(w)
			cnt2 <= cnt2 + 3'd1;
		else
			cnt2 <= cnt2;
	end
	else
		cnt2 <= 3'd0;	
end	

endmodule

 Q3b: FSM

【题目】:

        根据下面的状态表实现状态机,状态机复位状态为000。

【个人思路】:

        这题跟着上面的表做就行了,比较容易。

module top_module (
    input 	clk		,
    input 	reset	,   // Synchronous reset
    input 	x		,
    output 	z
);

//定义状态机状态
parameter	A = 3'd0,	
			B = 3'd1,
			C = 3'd2,
			D = 3'd3,
			E = 3'd4;
			
reg	[2:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器			
//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk)begin
	if(reset)
		cur_state <= A;
	else
		cur_state <= next_state;
end
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	//next_state = A;
	case(cur_state)
		A 		: 	next_state = x ? B : A;
		B 		: 	next_state = x ? E : B;
		C 		: 	next_state = x ? B : C;
		D		: 	next_state = x ? C : B;
		E 		: 	next_state = x ? E : D;
		default	:	next_state = A;
	endcase
end	
//三段式状态机第三段
assign z = (cur_state ==D || cur_state ==E);
	
endmodule

 FSM logic

【题目】:

        根据下面的状态表实现状态机,状态机复位状态为000。

【个人思路】:

        这题基本上跟上题是一样的,只不过现态变成了输入,而不是中间变量。

module top_module (
    input clk,
    input [2:0] y,
    input x,
    output Y0,
    output z
);

    parameter A = 3'd0, B = 3'd1, C = 3'd2;
    parameter D = 3'd3, E = 3'd4;
 
    reg [2:0]	next_state;
    
    always@(*)begin
        case(y)
            A:begin
                next_state = x ? B : A;
            end
            B:begin
                next_state = x ? E : B;
            end
            C:begin
                next_state = x ? B : C;
            end
            D:begin
                next_state = x ? C : B;
            end
            E:begin
                next_state = x ? E : D;
            end
            default:begin
                next_state = A;
            end
        endcase
    end
    
    assign Y0 = (next_state == B || next_state == D);
    assign z  = (y == D || y == E);   
    
endmodule

 Q6b:FSM next-state logic

        状态机中共有 6 个状态 A - F 分别用 000,001... 101 表示。使用二进制方式编码状态。

module top_module (
    input [3:1] y,
    input w,
    output Y2);
    
reg 	[5:0]   next_state;
	
parameter	A  = 6'd0,
			B  = 6'd1,
			C  = 6'd2,
			D  = 6'd3,
			E  = 6'd4,
			F  = 6'd5;
	
always @(*) begin
	case (y)
		A:	next_state = w ? A : B;
		B:	next_state = w ? D : C;
		C:	next_state = w ? D : E;
		D:	next_state = w ? A : F;
		E:	next_state = w ? D : E;
		F:	next_state = w ? D : C;
		default: next_state = A;
	endcase
end

assign  Y2   =   next_state[1];
    
endmodule

Q6c FSM one-hot next-state logic

        基本和上题一样,不过换成了独热码。

module top_module (
    input [6:1] y,
    input 		w,
    output 		Y2,
    output 		Y4);

    reg 	[5:0]   next_state;

    always @(*) begin
        next_state[0]    <=  w ? y[1] || y[4] : 1'b0;
        next_state[1]    <=  w ? 1'b0 : y[1];
        next_state[2]    <=  w ? 1'b0 : y[6] || y[2];
        next_state[3]    <=  w ? y[2] || y[3] || y[5] || y[6]: 1'b0;
        next_state[4]    <=  w ? 1'b0 : y[5] || y[3];
        next_state[5]    <=  w ? 1'b0 : y[4];
    end
 
    assign  Y2   =   next_state[1];
    assign  Y4   =   next_state[3];
    
endmodule

Q6 FSM 

【题目】:

        根据下面的状态转移图实现FSM。

【个人思路】:

        经典的有限状态机,直接用三段式就完事了。  

module top_module (
    input 	clk,
    input 	reset,     // synchronous reset
    input 	w,
    output 	z);	
	
//定义状态机状态
parameter	A = 3'd0,	
			B = 3'd1,	
			C = 3'd2,	
			D = 3'd3,	
			E = 3'd4,	
			F = 3'd5;

reg	[2:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk)begin
	if(reset)
		cur_state <= A;
	else
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	next_state = A;
	case(cur_state)
		A 		: 	next_state = w ? A : B;
		B 		: 	next_state = w ? D : C;
		C 		: 	next_state = w ? D : E;
		D 		:	next_state = w ? A : F;
		E 		:	next_state = w ? D : E;
		F 		:	next_state = w ? D : C;
		default	:	next_state = A;
	endcase
end	
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk)begin
	if(reset)
		z<= 1'b0;
	else
		case(next_state)				
			A 		:	z<= 1'b0;
		    B 		:	z<= 1'b0;
		    C 		:	z<= 1'b0;
		    D 		:	z<= 1'b0;
		    E 		:	z<= 1'b1;
		    F 		:	z<= 1'b1;
			default	:	z<= 1'b0;
		endcase
end
    
endmodule

 Q2a FSM 

【题目】:

        根据下面的状态转移图实现FSM。

【个人思路】:

        经典的有限状态机,直接用三段式就完事了。 注意这题目的状态转移和上图是有区别的。

module top_module (
    input 	clk,
    input 	reset,     // synchronous reset
    input 	w,
    output 	z);	
	
//定义状态机状态
parameter	A = 3'd0,	
			B = 3'd1,	
			C = 3'd2,	
			D = 3'd3,	
			E = 3'd4,	
			F = 3'd5;

reg	[2:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器

//三段式状态机第一段:同步时序描述状态转移						
always @(posedge clk)begin
	if(reset)
		cur_state <= A;
	else
		cur_state <= next_state;
end

//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出    
always@(*)begin
	next_state = A;
	case(cur_state)
		A 		: 	next_state = w ? B : A;
		B 		: 	next_state = w ? C : D;
		C 		: 	next_state = w ? E : D;
		D 		:	next_state = w ? F : A;
		E 		:	next_state = w ? E : D;
		F 		:	next_state = w ? C : D;
		default	:	next_state = A;
	endcase
end	
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk)begin
	if(reset)
		z<= 1'b0;
	else
		case(next_state)				
			A 		:	z<= 1'b0;
		    B 		:	z<= 1'b0;
		    C 		:	z<= 1'b0;
		    D 		:	z<= 1'b0;
		    E 		:	z<= 1'b1;
		    F 		:	z<= 1'b1;
			default	:	z<= 1'b0;
		endcase
end
    
endmodule

Q2b One-hot FSM equations

本题和此前的题目类似,使用独热码编码方式来描述上题中的状态机。

module top_module (
    input [5:0] y,
    input w,
    output Y1,
    output Y3
);
 
    parameter A = 3'd0, B = 3'd1, C = 3'd2;
    parameter D = 3'd3, E = 3'd4, F = 3'd5;
    
    assign Y1 = w & y[A];
    assign Y3 = ~w & (y[B] | y[C] | y[E] | y[F]);
 
endmodule

 Q2a FSM

【题目】:

本题需要实现一个如下图的状态机。

本状态机扮演一个仲裁电路,控制三个设备对于某种资源的访问权限。

每个设备通过置起 r[i] 信号为 1'b1 来表示对这种资源的请求。r[1],r[2].r[3]分别对应三个设备的请求信号。三个请求信号作为 FSM 的输入信号。

FSM 在没有任何请求时处于状态 A。当出现了一个或多项请求时,由 FSM 决定哪台设备获得资源的,并向其发出许可信号 g[i],置为 1'b1。g[i] 信号是 FSM 的输出信号。

本系统存在优先级,设备 1 拥有最高权限,设备 2 次之,设备 3 的权限最低。因此设备 3 只能在系统处于状态 A 的情况下才能获得资源访问权限。一旦设备获得 FSM 给出的许可信号后,将持续持有资源直至其将请求信号置低为止,请求信号为高期间不能被打断。

编写 Verilog 代码实现这个 FSM。分别使用两个 always 块来实现状态跳转以及状态触发器逻辑。输出逻辑可以使用 assign 连续赋值也可以使用 always 块实现,随你的便。状态编码方式也随你的便。

【个人思路】:

        状态转移图题目都给出来了,就照着这个写三段式的FSM就完事了。

module top_module (
    input clk,
    input resetn,    // active-low synchronous reset
    input [3:1] r,   // request
    output [3:1] g   // grant
); 

parameter	A = 4'b0001,	
			B = 4'b0010,
			C = 4'b0100,
			D = 4'b1000;

reg	[3:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器
						
always @(posedge clk)begin
	if(!resetn)
		cur_state <= A;
	else
		cur_state <= next_state;
end
  
always@(*)begin
	next_state = A;
	case(cur_state)
		A	: 	
			if(r[1])
				next_state = B;
			else if(r[2])
				next_state = C;
			else if(r[3])
				next_state = D;	
			else
				next_state = A;	
		B	:
			if(r[1])
				next_state = B;
			else 
				next_state = A;		
		C	:
			if(r[2])
				next_state = C;
			else 
				next_state = A;	
		D	: 
			if(r[3])
				next_state = D;
			else 
				next_state = A;	
		default	:	next_state = A;
	endcase
end

assign g[1] = (cur_state == B);
assign g[2] = (cur_state == C);
assign g[3] = (cur_state == D);

endmodule

Q2b: Another FSM

【题目】:

        考虑一个用于控制某种类型电机的有限状态机。FSM有来自电机的输入x和y,并产生控制电机的输出f和g。还有一个时钟输入称为clk和一个复位输入称为reset。

        FSM的工作方式如下:

        只要断言了复位输入,FSM就保持在开始状态,称为状态a。当复位信号被取消断言时,下一个时钟边缘之后,FSM必须将输出f设为1,持续一个时钟周期。然后,FSM必须监控x的输入。当x在三个连续的时钟周期中产生值1,0,1时,那么g应该在下一个时钟周期中设为1。当g = 1时,FSM必须监控y的输入。如果在最多两个时钟周期内y值为1,则FSM需要永久保持g = 1(即直到复位)。但是如果在两个时钟周期内y没有变为1,则FSM应该将g永久设置为0(直到复位)。

【个人思路】:

        状态转移图:

        

  • IDLE    : 初始状态,复位结束后跳转到状态OUT_F
  • OUT_F: 这个状态用来将f拉高一个时钟周期
  • S         : 序列“101”检测的初始状态,若检测到x为1,则跳转到状态S1;若检测到,则保持该状态
  • S1       : 若检测到x为0,则跳转到状态S10;若检测到x为1,则保持该状态
  • S10     : 若检测到x为1,则跳转到状态S101;若检测到x为0,则跳转到状态S
  • S101   : 此时已经检测到了序列“101”,需要判断接下来y是否变成了1:若检测到y为1,则跳转到状态P1,说明输出g应该永久拉高;若检测到y为0,说明第一个周期不满足条件,则跳转到状态JUG,在状态JUG在判断是否满足条件
  • P1       : 永远保持在此状态,用来输出永恒的g==1
  • JUG    : 若检测到y为1,则跳转到状态P1,说明输出g应该永久拉高;若检测到y为0,则跳转到状态P0,说明输出g应该永久拉拉低
  • P0       : 永远保持在此状态,用来输出永恒的g==0 
module top_module (
    input clk,
    input resetn,    // active-low synchronous reset
    input x,
    input y,
    output f,
    output g
);

parameter	IDLE  = 4'd0,
			OUT_F = 4'd1,
			S	  = 4'd2,
			S1	  = 4'd3,
			S10	  = 4'd4,
			S101  = 4'd5,
			P1	  = 4'd6,
			JUG	  = 4'd7,
			P0	  = 4'd8;

reg	[8:0]	cur_state	,				//定义现态寄存器 
			next_state	;   			//定义次态寄存器
						
always @(posedge clk)begin
	if(!resetn)
		cur_state <= IDLE;
	else
		cur_state <= next_state;
end
  
always@(*)begin
	next_state = IDLE;
	case(cur_state)
		IDLE	: 	next_state = OUT_F;	
		OUT_F	:	next_state = S;			
		S		:	next_state = x ? S1 : S;	
		S1		:	next_state = x ? S1 : S10;	
		S10		:	next_state = x ? S101 : S;	
		S101	:	next_state = y ? P1 : JUG;	
		P1		:	next_state = P1;
		JUG		:	next_state = y ? P1 : P0;
		P0		:	next_state = P0;	
		default	:	next_state = IDLE;
	endcase
end

assign f = (cur_state == OUT_F);
assign g = (cur_state == S101 || cur_state == JUG || cur_state == P1);

endmodule

        

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐