SystemVerilog

SystemVerilog 是一種在現代積體電路（尤其是超大型積體電路）的設計及驗證流程中，由 Verilog 發展而來的硬體描述、硬體驗證統一語言，前一部分基本上是 2005 年版 Verilog 的擴充套件，而後一部分功能驗證特性則是一門面向物件程式設計語言。面向物件特性很好地彌補了傳統 Verilog 在晶片驗證領域的缺陷，改善了程式碼可重用性，同時可以讓驗證工程師在比暫存器傳輸級更高的抽象級別，以事務而非單個訊號作為監測物件，這些都大大提高了驗證平臺搭建的效率。

SystemVerilog 已經被採納為電氣電子工程師學會 1800-2009 標準，並獲得了主流電子設計自動化工具供應商的支援。雖無任一個模擬系統能聲稱自己完全支援 SystemVerilog 語言參考手冊（Language Reference Manual, LRM）裡介紹的所有語言結構，要改善測試平臺的互操作性相當困難，但推進跨平臺相容性的研究開發工作已在進行中。若干種驗證方法學相繼出現，以預定義類的形式對測試平臺模組進行標準化，如今最新基於 SystemVerilog 的驗證方法學為通用驗證方法學。此方法學主要包括開放原始碼的類庫以及支援可重用測試平臺、開發驗證 IP 核的預置格式。許多第三方提供商則開始推出基於 SystemVerilog 的驗證 IP 核。

發展歷史

SystemVerilog 的歷史可以追溯到 2002 年，當時一個被稱為 “Superlog” 的語言被捐贈給 Accellera 公司（Verilog 的主要支持者）。而 Synopsys 公司捐贈的 OpenVera 後來發展成為 SystemVerilog 中硬體驗證語言子集。2005 年，SystemVerilog 獲批成為電氣電子工程師學會 1800-2005 標準。當時 Verilog 作為電氣電子工程師學會 1364-2005 標準尚獨立存在。2009 年，SystemVerilog 與的 Verilog 進行了合併，成為了新的電氣電子工程師學會 1800-2009 標準。現在的最新標準是電氣電子工程師學會 1800-2017 標準。

設計特性

對於電路設計工程師來說，SystemVerilog 中有大部分內容繼承自 Verilog，但是也提供了一些增強或者改進的特性。下面的章節主要講述 SystemVerilog 的這些特性。

2.1 新的資料型別

2.1.1 邏輯型變數

SystemVerilog 定義了一種新的邏輯型（logic）變數。和 Verilog 中變數的宣告類似，以下程式碼描述了一個 32 位的邏輯型變數：

logic 31:0 my_var;

在傳統的 Verilog 中，變數主要分為線網型（wire）和暫存器型（reg）兩大型別。只有暫存器型變數才能夠在過程程式碼塊中被賦值，而線網型變數只能在過程程式碼塊之外被連續賦值。暫存器型變數的過程賦值和線網型變數的連續賦值使用了完全不同的語句結構。在 Verilog 中，二者的區分比較微妙，以至於有些專業的工程師也在設計中混淆二者。由於 always 過程程式碼塊不僅可以描述時序邏輯電路，還可以透過將所有暫存器型的輸入變數新增到敏感列表來實現純組合邏輯電路，因此 “暫存器型” 這個帶有時序邏輯意義的術語本身也令人誤會。

SystemVerilog 增強了暫存器型變數的功能，它可以像 Verilog 中線網型變數一樣由線網（如邏輯閘等模組的輸出）驅動（這樣的線網驅動暫存器的方式在 Verilog 中是不允許的）。這種增強的變數型別被命名為 “邏輯型”，從而避免 “暫存器型” 在字面上給人帶來的誤會。在大多數情況中，SystemVerilog 中的 logic 可以替代 Verilog 中的 reg 和 wire，但是如果某個某個變數具有多個驅動源，那麼就不能使用 logic，而要使用嚴格的 wire 來定義它。

2.1.2 多維壓縮陣列

這種結構將 Verilog 中與暫存器、儲存器相關的概念進行了合併和擴充套件。

在 Verilog 中，變數名稱左邊的索引被用來表示二進位制變數的位寬，Verilog 規定它只能是一維的。而這個陣列名稱右邊的索引用來表示以這種位寬變數組成陣列的元素個數，因為陣列可以是一維陣列、二維陣列或者多維陣列，因此這個索引可以是任意整數。在 SystemVerilog 中，如果在變數名稱左邊指定了由高至低的位寬（如 8 位訊號由 7:0 表示），則稱之為 “壓縮陣列”（packed array，有時也被譯為 “合併陣列”）。壓縮陣列本身可以是多維的，即變數名稱左邊可以具有多維索引。如果在變數名稱右邊指定了陣列尺寸，則稱之為 “非壓縮陣列”。下面用示例程式碼表示了一個由二維壓縮陣列構成的一維非壓縮陣列：

