Verilog編程技巧01——如何編寫三段式狀態機

前言

Verilog編程技巧系列文章將聚焦于介紹Verilog的各種編程范式或者說技巧,編程技巧和編程規范有部分重合,但并非完全一樣。規范更注重編碼的格式,像變量命名、縮進、注釋風格等,而編程技巧則更偏重更直觀易讀、更便于維護、綜合效果更好的Verilog/SV代碼寫法,像:如何編寫狀態機、如何進行參數化設計、如何進行流水線設計等。可以說編程技巧就是一些編程套路,熟練掌握這些技巧可幫助我們更高效的完成FPGA開發工作。

本文是Verilog編程技巧系列的第一篇文章,介紹了如何編寫三段式狀態機。

在FPGA設計中,三段式狀態機因其結構清晰、可靠性高等特點,成為實現狀態機的最佳方式。本文主要說明了以下問題:

  1. 如何評價一個狀態機寫的好不好;
  2. 為什么三段式狀態機更好;
  3. 為什么更推薦獨熱碼;
  4. 第三段輸出邏輯應該怎么寫。

最后,本文分享了Verilog與SV的三段式狀態機模板。通過閱讀本文,你將能夠更深入的理解三段式狀態機的寫法及其背后的原理。

本文主要參考了Clifford E. Cummings的文章,他是數字電路設計領域的技術先驅,享有一定國際聲譽。他的文章可從此網站(界面如下圖)下載:http://www.sunburst-design.com/papers/。他的一些關于狀態機的文章給本文的一些結論提供了理論和實驗支撐。

一. 什么是狀態機

狀態機是描述系統行為的數學模型。包含狀態(系統某時刻狀況)、轉移(狀態間切換)、事件(觸發轉移因素)和動作(轉移時執行的操作),用于分析和設計系統邏輯,在多領域廣泛應用。

依據狀態數量,狀態機可分為兩類:

特征有限狀態機(FSM)無限狀態機(ISM)
全稱與簡稱Finite State Machine,簡稱FSMInfinite State Machine,簡稱ISM
狀態數量有限無限
轉移方式離散、明確條件可能連續(如基于變量變化)
典型應用離散邏輯控制連續系統或復雜動態系統

在HDL編碼時,我們構建的總是有限狀態機,所以這里我們說的狀態機默認是指有限狀態機

依據輸出與當前狀態的關系,狀態機也可分為兩類:

狀態機類型輸出決定因素特點描述
Moore型(摩爾型)僅取決于當前狀態任意時刻,只要狀態確定,輸出就確定,輸出與輸入無關
Mealy型(米利型)取決于當前狀態和輸入信號輸出不僅和當前狀態有關,還受當前輸入的影響

這兩種狀態機的命名均來源于人名,看著比較奇怪。

實際應用中,無需刻意去區分狀態機屬于哪種類型,沒有什么意義。

在Verilog/SV編碼中,狀態機應用極其廣泛,它幾乎可以描述所有邏輯,也就是說如果你想,所有的模塊的內部邏輯都可以寫成狀態機。所以,研究如何編寫更好的狀態機很有必要。

二. 一段、二段、三段與四段式狀態機

在Verilog/SV中,狀態機按功能可以分為三個部分:

  1. 狀態轉移:負責在不同的狀態之間進行切換,它決定了狀態機如何從當前狀態遷移到下一個狀態。
  2. 狀態判斷:根據當前狀態和輸入信號來確定下一個狀態是什么。
  3. 輸出邏輯:根據當前狀態機的狀態來產生相應的輸出信號。

根據這三部分在代碼中用幾個always塊來描述,可將狀態機分為以下幾種:

2.1 一段式狀態機

狀態轉移、狀態判斷與輸出邏輯全都寫在同一個always塊中,稱為一段式狀態機。示例如下:

//?狀態定義
localparam?S0?=?1'b0,?S1?=?1'b1;
reg?state;

