FPGA學習筆記(七)——FSM(Finite State Machine,有限狀態機)設計
FPGA設計中,最重要的設計思想就是狀態機的設計思想!狀態機的本質就是對具有邏輯順序和時序規律的事件的一種描述方法,它有三個要素:狀態、輸入、輸出:狀態也叫做狀態變量(比如可以用電機的不同轉速作為狀態),輸出指在某一個狀態的特定輸出,輸入指狀態機中進入每個狀態的條件。根據狀態機的輸出是否和輸入有關,可分為摩爾(Moore)型狀態機和米勒型(Mealy)狀態機:摩爾型狀態機的輸出只取決於當前狀態,而米勒型狀態機的輸出不僅取決於當前狀態,還與當前輸入有關。通常,我們描述狀態機有三種方法:狀態轉移圖、狀態轉移表、HDL描述,狀態轉移圖直觀,設計用,而HDL語言方便描述,實現時用。
那麽,如何用HDL描述一個好的狀態機呢?主要有以下四點:安全、穩定性高
實例.檢測“Hello”序列狀態機
1、功能:在輸入一串字符中檢測“Hello”序列,檢測到後將led狀態進行翻轉;
2、根據設計的FSM狀態轉移圖(visio繪制):
3、一段式描述法:在一個always塊裏既描述狀態轉移,又描述狀態的輸入和輸出;
verilog代碼如下:
//檢測“Hello”後led狀態翻轉 module check_hello( input clk, //50M時鐘信號 input rst, //低電平復位 input [7:0]asci, //字符輸入 output reg led //控制led ); //狀態寄存器 reg [4:0]NS; //nextstate //狀態獨熱編碼 localparam CHECK_H = 5‘b0_0001, CHECK_e = 5‘b0_0010, CHECK_la = 5‘b0_0100, CHECK_lb = 5‘b0_1000, CHECK_o = 5‘b1_0000; //一段式狀態機 always@(posedge clk,negedge rst) if(!rst)begin NS <= CHECK_H; led <= 1‘b1; //led熄滅 end else begin case(NS) CHECK_H: begin if(asci == "H") NS <= CHECK_e; else NS <= CHECK_H; end CHECK_e: begin if(asci == "e") NS <= CHECK_la; else NS <= CHECK_H; end CHECK_la: begin if(asci == "l") NS <= CHECK_lb; else NS <= CHECK_H; end CHECK_lb: begin if(asci == "l") NS <= CHECK_o; else NS <= CHECK_H; end CHECK_o: begin if(asci == "o") led <= ~led; else led <= led; NS <= CHECK_H; end default //排除任意情況,增強FSM安全性 begin NS <= CHECK_H; led <= 1‘b1; end endcase end endmodule
testbench測試文件如下:
`timescale 1ns/1ps `define clk_period 20 module check_hello_tb(); reg clk; //50M時鐘信號 reg rst; //低電平復位 reg [7:0]asci; //字符輸入 wire led; //控制led //例化測試模塊 check_hello check_hello_test( .clk(clk), //50M時鐘信號 .rst(rst), //低電平復位 .asci(asci), //字符輸入 .led(led) //控制led ); //產生50M時鐘信號 initial clk = 1; always #(`clk_period / 2)clk <= ~clk; //開始測試 initial begin rst = 0; //系統復位 asci = 3‘bx; #(`clk_period * 2); rst = 1; #(`clk_period); asci = "H"; #(`clk_period); asci = "e"; #(`clk_period); asci = "l"; #(`clk_period); asci = "l"; #(`clk_period); asci = "o"; #(`clk_period); asci = "2"; #(`clk_period); asci = "e"; #(`clk_period); asci = "h"; #(`clk_period); asci = "l"; #(`clk_period); $stop; end endmodule
測試結果如下,可以看到,剛開始輸入數據是任意數據,FSM為CHECK_H狀態,led輸出高電平,保持熄滅;當檢測到Hello序列時,led輸出低電平,狀態翻轉:
狀態轉移圖如下,可以看到按照預定設計執行:
綜合出來的電路圖如下,可以看到FSM實現的重點在於狀態寄存器,耗費資源很少,由綜合報告也可看出:
在一段式描述方法中可以看到,雖然一個alaways塊就可以解決問題,但描述不清晰,不利於維護修改,並且不利用附加約束,不利於綜合其和布局布線器對設計的優化;
4、兩段式描述法:一個always塊描述狀態轉移,另一個always塊描述狀態判斷轉移條件
verilog代碼如下:
//檢測“Hello”後led狀態翻轉 module check_hello( input clk, //50M時鐘信號 input rst, //低電平復位 input [7:0]asci,//字符輸入 output reg led //控制led ); //狀態寄存器 reg [4:0]NS; //nextstate reg [4:0]CS; //currentstate //狀態獨熱編碼 localparam CHECK_H = 5‘b0_0001, CHECK_e = 5‘b0_0010, CHECK_la = 5‘b0_0100, CHECK_lb = 5‘b0_1000, CHECK_o = 5‘b1_0000; //兩段式狀態機 //第一個always塊描述狀態轉移 always@(posedge clk,negedge rst) if(!rst) CS <= CHECK_H; else CS <= NS; //狀態轉移到下一狀態 //第二個always塊描述狀態輸出以及判斷狀態轉移 always@(*) case(CS) CHECK_H: begin led <= 1‘b1; if(asci == "H") NS <= CHECK_e; else NS <= CHECK_H; end CHECK_e: begin led <= 1‘b1; if(asci == "e") NS <= CHECK_la; else NS <= CHECK_H; end CHECK_la: begin led <= 1‘b1; if(asci == "l") NS <= CHECK_lb; else NS <= CHECK_H; end CHECK_lb: begin led <= 1‘b1; if(asci == "l") NS <= CHECK_o; else NS <= CHECK_H; end CHECK_o: begin if(asci == "o") led <= 1‘b0; else led <= 1‘b1; NS <= CHECK_H; end default begin NS <= CHECK_H; led <= 1‘b1; end endcase endmodule
FPGA學習筆記(七)——FSM(Finite State Machine,有限狀態機)設計