注:本文僅針對個人項目,做個人筆記使用,描述較爲混亂。勿怪。
使用狀態機寫一個超簡單的話機屏幕顯示,卻浪費了超多時間。摸爬滾打,從凌晨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