verilog 狀態機的坑(話機屏幕)

注:本文僅針對個人項目,做個人筆記使用,描述較爲混亂。勿怪。

使用狀態機寫一個超簡單的話機屏幕顯示,卻浪費了超多時間。摸爬滾打,從凌晨3點開始調試bug,一直調試到現在凌晨6點,才發現自己對狀態機認知很是不足。記得上課時或者各種網上狀態機介紹中都會講,“三段式狀態機好,兩段式狀態機也不錯,儘量不要用一段式狀態機,一段狀態機太亂,條理不清楚!”其實也怪自身沒有仔細思考。牢騷到此爲止。

各類狀態機沒有好壞之分,用途不同、選擇不同。

由於本人較爲喜歡使用兩段式狀態機,在此僅對兩段式做說明,三段式類似。

兩段式狀態機

請注意兩段式狀態機的循環條件。
第一個always的判斷條件是:always@ (posedge clk, negedge rst)
第二個always的判斷條件應該是:always@ (state_current)
該原因是因爲,第二個狀態機要在state_current 改變完後才能進行內部判斷。如果第二個always也使用always@ (posedge clk, negedge rst),會導致第一個和第二個並行進入,因此第二個狀態機內部使用的case(state_current)中的state_current,和第一個中的state_current一致,這會導致狀態機非正常跳轉。

但是,如果在第二個always中使用always@ (state_current),並不一定能達到預期效果。例如我剛纔調試BUG的狀態機。想要在clk的時鐘下進行循環檢測,但是卻在第二個always中寫入各類條件,而各類判斷條件改變的是state_next。這樣,第二個always只會進入一次,因爲第一個always只使用了:

	always @(posedge clk_1M, negedge rst)
	begin
		if(!rst)
			state_current <= idle;
		else
			state_current <= state_next;
	end

結果就是導致第二個always只會進入一次。隨後state_current不改變。

以下爲個人所寫狀態機,簡單話機屏幕顯示,按鍵輸入號碼(最多11位),按OK鍵撥打。按DELE刪除上一個數字,按EXIT退出(清除所有已輸入的按鍵或退出撥打界面),代碼很簡單,已經過板級驗證,不詳細解釋。

/*
* Title:    <話機屏幕顯示>
* description:
* @author:  fateszs
* @data:    2018.7.06
*
*/
module phone(
	input rst,
	input clk_1M,
	input valid,
	input [3:0]key,
	output [43:0]p_number,
	output [3:0] p_time,
	output [1:0] p_state
);

	parameter OK	= 4'd10;		//OK
	parameter DELE	= 4'd11;		//dele
	parameter EXIT	= 4'd12;		//exit
	
	parameter IDLE 	= 	3'd0;
	parameter FIGURE 	= 	3'd1;
	parameter JUDGE 	= 	3'd2;			//判斷key有效信號
	parameter CHOICE 	= 	3'd3;			//選擇相應的功能
	parameter DELETE	= 	3'd4;
	parameter CONNECT = 	3'd5;
	
	reg [2:0] state_cu, state_ne;
	reg [43:0]number;
	reg [3:0] time_r;
	reg [1:0] p_state_r;
//	always @(posedge clk_1M, negedge rst)
//	begin
//		if(!rst)
//			state_cu <= IDLE;
//		else
//			state_cu <= state_ne;
//	end
	always @(posedge clk_1M, negedge rst)
	begin
		if(!rst)
		begin
			state_cu <= IDLE;
			time_r <= 4'd0;
			p_state_r <= 2'd0;
			number[43:0] <= 44'd0;
		end
		else
			case(state_cu)
				IDLE:
				begin
					time_r <= 4'd0;
					number[43:0] <= 44'd0;
					if(valid && key <=4'd9 && key>= 4'd0)					//輸入電話號碼第一位
					begin
						time_r <=  4'd1; 
						state_cu <= FIGURE;
					end
				end		
				
				FIGURE:
				begin
					
					case (time_r)
						4'd1 : number[3:0] <= key;
						4'd2 : number[7:4] 	<= key;
						4'd3 : number[11:8] 	<= key;
						4'd4 : number[15:12] <= key;
						4'd5 : number[19:16] <= key;
						4'd6 : number[23:20] <= key;
						4'd7 : number[27:24] <= key;
						4'd8 : number[31:28] <= key;
						4'd9 : number[35:32] <= key;
						4'd10 : number[39:36] <= key;
						4'd11 : number[43:40] <= key;
					endcase
					state_cu <= JUDGE;
				end
				
				JUDGE :
				begin
					if(valid)
						 state_cu <= CHOICE ;
				end
				
				CHOICE :
				begin
					if(key == DELE )
						state_cu <= DELETE ;
					else if(key == OK)
						state_cu <= CONNECT;
					else if(key == EXIT)
						state_cu <= IDLE;
					else if(key <= 4'd9 && key>= 4'd0)
					begin
						if (time_r == 4'd11)
							state_cu <= JUDGE;
						else
						begin
							time_r <= time_r + 4'd1; 
							state_cu <= FIGURE;
						end
					end
					else 
						state_cu <= JUDGE;
				end
				
				DELETE :
				begin
					if(time_r == 4'd1)							
						state_cu <= IDLE;
					else 
					begin
						time_r <= time_r - 4'd1;
						state_cu <= JUDGE;
					end
				end
				
				CONNECT:
				begin
					if(valid && key == EXIT)
						state_cu <= IDLE;
					//	cnt_time <= 0;						//加計時中斷
				end
				
			endcase
	end

	assign p_number = number;
	assign p_time = time_r ;
//	assign p_state = p_state_r;
	
endmodule


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章