FPGA的特點(diǎn)是并行執(zhí)行,但如果需要處理一些具有前后順序的事件,就需要使用狀態(tài)機(jī)。狀態(tài)機(jī)是一種用于處理具有前后順序的事件的計(jì)算機(jī)模型,包含現(xiàn)態(tài)、條件、動(dòng)作和次態(tài)四個(gè)要素,它可以將一個(gè)復(fù)雜的控制流程分解成多個(gè)互相獨(dú)立的狀態(tài),從而簡(jiǎn)化設(shè)計(jì)過(guò)程并提高了系統(tǒng)的可靠性和性能。本文將對(duì)FPGA狀態(tài)機(jī)進(jìn)行詳細(xì)介紹,幫助大家了解狀態(tài)機(jī)的設(shè)計(jì)和應(yīng)用。
一、FPGA狀態(tài)機(jī)基礎(chǔ)
1、基礎(chǔ)概念
FPGA狀態(tài)機(jī)是一種能夠描述對(duì)象在運(yùn)行周期內(nèi)的所有狀態(tài),以及從一個(gè)狀態(tài)到另一個(gè)狀態(tài)轉(zhuǎn)換的過(guò)程的抽象模型。狀態(tài)機(jī)可歸納為4個(gè)要素,即現(xiàn)態(tài)、條件、動(dòng)作、次態(tài)。
①現(xiàn)態(tài):當(dāng)前所處的狀態(tài)。
②條件:當(dāng)一個(gè)條件被滿足,將會(huì)觸發(fā)一個(gè)動(dòng)作,或者執(zhí)行一次運(yùn)行狀態(tài)的變化。
③動(dòng)作:條件滿足后執(zhí)行的動(dòng)作。動(dòng)作不是必需的,也可以直接遷移到新?tīng)顟B(tài)而不進(jìn)行任何動(dòng)作。
④次態(tài):條件滿足后要跳轉(zhuǎn)到的新?tīng)顟B(tài)。其中,“次態(tài)”是相對(duì)于“現(xiàn)態(tài)”而言的,一旦被跳轉(zhuǎn)后,“次態(tài)”就轉(zhuǎn)變成新的“現(xiàn)態(tài)”了。
2、狀態(tài)機(jī)分類(lèi)
通常情況下,F(xiàn)PGA狀態(tài)機(jī)一般有兩種類(lèi)型:
- Moore型狀態(tài)機(jī):下一狀態(tài)只由當(dāng)前狀態(tài)決定 。
- Mealy 型狀態(tài)機(jī):下一狀態(tài)不但與當(dāng)前狀態(tài)有關(guān),還與當(dāng)前輸入值有關(guān) 。
由于Mealy型狀態(tài)機(jī)的輸出與輸入有關(guān),輸出信號(hào)很容易出現(xiàn)毛刺,所以一般采用Moore型狀態(tài)機(jī)。
(1)Mealy狀態(tài)機(jī)
輸出邏輯不但取決于當(dāng)前“狀態(tài)”還取決于“輸入”,如圖所示。
(2)Moore狀態(tài)機(jī)
輸出邏輯僅僅取決于當(dāng)前狀態(tài),且與當(dāng)前時(shí)刻的輸入無(wú)關(guān),如圖所示。
二、FPGA狀態(tài)機(jī)實(shí)現(xiàn)方式
FPGA狀態(tài)機(jī)的描述方式主要分為3種,分別是一段式、兩段式、三段式。
1、一段式狀態(tài)機(jī)
一段式狀態(tài)機(jī)使用1個(gè)always塊,把狀態(tài)跳轉(zhuǎn)和寄存器輸出邏輯都寫(xiě)在一起,其輸出是寄存器輸出,無(wú)毛刺,但是這種方式代碼較混亂,邏輯不清晰,難于修改和調(diào)試,應(yīng)該盡量避免使用。
下面給出一個(gè)一段式的Mealy狀態(tài)機(jī)示例:
input clk,
input rst_n,
input [1:0] inp,
output reg outp
);
// 定義狀態(tài)
localparam STATE_0 = 0,
STATE_1 = 1,
STATE_2 = 2,
STATE_3 = 3;
// 定義狀態(tài)寄存器和初始狀態(tài)
reg [1:0] state_r;
// 初始化狀態(tài)寄存器
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
state_r<= STATE_0;
end else begin
case (state_reg)
STATE_0: begin
if (inp == 2'b00) begin
state_r <= STATE_0;
outp <= 0;
end else if (inp == 2'b01) begin
state_r <= STATE_1;
outp <= 1;
end else if (inp == 2'b10) begin
state_r <= STATE_2;
outp <= 0;
end else begin
state_r <= STATE_3;
outp <= 1;
end
end
STATE_1: begin
if (inp == 2'b00) begin
state_r <= STATE_1;
outp <= 1;
end else if (inp == 2'b01) begin
state_r <= STATE_2;
outp <= 0;
end else if (inp == 2'b10) begin
state_r <= STATE_3;
outp <= 1;
end else begin
state_r <= STATE_0;
outp <= 0;
end
end
STATE_2: begin
if (inp == 2'b00) begin
state_r <= STATE_2;
outp <= 0;
end else if (inp == 2'b01) begin
state_r <= STATE_3;
outp <= 1;
end else if (inp == 2'b10) begin
state_r <= STATE_0;
outp <= 0;
end else begin
state_r <= STATE_1;
outp <= 1;
end
end
STATE_3: begin
if (inp == 2'b00) begin
state_r <= STATE_3;
outp <= 1;
end else if (inp == 2'b01) begin
state_r <= STATE_0;
outp <= 0;
end else if (inp == 2'b10) begin
state_r <= STATE_1;
outp <= 1;
end else begin
state_reg <= STATE_2;
outp <= 0;
end
end
endcase
end
end
endmodule
2、二段式狀態(tài)機(jī)
二段式狀態(tài)機(jī)使用2個(gè)always塊,都是時(shí)序邏輯,其中一個(gè)always塊用于寫(xiě)狀態(tài)機(jī)的狀態(tài)跳轉(zhuǎn)邏輯,另一個(gè)always塊用于寫(xiě)當(dāng)前狀態(tài)下的寄存器輸出邏輯。這種方式邏輯代碼清晰,易于調(diào)試和理解,是比較推薦的一個(gè)方式。
下面給出一個(gè)二段式的Moore狀態(tài)機(jī)示例:
input clk,
input rst_n,
output reg out_reg
);
// 狀態(tài)寄存器和下一個(gè)狀態(tài)寄存器
reg [1:0] state_r;
// 狀態(tài)定義
parameter IDLE = 2'b00;
parameter STATE1 = 2'b01;
parameter STATE2 = 2'b10;
parameter STATE3 = 2'b11;
// always @(posedge clk or negedge rst_n) 時(shí)序邏輯代碼塊,實(shí)現(xiàn)狀態(tài)跳轉(zhuǎn)邏輯
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
state_r <= IDLE;
end else begin
case(state_r)
IDLE: begin
state_r <= STATE1;
end
STATE1: begin
state_r <= STATE2;
end
STATE2: begin
state_r <= STATE3;
end
STATE3: begin
state_r <= IDLE;
end
endcase
end
end
// always @(*) 時(shí)序邏輯代碼塊,實(shí)現(xiàn)狀態(tài)輸出邏輯
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
out_reg <= 1'b0;
end else begin
case(state_r)
IDLE: begin
out_reg <= 1'b0;
end
STATE1: begin
out_reg <= 1'b1;
end
STATE2: begin
out_reg <= 1'b1;
end
STATE3: begin
out_reg <= 1'b0;
end
endcase
end
end
endmodule
3、三段式狀態(tài)機(jī)
三段式狀態(tài)機(jī)使用3個(gè)always塊,其中一個(gè)組合always塊用于寫(xiě)狀態(tài)機(jī)的狀態(tài)跳轉(zhuǎn)邏輯,一個(gè)時(shí)序always塊用于緩存狀態(tài)寄存器,另一個(gè)always塊用于寫(xiě)當(dāng)前狀態(tài)下的寄存器輸出邏輯。這種方式邏輯代碼清晰,易于調(diào)試和理解,也是比較推薦的一個(gè)方式。
input clk,
input rst_n,
input [1:0] inp,
output reg outp
);
// 定義狀態(tài)
localparam STATE_0 = 0,
STATE_1 = 1,
STATE_2 = 2,
STATE_3 = 3;
// 定義狀態(tài)寄存器和初始狀態(tài)
reg [1:0] state_r, next_state ;
// 定義狀態(tài)寄存器
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
state_r <= STATE_0;
end else begin
state_r <= next_state;
end
end
// 定義狀態(tài)轉(zhuǎn)移邏輯
always @(*) begin
case (state_r)
STATE_0: begin
if (inp == 2'b00) begin
next_state = STATE_0;
end else if (inp == 2'b01) begin
next_state = STATE_1;
end else if (inp == 2'b10) begin
next_state = STATE_2;
end else begin
next_state = STATE_3;
end
end
STATE_1: begin
if (inp == 2'b00) begin
next_state = STATE_1;
end else if (inp == 2'b01) begin
next_state = STATE_2;
end else if (inp == 2'b10) begin
next_state = STATE_3;
end else begin
next_state = STATE_0;
end
end
STATE_2: begin
if (inp == 2'b00) begin
next_state = STATE_2;
end else if (inp == 2'b01) begin
next_state = STATE_3;
end else if (inp == 2'b10) begin
next_state = STATE_0;
end else begin
next_state = STATE_1;
end
end
STATE_3: begin
if (inp == 2'b00) begin
next_state = STATE_3;
end else if (inp == 2'b01) begin
next_state = STATE_0;
end else if (inp == 2'b10) begin
next_state = STATE_1;
end else begin
next_state = STATE_2;
end
end
endcase
end
// 定義輸出邏輯
always @(*) begin
case (state_r)
STATE_0: outp = 0;
STATE_1: outp = 1;
STATE_2: outp = 0;
STATE_3: outp = 1;
endcase
end
endmodule
注意:組合邏輯代碼中,if語(yǔ)句和case語(yǔ)句必須寫(xiě)滿,否則容易形成latch,導(dǎo)致實(shí)際運(yùn)行出問(wèn)題。
三、狀態(tài)機(jī)的編碼方式
1、獨(dú)熱碼
獨(dú)熱碼(One-hot)是一種狀態(tài)編碼方式,其特點(diǎn)是對(duì)于任意給定的狀態(tài),狀態(tài)寄存器中只有1位為1,其余位都為0。使用獨(dú)熱碼可以簡(jiǎn)化譯碼邏輯電路,因?yàn)闋顟B(tài)機(jī)只需對(duì)寄存器中的一位進(jìn)行譯碼,同時(shí)可用省下的面積抵消額外觸發(fā)器占用的面積。相比于其他類(lèi)型的有限狀態(tài)機(jī),加入更多的狀態(tài)時(shí),獨(dú)熱碼的譯碼邏輯并不會(huì)變得更加復(fù)雜,速度僅取決于到某特定狀態(tài)的轉(zhuǎn)移數(shù)量。
此外,獨(dú)熱碼還具有諸如設(shè)計(jì)簡(jiǎn)單、修改靈活、易于綜合和調(diào)試等優(yōu)點(diǎn)。但值得注意的是,相對(duì)于二進(jìn)制碼,獨(dú)熱碼速度更快但占用面積較大。
input clk,
output reg [3:0] state_out
);
localparam STATE_A = 4'b0001;
localparam STATE_B = 4'b0010;
localparam STATE_C = 4'b0100;
localparam STATE_D = 4'b1000;
reg [3:0] current_state, next_state;
always @(posedge clk) begin
current_state <= next_state; // 當(dāng)時(shí)鐘上升沿到來(lái)時(shí)更新?tīng)顟B(tài)
end
always @(*) begin
case (current_state)
STATE_A: next_state = STATE_B;
STATE_B: next_state = STATE_C;
STATE_C: next_state = STATE_D;
STATE_D: next_state = STATE_A;
default: next_state = STATE_A; // 默認(rèn)情況下返回初始狀態(tài)
endcase
end
assign state_out = current_state; // 將當(dāng)前狀態(tài)作為輸出
endmodule
2、格雷碼
格雷碼是一種相鄰的兩個(gè)碼組之間僅有一位不同的編碼方式。在格雷碼中,相鄰的兩個(gè)碼組之間僅有一位不同,這種編碼方式可以用于實(shí)現(xiàn)相鄰的兩個(gè)狀態(tài)之間只有一位不同的狀態(tài)機(jī);
FPGA 中的狀態(tài)機(jī)通常需要高速運(yùn)行,因此使用格雷碼可以減少狀態(tài)轉(zhuǎn)換的開(kāi)銷(xiāo),并提高時(shí)序性能。
input clk,
output reg [3:0] state_out
);
localparam G0 = 4'b0000;
localparam G1 = 4'b0001;
localparam G2 = 4'b0011;
localparam G3 = 4'b0010;
reg [3:0] current_state, next_state;
always @(posedge clk) begin
current_state <= next_state; // 當(dāng)時(shí)鐘上升沿到來(lái)時(shí)更新?tīng)顟B(tài)
end
always @(*) begin
case (current_state)
G0: next_state = G1;
G1: next_state = G3;
G2: next_state = G0;
G3: next_state = G2;
default: next_state = G0; // 默認(rèn)情況下返回初始狀態(tài)
endcase
end
assign state_out = current_state; // 將當(dāng)前狀態(tài)作為輸出
endmodule
3、普通二進(jìn)制碼
FPGA狀態(tài)機(jī)可以用普通二進(jìn)制碼表示,不同狀態(tài)按照二進(jìn)制數(shù)累加表示,是常用的一種方式,仿真調(diào)試時(shí),狀態(tài)顯示清晰,易于理解代碼。
input clk,
output reg [1:0] state_out
);
localparam STATE_A = 2'b00;
localparam STATE_B = 2'b01;
localparam STATE_C = 2'b10;
reg [1:0] current_state, next_state;
always @(posedge clk) begin
current_state <= next_state; // 當(dāng)時(shí)鐘上升沿到來(lái)時(shí)更新?tīng)顟B(tài)
end
always @(*) begin
case (current_state)
STATE_A: next_state = STATE_B;
STATE_B: next_state = STATE_C;
STATE_C: next_state = STATE_A;
default: next_state = STATE_A; // 默認(rèn)情況下返回初始狀態(tài)
endcase
end
assign state_out = current_state; // 將當(dāng)前狀態(tài)作為輸出
endmodule
4、格雷碼與普通二進(jìn)制碼互轉(zhuǎn)
- 二進(jìn)制碼:一個(gè)n位的二進(jìn)制碼可以表示2^n種狀態(tài),它有2^(n-1)個(gè)相鄰狀態(tài)之間只有一個(gè)位不同。
- 格雷碼:一個(gè)n位的格雷碼可以表示2^n種狀態(tài),它有2^(n-1)個(gè)相鄰狀態(tài)之間只有一位變化。
(1)二進(jìn)制碼轉(zhuǎn)換成格雷碼
假設(shè)當(dāng)前的狀態(tài)用二進(jìn)制碼表示為B,那么它所對(duì)應(yīng)的格雷碼G可以按照以下方式計(jì)算得出:
G = B xor (B >> 1)
其中,">>"表示右移操作,"xor"表示異或操作。具體來(lái)說(shuō),我們先將B右移一位,再與原來(lái)的B進(jìn)行異或運(yùn)算,就可以得到對(duì)應(yīng)的格雷碼G。
(2)格雷碼轉(zhuǎn)換成二進(jìn)制碼
假設(shè)當(dāng)前的狀態(tài)用格雷碼表示為G,那么它所對(duì)應(yīng)的二進(jìn)制碼B可以按照以下方式計(jì)算得出:
B = G xor (G >> 1)
與二進(jìn)制碼轉(zhuǎn)換成格雷碼的方法類(lèi)似,我們先將G右移一位,再與原來(lái)的G進(jìn)行異或運(yùn)算,就可以得到對(duì)應(yīng)的二進(jìn)制碼B。
個(gè),下面給出“4位格雷碼與4位二進(jìn)制碼轉(zhuǎn)換”的示例代碼:
input [3:0] bin,
output reg [3:0] gray
);
always @* begin
gray[0] = bin[0];
gray[1] = bin[1] ^ bin[0];
gray[2] = bin[2] ^ bin[1];
gray[3] = bin[3] ^ bin[2];
end
endmodule
注意用來(lái)轉(zhuǎn)換格雷碼時(shí),如果輸入的格雷碼不是有效的格雷碼,輸出的結(jié)果將會(huì)是無(wú)意義的。
四、總結(jié)
以上總結(jié)了FPGA狀態(tài)機(jī)中有關(guān)的知識(shí)點(diǎn),大家可以參考下,建議狀態(tài)機(jī)的寫(xiě)法一般用二段式或三段式,代碼邏輯清晰,易于理解和調(diào)試,同時(shí)需要注意always@(*)塊中的組合邏輯,使用if和case語(yǔ)句都要寫(xiě)滿,否則綜合后容易形成latch,導(dǎo)致上板與仿真結(jié)果不一致。
評(píng)論
查看更多