日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数字集成电路设计-12-状态机的四种写法

發布時間:2023/12/14 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数字集成电路设计-12-状态机的四种写法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引言

在實際的數字電路設計中,狀態機是最常用的邏輯,而且往往是全部邏輯的核心部分,所以狀態機的質量,會在比較大的程度上影響整個電路的質量。

本小節我們通過一個簡單的例子(三進制脈動計數器)來說明一下狀態機的4中寫法。


1,模塊功能

由于我們的目的在于說明狀態機的寫作方式,所以其邏輯越簡單有利于理解。就是一個簡單的脈動計數器,每個三個使能信號輸出一個標示信號。


2,一段式

狀態機的寫法,一般有四種,即一段式,兩段式,三段式,四段式。對于一段式的寫法,整個狀態機的狀態轉移、轉移條件、對應狀態的輸出都寫在一個always塊里,故稱‘一段式’。那么,脈動計數器狀態機的一段式寫法該怎么寫呢?如下所示:


/* * file : fsm1.v * author: Rill * date : 2014-05-11 */module Mfsm1 ( clk, rst,enable, done );input wire clk; input wire rst; input wire enable; output reg done;parameter s_idle = 4'd0; parameter s_1 = 4'd1; parameter s_2 = 4'd2; parameter s_3 = 4'd3;reg [3:0] state;always @(posedge clk) beginif(rst)begindone <=1'b0;state <= s_idle;endelsebegincase(state)s_idle:beginif(enable)state <= s_1;done <= 1'b0;end s_1:beginif(enable)state <= s_2;done <= 1'b0;end s_2:beginif(enable)beginstate <= s_3;done <= 1'b1;endelsebegindone <= 1'b0;endends_3:beginstate <= s_idle;done <= 1'b0;enddefault:beginstate <= s_idle;done <= 1'b0;endendcaseend endendmodule

3,兩段式

狀態機的另外一種寫法是‘兩段式’的。兩段式的寫法,整個狀態機由兩個always塊組成,第一個塊只負責狀態轉移,第二個塊負責轉移條件和對應狀態的輸出。其中第一個塊是時序邏輯,第二個塊是組合邏輯。脈動計數器狀態機的兩段式寫法又是怎樣的呢?


/* * file : fsm2.v * author: Rill * date : 2014-05-11 */module Mfsm2 ( clk, rst,enable, done );input wire clk; input wire rst; input wire enable; output reg done;parameter s_idle = 4'd0; parameter s_1 = 4'd1; parameter s_2 = 4'd2; parameter s_3 = 4'd3;reg [3:0] current_state; reg [3:0] next_state;always @(posedge clk) beginif(rst)begincurrent_state <= s_idle;endelsebegincurrent_state <= next_state;end endalways @(*) begincase(current_state)s_idle:beginif(enable)next_state = s_1;done = 1'b0;end s_1:beginif(enable)next_state = s_2;done = 1'b0;end s_2:beginif(enable)next_state = s_3;done = 1'b0;ends_3:beginnext_state = s_idle;done = 1'b1;enddefault:beginnext_state = s_idle;done = 1'b0;endendcase endendmodule


4,三段式

從上面可以看出,兩段式的寫法是從一段式發展而來的,將一段式的寫法中將狀態轉移部分提取出來,作為一個獨立的always塊,就變成了兩段式。按照這個思路繼續推進,如果將兩段式的第二個塊中的轉移條件提取出來,也作為一個獨立的塊,就變成了‘三段式’,三段式的寫法中,狀態轉移塊是時序邏輯,轉移條件塊是組合邏輯,對應狀態的輸出是時序邏輯。那么,脈動計數器狀態機的三段式寫法是怎樣的呢?


/* * file : fsm3.v * author: Rill * date : 2014-05-11 */module Mfsm3 ( clk, rst,enable, done );input wire clk; input wire rst; input wire enable; output reg done;parameter s_idle = 4'd0; parameter s_1 = 4'd1; parameter s_2 = 4'd2; parameter s_3 = 4'd3;reg [3:0] current_state; reg [3:0] next_state;always @(posedge clk) beginif(rst)begincurrent_state <= s_idle;endelsebegincurrent_state <= next_state;end endalways @(*) begincase(current_state)s_idle:beginif(enable)next_state = s_1;ends_1:beginif(enable)next_state = s_2;ends_2:beginif(enable)next_state = s_3;ends_3:beginnext_state = s_idle;enddefault:beginnext_state = s_idle;endendcase endalways @(posedge clk) beginif(rst)begindone <= 1'b0;endelsebegincase(next_state)s_idle:begindone <= 1'b0;end s_1:begindone <= 1'b0;end s_2:begindone <= 1'b0;ends_3:begindone <= 1'b1;enddefault:begindone <= 1'b0;endendcaseendendendmodule


5,四段式

上面的三種狀態機的寫法是我們經常提到的,也是經典的三種。這三種寫法在邏輯上是完全等價的,也就是是說,無論采用哪種寫法,模塊的功能都是一樣的,但前兩種一般只出現在教科書中,在實際的項目中是很少見到的。原因在于生成網表的綜合器,由于目前的綜合器還不夠智能,其優化算法對三種寫法的敏感度不同,造成最終生成的電路有所區別,有時候區別較大,尤其是對于復雜的狀態機。無數血與淚的實踐證明,使用前面兩種寫法生成的電路在時序、性能、功耗和面積等方面的表現都不如三段式的寫法,所以即使三段式的寫法會讓你多敲幾次鍵盤,在實際的電路設計中盡量采用三段式的寫法來描述狀態機,多敲的那幾次鍵盤換來的電路質量的提高是完全值得的。
俗話說,“沒有最好,只有更好”。三段式的寫法是不是最好的呢?我認為不見得如此。上面說到,如果采用三段式的寫法,代碼會變長,如果是大的狀態機,結果會更明顯。那么,有沒有一種寫法,既能產生優質的電路,又能少敲幾次鍵盤呢?答案是肯定的。
仔細觀察上面三種寫法,你會發現,無論是哪種寫法,都會使用case語句,case語句不僅占用的代碼行數最多,而且綜合器對case語句還有不同的解析(full case和parallel case),如果我們將三段式的寫法中的case語句換成assign語句,并將狀態轉移塊進一步將當前狀態和下一個狀態拆分開,就變成了“四段式”,四段式的寫法由狀態識別,狀態轉移,轉移條件和對應狀態的輸出四部分組成。那么,脈動計數器狀態機四段式的寫法又是如何實現的呢?


