- 狀態機設計
- 狀態機概念
- 什麼是 RTL級好的FSM 描述?
- “一段式”寫法
- “二段式”寫法
- “二段式”描述方法
- “二段式”缺點以及解決辦法
- 程式碼
- “三段式”寫法
- “三段式”的描述方法
- “三段式”的優點
- 三種寫法的優缺點
- 狀態機概念
- 獨熱碼
- Moore和Mealy
- 兩種狀態機的區別:主要是輸出是否與輸入有關
- 例題分析
- (1)Moore型狀態機程式碼
- (2)Mealy型狀態機程式碼
狀態機設計
狀態機概念
-
狀態機的本質
對具有邏輯順序或時序規律事件的一種描述方法。所有具有邏輯順序和時序規律的事情都適合用狀態機描述
-
狀態機的應用思路
- 從狀態變數入手如果一個電路具有時序規律或者邏輯順序,我們就可以自然而然地規劃出狀態,從這些狀態入手,分析每個狀態的輸入,狀態轉移和輸出,從而完成電路功能;
- 明確電路的輸出的關係,這些輸出相當於狀態的輸出,回溯規劃每個狀態和狀態轉移條件與狀態輸入,無論那種思路,使用狀態機的目的都是要控制某部分電路,完成某種具有邏輯順序或時序規律的電路設計。
-
狀態機的基本描述方式
狀態機的基本要素:狀態、輸出、輸入
基本描述方式:狀態轉移圖、狀態轉移表、HDL語言描述
什麼是 RTL級好的FSM 描述?
- FSM要安全,穩定性高。(優先順序最高,最需要保證)
- 不會進入死迴圈,不會進入非預知的狀態
- 要求該FSM的綜合實現結果,無毛刺等異常擾動
- 要求狀態機要完備這可能就會使用”full case”的編碼方式,使得所有的情況都有所處理,勢必會花費更多的設計資源,有時會影響FSM的頻率
- FSM速度快,滿足設計的頻率要求。
- FSM面積小,滿足設計的面積要求。
- FSM設計要清晰易懂、易維護。
“一段式”寫法
只利用一個always組合塊,把需要寫出的此刻的狀態的不斷變化,輸出也糅合進去。
在描述當前狀態時要考慮下個狀態的輸出,整個程式碼不清晰
- 不利於維護修改
- 不利於附加約束
- 不利於綜合器和佈局佈線器對設計的最佳化
- 另外,這種描述相對於兩段式描述比較冗長。對複雜的狀態機來說,一段式要比二段式冗長大約80%到150%左右
module top_module(clk, reset, in, out);
input clk;
input reset; // 同步的復位,高電平有效
input in;
output out;
reg out;
//此時的狀態
reg state;
parameter A = 0;
parameter B = 1;
always @(posedge clk) begin
if (reset) begin
state = B;
out <= B;
end else begin
case (state)
B:
if(in) begin
state <= B;
out <= 1'd1;
end
else begin
state <= A;
out <= 1'd0;
end
A:
if(in) begin
state <= A;
out <= 1'd0;
end
else begin
state = B;
out <= 1'd1;
end
default: begin
state = B;
out <= 1'd1;
end
endcase
end
end
endmodule
“二段式”寫法
“二段式”描述方法
- 一個always組合塊使用組合邏輯電路描述下一個狀態的變化
- 一個always時序塊使用時序邏輯電路描述此刻狀態轉移
- 使用assign或者always組合塊使用組合邏輯電路描述輸出狀態的變化
“二段式”缺點以及解決辦法
其輸出一般使用組合邏輯描述,而組合邏輯易產生毛刺等不穩定因素。
解決辦法
- 兩段式 FSM 描述方法,如果時序允許插入,一個額外的時鐘節拍,可以有效的解決毛刺現象,
- 但有的無法新增節拍,使用三段式的描述方法
程式碼
module top_module(
input clk,
input reset, // 同步復位,高電平有效
input in,
output out);//
parameter A=0, B=1;
reg state, next_state;
// 此時的狀態的變化
always @(posedge clk) begin
if(reset) state <= B;
else state <= next_state;
end
// 使用組合邏輯電路來判斷下一個狀態的變化
always @(*) begin // This is a combinational always block
// State transition logic
case(state)
A: next_state = in?A:B;
B: next_state = in?B:A;
endcase
end
// 使用組合邏輯電路產生輸出
// Output logic
// assign out = (state == ...);
assign out = (state == B);
endmodule
雖然下面這種只用了一個always塊,但其基本思想依舊是”二段式”的思想
module top_module(clk, reset, in, out);
input clk;
input reset; // 同步的復位,高電平有效
input in;
output out;
//此時的狀態
reg state;
parameter A = 0;
parameter B = 1;
always @(posedge clk) begin
if (reset) begin
state = B;
end else begin
case (state)
B:
if(in)
state = B;
else
state = A;
A:
if(in)
state = A;
else
state = B;
default:
state = B;
endcase
end
end
assign out = state;
endmodule
“三段式”寫法
“三段式”的描述方法
- 一個always時序塊使用時序邏輯電路描述此刻狀態轉移
- 一個always組合塊使用組合邏輯電路描述下一個狀態的變化
- 在使用一個always時序塊去描述輸出
“三段式”的優點
- 使FSM做到了同步暫存器輸出
- 消除了組合邏輯輸出的不穩定與毛刺的隱患
- 更利於時序路徑分組
- 在FPGA/CPLD 等可程式設計邏輯器件上的綜合與佈局佈線效果更佳
module top_module(
input clk,
input reset, // 同步復位,高電平有效
input in,
output reg out);
parameter A=0, B=1;
reg state, next_state;
// 此時的狀態的變化
always @(posedge clk) begin
if(reset) state <= B;
else state <= next_state;
end
// 使用組合邏輯電路來判斷下一個狀態的變化
always @(*) begin // This is a combinational always block
// State transition logic
case(state)
A: next_state = in?A:B;
B: next_state = in?B:A;
endcase
end
// 使用組合邏輯電路產生輸出
// Output logic
// assign out = (state == ...);
always@(posedge clk) begin
if(reset) out <= 1'b1;
else if(next_state==A) out <= 1'b0;
else out <= 1'b1;
end
endmodule
endmodule
三種寫法的優缺點
-
“一段式”和”三段式”比較
- 一段式(不符合思維模式)
- 必須要綜合考慮現態在何種狀態轉移條件下會進入哪些次態
- 然後在每個現態的case分支下分別描述每個次態的輸出
- 三段式
- 只需指定case敏感表為次態暫存器
- 然後直接在每個次態的case分支中描述該狀態的輸出即可,根本不用考慮狀態轉移條件
- 一段式(不符合思維模式)
-
“二段式”和“三段式”比較
雖然可以改善輸出的時序條件還能避免組合電路的毛刺是更為推薦的描述方式,電路設計不是一成不變地,在某些情況下,兩段式結構比三段式結構更有優勢。
- 兩段式建模用狀態暫存器,分割了兩部分組合邏輯(狀態轉移條件組合邏輯,輸出組合邏輯),因此電路時序路徑較短,可以獲得較高的效能;
- 三段式建模從輸入到暫存器狀態輸出的路徑上,要經過兩部分組合邏輯(狀態轉移條件組合邏輯,輸出組合邏輯)從時序上,這兩部分組合邏輯完全可以看為一體,這條路徑的組合邏輯就比較繁雜,該路徑的時序相對緊張。
獨熱碼
parameter IDLE = 3'b000;//初始狀態
parameter STATE1=3'b001;//狀態1
parameter STATE2=3'b010;//狀態2
parameter STATE3=3'b100;//狀態3
利用這種編碼方式,雖然比起自然二進位制編碼多使用了觸發器,但其使用組合邏輯更簡單,所以一般編寫狀態機更加推薦此種編碼方式。
可以使用下面這種程式設計方法,節省邏輯電路。
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;
// State transition logic: Derive an equation for each state flip-flop.
//當然組合邏輯電路也可以使用always@(*)
assign next_state[A] = ((state[A]|state[C])&(~in));
assign next_state[B] = (state[A]|state[B]|state[D])∈
assign next_state[C] = (state[B]|state[D])&(~in);
assign next_state[D] = state[C]∈
// Output logic:
assign out = state[D];
endmodule
Moore和Mealy
這篇文章介紹的不錯,可以看完這篇文章之後再看下面介紹
關於摩爾型狀態機與米利型狀態機的區別_CrazyUncle的部落格-CSDN部落格_摩爾狀態機與米利狀態機
Mealy型與Moore
- 摩爾型:輸出只與狀態暫存器的狀態有關(大部分狀態機使用此型別,以上三種寫法也主要是這種型別)
- 米粒型:輸出與狀態暫存器和輸入有關
兩種狀態機的區別:主要是輸出是否與輸入有關
Moore型狀態機
- 而Mealy型狀態機與輸入有關,於是在輸入變化之時,輸出就會立刻發生變化
Mealy型狀態機
- Moore型狀態機由於與輸入無關,所以可以但看到,其輸出與輸入之間總是差一個時鐘週期,也就是說,當輸入變化時,輸出將會在下個週期才會有所體現
以上說明moore型狀態機和mealy型狀態機的區別所用的是兩段式狀態機,也就是狀態輸出都是採用組合邏輯輸出。如果狀態輸出採用暫存器輸出,則需要在輸出端加一個暫存器,這樣會多消耗一個時鐘週期,但是功能不會發生錯誤。
-
“三段式”程式碼需要注意
三段式狀態機中為了不消耗一個額外的時鐘週期,採用next_state作為判斷條件,但是如果採用mealy型狀態機,則此時的輸入將會和next_state一起作為判斷條件來判斷輸出,這樣就會發生功能錯誤,如下圖所示
正確的做法是將輸出_temp與當前輸入再用組合邏輯輸出,如下圖所示,這樣功能不會發生錯誤。但是其實這種做法是多此一舉的,因為這樣做輸出還是組合邏輯,沒有采用暫存器輸出,並且這條通路的時序相對緊張,這麼一搞的話其實應該用二段式更好一些
例題分析
檢測“101”序列,題目要求重疊檢測(比如對於序列“101010”,重疊檢測會輸出兩次高電平,不重疊檢測會輸出一次高電平)
Moore型
Mealy型
(1)Moore型狀態機程式碼
-
例題程式碼——三段式
//三段式FSM module top_module ( input clk, input aresetn, // Asynchronous active-low reset input x, output reg z ); parameter S0 = 2'd0, S1 = 2'd1, S2 = 2'd2, S3 = 2'd3; reg [1:0] current_state; reg [1:0] next_state; always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin current_state <= S0; end else begin current_state <= next_state; end end always@(*)begin case(current_state) S0:begin next_state = x ? S1 : S0; end S1:begin next_state = x ? S1 : S2; end S2:begin next_state = x ? S3 : S0; end S3:begin next_state = x ? S1 : S2; end default:begin next_state = S0; end endcase end always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin z <= 1'b0; end else begin if(next_state == S3)begin z <= 1'b1; end else begin z <= 1'b0; end end end endmodule
-
設計模板
設計方法:就上面的三段式
*//Moore型狀態機的Verilog HDL一般結構:* **module** state_moore (reset,clk,High,Low,Cold,Heat); **input** reset,clk,High,Low; **output** **<**output1**>**, **<**output2**>**, **<**output3**>**; *//定義狀態和名稱* **parameter** st1_**<**name_state**>** **=** st1_**<**encode**>**; **parameter** st2_**<**name_state**>** **=** st2_**<**encode**>**; **parameter** st3_**<**name_state**>** **=** st3_**<**encode**>**; *//定義狀態暫存器* **reg** Cold,Heat; **reg**[1**:**0] state, next_state; *//狀態機的第一段採用同步時序描述狀態轉移* **always** @(**posedge** clk) **begin:** SYNC_PROC **if**(reset **==** 1) state **<=** st1_**<**name_state**>**; **else**state **<=** next_state; **end** *//狀態機的第二段採用組合邏輯判斷狀態轉移條件* **always** @(state **or** **<**input_1**>** **or** **<**input_2**>**) **begin:** NEXT_STATE_DECODE next_state **<=** state; **case** (state) st1: **beginif** (**<**input_1**>** **==** 1) next_state **<=** st2; **end** st2: **beginif** (**<**input_2**>** **==** 1) next_state **<=** st1; **end** st3: **begin** ...... **end endcaseend** *//狀態機的第三段描述狀態輸出,只與當前狀態有關 //但真實編寫程式碼時,應該使用 //always@(posedge clk)去延時一個時鐘來表示狀態的轉移 //並使用暫存器型別的out_temp在內部當成<output>的緩衝 //並使用assign output = out_temp;來輸出* always @(state) begin: OUTPUT_DECODE if(state == st1_<name_state>) begin<output1><=1’b1; <output2><=1’b0; end else if(state == st2_<name_state>) begin <output1><=1’b0; <output2><=1’b1; end end //而如果使用三段式的話,這一部分可以這麼寫 always @(posedge clk) begin if(next_state == st1_<name_state>) begin<output1><=1’b1; <output2><=1’b0; end else if(next_state == st2_<name_state>) begin <output1><=1’b0; <output2><=1’b1; end end
(2)Mealy型狀態機程式碼
-
例題程式碼——二段式
module top_module ( input clk, input aresetn, // Asynchronous active-low reset input x, output z ); parameter S0 = 2'd0, S1 = 2'd1, S2 = 2'd2; reg [1:0] current_state; reg [1:0] next_state; always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin current_state <= S0; end else begin current_state <= next_state; end end always@(*)begin case(current_state) S0:begin next_state = x ? S1 : S0; end S1:begin next_state = x ? S1 : S2; end S2:begin next_state = x ? S1 : S0; end default:begin next_state = S0; end endcase end assign z = ((current_state == S2) && (x == 1'b1)) ? 1'b1 : 1'b0; /* //second way always@(*)begin case(current_state) S0:begin z = 1'b0; end S1:begin z = 1'b0; end S2:begin z = x; end endcase end */ endmodule
-
例題程式碼——三段式
module top_module ( input clk, input aresetn, // Asynchronous active-low reset input x, output z ); parameter S0 = 2'd0, S1 = 2'd1, S2 = 2'd2; reg [1:0] current_state; reg [1:0] next_state; reg z_temp; always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin current_state <= S0; end else begin current_state <= next_state; end end always@(*)begin case(current_state) S0:begin next_state = x ? S1 : S0; end S1:begin next_state = x ? S1 : S2; end S2:begin next_state = x ? S1 : S0; end default:begin next_state = S0; end endcase end always@(posedge clk or negedge aresetn)begin if(aresetn == 1'b0)begin z_temp <= 1'b0; end else begin if(next_state == S2)begin z_temp <= 1'b1; end else begin z_temp <= 1'b0; end end end assign z = z_temp == 1'b1 && x == 1'b1; endmodule
-
設計模板
*//Mealy型狀態機的Verilog HDL一般結構:* **module** state_moore (reset,clk,High,Low,Cold,Heat); **input** reset,clk,High,Low; **output** **<**output1**>**, **<**output2**>**, **<**output3**>**; *//定義狀態和名稱* **parameter** st1_**<**name_state**>** **=** st1_**<**encode**>**; **parameter** st2_**<**name_state**>** **=** st2_**<**encode**>**; **parameter** st3_**<**name_state**>** **=** st3_**<**encode**>**; *//定義狀態暫存器* **reg** Cold,Heat; **reg**[1**:**0] state, next_state; *//狀態機的第一段採用同步時序描述狀態轉移* **always** @(**posedge** clk) **begin:** SYNC_PROC **if**(reset **==** 1) state **<=** st1_**<**name_state**>**; **else**state **<=** next_state; **end** *//狀態機的第二段採用組合邏輯判斷狀態轉移條件* **always** @(state **or** **<**input_1**>** **or** **<**input_2**>**) **begin:** NEXT_STATE_DECODE next_state **<=** state; **case** (state) st1: **beginif** (**<**input_1**>** **==** 1) next_state **<=** st2; **end**st2: **beginif** (**<**input_2**>** **==** 1) next_state **<=** st1; **end**st3: **begin** ...... **end endcase end** *//狀態機的第三段描述狀態輸出,與當前狀態和輸入訊號都有關* **always** @(state **or** t1 **or** t2 **or** t3) **begin:** OUTPUT_DECODE **if** (state **==** st3_**<**name**>** **and** **<**input1**>** **==** 1) **<output>**_i **<=** 1’b1; **else<output>**_i **<=**1’b0; **end //三段式的描述方法 always** @(posegde clk) **begin:** OUTPUT_DECODE **if** (next_state **==** st3_**<**name**>** **and** **<**input1**>** **==** 1) **<output>**_i **<=** 1’b1; **else <output>**_i **<=**1’b0; **end**