//?狀態轉移、判斷與輸出
always?@(posedge?clk)?begin
??if?(~rstn)?{state,?out}?<=?{S0,?1'b0};
??else
????case?(state)
??????S0:?if?(in)?state?<=?S1;?else?state?<=?S0;?out?<=?1'b0;
??????S1:?if?(in)?{state,?out}?<=?{S0,?1'b1};?else?state?<=?S0;
??????default:?{state,?out}?<=?{S0,?1'b0};
????endcase
end

出于排版的考慮,去掉了非必要的begin-end,盡量縮短了代碼行數,下同。

2.2 二段式狀態機

狀態轉移寫在一個時序always塊中狀態判斷與輸出寫在一個組合always塊中,稱為二段式狀態機。示例如下:

//?狀態定義
localparam?S0?=?1'b0,?S1?=?1'b1;
reg?state,?next;

//?第一段:時序邏輯,狀態轉移
always?@(posedge?clk)?begin
??if?(~rstn)?state?<=?S0;
??else?state?<=?next;
end

//?第二段:組合邏輯,次態和輸出邏輯
always?@(*)?begin
??case?(state)
????S0:?if?(in)?{next,?out}?=?{S1,?1'b0};?else?{next,?out}?=?{S0,?1'b0};
????S1:?if?(in)?{next,?out}?=?{S0,?1'b1};?else?{next,?out}?=?{S0,?1'b0};
????default:?{next,?out}?=?{S0,?1'b0};
??endcase
end

2.3 三段式狀態機

狀態轉移寫在一個時序always塊中狀態判斷寫在一個組合always塊中輸出寫在一個時序或組合always塊中,稱為三段式狀態機。示例如下:

//?狀態定義
localparam?S0?=?1'b0,?S1?=?1'b1;
reg?state,?next;

//?第一段:時序邏輯,狀態轉移
always?@(posedge?clk)?begin
??if?(~rstn)?state?<=?S0;
??else?state?<=?next;
end

//?第二段:組合邏輯,計算次態
always?@(*)?begin
??case?(state)
????S0:?next?=?in???S1?:?S0;
????S1:?next?=?in???S0?:?S0;
????default:?next?=?S0;
??endcase
end

//?第三段:時序邏輯,輸出邏輯
always?@(posedge?clk)?begin
??if?(~rstn)?out_a?<=?1'b0;
??else?out_a?<=?(state?==?S1?&&?in)???1'b1?:?1'b0;
end

//?第三段:組合邏輯,輸出邏輯
always?@(*)?begin
??out_b?=?(state?==?S1?&&?in)???1'b1?:?1'b0;
end

對于第三段,因為時序邏輯的輸出相對組合邏輯會慢一個時鐘周期,如果對輸出延時有極高的要求,則可以考慮用組合邏輯。但通常還是建議用時序邏輯,時序邏輯的輸出更加穩定(無競爭和冒險),也更便于整體的時序分析和約束。

2.4 四段式狀態機

四段式狀態機是將三段式狀態機的輸出寫成兩個always塊,一個固定為組合always塊,寫輸出使能邏輯;另一個固定為時序always塊,寫輸出邏輯。示例如下:

//?前面與三段式相同,僅第三段輸出邏輯并分為兩段
//?組合邏輯,輸出使能
always?@(*)?begin
??out_en?=?(state?==?S1?&&?in);
end

//?時序邏輯,輸出邏輯
always?@(posedge?clk)?begin
??if?(~rstn)
????out?<=?1'b0;
??else?if?(out_en)?
????out?<=?1'b1;
??else?
????out?<=?1'b0;
end

從實現的功能上來說,三段式和四段式完全相同,但在設計理念靈活性可維護性等方面,兩者存在明顯區別:

對比維度三段式狀態機四段式狀態機
核心思想輸出與狀態/輸入直接耦合,無中間控制信號。引入輸出使能信號(out_en),分離輸出條件與賦值邏輯。
代碼結構輸出邏輯與狀態轉移混合在時序邏輯中。輸出使能(組合邏輯)與輸出賦值(時序邏輯)分離,模塊化更清晰。
輸出條件修改需直接修改時序邏輯中的條件判斷,可能影響其他邏輯。只需修改組合邏輯中的out_en,輸出賦值邏輯保持不變。
擴展性新增輸出條件需修改時序邏輯,可能導致冗余。新增條件只需擴展out_en邏輯,不影響輸出賦值部分。
適用場景簡單場景(如固定序列檢測、單一輸出控制)。復雜場景(如多條件輸出、動態使能控制)。

總的來說,四段式狀態機在負責場景有一些代碼維護上的優勢。

根據2019 CummingsSNUG2019SV_FSM 狀態機設計.pdf中的內容,四段式狀態機在復雜邏輯的綜合中會有一定優勢,但三段式代碼更加簡潔,更符合直觀邏輯,所以在FPGA開發中,作者推薦總是優先使用三段式,僅在復雜場景開發的最后,將三段式改為四段式,以提升綜合效果。

綜合效果:指的是綜合工具對不同代碼寫法的優化支持程度。現代綜合工具對四段式會有更好的優化效果,使得四段式在資源效率和時序性能方面通常優于三段式,但具體效果還需實測。

綜上,針對FPGA開發,本文僅討論三段式狀態機,如需針對復雜場景進行優化,各位同學可根據需要將三段式改為四段式,并實際比較綜合后的資源效率和時序性能確定最終方案。

三. 什么樣的狀態機是好的狀態機?

換句話說,評價一個狀態機好壞的標準是什么?一般來說,可以從以下4個維度進行評價:

  1. 可維護性:代碼易于修改、擴展,適應需求變更。

  2. 可讀性:代碼結構清晰、邏輯緊湊,避免冗余,便于閱讀理解。

  3. 可調試性:支持高效的仿真驗證,便于在仿真或實測時快速定位問題。

  4. 綜合效果:對綜合工具友好,代碼在轉化為硬件時的面積、速度與功耗等性能指標優異。

上述四種狀態機從這四個維度進行評價,表格如下:

指標一段式二段式三段式四段式
可維護性★★★★★★★★★
可讀性★★★★★★★★
可調試性★★★★★★★★
綜合效果★★★★★★★★★

說明:

  1. 一顆星(★)表示“差”,兩顆星(★★)表示“良”,三顆星(★★★)表示“優” 。
  2. 四段式的綜合效果在非常復雜場景可能略好于三段式,但絕大多數場景下,三段式的綜合效果同樣優異,兩者區別不大,所以兩者都標注為三顆星。

對于一個功能需求來說,最重要的兩件事是開發效率模塊性能。可維護性、可讀性和可調試性影響開發效率,綜合效果則影響模塊性能。

綜上,我們可以得出結論:

  1. 永遠不應該用一段式狀態機:一段式狀態機邏輯不清,導致代碼難以維護、調試和擴展,極易成為"屎山代碼"的一部分。其唯一優勢(綜合效果高)完全無法抵消后期維護的災難性代價。即使在非常簡單的場景中,也不應該使用。
  2. 不推薦用二段式狀態機:二段式雖然分離了部分邏輯,但輸出仍依賴組合邏輯,存在 輸出毛刺風險時序收斂困難,且可調試性和綜合效果低于三段式,沒有獨特的應用場景優勢。
  3. 不推薦直接用四段式狀態機:四段式雖然邏輯分層更徹底,但會導致 代碼冗余開發效率下降。它僅適用于極少數復雜場景(例如多路獨立輸出需嚴格時序控制),對大多數設計屬于"過度設計"。
  4. 僅推薦使用三段式狀態機:三段式是經過驗證的最佳實踐,沒有短板。

四. 狀態編碼的類型和優缺點

狀態編碼指的是狀態機的不同狀態用怎樣的二進制去表示,狀態編碼一般有以下幾種類型:

  1. 二進制碼(Binary):用二進制自然序列表示狀態,N個狀態需滿足 2^N ≥ S(S為狀態總數)。8個狀態編碼為 000, 001, 010, 011, 100, 101, 110, 111
  2. 格雷碼(Gray Code):相鄰狀態僅有一位不同,形成循環單步跳變。3位格雷碼為 000, 001, 011, 010, 110, 111, 101, 100
  3. 獨熱碼(One-Hot):每個狀態獨占一個比特位,總位數等于狀態數(N = S)。3個狀態編碼為 100, 010, 001
  4. 零空閑獨熱碼(Zero-Idle One-Hot):獨熱碼基礎上引入全零( 000...)作為空閑狀態,其他狀態為單一高位。5個狀態編碼為 0000(空閑態), 1000, 0100, 0010, 0001
  5. 獨冷碼(One-Cold):與獨熱碼相反,每個狀態由唯一一個低電平位(0)表示,其他位為1。3個狀態編碼為 011, 101, 110
  6. 約翰遜碼(Johnson Code):也稱為扭環碼(Twisted Ring Code),是一種特殊的環形移位碼,相鄰狀態通過左移或右移,然后補移出位的反碼生成,最終形成一個環。3位右移約翰遜碼為 000, 100, 110, 111, 011, 001(循環)。
  7. 混合編碼(Hybrid Encoding):高位獨熱碼 + 低位二進制碼。狀態數>20時,平衡資源與性能。

在FPGA設計中,零空閑獨熱碼、獨冷碼與獨熱碼的資源消耗和實現邏輯高度相似,而約翰遜碼和混合編碼的實際應用場景較為局限。因此,工程中的核心選擇通常集中在以下三種編碼:二進制碼、格雷碼和獨熱碼。這三種編碼方式的優缺點對比如下表:

指標二進制碼(Binary)格雷碼(Gray Code)獨熱碼(One-Hot)
時序邏輯資源消耗
(觸發器,FF)
★★★★☆
極低:僅需 ?log2(N)? 個觸發器(N為狀態數)
★★★☆☆
:觸發器數與二進制相同,但需額外觸發器存儲轉換碼(可選)
★★☆☆☆
:需 N 個觸發器,但FPGA中觸發器資源充足
組合邏輯資源消耗
(LUT)
★★☆☆☆
中高:狀態跳變需復雜組合邏輯(如狀態譯碼器)
★★★☆☆
:需異或門實現碼值轉換
★★★★☆
極低:狀態跳變僅需單比特操作
時序性能★★☆☆☆
:多比特翻轉限制頻率
★★★☆☆
中高:單比特跳變,但轉換邏輯增加延遲
★★★★★
最優:單比特跳變,路徑最短,適合高頻
功耗★★☆☆☆
:多比特翻轉動態功耗大
★★★★☆
:單比特翻轉降低功耗
★★★☆☆
:靜態功耗略高(觸發器多),動態功耗低
容錯性★☆☆☆☆
:非法狀態多,需額外檢測
★★★☆☆
:非法狀態較少
★★★★☆
:非法狀態極易檢測(非單比特為1)
可讀性★★★☆☆
:自然順序易理解,跳變邏輯復雜
★★☆☆☆
中低:編碼非自然排列,需查表輔助
★★★★★
極優:每個狀態對應唯一比特,仿真直觀
推薦優先級★★☆☆☆
- 資源敏感型設計
- 簡單狀態機
★★★☆☆
- 低功耗/跨時鐘域
- 異步FIFO
★★★★★
- FPGA高頻設計
- 狀態數≤16

星級說明:

  • ★★★★★: 強烈推薦(顯著優勢,無替代方案)
  • ★★★★☆: 推薦(優勢明顯,適用場景明確)
  • ★★★☆☆: 一般推薦(需權衡利弊)
  • ★★☆☆☆: 不推薦(僅限特定場景)
  • ★☆☆☆☆: 避免使用(缺陷明顯)

由此可以得出結論

  1. 二進制碼(★★☆☆☆)
    • 僅限極簡設計(如ASIC或低成本FPGA),需承擔時序和功耗風險。
    • 添加輸出寄存器過濾毛刺。
  2. 格雷碼(★★★☆☆)
    • 低功耗/跨時鐘域專用,如異步FIFO指針同步,但需手動添加轉換邏輯。
    • 避免在復雜狀態機中濫用。
  3. 獨熱碼(★★★★★)
    • FPGA設計首選,因LUT資源占用低、時序性能最優、容錯性高,完美適配FPGA架構。
    • 僅需注意狀態數≤16(超過時可混合編碼)。

所以,在FPGA中進行狀態機設計時,總是應該使用獨熱碼

五. 第三段輸出應該用組合邏輯還是時序邏輯?

指標組合邏輯輸出時序邏輯輸出
優點?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/85953.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/85953.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/85953.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

豆包和deepseek 元寶 百度ai區別是什么

豆包、DeepSeek、元寶和百度 AI 有以下區別&#xff1a; 開發公司 豆包5&#xff1a;由字節跳動公司基于云雀模型開發。DeepSeek4&#xff1a;是深度求索打造的開源多模態大模型。元寶1&#xff1a;是騰訊混元模型的落地產品&#xff0c;整合了 DeepSeek - R1 與混元模型。百…

網頁端 js 讀取發票里的二維碼信息(圖片和PDF格式)

起因 為了實現在報銷流程中&#xff0c;發票不能重用的限制&#xff0c;發票上傳后&#xff0c;希望能讀出發票號&#xff0c;并記錄發票號已用&#xff0c;下次不再可用于報銷。 基于上面的需求&#xff0c;研究了OCR 的方式和讀PDF的方式&#xff0c;實際是可行的&#xff…

讀文獻先讀圖:GO弦圖怎么看?

GO弦圖&#xff08;Gene Ontology Chord Diagram&#xff09;是一種用于展示基因功能富集結果的可視化工具&#xff0c;通過弦狀連接可以更直觀的展示基因與GO term&#xff08;如生物過程、分子功能等&#xff09;之間的關聯。 GO弦圖解讀 ①內圈連線表示基因和生物過程之間的…

pandas隨筆

主要操作兩個對象&#xff1a;一維帶標簽數組 和 二維表格DataFrame 一維帶標簽數組Series pd.Series([1, 3, 5, np.nan, 6, 8]) &#xff0c;結果如下&#xff1a; 可指定索引&#xff0c;pd.Series([1, 3, 5], index[a, b, c]) 二維表格DataFrame 創建時需要指定列名&a…

java教程筆記(十一)-泛型

Java 泛型&#xff08;Generics&#xff09;是 Java 5 引入的重要特性之一&#xff0c;它允許在定義類、接口和方法時使用類型參數。泛型的核心思想是將類型由具體的數據類型推遲到使用時再確定&#xff0c;從而提升代碼的復用性和類型安全性。 1.泛型的基本概念 1. 什么是泛…

力扣刷題(第四十九天)

靈感來源 - 保持更新&#xff0c;努力學習 - python腳本學習 反轉鏈表 解題思路 迭代法&#xff1a;通過遍歷鏈表&#xff0c;逐個改變節點的指針方向。具體步驟如下&#xff1a; 使用三個指針&#xff1a;prev&#xff08;初始為None&#xff09;、curr&#xff08;初始為…

設置應用程序圖標

(1)找一張圖片 (2)然后轉ico圖片 在線生成透明ICO圖標——ICO圖標制作 驗證16x16就可以 降低exe大小 (3) 在xxx.pro修改 添加 &#xff08;4&#xff09; 刪除 build 和 xxxpro_user文件 (5)編譯project 和運行xx.exe (6)右鍵 設置快捷方式

免費wordpress模板下載

西瓜紅色的免費wordpress模板&#xff0c;簡潔實用又容易上手&#xff0c;適合新手使用。 下載 https://www.waimaoyes.com/moban/2231.html

【React】React 18 并發特性

React 18 引入了 并發特性&#xff08;Concurrent Features&#xff09;&#xff0c;這是一次對 React 渲染機制的重大升級&#xff0c;讓 React 更加智能、響應更流暢、資源更節省。 我們來詳細講解一下它的原理、特性、API 以及實際應用。 &#x1f9e0; 一、什么是并發特性…

FFMPEG 提取視頻中指定起始時間及結束時間的視頻,給出ffmpeg 命令

以下是提取視頻中指定起始時間及結束時間的 ffmpeg 命令示例: bash 復制 ffmpeg -i input.mp4 -ss 00:01:30.00 -to 00:05:00.00 -c copy output.mp4 其中,-i input.mp4 是指定要處理的輸入視頻文件為 “input.mp4”。 -ss 00:01:30.00 表示指定視頻的起始時間為 1 分 30 …

mybatis的if判斷==‘1‘不生效,改成‘1‘.toString()才生效的原因

mybatis的xml文件中的if判斷‘1’不生效&#xff0c;改成’1’.toString()才生效 Mapper接口傳入的參數 List<Table> queryList(Param("state") String state);xml內容 <where><if test"state ! null and state 1">AND EXISTS(select…

AI 模型分類全解:特性與選擇指南

人工智能&#xff08;AI&#xff09;技術正以前所未有的速度改變著我們的生活和工作方式。AI 模型作為實現人工智能的核心組件&#xff0c;種類繁多&#xff0c;功能各異。從簡單的線性回歸模型到復雜的深度學習網絡&#xff0c;從文本生成到圖像識別&#xff0c;AI 模型的應用…

01-python爬蟲-第一個爬蟲程序

開始學習 python 爬蟲 第一個獲取使用最多的網站-百度 源代碼 并將源代碼保存到文件中 from urllib.request import urlopenurl https://www.baidu.com resp urlopen(url)with open(baidu.html, w, encodingutf-8) as f:f.write(resp.read().decode(utf-8))知識點&#xf…

四六級監考《培訓學習》+《培訓考試》

1 線上注冊 &#xff08;網址&#xff1a; https://passport.neea.edu.cn 2 登錄培訓平臺參加線上必修課程學習和考核 &#xff08;平臺網址&#xff1a; https://kwstudy.neea.edu.cn 注意選擇學員入口&#xff09; 3 考試要求&#xff1a;考試成績須達應到80分以上&#xf…

回顧Java與數據庫的30年歷程

當 Java 1.0 于 1996 年推出時&#xff0c;語言和互聯網都與今天大不相同。當時&#xff0c;網絡主要是靜態的&#xff0c;而 Java 承諾通過注入交互式游戲和動畫來為網絡注入活力&#xff0c;這一承諾極具前景。根據 1995 年寫給《連線》雜志的 David Banks 的說法&#xff0c…

simulink有無現成模塊可以實現將三個分開的輸入合并為一個[1*3]的行向量輸出?

提問 simulink有無現成模塊可以實現將三個分開的輸入合并為一個[1*3]的行向量輸出&#xff1f; 回答 Simulink 本身沒有一個單獨的模塊能夠直接將三個分開的輸入合并成一個 [13] 行向量輸出&#xff0c;但是可以通過 組合模塊實現你要的效果。 ? 推薦方式&#xff1a;Mux …

代碼訓練LeetCode(24)數組乘積

代碼訓練(24)LeetCode之數組乘積 Author: Once Day Date: 2025年6月5日 漫漫長路&#xff0c;才剛剛開始… 全系列文章可參考專欄: 十年代碼訓練_Once-Day的博客-CSDN博客 參考文章: 238. 除自身以外數組的乘積 - 力扣&#xff08;LeetCode&#xff09;力扣 (LeetCode) 全…

NLP學習路線圖(十七):主題模型(LDA)

在浩瀚的文本海洋中航行&#xff0c;人類大腦天然具備發現主題的能力——翻閱幾份報紙&#xff0c;我們迅速辨別出"政治"、"體育"、"科技"等板塊&#xff1b;瀏覽社交媒體&#xff0c;我們下意識區分出美食分享、旅行見聞或科技測評。但機器如何…

vue對axios的封裝和使用

在 Vue 項目中&#xff0c;使用 axios 進行 HTTP 請求是非常常見的做法。為了提高代碼的可維護性、統一錯誤處理和請求攔截/響應攔截邏輯&#xff0c;對axios進行封裝使用。 一、基礎封裝&#xff08;適用于 Vue 2 / Vue 3&#xff09; 1. 安裝 axios npm install axios2. 創…

HTML實現端午節主題網站:龍舟爭渡,憑吊祭江誦君賦。

名人說:龍舟爭渡,助威吶喊,憑吊祭江誦君賦。——蘇軾《六幺令天中節》 創作者:Code_流蘇(CSDN)(一個喜歡古詩詞和編程的Coder??) 目錄 一、項目概覽:傳統與現代的技術碰撞1. 核心特性一覽2. 網站結構設計二、技術亮點深度解析1. 響應式布局的精妙設計2. CSS動畫系統的…