/* * file : fsm4.v * author: Rill * date : 2014-05-11 */module Mfsm4 ( clk, rst,enable, done );input wire clk; input wire rst; input wire enable; output done;parameter s_idle = 4'd0; parameter s_1 = 4'd1; parameter s_2 = 4'd2; parameter s_3 = 4'd3;reg [3:0] current_state;wire c_idle = (current_state == s_idle); wire c_1 = (current_state == s_1); wire c_2 = (current_state == s_2); wire c_3 = (current_state == s_3);wire n_idle = c_3; wire n_1 = c_idle & enable; wire n_2 = c_1 & enable; wire n_3 = c_2 & enable;wire [3:0] next_state = {4{n_idle}} & s_idle |{4{n_1}} & s_1 |{4{n_2}} & s_2 |{4{n_3}} & s_3;always @(posedge clk) beginif(rst)current_state <= s_idle;else if(n_idle | n_1 | n_2 | n_3)current_state = next_state;endassign done = c_3; endmodule


6,驗證

通過對比,我們很容易就會發現,采用四段式寫法寫出來的狀態機,代碼數量會減少很多,不僅如此,由于使用的語句類型減少了(只有賦值語句),生成電路的質量也會有所改善。那是否在進行電路設計的時候采用四段式的寫法就沒有缺點了呢?還有句俗話叫“金無足赤,人無完人”,由于四段式的寫法將狀態機拆分的過于零散,以至于綜合器都識別不出來它是一個狀態機了,所以在做覆蓋率(coverage)分析的時候,分析工具只會按一般的邏輯進行分析,各個狀態之間的轉換概率就分析不出來了。
既然狀態機有這么多種寫法,在實際工作中采用哪一種呢?我認為三段式和四段式都是可以接受的(我個人習慣四段式的寫法)。如果將來有一天綜合器對四種寫法綜合出來的電路都差不多,那讀者就可以根據自己的喜好來任意選擇了。?
上面提到,無論采用哪種寫法,模塊實現的功能都是完全相同的,倒底是不是呢?我們需要寫一個簡單的測試激勵(testbench)來驗證一下。


/* * file : tb.v * author: Rill * date : 2014-05-11 */module tb;reg clk; reg rst; reg enable; wire done1; wire done2; wire done3; wire done4;Mfsm1 fsm1 ( .clk(clk), .rst (rst), .enable(enable), .done(done1) );Mfsm2 fsm2 ( .clk(clk), .rst (rst), .enable(enable), .done(done2) );Mfsm3 fsm3 ( .clk(clk), .rst (rst), .enable(enable), .done(done3) );Mfsm4 fsm4 ( .clk(clk), .rst (rst), .enable(enable), .done(done4) );always #1 clk = ~clk;integer loop;initial beginclk = 0;rst = 0;enable = 0;loop = 0;repeat (10) @(posedge clk);rst = 1;repeat (4) @(posedge clk);rst = 0;repeat (100) @(posedge clk);for(loop=1;loop<10;loop=loop+1)beginenable = 1;@(posedge clk);enable = 0;@(posedge clk);endrepeat (100) @(posedge clk);$stop;endendmodule

7,modelsim下的波形




8,ncsim的波形

上面是用windows下的modelsim得到的仿真波形,如果我們用ncsim(IUS),并且在Linux下,我們最好寫一個簡單的腳本來進行仿真,提高工作效率。


#! /bin/bash# # fsm.sh # usage: ./fsm.sh c/w/r # Rill create 2014-09-03 #TOP_MODULE=tbtcl_file=run.tclif [ $# != 1 ];then echo "args must be c/w/r" exit 0 fiif [ $1 == "c" ]; then echo "compile lib..." ncvlog -f ./vflist -sv -update -LINEDEBUG; ncelab -delay_mode zero -access +rwc -timescale 1ns/10ps ${TOP_MODULE} exit 0 fiif [ -e ${tcl_file} ];then rm ${tcl_file} -f fi touch ${tcl_file}if [ $1 == "w" ];then echo "open wave..." echo "database -open waves -into waves.shm -default;" >> ${tcl_file} echo "probe -shm -variable -all -depth all;" >> ${tcl_file} echo "run" >> ${tcl_file} echo "exit" >> ${tcl_file} fiif [ $1 == "w" -o $1 == "r" ];then echo "sim start..." ncsim ${TOP_MODULE} -input ${tcl_file} fiecho "$(date) sim done!"
運行腳本:?


./fsm.sh c ./fsm.sh w
執行:


simvision wave/wave.trn
即可得到仿真波形,如下所示:




從中可以看出,ncsim和modelsim得到的仿真波形有所不同。原因在于前面的三種三段式寫法是寄存器輸出,第四種是組合邏輯輸出。


總結

以上是生活随笔為你收集整理的数字集成电路设计-12-状态机的四种写法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。