logic 1:02:0 my_pack32;

在上面的例子裡，非壓縮陣列陣列 my_pack 具有 32 個元素（這裡用到了類似 C 語言的陣列元素個數表示方法，這裡也可以寫成 Verilog 中常見的 31:0 形式）。這個非壓縮陣列每一個元素本身又是壓縮陣列，即 2 個位寬為 3 的邏輯型變數，因此非壓縮陣列的每一個元素包含六位二進位制數的資訊。A variable of packed array type maps 1:1 onto an integer arithmetic quantity.

2.1.3 列舉

SystemVerilog 引入了列舉型變數，它使用一系列有實際字面意義的名稱來代表若干變數。如果不進行專門的資料型別轉換，一個列舉型變數不能直接賦值給另一個列舉型的變數。過去在 Verilog 中描述有限狀態機常常使用引數（關鍵字為 parameter），而在 SystemVerilog 中，使用列舉則更為方便。

typedef enum logic 2:0 {RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW} color_t;

colort mycolor = GREEN;

initial $display ("The color is % s", my_color.name );

上面的例子使用了 typedef 來建立了一個新的資料型別名稱，從而可以用它來建立一系列列舉資料。列舉型別的資料型別，為位寬為 3 的邏輯型變數。3 位二進位制數能夠逐一指代六種顏色。使用程式碼 colort mycolor = GREEN; 建立了一個新的 colort 型變數，其值初始化為六種顏色中的綠色。系統函式 $display 的作用與 Verilog 相同，其引數是 mycolor 所屬函式 name 的返回值，即當前列舉值的 ASCII 值。

2.1.4 其他新增的資料型別

除了基本的邏輯型變數（logic），SystemVerilog 正還提供了位元組型變數（byte，8 位）、短整型變數（shortint，16 位）、整型變數（int，32 位）和長整型變數（longint64 位）、位元型變數（bit，1 位，僅具有兩個邏輯狀態，和邏輯型資料（logic）缺少了未知邏輯（x）和高阻態（z）。使用兩態邏輯可以提高邏輯模擬速度）。

2.1.5 結構體和聯合體

這兩種資料型別和 C 語言中的結構體和聯合體類似。在 SystemVerilog 中，與這兩種資料型別相關的增強特性為壓縮屬性（packed）和標籤屬性（tagged）。壓縮屬性使得 mapped 1:1 onto a packed array of bits，而標籤屬性允許跟蹤當前聯合體中實際被使用的成員。這些結構佔據了連續的儲存空間。

typedef struct packed {

bit 10:0 expo;

bit sign;

bit 51:0 mant;

} FP;

FP zero = 64'b0;

2.2 條件、選擇語句的唯一性和優先性

當條件、選擇語句的路徑分支較為複雜時，設計人員稍不留意就可能造成程式碼所描述的行為違背設計人員預計的優先級別，或者被判斷的表示式同時滿足多個分支條件，從而在模擬過程中產生無規律結果。

為此，SystemVerilog 增強了條件、選擇語句的功能，允許設計人員為這分支流程的執行設定特別的約束。在多級條件、選擇語句中使用關鍵字 unique，可以限定有且只有一個分支可能被執行到，否則將產生一個警告。而關鍵字 priority 則指出某些分支路徑具有更高的優先順序。過去，在傳統的 Verilog 中，要實現類似的功能，設計人員需要在可綜合程式碼中附帶一些特殊的註釋（例如： //synopsys fullcase parallelcase）來通知邏輯綜合工具產生正確的分支邏輯電路。不過，註釋並非 SystemVerilog 程式碼的正式組成部分，它們只是在邏輯綜合過程被特定的工具讀取，而不嚴謹的註釋很可能造成綜合後電路與綜合前模擬結果不同的情況。

2.3 過程程式碼

Verilog 中的過程程式碼可以描述時序邏輯電路，也可以描述組合邏輯電路，這一點容易造成概念的混淆，如果程式碼不嚴謹，邏輯綜合很可能推斷出鎖存器，從而產生不符合預期的硬體模型。SystemVerilog 除了繼續支援原來 Verilog 中的老式過程結構 always、initial，還在原有 always 基礎上針對組合邏輯電路、觸發器、鎖存器設計了專用的 always 結構。雖然它們顯得不夠通用，但是三類硬體模型的差異得到了明確地區分，避免使用老式 always 時容易出現的鎖存器推斷：

