1. 程式人生 > >FPGA--有限狀態機(FSM)的設計

FPGA--有限狀態機(FSM)的設計

學習有限狀態機(FSM)的設計

什麼是狀態機?簡單來說,就是通過不同的狀態遷移來完成一些特定的順序邏輯。
FSM定義:
一個有限狀態機是一個裝置,或者是一個裝置模型,具有有限數量的狀態,它可以在任何給定的時間根據輸入進行操作,使得一個狀態變換到另一個狀態,或者是使一個輸入或者一種行為的發生。一個有限狀態機在任何瞬間只能處在一種狀態。
它的優點:
1.程式設計快速簡單,2.易於除錯,3.很少的計算開銷,4.直覺性,5.靈活性。

狀態機圖示
圖1-1 狀態機圖例

無論是在C\C++等的高階語言程式設計,還是用硬體描述語言(verilog HDL)中都會涉及到相關的知識點,學習下來作為自己的知識儲備確是一件幸事。

狀態機設計

1、狀態機分類

如果時序邏輯的輸出不但取決於狀態還取決於輸入,稱為 Mealy 狀態機。如果輸入變化,這類狀態機的輸出可能在一個時鐘週期的中間跟著改變。

1
圖1-2 Mealy 狀態機

而有些時序邏輯電路的輸出只取決於當前狀態,這樣的電路就稱為 Moore狀態機。這類狀態機輸出和輸入成隔離狀態:輸出將在剩餘的時鐘週期內保持穩定(為一個常數),即使輸入在時鐘週期內變化。

2
圖1-3 Moore狀態機

2、有限狀態機的狀態編碼

常用的編碼有三種:二進位制編碼、 格雷碼 (Gray) 、獨熱碼 (one-hot) 編碼。另外,還可以自定義編碼,比如在高速設計中以狀態編碼作為輸出。

二進位制碼

二進位制碼從一個狀態轉換到相鄰狀態時,可能有多個位元位發生變化,易產生中間狀態轉移問題,狀態機的速度也要比採用其它編碼方式慢。

格雷碼

格雷碼兩個相鄰的碼值僅有一位就可區分,這將會減少電路中相鄰物理訊號線同時變化的情況,因而可以減少電路中的電噪聲。Johnson碼也有同樣的特點,但是要用較多的位數。

獨熱碼

獨熱碼(One-hot)指對任意給定的狀態,狀態暫存器中只有l位為1,其餘位都為0。n狀態的有限狀態機需要n個觸發器,但這種有限狀態機只需對暫存器中的一位進行譯碼,簡化了譯碼邏輯電路,額外觸發器佔用的面積可用譯碼電路省下來的面積抵消。當設計中加入更多的狀態時,譯碼邏輯沒有變得更加複雜,有限狀態機的速度僅取決於到某特定狀態的轉移數量,而其它型別有限狀態機在狀態增加時速度會明顯下降。獨熱碼還具有設計簡單、修改靈活、易於綜合和除錯等優點。獨熱碼相對於二進位制碼,速度快但佔用面積大。

自定義編碼

自定義編碼相對靈活。在設計高速電路時,常常有必要使狀態機的輸出與時鐘幾乎完全同步。有一個辦法是把狀態變數(也可能是狀態變數中的幾位)直接用作輸出,稱為輸出編碼的狀態指定。屬於 Moore 狀態機。但這種方法也有缺點,就是開關的維持時間必須與狀態維持的時間一致,需要增加狀態才能實現。
設計高速狀態機時,在輸出邏輯 G 後面再加一組與時鐘同步的暫存器輸出流水線暫存器,讓 G 所有的輸出訊號在下一個時鐘跳變沿時同時存入暫存器組,即完全同步地輸出,把這種輸出稱為流水線化的輸出。

編碼總結

