SystemVerilog

發展歷史

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

設計特性

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

邏輯型變數

SystemVerilog定義了一種新的邏輯型（logic）變數。和Verilog中變數的聲明類似，以下代碼描述了一個32位元的邏輯型變數：
logic [31:0] my_var;
在傳統的Verilog中，變數主要分為線網型（wire）和暫存器型（reg）兩大類型。只有暫存器型變數才能夠在過程代碼塊中被賦值，而線網型變數只能在過程代碼塊之外被連續賦值。暫存器型變數的過程賦值和線網型變數的連續賦值使用了完全不同的語句結構。在Verilog中，二者的區分比較微妙，以至於有些專業的工程師也在設計中混淆二者。由於always過程代碼塊不僅可以描述序向邏輯電路，還可以通過將所有暫存器型的輸入變數添加到敏感列表來實現純組合邏輯電路，因此「暫存器型」這個帶有序向邏輯意義的術語本身也令人誤會。
SystemVerilog增強了暫存器型變數的功能，它可以像Verilog中線網型變數一樣由線網（如邏輯閘等模組的輸出）驅動（這樣的線網驅動暫存器的方式在Verilog中是不允許的）。這種增強的變數類型被命名為「邏輯型」，從而避免「暫存器型」在字面上給人帶來的誤會。在大多數情況中，SystemVerilog中的logic可以替代Verilog中的reg和wire，但是如果某個某個變數具有多個驅動源，那麼就不能使用logic，而要使用嚴格的wire來定義它。

多維壓縮陣列