alwayscomb：用於組合邏輯電路（相當於 Verilog 中對所有輸入變數電平敏感的 always，但 alwayscomb 無需手動列出所有輸入變數，系統會自動識別）；
always_ff：用於觸發器及相關的時序邏輯電路（相當於 Verilog 中對某個或某幾個訊號有效跳變沿敏感、並帶有訊號儲存特性的 always）；
always_latch：用於鎖存器級相關的時序邏輯電路（相當於 Verilog 中對某個或某幾個訊號電平敏感、並帶有訊號儲存特性的的 always）。

三種專用的 always 結構在使用時會檢查設計程式碼是否符合對應硬體模型的特徵。如果設計人員在過程程式碼中描述了與對應硬體不符合的行為（例如在 always_latch 的敏感列表中添加了定時器訊號的上升沿），那麼系統會發出警告。這是 SystemVerilog 種專用型過程結構的優越之處。

下面的例子使用 alwayscomb 來描述組合邏輯電路。在 SystemVerilog 中無需像 Verilog 中描述組合邏輯電路那樣寫出敏感列表，alwayscomb 會告訴邏輯綜合工具這是一個組合邏輯電路，因此係統會預設對所有輸入訊號的電平敏感。在表示組合邏輯電路的過程程式碼中，一般使用阻塞賦值。

always_comb begin

tmp = b b - 4 a * c;

