盡管接觸Verilog已經(jīng)兩年多了,但似乎沒有很系統(tǒng)地區(qū)學(xué)習(xí)過。一接觸的時候就在學(xué)習(xí)別人寫的程序,終于再次拿起書本,發(fā)現(xiàn)很多知識點,只是知道而已,卻不怎么熟悉。今天學(xué)習(xí)的一個重點就是狀態(tài)機。
1 有限狀態(tài)機 有限狀態(tài)機(Finite State Machine,FSM)在數(shù)字系統(tǒng)設(shè)計中應(yīng)用十分廣泛。根據(jù)狀態(tài)機的輸出是否與輸入有關(guān),可將狀態(tài)機分為兩大類:摩爾(Moore)型狀態(tài)機和米莉 (Mealy)型狀態(tài)機。Moore型狀態(tài)機的輸出僅與現(xiàn)態(tài)有關(guān);Mealy型狀態(tài)機的輸出不僅與現(xiàn)態(tài)有關(guān),而且和輸入也有關(guān)。 有限狀態(tài)機一般包括三個部分,其中組合邏輯部分包括狀態(tài)譯碼器和輸出譯碼器,狀態(tài)譯碼器確定狀態(tài)機的下一個狀態(tài),輸出譯碼器確定狀態(tài)機的輸出,狀態(tài)寄存器屬于時序邏輯部分,用來存儲狀態(tài)機的內(nèi)部狀態(tài)。 2 好的狀態(tài)機標準 好的狀態(tài)機的標準很多,最重要的幾個方面如下: 第一,狀態(tài)機要安全,是指FSM不會進入死循環(huán),特別是不會進入非預(yù)知的狀態(tài),而且由于某些擾動進入非設(shè)計狀態(tài),也能很快的恢復(fù)到正常的狀態(tài)循環(huán)中來。這里面有兩層含義。其一要求該FSM的綜合實現(xiàn)結(jié)果無毛刺等異常擾動,其二要求FSM要完備,即使受到異常擾動進入非設(shè)計狀態(tài),也能很快恢復(fù)到正常狀態(tài)。 第二,狀態(tài)機的設(shè)計要滿足設(shè)計的面積和速度的要求。 第三,狀態(tài)機的設(shè)計要清晰易懂、易維護。 在芯片設(shè)計中,對綜合結(jié)果評判的兩個基本標準為:面積和速度。“面積”是指設(shè)計所占用的邏輯資源數(shù)量;“速度”指設(shè)計在芯片上穩(wěn)定運行所能夠達到的最高頻率。兩者是對立統(tǒng)一的矛盾體,要求一個設(shè)計同時具備設(shè)計面積最小,運行頻率最高,這是不現(xiàn)實的。科學(xué)的設(shè)計目標應(yīng)該是:在滿足設(shè)計時序要求(包含對設(shè)計最高頻率的要求)的前提下,占用最小的芯片面積,或者在所規(guī)定的面積下,使設(shè)計的時序余量更大,頻率更高。另外,如果要求FSM安全,則很多時候需要使用full case的編碼方式,即將狀態(tài)轉(zhuǎn)移變量的所有向量組合情況都在FSM 中有相應(yīng)的處理,這經(jīng)常勢必意味著要多花更多的設(shè)計資源,有時也會影響FSM的頻率所以,上述的標準要綜合考慮,根據(jù)設(shè)計的要求進行權(quán)衡。 3 狀態(tài)機描述方法 狀態(tài)機描述時關(guān)鍵是要描述清楚幾個狀態(tài)機的要素,即如何進行狀態(tài)轉(zhuǎn)移,每個狀態(tài)的輸出是什么,狀態(tài)轉(zhuǎn)移的條件等。具體描述時方法各種各樣,最常見的有三種描述方式: (1)一段式:整個狀態(tài)機寫到一個always模塊里面,在該模塊中既描述狀態(tài)轉(zhuǎn)移,又描述狀態(tài)的輸入和輸出; (2)二段式:用兩個always模塊來描述狀態(tài)機,其中一個always模塊采用同步時序描述狀態(tài)轉(zhuǎn)移;另一個模塊采用組合邏輯判斷狀態(tài)轉(zhuǎn)移條件,描述狀態(tài)轉(zhuǎn)移規(guī)律以及輸出; (3)三段式:在兩個always模塊描述方法基礎(chǔ)上,使用三個always模塊,一個always模塊采用同步時序描述狀態(tài)轉(zhuǎn)移,一個always采用組合邏輯判斷狀態(tài)轉(zhuǎn)移條件,描述狀態(tài)轉(zhuǎn)移規(guī)律,另一個always模塊描述狀態(tài)輸出(可以用組合電路輸出,也可以時序電路輸出)。 一般推薦的FSM 描述方法是后兩種。因為FSM和其他設(shè)計一樣,最好使用同步時序方式設(shè)計,以提高設(shè)計的穩(wěn)定性,消除毛刺。狀態(tài)機實現(xiàn)后,一般來說,狀態(tài)轉(zhuǎn)移部分是同步時序電路而狀態(tài)的轉(zhuǎn)移條件的判斷是組合邏輯。 第二種描述方法同第一種描述方法相比,將同步時序和組合邏輯分別放到不同的always模塊中實現(xiàn),這樣做的好處不僅僅是便于閱讀、理解、維護,更重要的是利于綜合器優(yōu)化代碼,利于用戶添加合適的時序約束條件,利于布局布線器實現(xiàn)設(shè)計。在第二種方式的描述中,描述當前狀態(tài)的輸出用組合邏輯實現(xiàn),組合邏輯很容易產(chǎn)生毛刺,而且不利于約束,不利于綜合器和布局布線器實現(xiàn)高性能的設(shè)計。 第三種描述方式與第二種相比,關(guān)鍵在于根據(jù)狀態(tài)轉(zhuǎn)移規(guī)律,在上一狀態(tài)根據(jù)輸入條件判斷出當前狀態(tài)的輸出,從而在不插入額外時鐘節(jié)拍的前提下,實現(xiàn)了寄存器輸出。 4 狀態(tài)機的編碼 二進制編碼(Binary)、格雷碼(Gray-code)編碼使用最少的觸發(fā)器,較多的組合邏輯,而獨熱碼(One-hot)編碼反之。獨熱碼編碼的最大優(yōu)勢在于狀態(tài)比較時僅僅需要比較一個位,從而一定程度上簡化了比較邏輯,減少了毛刺產(chǎn)生的概率。由于CPLD更多地提供組合邏輯資源,而FPGA更多地提供觸發(fā)器資源,所以CPLD多使用二進制編碼或格雷碼,而FPGA多使用獨熱碼編碼。另一方面,對于小型設(shè)計使用二進制和格雷碼編碼更有效,而大型狀態(tài)機使用獨熱碼更高效。 5 避免從case語句中綜合出鎖存器 不完整的case語句會生成鎖存器。 解決方法: (1)在case語句中對變量賦初值。 always @ (state or A or B) begin q=0; case(state) s0: q=A & B; s1: q=A| B; endcase end (2)在case語句中增加default語句。 always @ (state or A or B) begin case(state) s0: q=A & B; s1: q=A| B; default: q=0; endcase end (3)在case語句中使用full_case綜合指令。 always @ (state or A or B) begin case(state) /* synthesis full_case*/ s0: q=A & B; s1: q=A| B; endcase end 附加說明:當case語句中的所有選擇項是互斥的,為了簡化電路,不出現(xiàn)優(yōu)先級解碼邏輯,可以使用 “synthesis parallel_case”綜合指令,減少不必要的邏輯資源。 6 狀態(tài)機例子 設(shè)計一個自動飲料售賣機,飲料10分錢,硬幣有5分和10分兩種,并考慮找零。 (1)點路變量分析:投入5分硬幣為一個變量,定義為A,為輸入;投入10分硬幣為一個變量,定義為B,為輸入;售貨機給出飲料為一變量,定義為Y,為輸出;售貨機找零為一變量,定義為Z,為輸出。 (2)狀態(tài)確定:電路共有兩個狀態(tài):狀態(tài)S0,表示未投入任何硬幣;狀態(tài)S1,表示投入了5分硬幣。 (3)設(shè)計過程:設(shè)當前為S0狀態(tài),當接收到5分硬幣時,轉(zhuǎn)換到S1狀態(tài),等待繼續(xù)投入硬幣;當接收到10分硬幣時,保持S0狀態(tài),彈出飲料,不找零。當前狀態(tài)為S1時,表示已經(jīng)有5分硬幣,若再接收5分硬幣,轉(zhuǎn)換到S0狀態(tài),彈出飲料,不找零;若接收到10分硬幣,轉(zhuǎn)換到S0狀態(tài),彈出飲料,找零。 
module machine(clk,rst,A,B,Y,Z);
input clk,rst,A,B;
output Y,Z;
parameter S0=2’b01;
parameter S1=2’b10;
reg Y,Z;
reg [1:0] ns,cs;
always @ (posedge clk or negedge rst)
begin
if(!rst)
cs<=2'd0;
else
cs<=ns;
end always @ (cs,A,B,rst)
begin
if(!rst)
begin
ns=2'd0;Y=1'd0;Z=1'd0;
end
else
begin
case(cs)
S0:
begin
if(A) //5
begin
ns=S1; Y=1'd0;Z=1'd0;
end
else if(B) //10
begin
ns=S0;Y=1'd1;Z=1'd0;
end
else
begin
ns=S0;Y=1'd0;Z=1'd0;
end
end
S1:
begin
if(A) //5
begin
ns=S0; Y=1'd1;Z=1'd0;
end
else if(B) //10
begin
ns=S0; Y=1'd1;Z=1'd1;
end
else
begin
ns=S1; Y=1'd0;Z=1'd0;
end
end
endcase
end
end endmodule
|