這種結構將Verilog中與暫存器、記憶體相關的概念進行了合併和擴充功能。
在Verilog中，變數名稱左邊的索引被用來表示二進位變數的位寬，Verilog規定它只能是一維的。而這個陣列名稱右邊的索參考來表示以這種位寬變數組成陣列的元素個數，因為陣列可以是一維陣列、二維陣列或者多維陣列，因此這個索引可以是任意整數。在SystemVerilog中，如果在變數名稱左邊指定了由高至低的位寬（如8位元訊號由[7:0表示），則稱之為「壓縮陣列」（packed array，有時也被譯為「合併陣列」）。壓縮陣列本身可以是多維的，即變數名稱左邊可以具有多維索引。如果在變數名稱右邊指定了陣列尺寸，則稱之為「非壓縮陣列」。下面用範例代碼表示了一個由二維壓縮陣列構成的一維非壓縮陣列：
logic [1:0][2:0] my_pack[32];
在上面的例子裡，非壓縮陣列陣列my_pack具有32個元素（這裡用到了類似C語言的陣列元素個數表示方法，這裡也可以寫成Verilog中常見的[31:0]形式）。這個非壓縮陣列每一個元素本身又是壓縮陣列，即2個位寬為3的邏輯型變數，因此非壓縮陣列的每一個元素包含六位二進位數的資訊。A variable of packed array type maps 1:1 onto an integer arithmetic quantity.

列舉

SystemVerilog引入了列舉型變數，它使用一系列有實際字面意義的名稱來代表若干變數。如果不進行專門的資料類型轉換，一個列舉型變數不能直接賦值給另一個列舉型的變數。過去在Verilog中描述有限狀態機常常使用參數（關鍵字為parameter），而在SystemVerilog中，使用列舉則更為方便。
typedef enum logic [2:0] {
   RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW} color_t;color_t   my_color = GREEN;initial $display("The color is %s", my_color.name());
上面的例子使用了typedef來建立了一個新的資料類型名稱，從而可以用它來建立一系列列舉資料。列舉類型的資料類型，為位寬為3的邏輯型變數。3位二進位數能夠逐一指代六種顏色。使用代碼color_t my_color = GREEN;建立了一個新的color_t型變數，其值初始化為六種顏色中的綠色。系統函式$display的作用與Verilog相同，其參數是my_color所屬函式name()的返回值，即當前列舉值的ASCII值。

其他新增的資料類型

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

結構體和聯合體

這兩種資料類型和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;

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

當條件、選擇語句的路徑分支較為複雜時，設計人員稍不留意就可能造成代碼所描述的行為違背設計人員預計的優先級別，或者被判斷的運算式同時滿足多個分支條件，從而在仿真過程中產生無規律結果。
為此，SystemVerilog增強了條件、選擇語句的功能，允許設計人員為這分支流程的執行設定特別的約束。在多級條件、選擇語句中使用關鍵字unique，可以限定有且只有一個分支可能被執行到，否則將產生一個警告。而關鍵字priority則指出某些分支路徑具有更高的優先級。過去，在傳統的Verilog中，要實現類似的功能，設計人員需要在可綜合代碼中附帶一些特殊的注釋（例如： // synopsys full_case parallel_case）來通知邏輯綜合工具產生正確的分支邏輯電路。不過，注釋並非SystemVerilog代碼的正式組成部分，它們只是在邏輯綜合過程被特定的工具讀取，而不嚴謹的注釋很可能造成綜合後電路與綜合前仿真結果不同的情況。

介面

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

驗證特性

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

新的資料類型

SystemVerilog引入了專門的字串型變數，用關鍵字string表示，例如：
string s1 = "Hello";string s2 = "world";string p = ".?!";string s3 = {s1, ", ", s2, p[2]}; // 字符串的拼接$display("[%d] %s", s3.len(), s3); // 仿真结果显示为：“[13] Hello, world!”
除了在設計中使用靜態陣列，SystemVerilog還提供了動態陣列、關聯陣列（associative array）和佇列（queues）：
int cmdline_elements; // # elements for dynamic arrayint da[];       // 动态数组int ai[int];    // 以整数位索引的关联数组int as[string]; // 以字符串为索引的关联数组int qa[$];      // 队列initial begin
    cmdline_elements = 16;
    da = new[ cmdline_elements ]; // 为da数组分配16个元素end
動態陣列的行為和非壓縮陣列相似，不過它允許仿真在執行時動態分配元素個數（正如上面例子所示）。壓縮陣列部分的元素個數（以往稱之為「位寬」）必須在編譯的時候已知（通過由常數或常數運算式直接確定），而非壓縮陣列的動態大小則可以在程式執行時，由另一個執行中的程式變數進行初始化，這樣就可以在執行中根據所需改變陣列的大小。

類

SystemVerilog為驗證代碼的編寫提供了物件導向程式設計的模型。
SystemVerilog支援類的單一繼承，即不允許一個類繼承多個基本類。需要注意的是, 雖然SystemVerilog里提供了與Java中名稱相同的「介面」概念，但是二者的實際含義際ha卻大有不同，後者的介面能夠提供類似多繼承的功能。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); endfunctionendclassclass SRAM #(parameter AWIDTH=10) extends Memory;
    bit [31:0] mem [1<<AWIDTH];

    virtual function bit [31:0] read(bit [31:0] addr);
        return mem[addr];
    endfunction

    virtual function void write(bit [31:0] addr, bit [31:0] data);
        mem[addr] = data;
    endfunctionendclass

含約束的隨機化

可以對定義在類或者其他語句塊中的整數型變數進行隨機化，從而實現輸入激勵的隨機化，大大降低了驗證人員專門定義輸入激勵的複雜程度。這時使用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 good_fr {
        fcs_corrupt == 0;
    }endclass
在上面這一例子中，成員變數fcs未被聲明為隨機變數，而其他幾個成員變數則被聲明為隨機變數。在這個類中，緊隨變數聲明的是兩個對隨機化進行約束的語句塊，用關鍵字constraint予以標識。例如第一個名為basic的約束要求成員變數payload[]（動態陣列）的大小在46和1500之間（包含這兩個邊界值）。如果沒有專門指定約束條件，則變數在可能的取值範圍內的隨機化不受任何約束。約束塊內代碼的寫法有若干種，驗證人員可以設定更加複雜的約束關係，例如變數取得區間甲內某一數值的權重為某一定值，或者採用多個數學關係式來限定變數的隨機化取值。當然，這樣做的前提是數學上能夠找到數值來同時滿足這些約束。SystemVerilog的解析器會儘量產生符合條件的亂數值，但是如果沒能夠成功，則會返回資訊告知驗證人員此隨機化過程失敗。

