SystemVerilog

硬體描述・硬體驗證統一語言

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

SystemVerilog
編程範型結構化 (設計)
物件導向 (驗證)
設計者Synopsys, 後來是IEEE
釋出時間2002年,​22年前​(2002
型態系統靜態, 弱型別
副檔名.sv, .svh
啟發語言
設計: Verilog, VHDL, C++, 驗證: OpenVera, Java

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

發展歷史

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

設計特性

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

新的資料類型

邏輯型變數

SystemVerilog定義了一種新的邏輯型(logic)變數。和Verilog中變數的聲明類似,以下代碼描述了一個32位元的邏輯型變數:

logic [31:0] my_var;

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

SystemVerilog增強了暫存器型變數的功能,它可以像Verilog中線網型變數一樣由線網(如邏輯門等模組的輸出)驅動(這樣的線網驅動暫存器的方式在Verilog中是不允許的)。這種增強的變數類型被命名為「邏輯型」,從而避免「暫存器型」在字面上給人帶來的誤會。在大多數情況中,SystemVerilog中的logic可以替代Verilog中的regwire,但是如果某個某個變數具有多個驅動源,那麼就不能使用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中的過程代碼可以描述序向邏輯電路,也可以描述組合邏輯電路,這一點容易造成概念的混淆,如果代碼不嚴謹,邏輯綜合很可能推斷出鎖存器,從而產生不符合預期的硬件模型。SystemVerilog除了繼續支援原來Verilog中的老式過程結構alwaysinitial,還在原有always基礎上針對組合邏輯電路正反器鎖存器設計了專用的always結構。雖然它們顯得不夠通用,但是三類硬件模型的差異得到了明確地區分,避免使用老式always時容易出現的鎖存器推斷:

  • always_comb:用於組合邏輯電路(相當於Verilog中對所有輸入變數電平敏感的always,但always_comb無需手動列出所有輸入變數,系統會自動辨識);
  • always_ff:用於正反器及相關的序向邏輯電路(相當於Verilog中對某個或某幾個訊號有效跳變沿敏感、並帶有訊號儲存特性的always);
  • always_latch:用於鎖存器級相關的序向邏輯電路(相當於Verilog中對某個或某幾個訊號電平敏感、並帶有訊號儲存特性的的always)。

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

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

always_comb begin
    tmp = b * b - 4 * a * c;
    no_root = (tmp < 0);
end

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

always_ff @(posedge clk)
    count <= count + 1;

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

always_latch
    if (en) q <= d;

介面

對於小型的設計,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基礎上增加了許多專門針對驗證的特性,使其成為一種傑出的硬件驗證語言。下面所提到的驗證代碼通常是不可綜合的,它們的作用主要體現在測試平台的搭建過程中。[5]

新的資料類型

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 array
int 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中名稱相同的「介面」概念,但是二者的實際含義卻大有不同,後者的介面能夠提供類似多繼承的功能。SystemVerilog的類可以內建組態參數,這種類被稱為參數化的類,其功能與C++的模板類似,不過SystemVerilog不支援模板特化和函數模板。SystemVerilog的多型性和C++類似,在子類中被覆蓋的同名方法在基本類中必須使用關鍵字virtual予以標識。SystemVerilog里也有諸如localprotected等關鍵字來管理數據、方法成員的可見性。預設情況下,所有方法、數據都是對外公有的。類的範例由建構函式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<<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;
    endfunction
endclass

含約束的隨機化

可以對定義在類或者其他陳述式塊中的整數型變數進行隨機化,從而實現輸入激勵的隨機化,大大降低了驗證人員專門定義輸入激勵的複雜程度。這時使用SystemVerilog構建隨機驗證平台的重要技術基礎。在類數據變數聲明前使用randrandc這兩個關鍵字,可以讓系統得知這些變數在隨後被隨機化。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類都有隱含的三個與隨機化有關的方法[註 1],它們是pre_randomizerandomizepost_randomize。其中,randomize方法為整個隨機化過程中最重要的方法,驗證人員可以在過程代碼中使用該方法來啟動變數的隨機化過程。注意該方法的主體是事務對象本身(在通用驗證方法學中,事務一般是一個具有uvm_sequence_item類型或從它繼承而來的類型),因此需要用object.randomize()來呼叫該方法,每執行一次該方法,該對象所有的隨機變數將會得到滿足約束條件(如果有)的亂數值。如果隨機化失敗,例如沒能找到滿足工程師定義約束的數值,則該方法將返回0表示隨機化存在問題。另外兩個方法pre_randomizepost_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)兩部分組成。屬性可以是多個序列在時間上的組合。序列主要用布林表達式和時序運算子來描述。[6]

sequence S1;
    @(posedge clk) req ##1 gnt;
endsequence

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

property req_gnt;
    @(posedge clk) req |=> gnt;
endproperty

assert_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;
   endgroup
endclass

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

同步化

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

其他與傳統Verilog類似概念的比較

除了前面提到的特性,SystemVerilog還在以下方面對傳統的Verilog進行了增強:[1]

  • 過程設定運算子(包括阻塞和非阻塞)能夠直接對陣列進行操作
  • 埠(輸入、輸出、雙向)可以支援更多的資料類型,包括結構體、列舉、實數等
  • For迴圈內部可以自動定義變數,並為它添加了continuebreak功能
  • 在原有的while迴圈基礎上添加了do-while迴圈
  • 可以使用關鍵字const來聲明常數
  • 可以對整個陣列進行初始化
  • 預處理器的字串宏的功能得到增強
  • 除了原有的並行過程陳述式塊fork/join結構,還提供join_nonejoin_any兩種代碼塊結尾方式
  • 任務的埠能夠被聲明為參照類型(ref),這使得任務能夠直接取得任務參數自身(參照傳遞),而不是一個臨時複製的數值(值傳遞)。因此,對輸入方向的形式參數的操作會直接改變對應的實際參數。
  • 函數能夠被聲明為空類型void,即不返回任何數值
  • 參數(parameters)可以被聲明為任何類型,包括用戶使用typedef定義的新類型
  • 提供SystemVerilog DPI(即SystemVerilog直接編程介面)來連接以C語言、C++編寫的模組

註釋

  1. ^ 由於SystemVerilog是一門物件導向程式設計語言,因此通常將Verilog中類似的任務(task)和函數(function)統稱為方法。

參考文獻

參照

  1. ^ 1.0 1.1 鍾文楓. SystemVerilog与功能验证. 機械工業出版社. 2010. ISBN 978-7-111-31373-1. 
  2. ^ Rich, D. The evolution of SystemVerilog. IEEE Design and Test of Computers. 
  3. ^ IEEE approves SystemVerilog, revision of Verilog. EE Times. 2005-11-03 [2013-07-14]. (原始內容存檔於2007-09-29). 
  4. ^ 1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language. [2021-11-03]. (原始內容存檔於2021-12-13). 
  5. ^ 5.0 5.1 克里斯·斯皮爾. SystemVerilog验证:测试平台编写指南. 科學出版社. 2009. ISBN 978-7-03-025306-4. 
  6. ^ Srikanth Vijayaraghavan, Meyyappan Ramanathan. SystemVerilog Assertions 应用指南. 清華大學出版社. 2006. ISBN 978-7-30-213441-1. 

來源

外部連結

IEEE 標準文獻

教程

標準開發

語言延伸

參見