從設計大小的角度來看,在小設計中可以考慮使用 Gray 碼或 one-hot。大設計中,由於現在技術進步幾乎不用考慮邏輯資源不夠的問題,可以考慮使用one-hot 編碼以提高速度。至於順序二進位制編碼,一般不予考慮。但是要達到最佳效能,需要使用更高階的編碼演算法,針對給定的狀態機進行分析。
從 FPGA/CPLD 的角度來看,由於 CPLD 提供更多的組合邏輯資源,而 FPGA提供更多的觸發器資源,所以 CPLD 多使用 Gray 碼,而 FPGA 多使用 one-hot編碼。

3、狀態機的設計準則

1 基本準則

  (1) 一個Verilog模組至多描述一個有限狀態機。這樣不僅可以簡化狀態的定義、修改和除錯,還可以利用一些EDA工具來協助設計。
  (2) 使用引數給狀態賦值,而不是用巨集定義(‘define)。因為’define巨集定義在編譯時自動替換整個設計中所定義的巨集,而parameter僅僅定義模組內部的引數,定義的引數不會與模組外的其它狀態機混淆。
  (3) 用always模組寫組合邏輯時,採用阻塞賦值,而在always塊中建立時序電路時,用非阻塞賦值。這樣才能保證有限狀態機綜合前和綜合後模擬的一致性。

2 一般步驟

  1. 邏輯抽象得出狀態轉換圖
      就是把給出的一個實際邏輯關係表示為時序邏輯函式,可以用狀態轉換表來描述,也可以用狀態轉換圖來描述。這就需要:
      1) 分析給定的邏輯問題,確定輸入變數、輸出變數以及電路的狀態數。通常是取原因(或條件)作為輸入變數,取結果作為輸出變數。
      2) 定義輸入、輸出邏輯狀態的含義,並將電路狀態順序編號。
      3) 按照要求列出電路的狀態轉換表或畫出狀態轉換圖。
    這樣,就把給定的邏輯問題抽象到了一個時序邏輯函數了。

  2. 狀態化簡
      如果在狀態轉換圖中出現這樣兩個狀態,它們在相同的輸入轉換到同一狀態去,並得出一樣的輸出,則稱為等價狀態。顯然等價狀態是重複的,一合併為一個電路的狀態數越少,儲存電路也就越簡單。狀態化簡的目的就是在於將等價狀態儘可能地的合併,以得到最簡的狀態轉換圖。

  3. 狀態分配

  4. 選定觸發器的型別並求出狀態方程、驅動方程和輸出方程。

  5. 按照方程得出邏輯圖
      用Verilog HDL來描述有限狀態機,可以充分發揮硬體描述語言的抽象建模能力,使用always塊語句和case(if)等條件語句及賦值語句即可方便實現。具體的邏輯化簡、邏輯電路到觸發器對映均可有計算機自動完成。步驟中的2、4、5不再需要人為干預,使電路設計工作得到簡化,效率也有很大的提高。
    ———————————–節選之夏宇聞老師的Verilog HDL數字系統塊設計教程(第2版)

4、狀態機的描述方法

(1)一段式
使用一個always模組包含整個狀態機他不符合將時序和組合邏輯分開描述的Coding Style,而且在描述當前狀態時要考慮下個狀態的輸出,整個程式碼不清晰,不利於維護修改,並且不利於附加約束,不利於綜合器和佈局佈線器對設計的優化。一般來說,一段式程式碼長度會比兩段式冗長大約80%到150%左右。

一段式
圖1-4 一段式狀態機

(2)兩段式
使用兩個always模組,其中一個always模組採用同步時序的方式描述狀態轉移,而另一個模組採用組合邏輯的方式判斷狀態轉移條件、描述狀態轉移規律,稱兩段式FSM描述方法;為了使FSM描述清晰簡潔、易於維護、易於附加時序約束,使綜合器和佈局佈線器更好的優化設計,推薦使用兩段式FSM描述方法。請注意,雖然下一狀態暫存器 NS 為暫存器型別,但是在兩段式 FSM 的判斷狀態轉移條件的 always 塊中,實際對應的真實硬體電路是純組合邏輯電路。

