1. 程式人生 > 其它 >Vivado中呼叫FIFO,用三段式狀態機改進

Vivado中呼叫FIFO,用三段式狀態機改進

  狀態機全稱是有限狀態機(Finite State Machine、FSM),是表示有限個狀態以及在這些狀態之間的轉移和動作等行為的數學模型。

  狀態機可根據控制訊號按照預先設定的狀態進行狀態轉移,這就出現瞭如何對狀態進行有效編碼的問題。編碼方式,最簡單的就是直接使用二進位制編碼進行表示,除此之外還有使用格雷碼、獨熱碼。獨熱碼,每一個狀態均使用一個暫存器,在與狀態比較時僅僅需要比較一位,相比其他譯碼電路簡單;格雷碼,所需暫存器數與二進位制碼一樣,譯碼複雜,但相鄰位只跳動一位,一般用於非同步多時鐘域多 bit 位的轉換,如非同步 FIFO;二進位制碼,最為常見的編碼方式,所用暫存器少,譯碼較複雜。按照 FPGA 廠商給的建議,選擇哪一種編碼格式是要根據狀態機的複雜度、器件型別以 及從非法狀態中恢復出來的要求來確定。在使用不同的編碼格式生成出來的 RTL 檢視中可以看出二進位制比獨熱碼使用更少的暫存器。二進位制用 7 個暫存器就可以實現 100 個狀態的 狀態機,但是獨熱碼就需要 100 個暫存器。但是另一方面,雖然獨熱碼使用更多的暫存器但是其組合邏輯相對簡單。一般在 CPLD 中,由於提供較多的組合邏輯資源而推薦使用前者, 而在 FPGA 中提供較多的時序邏輯而推薦使用後者。

  狀態機描述方式,可分為一段式、兩段式以及三段式。 一段式,整個狀態機寫到一個 always 模組裡面。在該模組中既描述狀態轉移,又描述狀態的輸入和輸出。 兩段式,用兩個 always 模組來描述狀態機。其中一個 always 模組採用同步時序描述狀態轉移,另一個模組採用組合邏輯判斷狀態轉移條件,描述狀態轉移規律及其輸出。 三段式,在兩個 always 模組描述方法基礎上,使用三個 always 模組。一個 always 模組採用同步時序描述狀態轉移,一個 always 採用組合邏輯判斷狀態轉移條件,描述狀態轉移規律,另一個 always 模組描述狀態輸出(可以用組合電路輸出,也可以時序電路輸出)。

  編寫狀態機還應主要注意的事項是,為了避免不必要的鎖存器生成,需要窮舉所有狀態對應的輸出動作,或者使用 default 來定義未定義狀態動作;在定義狀態時,推薦使用引數定義 localparam 或 parameter,這樣可以在編寫時狀態更清晰且不容易出錯,也方便修改。 在復位或者跑飛能回到初始態或者預定態,所以在設計中要有非同步或者同步復位來確保狀態機上電有個初始態。

  三段式狀態機模板:

//第一個always塊,描述現態到次態的狀態轉移
always@(posedge clk or negedge rstn)begin
  if(~rstn)
    current_state <= IDLE;
  
else current_state <= next_state; end
//第二個程序,組合邏輯always模組,描述狀態轉移條件判斷
always@(*)begin
  next_state = x;   //要初始化,使得系統復位後能進入正確的狀態
  case(current_state)
      S1: if(...)
          next_state = S2; //阻塞賦值
      S2: if(...)
          next_state = S3; //阻塞賦值
      ……
  endcase
end
//第三個程序,時序邏輯always模組,描述次態暫存器輸出
always@(posedge clk or negedge rstn)begin
  ...//初始化
  case(next_state)
      S1:
          out1 <= 1'b1; //注意是非阻塞邏輯
      S2:
          out2 <= 1'b1;
      default:... //default 的作用是免除綜合工具綜合出鎖存器
  endcase
end

  本文對之前的FIFO測試模組用三段式狀態機進行修改,使程式碼思路更加清晰:



module fifo_test(
    input                               clk                        ,
    input                               rstn                       ,
    output             [   7:0]         dout                        
    );
wire                                    full                       ;//例化FIFO
wire                                    empty                      ;
reg                                     wr_en                      ;
reg                                     rd_en                      ;
reg                    [   7:0]         din                        ;
    fifo u_fifo(
    .clk                               (clk                       ),
    .srst                              (!rstn                     ),
    .din                               (din                       ),
    .wr_en                             (wr_en                     ),
    .rd_en                             (rd_en                     ),
    .dout                              (dout                      ),
    .full                              (full                      ),
    .empty                             (empty                     )
);

localparam idle = 3'b001, write = 3'b010, read = 3'b100; reg                    [   2:0]         current_state              ; reg                    [   2:0]         next_state                 ;
always @(posedge clk ) begin                                        //第一段,描述狀態轉移     if(!rstn)         current_state<=idle;     else         current_state<=next_state; end
always @(*) begin                                                   //第二段,判斷狀態轉移條件,描述狀態轉移規律     case (current_state)         idle:             if(empty)                 next_state=write;             else                 next_state=idle;         write:             if(full)                 next_state=read;             else                 next_state=write;         read:             if(empty)                 next_state=write;             else                 next_state=read;         default: current_state=idle;     endcase end
always @(posedge clk ) begin                                        //第三段,描述狀態輸出     if(!rstn)begin         wr_en<=0;         rd_en<=0;         din<=0;     end     else case (next_state)         idle:begin             wr_en<=0;             rd_en<=0;             din<=0;         end         write:begin             wr_en<=1;             din<=din+1'b1;             rd_en<=0;         end         read:begin             wr_en<=0;             din<=0;             rd_en<=1;         end         default: wr_en<=0;     endcase end endmodule
 

  利用獨熱碼進行編碼,共有三個狀態,分別是空閒狀態,寫狀態,讀狀態。第一段是時序邏輯always塊,描述當前狀態到下一狀態的轉移;第二段是組合邏輯always塊,判斷狀態轉移條件,描述狀態轉移規律;第三段是時序邏輯always塊,描述狀態輸出,注意別落了default。