no_root = (tmp

邏輯綜合工具會將 always_ff 推斷為同步時序邏輯，這裡需要寫明對什麼訊號的什麼邊緣（上升或者下降）敏感。在表示時序邏輯電路的過程程式碼中，一般使用非阻塞賦值。

always_ff @(posedge clk)

count

邏輯綜合工具還會將 always_latch 推斷為電平敏感的鎖存器電路，對輸入訊號的電平敏感，但是具有訊號儲存特性：

always_latch

if (en) q

介面

對於小型的設計，Verilog 設計人員可以使用埠（port）來簡潔地描述模組與外部環境的連線情況。不過，在規模較大、抽象層次較多的設計中，處於樞紐地位的模組往往具有大量連線與外界的連通。為此，SystemVerilog 引入了介面（interface）的概念，這個概念一方面減少了大量需要宣告的埠名稱，另一方面，它還方便設計人員將某些相關訊號的通道作為一捆相對獨立的線網組，這樣就使複雜的設計更加簡單、明晰。另一個概念是 modport，它顯示了邏輯連線的方向。例如：

interface intf;

logic a;

logic b;

modport in (input a, output b);

modport out (input b, output a);

endinterface

module top;

intf i ;

u_a m1 (.i1 (i));

u_b m2 (.i2 (i));

endmodule

module u_a (intf.in i1);

endmodule

module u_b (intf.out i2);

endmodule

驗證特性

隨著積體電路整合規模的不斷提高，電路的複雜程度也越來越大。在這種情況下，設計驗證在整個設計流程中所佔用的時間越來越高。SystemVerilog 在 Verilog 基礎上增加了許多專門針對驗證的特性，使其成為一種傑出的硬體驗證語言。下面所提到的驗證程式碼通常是不可綜合的，它們的作用主要體現在測試平臺的搭建過程中。

4.1 新的資料型別

SystemVerilog 引入了專門的字串型變數，用關鍵字 string 表示，例如：

string s1 = "Hello";

string s2 = "world";

string p = ".?!";

string s3 = {s1, ",", s2, p2}; // 字串的拼接

$display ("% d % s", s3.len , s3); // 模擬結果顯示為：“13 Hello, world!”

除了在設計中使用靜態陣列，SystemVerilog 還提供了動態陣列、關聯陣列（associative array）和佇列（queues）：

int cmdline_elements; // # elements for dynamic array

int da; // 動態陣列

int aiint; // 以整數位索引的關聯陣列

int asstring; // 以字串為索引的關聯陣列

int qa$; // 佇列

initial begin

cmdline_elements = 16;

da = new cmdline_elements ; // 為 da 陣列分配 16 個元素

end

動態陣列的行為和非壓縮陣列相似，不過它允許模擬在執行時動態分配元素個數（正如上面例子所示）。壓縮陣列部分的元素個數（以往稱之為 “位寬”）必須在編譯的時候已知（透過由常數或常數表示式直接確定），而非壓縮陣列的動態大小則可以在程式執行時，由另一個執行中的程式變數進行初始化，這樣就可以在執行中根據所需改變陣列的大小。

4.2 類

SystemVerilog 為驗證程式碼的編寫提供了面向物件程式設計的模型。

SystemVerilog 支援類的單一繼承，即不允許一個類繼承多個基本類。需要注意的是，雖然 SystemVerilog 裡提供了與 Java 中名稱相同的 “介面” 概念，但是二者的實際含義卻大有不同，後者的介面能夠提供類似多繼承的功能。SystemVerilog 的類可以自帶配置引數，這種類被稱為引數化的類，其功能與 C++ 的模板類似，不過 SystemVerilog 不支援模板特化和函式模板。SystemVerilog 的多型性和 C++ 類似，在子類中被覆蓋的同名方法在基本類中必須使用關鍵字 virtual 予以標識。SystemVerilog 裡也有諸如 local、protected 等關鍵字來管理資料、方法成員的可見性。預設情況下，所有方法、資料都是對外公有的。類的示例由建構函式 new 來建立，而解構函式則無需驗證人員手動建立。如果某個物件沒有被任何控制代碼指向，系統會自動銷燬它。SystemVerilog 的類是構建諸如通用驗證方法學類庫結構的基礎。

virtual class Memory;

virtual function bit 31:0 read (bit 31:0 addr); endfunction

virtual function void write (bit 31:0 addr, bit 31:0 data); endfunction

endclass

class SRAM #(parameter AWIDTH=10) extends Memory;

bit 31:0 mem 1

4.3 含約束的隨機化

可以對定義在類或者其他語句塊中的整數型變數進行隨機化，從而實現輸入激勵的隨機化，大大降低了驗證人員專門定義輸入激勵的複雜程度。這時使用 SystemVerilog 構建隨機驗證平臺的重要技術基礎。在類資料變數宣告前使用 rand 和 randc 這兩個關鍵字，可以讓系統得知這些變數在隨後被隨機化。randc 產生的隨機數序列可以保證變數被再次賦值為同一數值之前，所有可能數值已經被遍歷，而宣告為 rand 的變數則在隨機化過程中沒有這一特性。

class eth_frame;

rand bit 47:0 dest;

rand bit 47:0 src;

rand bit 15:0 type;

rand byte payload;

bit 31:0 fcs;

rand bit 31:0 fcs_corrupt;

constraint basic {payload.size inside {46:1500};

}

constraint goodfr {fcscorrupt == 0;}

endclass

在上面這一例子中，資料成員 fcs 未被宣告為隨機變數，而其他幾個資料成員則被宣告為隨機變數。在這個類中，緊隨變數宣告的是兩個對隨機化進行約束的語句塊，用關鍵字 constraint 予以標識。例如第一個名為 basic 的約束要求資料成員 payload（動態陣列）的大小在 46 和 1500 之間（包含這兩個邊界值）。如果沒有專門指定約束條件，則變數在可能的取值範圍內的隨機化不受任何約束。約束塊內程式碼的寫法有若干種，驗證人員可以設定更加複雜的約束關係，例如變數取得區間甲內某一數值的權重為某一定值，或者採用多個數學關係式來限定變數的隨機化取值。當然，這樣做的前提是數學上能夠找到數值來同時滿足這些約束。SystemVerilog 的解析器會盡量產生符合條件的隨機數值，但是如果沒能夠成功，則會返回資訊告知驗證人員此隨機化過程失敗。

4.3.1 隨機化方法

每一個 SystemVerilog 類都有隱含的三個與隨機化有關的方法，它們是 prerandomize、randomize 和 postrandomize。其中，randomize 方法為整個隨機化過程中最重要的方法，驗證人員可以在過程程式碼中使用該方法來啟動變數的隨機化過程。注意該方法的主體是事務物件本身（在通用驗證方法學中，事務一般是一個具有 uvmsequenceitem 型別或從它繼承而來的型別），因此需要用 object.randomize 來呼叫該方法，每執行一次該方法，該物件所有的隨機變數將會得到滿足約束條件（如果有）的隨機數值。如果隨機化失敗，例如沒能找到滿足工程師定義約束的數值，則該方法將返回 0 表示隨機化存在問題。另外兩個方法 prerandomize 和 postrandomize 則可以被用來處理隨機化主過程之前、之後其他需要的事情。

4.3.2 控制約束條件

此外，SystemVerilog 還提供了 constraintmode 和 randommode 這兩個方法來在過程程式碼中控制隨機化，前者用於臨時開啟或關閉一個約束，而後者則用於臨時開啟或關閉某個變數的隨機化。此外還有 randomize with {constraint} 方法可以在該方法被呼叫時將花括號內的約束附加到原有的約束條件之上。

class eth_frame;

rand bit 47:0 dest;

rand bit 47:0 src;

rand bit 15:0 type;

rand byte payload;

bit 31:0 fcs;

rand bit corrupted_frame;

constraint basic {payload.size inside {46:1500};

}

constraint onesrccst {src == 48'h1f00}

endclass

.

.

.

ethframe myframe;

myframe.onesrccst.constraintmode (0); // 關閉 onesrccst 約束，之後該變數的隨機化將不再受該約束限制

myframe.type.randommode (0); // 關閉 type 變數的約束，之後的隨機化將不會給該變數賦予隨機數值

my_frame.randomize ;

4.4 斷言

與其他屬性規約語言類似，SystemVerilog 具有斷言這一概念。在整個晶片驗證流程中，斷言檢查器會自動檢查定義的屬性是否得到滿足，如果違背，則會給出警告。SystemVerilog 斷言由序列（sequences）和屬性（properties）兩部分組成。屬性可以是多個序列在時間上的組合。序列主要用布林表示式和時序運算子來描述。

sequence S1;

@(posedge clk) req ##1 gnt;

endsequence

這一簡單序列的意思是，訊號 gnt 會在訊號 req 為高電平一個時鐘週期後也為高電平，如果這一情況沒有發生，則斷言失敗。SystemVerilog 還提供其他一些時序運算子，例如重複、連續運算子。這些運算子可以讓驗證人員定義更加複雜的變數關係。在上面這個例子中，加入訊號 req 為低電平，則這個斷言永遠都是失敗的。我們可以使用蘊含運算子 |=> 來表示我們所需的情況。

property req_gnt;

@(posedge clk) req |=> gnt;

endproperty

assertreqgnt: assert property (req_gnt) else $error ("req not followed by gnt.");

上面這個例子顯示了蘊含（implication）運算子 |=> 的使用。它左邊的子句稱為先行運算元（antecedent）而右邊的子句稱為後續運算元（consequent）。這時，如果 req 未滿足高電平的要求，則後面的 gnt 無論是高電平還是低電平，整個斷言都預設為成功；但是如果 req 為高電平，則系統會根據 gnt 的邏輯值來決定斷言是否成功。除此之外，SystemVerilog 還支援斷言覆蓋。

4.5 功能覆蓋

在硬體驗證中，功能覆蓋是指驗證過程中對人工定義訊號事件的資料取樣和收集過程。功能覆蓋率在某種程度上反映了被測設計在所給輸入激勵下，其內部功能正確與否被檢測到的百分比。功能覆蓋率越高，一般代表測試越完備。注意功能覆蓋率和程式碼覆蓋率不同，後者只是測量被測設計的所有程式碼有多少在模擬過程中被執行過。功能覆蓋的目的是確保設計中的所有邊界情況都能夠被經歷。

SystemVerilog 覆蓋組（coverage group）內可以定義若干個倉（bins）來儲存相關變數取值在其範圍內的次數。交叉覆蓋（Cross-coverage）則可以進一步獲取多個變數分別在某一範圍內統計資料。透過使用覆蓋組的 sample 方法，可以啟動該覆蓋率的統計。

For example:

class eth_frame;

// Definitions as above

covergroup cov;

coverpoint dest {bins bcast1 = {48'hFFFFFFFFFFFF};

bins ucast1 = default;

}

coverpoint type {bins length16 = { 0:1535};

bins typed16 = {1536:32767};

bins other1 = default;

}

psize: coverpoint payload.size {bins size = { 46, 47:63, 64, 65:511, 512:1023, 1024:1499, 1500};

}

szxt: cross type, psize;

endgroup

endclass

在這一例子中，驗證工程師重點關注廣播和單播幀的分佈以及負載大小。

4.6 同步化

一個複雜的測試平臺往往是包含多個可重用驗證模組的環境，這些模組之間需要互相通訊。Verilog 的事件（event）允許不同語句塊之間相互觸發，不過這在複雜測試平臺的執行緒間通訊比較繁瑣。為此，SystemVerilog 提供了兩種新的語言要素，信箱（mailbox）和旗語（semaphore）。信箱類似一個引數化的 FIFO（意味著其內容的型別是可定義的），可以將符合其型別的物件 “裝入” 和 “取出” 信箱。而旗語則限定了對同一資源的訪問個數。