兩段式
圖1-5 兩段式狀態機

(3)三段式
使用三個 always 模組,一個 always 模組採用同步時序的方式描述狀態轉移,一個採用組合邏輯的方式判斷狀態轉移條件、描述狀態轉換規律,第三個 always模組使用同步時序電路描述每個狀態的輸出,稱三段式寫法。

三段式
圖1-6 三段式狀態機

綜上,得出三種描述 FSM 方法的比較表

比較表

5、狀態機模板

一段式

###兩段式

兩段式

二段式格式(組合邏輯輸出)
always @ (posedge clk or negedge nrst)
if (!nrst)
  CS <= IDLE;
else
  CS <=NS;
  
//敏感列表必須完整:當前狀態及所有相關組合邏輯輸入
always @ (nrst or CS or i1 or i2) begin
//第二段 always 中,組合邏輯電平要維持超過一個 clock,模擬時注意。
  NS = 3'b0; //此處賦值一般有三種方式:(1) X,(2) IDLE (3)不改變
        //注:因為模擬時“X”視為“don’t care”,所以賦值“X”適合 debug
        //而實現時,賦值“IDLE”或者“0”
  ERROR_out;
  case (CS)
    IDLE: begin
      IDLE_out;
      if (~i1) NS = IDLE;
      if (i1 && i2) NS = S1;
      if (i1 && ~i2) NS = ERROR;// 裡面判斷條件一定要包含所有情況(即 i1 和
      i2 的四種情況)!可以用 else 保證包含完全。
    end
    S1: begin
      S1_out;
      if (~i2) NS = S1;
      if (i2 && i1) NS = S2;
      if (i2 && (~i1)) NS = ERROR;
    end
    S2: begin
      S2_out;
      if (i2) NS = S2;
      if (~i2 && i1) NS = IDLE;
      if (~i2 && (~i1)) NS = ERROR;
    end
    ERROR: begin
      ERROR_out;
      if (i1) NS = ERROR;
      if (~i1) NS = IDLE;
    end
  endcase
end
task S1_out;//對於輸出一般用組合邏輯描述,比較簡便的方法是用 task 將輸出封裝起來。
  {o1,o2,err} = 3'b100;
Endtask

三段式(一)

三段式
圖1-7 三段式狀態機示例
module fsm_cc8_3r(output reg y1, y2, y3,input jmp, go, sk0, sk1, clk, rst_n);
parameter S0 = 4'b0000,
      S1 = 4'b0001,
      S2 = 4'b0010,
      S3 = 4'b0011,
      S4 = 4'b0100,
      S5 = 4'b0101,
      S6 = 4'b0110,
      S7 = 4'b0111,
      S8 = 4'b1000,
      S9 = 4'b1001;
reg [3:0] state, next;
  
//第一個程序,同步時序 always 塊,格式化描述次態暫存器遷移到現態暫存器
always @(posedge clk or negedge rst_n) //非同步復位
  if (!rst_n)
    state <= S0;
  else
    state <= next; //注意,使用的是非阻塞賦值
//第二個程序,組合邏輯塊,判斷狀態轉移條件,電平觸發,敏感列表要完備
always @(state or jmp or go or sk0 or sk1) 
 begin
    next = 4'bS0; //要初始化,使得系統復位後能進入正確的狀態,和 case 中的 default 作用相當,但
            //不完全等同於把其放在 default 裡賦值。因為根據順序執行語句,這裡對所有未
            //在case 語句中賦值的變數賦值。

    case (state) //每一狀態的條件判斷要完備!
    S0 : if (!go) next = S0; //阻塞賦值
      else if (jmp) next = S3;
      else next = S1;
    S1 : if (jmp) next = S3;
        else next = S2;
    S2 : if (jmp) next = S3;
        else next = S9;
    S3 : if (jmp) next = S3;
        else next = S4;
    S4 : if (jmp) next = S3;
        else if (sk0 && !jmp) next = S6;
        else next = S5;
    S5 : if (jmp) next = S3;
        else if (!sk1 && !sk0 && !jmp) next = S6;
        else if (!sk1 && sk0 && !jmp) next = S7;
        else if ( sk1 && !sk0 && !jmp) next = S8;
        else next = S9;
    S6 : if (jmp) next = S3;
        else if (go && !jmp) next = S7;
        else next = S6;
    S7 : if (jmp) next = S3;
        else next = S8;
    S8 : if (jmp) next = S3;
        else next = S9;
    S9 : if (jmp) next = S3;
        else next = S0;
  endcase
 end

