文章目錄
- SV對verilog的擴展
- 📘 標準文檔名稱:
- 從SV到仿真
- 通用過程解讀
- 實例解讀
- SV的仿真過程
- 并行
- 仿真顆粒度
- SV仿真調度
- 調度區域
SV對verilog的擴展
SystemVerilog 和 Verilog 的語法標準由 **IEEE(美國電氣和電子工程師協會)**制定,正式文檔如下:
📘 標準文檔名稱:
名稱 | 簡稱 | 最新版本 | 標準編號 | 是否免費 |
---|---|---|---|---|
Verilog | Verilog HDL | IEEE 1364-2005 | IEEE Std 1364-2005 | ? 付費 |
SystemVerilog | SystemVerilog HDL | IEEE 1800-2017 | IEEE Std 1800-2017 | ? 付費 |
SystemVerilog 最新合并版 | Unified HDL (Verilog + SystemVerilog) | IEEE 1800-2023 | IEEE Std 1800-2023 | ? 付費 |
- SystemVerilog(SV)相對于傳統 Verilog 的主要擴展和增強,涵蓋語法、數據類型、驗證特性等方面。
類別 | Verilog 特性 | SystemVerilog 擴展 | 說明 | 參考文獻 |
---|---|---|---|---|
數據類型 | reg 、wire | 新增 logic 、bit 、int 、byte 、shortint 、longint 、time 、enum 、struct 、union 、string 、chandle | 更強的類型安全、結構化數據、枚舉和字符串支持 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [1]) |
數組與集合 | 連續向量 reg [7:0] a; | 動態數組 int da[]; 、關聯數組 int aa[string]; 、隊列 int q[$]; | 支持可變長度和哈希索引結構 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [2]) |
接口與包 | module 、function 、task | interface 、program 、package 、import /export | 接口封裝信號集合,package 封裝共享聲明 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [3]) |
模塊化 & 生成 | generate 塊 | generate 擴展語法、localparam 、typedef | 更靈活的生成語法,本地參數和類型重命名 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [4]) |
線程 & 時間控制 | always 、initial | always_ff 、always_comb 、always_latch 、unique 、priority | 明確組合/時序/鎖存塊,優化仿真和綜合 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [5]) |
約束隨機化 | 無 | class 、rand 、constraint 、solve...before | 面向驗證的隨機化對象支持 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [6]) |
面向對象 | 無 | 支持 class 、繼承 extends 、多態、new() 構造函數 | 引入面向對象編程,驗證組件化 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [7]) |
斷言 & 覆蓋率 | 無 | SVA 斷言 assert 、covergroup 、coverpoint 、cross | 內置斷言和功能覆蓋率建模 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [8]) |
TLM 接口 | 無 | uvm_sequence_item 、uvm_port 、uvm_export 、uvm_analysis_port | 事務級建模接口,為 UVM 提供通信通道 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [9]) |
并發擴展 | fork/join | fork...join_any 、fork...join_none -多種 join 語義 | 更靈活的并發控制 | (代碼覆蓋率原理簡介原創 - CSDN博客) (see [10]) |
簡要說明:
- 數據類型和數組:SV 增加了豐富的強類型和容器類型,解決了 Verilog 只有
reg
/wire
、固定向量的限制。 - 接口與包:通過
interface
和package
,SV 實現了代碼重用與封裝,提高了可維護性。 - 線程與時間控制:引入更語義化的
always_ff
等,幫助綜合工具和仿真工具區分不同用途。 - 面向對象 & 約束隨機:SV 將驗證帶入語言層面,結合 UVM 形成強大的驗證生態。
- 斷言 & 覆蓋率:SystemVerilog Assertions (SVA) 和功能覆蓋率讓驗證更加系統化。
- 事務級建模 (TLM):提供通用接口,讓組件間事務通信高度解耦,支撐 UVM 框架。
從SV到仿真
通用過程解讀
- 編譯
語法和語義分析 , 進行編譯 , 編譯出 中間文件
- 建模
按照設計集成關系, 組成頂層模塊1. 模塊例化2. 接口例化3. 程序例化4. 層次集成5. 計算參數6. 解決層次信號引用7. 建立模塊連接
// 類似C代碼的link階段
- 仿真
建立RTL模型和參數驗證環境, 以周期驅動(cycle-driven)或事件驅動(event-driven)的方式進行仿真
實例解讀
- Icarus Verilog(
iverilog
), VCS 和 Verilator 的典型使用流程
階段 | vcs | verilator | iverilog |
---|---|---|---|
編譯(compilation) | .sv → .o | .sv → .o | iverilog -g2005-sv -o tb.vvp <files>.sv |
建模(elaboration) | .o → simv | .o → obj_dir/V$(TB_NAME) | 隱式在生成 tb.vvp 時完成,不再單獨產物 |
仿真(simulation) | ./simv | ./obj_dir/V$(TB_NAME) | vvp tb.vvp |
與 VCS 和 Verilator 不同,
Icarus Verilog **不生成可執行 ELF**,也不生成 C++ 代碼。
它在編譯階段就完成了 elaboration,將所有模塊實例化和連接,并將結果編碼進 `tb.vvp`。
無需單獨生成可執行文件(`.o` 或 `simv`)。
SV的仿真過程
并行
SystemVerilog 采用 模塊(module
) 作為設計的基本單元,整個電路從 頂層模塊 開始,逐層實例化與端口互聯,最終形成完整的層次化結構。
在 SV世界中, 仿真一旦運行起來, 一個module 就類似于 軟件世界中的 一個線程(不區分線程進程)
那么就必然有線程之間的同步與通信1. 信號的變化(事件觸發) // @(posedge clk)2. 對特定事件的等待(時鐘周期) // wait 3. 延時(固定延時) // #200
一般來說, module 中的語句都是順序的, 但是如果你想 并行, 就需要用fork 關鍵字 來創建 并行結構(即創建新線程)即 軟件世界中的 一個線程 在硬件世界中的類比為 // 拓展知識 請查看 https://blog.csdn.net/u011011827/article/details/1474447921. module2. initial 包圍的 硬件代碼 3. always 包圍的 硬件代碼2. fork 包圍的 硬件代碼
仿真顆粒度
仿真需要指定 仿真時的 時間單位和精度不管是 用戶TB ,還是設計指定的 時間都要 以 timeprecision 算(四舍五入)出來.
搞 timeunit 這個關鍵字,只是為了 代碼的可讀性 : 讓用戶無需每次都寫帶單位的延時,只需寫 #1 即可代表 #(1 * timeunit)
一般 timeprecision 指定的單位 比 timeunit 小
timeprecision 不是越小越好,太細反而開銷大(調度器要管理的時間槽變多,導致仿真慢). // 這個要解讀 調度, 來深入理解
合適的 timeprecision看設計的最快時鐘——假如最高 500 MHz(周期 2ns),那么 timeprecision 設 100ps 就夠(20 個步)。看需要模擬的最小延時——如果你要模擬 #0.5ns 這類延時,就要精度細于 500ps;否則會被舍入。按 10 的級數對齊——常用 1ns/100ps、1ns/10ps、100ps/10ps 這樣的十倍關系,既好計算又利于讀寫。
SV仿真調度
sv代碼中module是并行的,但是cpu(仿真sv代碼的時候)是順序執行
那么在 0 時刻 ,有很多 并行代碼(假設有10條語句) 需要仿真那么 誰先做誰后做呢 , 這就是 仿真調度 要考慮的事情部分規則:時間片 : timeprecision指定的單位 // 如果 timeprecision = 100ps , 那么帶 時間槽所在的時刻 只能是 100ps的倍數同槽不同事:同一槽位的多條語句,靠 “事件區域” + “申明順序” 決定先后。效率至上 :調度器只跳到下一個有事的槽位,不會浪費時間檢查空槽 // 即 存在一種可能, 200ps 時, 沒有事件位于這一時刻, 那么 根本就不會 檢查 200ps 是否有 事情要做 // 即 不會 在 200ps 這個刻度 浪費 一丁點cpu時間
- 同一時間槽內的執行次序就是“仿真調度”
在每個離散的時間點(時間槽)上,仿真器會把所有待執行的事件分到幾個“事件區域”(Active、Inactive、NBA、Monitor…),再在各區域內按聲明順序執行。這套從區域到區域、從語句到語句的調度機制,就稱作仿真調度(event scheduling)。 - 仿真調度是為模擬并行硬件而設計的
真實 CPU 是串行執行指令的,而硬件模塊在同一時刻是并行更新歐。仿真器內部用“調度”來用順序算法準確地再現硬件的并行行為——保證同時發生的并行事件能在同一個時間點“好像同時”地被執行,只是按照調度規則分批跑完。 - 實際硬件里沒有“調度器”這層概念
硬件的寄存器、門電路、互連網是真正同時更新的,不存在“先做 A 再做 B”的軟件調度。調度只是仿真環境里用來序列化并行行為的實現細節。 - 仿真的時候 有 仿真調度, 但是硬件上沒有, 怎么保證在仿真時能過的代碼在硬件上一定沒問題呢?
- 可綜合子集:設計時只用可綜合的 SV/Verilog 語法(符合綜合工具的語義),這樣仿真行為和綜合后硬件邏輯在功能上是等價的。
- 靜態時序分析 (STA):綜合后用 STA 工具檢查硬件的時序收斂,保證所有信號路徑在目標時鐘下都能按預期收斂。
- 門級仿真:在部分關鍵場景下,還會跑門級網表加 SDF back-annotation 仿真,驗證時序與 RTL 仿真的一致性。
通過以上流程,仿真“看得見”的并行邏輯,以及綜合后驗證的時序約束,都能確保最終硅片上真實并行電路的正確性。
調度區域
SystemVerilog 離散事件調度器在每個時間槽(time slot)內依次執行的主要仿真調度區域(也稱為“事件區域”)及其作用:
區域序號 | 調度區域 | 英文名 / 縮寫 | 執行內容示例 | 說明 |
---|---|---|---|---|
1 | Active | Active Region | always 、initial 塊中無延時語句之后的語句 | 執行所有因為當前時間到達而“就緒”的過程后的第一批語句 |
2 | Inactive | Inactive Region | disable fork 、@ (事件觸發) | 處理在 Active 區域產生的新就緒過程,但不再執行延時、NBATasks |
3 | NBA | Non-Blocking Assigns | 所有<= 非阻塞賦值更新 | 收集并一次性更新非阻塞賦值,避免與 Active 中的順序沖突 |
4 | Monitor | Monitor Region | uvm_monitor 、$monitor 、覆蓋率采集、斷言 | 觀察信號變化,不影響驅動;常用于更新覆蓋率、斷言檢查 |
5 | Observed | Observed Region | @(<signal>) 等帶延時或事件觸發的觀察者 | 用于“觀察”在 Monitor 區域中改變的信號或變量 |
6 | Reactive | Reactive Region | PLI/VPI/VHPI 回調、DPI-C 調用 | 外部仿真接口的回調處理,保證與內部更新同步 |
調度流程簡述:
在仿真器中,每到一個時間槽,依次從 Active → Inactive → NBA → Monitor → Observed → Reactive 執行對應區域的所有就緒事件;然后仿真時間跳到下一個有事件的槽位。
這樣設計可以:
- 將順序、非阻塞賦值、監視、外部回調等不同種類的操作有序分隔,
- 保證同一時間點內的并行硬件行為被準確、可預測地“序列化”執行。