隨機化方法

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

控制約束條件

此外，SystemVerilog還提供了constraint_mode()和random_mode()這兩個方法來在過程代碼中控制隨機化，前者用於臨時開啟或關閉一個約束，而後者則用於臨時開啟或關閉某個變數的隨機化。此外還有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 one_src_cst {
        src == 48'h1f00
    }
    
endclass...eth_frame my_frame; 

my_frame.one_src_cst.constraint_mode(0); //关闭one_src_cst约束，之后该变量的随机化将不再受该约束限制my_frame.type.random_mode(0);        // 关闭type变量的约束，之后的随机化将不会给该变量赋予随机数值my_frame.randomize();

斷言

與其他屬性規約語言類似，SystemVerilog具有斷言這一概念。在整個晶片驗證流程中，斷言檢查器會自動檢查定義的屬性是否得到滿足，如果違背，則會給出警告。SystemVerilog斷言由序列（sequences）和屬性（properties）兩部分組成。屬性可以是多個序列在時間上的組合。序列主要用布林運算式和時序操作符來描述。
sequence S1;
    @(posedge clk) req ##1 gnt;endsequence
這一簡單序列的意思是，訊號gnt會在訊號req為高電平一個時鐘周期後也為高電平，如果這一情況沒有發生，則斷言失敗。SystemVerilog還提供其他一些時序操作符，例如重複、連續操作符。這些操作符可以讓驗證人員定義更加複雜的變數關係。在上面這個例子中，加入訊號req為低電平，則這個斷言永遠都是失敗的。我們可以使用蘊含操作符|=>來表示我們所需的情況。
property req_gnt;
    @(posedge clk) req |=> gnt;endpropertyassert_req_gnt: assert property (req_gnt) else $error("req not followed by gnt.");
上面這個例子顯示了蘊含（implication）操作符|=>的使用。它左邊的子句稱為先行算子（antecedent）而右邊的子句稱為後續算子（consequent）。這時，如果req未滿足高電平的要求，則後面的gnt無論是高電平還是低電平，整個斷言都預設為成功；但是如果req為高電平，則系統會根據gnt的邏輯值來決定斷言是否成功。除此之外，SystemVerilog還支援斷言覆蓋。

功能覆蓋

在硬體驗證中，功能覆蓋是指驗證過程中對人工定義訊號事件的資料取樣和收集過程。功能覆蓋率在某種程度上反映了被測設計在所給輸入激勵下，其內部功能正確與否被檢測到的百分比。功能覆蓋率越高，一般代表測試越完備。注意功能覆蓋率和代碼覆蓋率不同，後者只是測量被測設計的所有代碼有多少在仿真過程中被執行過。功能覆蓋的目的是確保設計中的所有邊界情況都能夠被經歷。
SystemVerilog覆蓋組（coverage group）內可以定義若干個倉（bins）來儲存相關變數取值在其範圍內的次數。交叉覆蓋（Cross-coverage）則可以進一步取得多個變數分別在某一範圍內統計資料。通過使用覆蓋組的sample方法，可以啟動該覆蓋率的統計。
For example:
class eth_frame;
   // Definitions as above
   covergroup cov;
      coverpoint dest {
          bins bcast[1] = {48'hFFFFFFFFFFFF};
          bins ucast[1] = default;
      }
      coverpoint type {
          bins length[16] = { [0:1535] };
          bins typed[16] = { [1536:32767] };
          bins other[1] = default;
      }
      psize: coverpoint payload.size {
          bins size[] = { 46, [47:63], 64, [65:511], [512:1023], [1024:1499], 1500 };
      }

      sz_x_t: cross type, psize;
   endgroupendclass
在這一例子中，驗證工程師重點關注廣播和單播影格的分布以及負載大小。

同步化

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