//第三個程序,同步時序 always 模組,格式化描述次態暫存器輸出
always @(posedge clk or negedge rst_n)
  if (!rst_n) 
  begin //初始化
    y1 <= 1'b0;
    y2 <= 1'b0;
    y3 <= 1'b0;
  end
  else begin //可以防止 latch,如上述。
    y1 <= 1'b0;
    y2 <= 1'b0;
    y3 <= 1'b0;
  case (next) //注意是下一個狀態,即預判
    S0, S2, S4, S5 : ; // default outputs
    S7 : y3 <= 1'b1; //注意是非阻塞邏輯
    S1 : y2 <= 1'b1;
    S3 : begin
      y1 <= 1'b1;
      y2 <= 1'b1;
    end
    S8 : begin
      y2 <= 1'b1;
      y3 <= 1'b1;
    end
    S6, S9 : begin
      y1 <= 1'b1;
      y2 <= 1'b1;
      y3 <= 1'b1;
    end
  endcase
 end
endmodule

三段式格式 (二)–onehot 編碼

parameter S0 = 0, S1 =1, S2 =2, S3 =3, S4 =4, S5= 5, S6 = 6, S7 = 7, S8 = 8, S9 =9;
reg [9:0] current_state, next_state;

//第一個程序,同步時序 always 模組,格式化描述次態暫存器遷移到現態暫存器
always @ (posedge clk or negedge rst_n) //非同步復位
  if(!rst_n) 
    begin
    current_state <=0;
    current_state[S0] <=1’b1;
    end
  else
    current_state <= next_state; //注意,使用的是非阻塞賦值
    
//第二個程序,組合邏輯 always 塊,判斷狀態轉移條件, onehot 需要加綜合約束
full case
always @ (current_state or input1 or input2 ) //電平觸發,包括所有組合邏輯輸入
  begin
    next_state = 0; //初始化(0 或 IDLE),使系統復位後能進入正確的狀態
    case(1’b1) // synthesis parallel_case full_case
      current_state[S0]: if(...)
      next_state [S1]= 1’b1; //阻塞賦值
      current_state[S1]: if(...)
      next_state [S2]= 1’b1; //阻塞賦值
      ...
    endcase
  end
 
//第三個程序,同步時序 always 模組,格式化描述次態暫存器輸出
always @ (posedge clk or negedge rst_n)
  if(!rst_n) begin
    Out1<= 1’b0;
    Out2<= 1’b0;
    Out3<= 1’b0;
  end
  else begin
    Out1<= 1’b0;
    Out2<= 1’b0;
    Out3<= 1’b0;
    case(1’b1) //注意是下一個狀態,即預判
      next_state [S0]: out1 <= 1'b1; //注意是非阻塞邏輯
      next_state [S1]: out2 <= 1'b1;
      …
    endcase //注意,沒有 default 選項了
  end

總結

有限狀態機是實現高效率、高可靠性數字系統的重要全知途徑,使用Verilog HDL描述有限狀態機時可以有不同的狀態編碼方式和描述風格,實際應用中應根據具體情況和要求來選擇,編碼方式的選擇跟器件結構和狀態數目有關,描述風格則推薦使用兩段式和三段式,應該儘量避免使用一段式。