Verilog

Verilog 是一種用於描述、設計電子系統（特別是數位電路）的硬體描述語言，主要用於在積體電路設計，特別是超大規模積體電路的計算機輔助設計。Verilog 是電氣電子工程師學會（IEEE）的 1364 號標準。

Verilog 能夠在多種抽象級別對數字邏輯系統進行描述：既可以在電晶體級、邏輯閘級進行描述，也可以在暫存器傳輸級對電路訊號在暫存器之間的傳輸情況進行描述。除了對電路的邏輯功能進行描述，Verilog 程式碼還能夠被用於邏輯模擬、邏輯綜合，其中後者可以把暫存器傳輸級的 Verilog 程式碼轉換為邏輯閘級的網表，從而方便在現場可程式設計邏輯閘陣列上實現硬體電路，或者讓硬體廠商製造具體的專用積體電路。設計人員還可以利用 Verilog 的擴充套件部分 Verilog-AMS 進行類比電路和混合訊號積體電路的設計。

發展歷史

1983 年末，Gateway 設計自動化公司的工程師創立了 Verilogs。當時 Gateway 設計自動化公司還叫做自動整合設計系統（Automated Integrated Design Systems），1985 年公司將名字改為 Gateway 設計自動化。該公司的菲爾・莫比（Phil Moorby）完成了 Verilog 的主要設計工作。1990 年，Gateway 設計自動化被 Cadence 公司收購。

1990 年代初，開放 Verilog 國際（Open Verilog International, OVI）組織（即現在的 Accellera）成立，Verilog 面向公有領域開放。1992 年，該組織尋求將 Verilog 納入電氣電子工程師學會標準 。最終，Verilog 成為了電氣電子工程師學會 1364-1995 標準，即通常所說的 Verilog-95。

設計人員在使用這個版本的 Verilog 的過程中發現了一些可改進之處。為了解決使用者在使用此版本 Verilog 過程中反映的問題，Verilog 進行了修正和擴充套件，這部分內容後來再次被提交給電氣電子工程師學會。這個擴充套件後的版本後來成為了電氣電子工程師學會 1364-2001 標準，即通常所說的 Verilog-2001。Verilog-2001 是對 Verilog-95 的一個重大改進版本，它具備一些新的實用功能，例如敏感列表、多維陣列、生成語句塊、命名埠連線等。目前，Verilog-2001 是 Verilog 的最主流版本，被大多數商業電子設計自動化軟體包支援。

2005 年，Verilog 再次進行了更新，即電氣電子工程師學會 1364-2005 標準。該版本只是對上一版本的細微修正。這個版本還包括了一個相對獨立的新部分，即 Verilog-AMS。這個擴充套件使得傳統的 Verilog 可以對整合的模擬和混合訊號系統進行建模。容易與電氣電子工程師學會 1364-2005 標準混淆的是加強硬體驗證語言特性的 SystemVerilog（電氣電子工程師學會 1800-2005 標準），它是 Verilog-2005 的一個超集，它是硬體描述語言、硬體驗證語言（針對驗證的需求，特別加強了面向物件特性）的一個整合。

2009 年，IEEE 1364-2005 和 IEEE 1800-2005 兩個部分合併為 IEEE 1800-2009，成為了一個新的、統一的 SystemVerilog 硬體描述驗證語言（hardware description and verification language, HDVL）。

以模組為基礎的設計

描述複雜的硬體電路，設計人員總是將複雜的功能劃分為簡單的功能，模組是提供每個簡單功能的基本結構。設計人員可以採取 “自頂向下” 的思路，將複雜的功能模組劃分為低層次的模組。這一步通常是由系統級的總設計師完成，而低層次的模組則由下一級的設計人員完成。自頂向下的設計方式有利於系統級別層次劃分和管理，並提高了效率、降低了成本。“自底向上” 方式是 “自頂向下” 方式的逆過程。

使用 Verilog 描述硬體的基本設計單元是模組（module）。構建複雜的電子電路，主要是透過模組的相互連線呼叫來實現的。模組被包含在關鍵字 module、endmodule 之內。實際的電路元件。Verilog 中的模組類似 C 語言中的函式，它能夠提供輸入、輸出埠，可以例項呼叫其他模組，也可以被其他模組例項呼叫。模組中可以包括組合邏輯部分、過程時序部分。例如，四選一的多路選擇器，就可以用模組進行描述。它具有兩個位選輸入訊號、四個資料輸入，一個輸出端，在 Verilog 中可以表示為：

module mux (out, select, in0, in1, in2, in3);

output out;

input 1:0 select;

input in0, in1, in2, in3;

// 具體的暫存器傳輸級程式碼

endmodule

設計人員可以使用一個頂層模組，透過例項呼叫上面這個模組的方式來進行測試。這個頂層模組常被稱為 “測試平臺（Testbench）”。

為了最大程度地對電路的邏輯進行功能驗證，測試程式碼需要儘可能多地覆蓋系統所涉及的語句、分支、條件、路徑、觸發、狀態機狀態，驗證人員需要在測試平臺裡建立足夠多的輸入激勵，並連線到被測模組的輸入端，然後檢測其輸出端的表現是否符合預期（諸如 SystemVerilog 的硬體驗證語言能夠提供針對驗證專門最佳化的資料結構，以隨機測試的方式進行驗證，這對於高度複雜的積體電路設計驗證可以起到關鍵作用）。例項呼叫模組時，需要將埠的連線情況按照這個模組宣告時的順序排列。這個頂層模組由於不需要再被外界呼叫，因此沒有輸入輸出埠：

module tester;

reg 1:0 SELECT;

reg IN0, IN1, IN2, IN3;

wire OUT;

mux mymux (OUT, SELECT, IN0, IN1, IN2, IN3); // 例項呼叫 mux 模組，這個例項被命名為 mymux

initial // 需要模擬的激勵程式碼

begin

end

endmodule

在這個測試平臺模組裡，設計人員可以設定模擬時的輸入訊號以及訊號監視程式，然後觀察模擬時的輸出情況是否符合要求，這樣就可以瞭解設計是否達到了預期。

示例中的對模組進行例項引用時，按照原模組宣告時的順序羅列了輸入變數。除此之外，還可以使用或者採用命名埠連線的方式。使用這種方式，埠的排列順序可以與原模組宣告時不同，甚至可以不連線某些埠：

mux my_mux (.out (OUT), .select (SELECT), .in0 (IN0), .in1 (IN1), .in2 (IN2), .in3 (IN3));

// 使用命名埠連線，括號外面是模組宣告時的埠，括號內是實際的埠連線

// 括號外相當於 C 語言的形式引數，括號內相當於實際引數

endmodule

上面所述的情況是，測試平臺頂層模組的測試變數直接連線了所設計的功能模組。測試平臺還可以是另一種形式，即測試平臺並不直接連線所設計的功能模組，而是在這個測試平臺之下，將激勵模組和功能模組以相同的抽象級別，透過線網相互連線。這兩種形式的測試平臺都可以完成對功能模組的測試。大型的電路系統，正是由各個層次不同模組之間的連線、呼叫，來實現複雜的功能的。

語言要素

Verilog 的設計初衷是成為一種基本語法與 C 語言相近的硬體描述語言。這是因為在 Verilog 設計之初，C 語言已經在許多領域得到廣泛應用，C 語言的許多語言要素已經被許多人習慣。一種與 C 語言相似的硬體描述語言，可以讓電路設計人員更容易學習和接受。不過，Verilog 與 C 語言還是存在許多差別。另外，作為一種與普通計算機程式語言不同的硬體描述語言，它還具有一些獨特的語言要素，例如向量形式的線網和暫存器、過程中的非阻塞賦值等。總的來說，具備 C 語言能力的設計人員將能夠很快掌握 Verilog 硬體描述語言。

3.1 基本規範

3.1.1 空白符

空白符是指程式碼中的空格（對應的轉義識別符號為 \b）、製表符（\t）和換行（\n）。如果這些空白符出現在字串裡，那麼它們不可忽略。除此之外，程式碼中的其他空白符在編譯的時候都將會被視為分隔識別符號，即使用 2 個空格或者 1 個空格並無影響。不過，在程式碼中使用合適的空格，可以讓上下行程式碼的外觀一致（例如使賦值運算子位於同一個豎直列），從而提高程式碼的可讀性。

3.1.2 註釋

為了方便程式碼的修改或其他人的閱讀，設計人員通常會在程式碼中加入註釋。與 C 語言一樣，有兩種方式書寫註釋。第一種為多行註釋，即註釋從 / 開始，直到 / 才結束；另一種為單行註釋，註釋從 // 開始，從這裡到這一行末尾的內容會被系統識別為註釋。

某些電子設計自動化工具，會識別出程式碼中以特殊格式書寫、含有某些預先約定關鍵詞的註釋，並從這些註釋所提取有用的資訊。這些註釋不是供人閱讀，而是向第三方工具提供有關設計專案的額外資訊。例如，某些邏輯綜合工具可以從註釋中讀取綜合的約束資訊。

3.1.3 大小寫敏感性

Verilog 是一種大小寫敏感的硬體描述語言。其中，它的所有系統關鍵字都是小寫的。

3.1.4 識別符號及保留字

Verilog 程式碼中用來定義語言結構名稱的字元稱為識別符號，包括變數名、埠名、模組名等等。識別符號可以由字母、數字、下劃線以及美元符（$）來表示。但是識別符號的第一個字元只能是字母、數字或者下劃線，不能為美元符，這是因為以美元符開始的識別符號和系統任務的保留字衝突。

和其他許多程式語言類似，Verilog 也有許多保留字（或稱為關鍵字），使用者定義的識別符號不能夠和保留字相同。Verilog 的保留字均為小寫。變數型別中的 wire、reg、integer 等、表示過程的 initial、always 等，以及所有其他的系統任務、編譯指令，都是關鍵字。可以查閱官方文獻以完整的關鍵字的列表。

3.1.5 轉義識別符號

轉義識別符號（又稱跳脫字元），是由 \ 開始，以空白符結束的一種特殊程式語言結構。這種結構可以用來表示那些容易與系統語言結構相同的內容（例如 "在系統中被用來表示字串，如果字串本身的內容包含一個與之形式相同的雙引號，那麼就必須使用轉義識別符號）。下面列出了常用的幾種轉義識別符號。除此之外，在反斜線之後也可以加上字元的 ASCII，這種轉義識別符號相當於一個字元。常用的轉義識別符號有 \n（換行）、\t（製表位）、\b（空格）、\\（反斜槓）和 \"（英文的雙引號）等。

3.2 資料型別

3.2.1 四值邏輯

邏輯值及其解釋
0：邏輯低電平，條件為假
1：邏輯高電平，條件為真
z：高阻態，浮動
x：未知邏輯電平
訊號強度（從強到弱）及其屬性
supply：驅動
strong：驅動
pull：驅動
large：儲存
weak：驅動
medium：儲存
small：儲存
highz：高阻態

上面列出了 Verilog 採用的具有八種訊號強度的四值邏輯（four-valued logic），數位電路中的訊號可以用邏輯值、訊號強度加以描述。當系統遇到訊號之間的競爭時，需要考慮各組訊號的狀態和強度。如果驅動統一線網的訊號強度不同，則輸出結果是訊號強度高的值；如果兩個強度相同的訊號之間連線到同一個線網，將會發生競爭，結果為不確定值 x。

3.2.2 線網與暫存器

Verilog 所用到的所有變數都屬於兩個基本的型別：線網型別和暫存器型別。

線網與我們實際使用的電線類似，它的數值一般只能透過連續賦值（continuous assignment），由賦值符右側連線的驅動源決定。線網在初始化之前的值為 x（trireg 型別的線網是一個例外，它相當於能夠儲存電荷的電容器）。如果未連線驅動源，則該線網變數的當前數值為 z，即高阻態。線網型別的變數有以下幾種：wire、tri、wor、trior、wand、triand、tri0、tri1、supply0、supply1、trireg，其中 wire 作為一般的電路連線使用最為普遍，而其他幾種用於構建匯流排，即多個驅動源連線到一條線網的情況，或搭建電源、接地等。當進行模組的埠宣告時，如果沒有明確指出其型別，那麼這個埠會被隱含地宣告為 wire 型別。因此，在宣告輸出埠時應該注意是否有必要加上 reg 關鍵字。以下面的程式碼片段為例：

module my_moule (out1, out2, in1, in2); // 該模組具有兩個輸出埠

output reg out1; //out1 埠被宣告為為 reg 型別，它可以儲存當前值

output out2; //out2 埠隱含地被宣告為為 wire 型別，它的數值必須依賴連續賦值語句維持

endmodule

暫存器與之不同，它可以儲存當前的數值，直到另一個數值被賦值給它。在保持當前數值的過程中，不需要驅動源對它進行作用。如果未對暫存器變數賦值，它的初始值則為 x。Verilog 中所說的暫存器型別變數與真實的硬體暫存器是不同的，它是指一個儲存數值的變數。如果要在一個過程（initial 過程或 always 過程）裡對變數賦值，這個變數必須是暫存器型別的。暫存器型別的變數有以下幾種：reg（普通暫存器）、integer（整數）、time（時間）、real（實數），其中 reg 作為一般的暫存器使用最為普遍。利用暫存器變數的陣列，還可以對 ROM 進行建模。

關於選擇線網型別還是暫存器型別，需要符合一定的規定。模組的輸入埠可以與外界的線網或暫存器型別的變數連線，但是這個模組輸出埠只能連線到外界的線網。再簡單點，就是在兩個模組的訊號連線點，提供訊號的一方可以是暫存器或者線網，但是接受訊號的一方只能是線網。此外，在 initial、always 過程程式碼塊中賦值的變數必須是暫存器型別的，而連續賦值的物件只能是線網型別的變數。

3.2.3 數字的表示

在 Verilog 裡，當一個變數的型別確定，即已經知道它是暫存器型別或者是線網型別，當把具體的數值賦值給它時，需要利用下面所述的數字表示方法。數字表示的基本語法結構為 '。其中，位寬是與資料大小相等的對應二進位制數的位數加上佔位所用 0 的位數，這個位數需要使用十進位制來表示。位寬是可選項，如果沒有指明位寬，則預設的資料位寬與模擬器有關（最小 32 位）；數制需要用字母來表示，h 對應十六進位制，d 對應十進位制，o 對應八進位制，b 對應二進位制。如果沒有指明數制，則預設資料為十進位制數。例如：

233：十進位制數 233（未指明位寬）
12'h123：十六進位制數 123（使用 12 位）
20'd44：十進位制數 44（使用 20 位，高位自動使用 0 填充）
4'b1010：二進位制數 1010（使用 4 位）
6'o77：八進位制數 77（使用 6 位）
-233：十進位制數 - 233（未指明位寬）
-32'd3: 十進位制數 - 3 (使用 32 位)
32'hfffffffd: 十六進位制數 fffffffd (使用 32 位)

如果某個數的最高位為 x 或 z，那麼系統會自動使用 x 或 z 來填充沒有佔據的更高位。如果最高位為其他情況，系統會自動使用 0 來填充沒有佔據的更高位。

3.2.4 向量

向量形式的資料是 Verilog 相對 C 語言較為特殊的一種資料，但是這種資料在硬體描述語言中十分重要。在 Verilog 中，標量的意思是隻具有一個二進位制位的變數，而向量表示具有多個二進位制位的變數。如果沒有特別指明位寬，系統預設它為標量。

在真實的數位電路，例如將兩個四位二進位制數相加的進位加法器中，我們可以發現，其中一個數是透過四條電線（每條線表示四位中的某一位）連線到加法器上的。我們可以用一個向量來表示這個多位數，分別用這個向量的各個分量來表示 “四條電線”，即四位中的某一位。這樣做的好處是，可以方便地在 Verilog 程式碼的其他地方選擇其中的一位（位選）或多位（域選）。當然，如果沒有進行位選或域選，則這個多位數整體被選擇。

向量的表示需要使用方括號，方括號裡的第一個數字為向量第一個分量的序號，第二個數字為向量最後一個分量的序號，中間用冒號隔開。向量分量的序號不像 C 語言的陣列一樣必須從 0 開始，不過為了和數字電路里二進位制數高低位的表示方法一致，我們常常讓最低位為 0（即對於四位二進位制數，其最高位為第 3 位，次高位為第 2 位，次低位為第 1 位，最低位為第 0 位），當然這只是一種習慣。例如，上面提到的四位二進位制數用向量表示為：

wire 3:0 inputadd; // 宣告名為 inputadd 的 4 位 wire 型向量

wire 4:1 input_add1; // 也是 4 位 wire 型向量，但是分量序號從 4 到 1

wire 0:3 input_add2; // 也是 4 位 wire 型向量，但是分量序號從 0 到 3

上面的向量宣告之後，我們就可以方便地選擇其中的某幾個分量進行操作。請注意用於域選的方括號的位置在向量名稱之後，方括號內的數字為所需的位數。例如我們可以進行以下操作：

inputadd 3 = 1'b1; // 將 1 賦值給 inputadd 向量的第三位（最高位）

inputadd 1:0 = 2'b01; // 將 0 和 1 分別賦值給 inputadd 向量的第 1、0 位（最低兩位）

當對向量進行賦值時，如果右邊的數值位寬大於左邊的變數，則多出來的位被丟棄；如果右邊的數值位寬小於左邊的變數，則不夠的位用 0 填補。

3.2.5 陣列

Verilog 中的幾種暫存器型別的資料，包括 reg、integer、time、real，以及由這幾種資料構成的向量，都可以構成陣列。宣告陣列時，方括號位於陣列名的後面，括號內的第一個數字為第一個元素的序號，第二個數字為最後一個元素的序號，中間用冒號隔開。如果陣列是由向量構成的，則陣列的其中某個元素是向量。同樣，出於習慣考慮，我們一般讓陣列第一個元素的序號為 0，後面元素的序號依次遞增。此外，和 C 語言類似，使用者可以宣告多維陣列。例如：

integer number 0:100; // 宣告一個有 101 個元素的整數陣列

number 25 = 1234; // 將 1234 賦值給 25 號（第 26 個）元素

reg 7:0 my_input 65535:0; // 宣告一個有 65536 個元素的 8 位向量暫存器

my_input 97 = 8'b10110101; // 將 10110101 分別賦值給 97 號（第 2 個）元素的 7 至 0 位

reg my_reg 0:30:4; // 宣告一個具有 20 個元素的二維暫存器陣列

my_reg 12 = 1'b1; // 將 1 賦值給上述二維陣列的第 2 行、第 3 列元素

由於陣列和向量的表示都使用了方括號，因此使用時需要注意這個變數或向量的名稱在最初被宣告為何種型別的資料。上面第三行的例子是 65536 個 8 位向量組成的向量陣列，它可以描述一個 64KB 的儲存器。

表示陣列某個元素時，允許使用變數來表示元素的索引（如 number i = 1234;），但是表示一個向量的一位或者幾位時，只允許使用數字來表示位的索引；此外，使用陣列時一次只能對一個元素進行操作，而不能向向量那樣同時對連續的幾個位進行操作，例如 my_input 655357:4 = 4'b1010; 將一個四位二進位制數賦值給第 65536 個元素的高四位。

3.2.6 引數

可以透過 parameter 關鍵字宣告引數。引數與常數的意義類似，不能夠透過賦值運算改變它的數值。在模組進行例項化時，可以能夠透過 defparam，即引數過載語句塊來改變模組例項的引數。另一種方法是在模組例項化時，使用 # 將所需的例項引數覆蓋模組的預設引數。區域性引數可以用 localparam 關鍵字宣告，它不能夠進行引數過載。

在設計中使用引數，可以使得模組程式碼在不同條件下被重複利用，例如四位數全加器和十六位數全加器可以透過引數例項化同一個通用全加器模組。

3.2.7 字串

Verilog 中的字串總體來說與 C 語言中的字串較為類似，其中每個字元以 ASCII 表示，佔 8 位。字串儲存在位寬足夠的向量暫存器中。字串中的空格、換行等特殊內容，以轉義識別符號（參見前面提到過的轉義識別符號）的形式表示。

3.3 流程控制

為了使設計人員方便地使用暫存器傳輸級描述，Verilog 提供了多種流程控制結構，包括 if、if...else、if...else if...else 等形式的條件結構，case 分支結構，for、while 迴圈結構。這些流程控制結構與 C 語言有著相似的用法。不同的迴圈結構可能造成不同的邏輯綜合結果。Verilog 也提供了一些 C 語言中沒有的流程控制結構以適應硬體描述語言的需要，例如 casex、casez 兩種選擇結構，前者可以條件數值中的 x、z 均作為無關值，後者僅將 z 作為無關值；此外還提供了 forever、repeat 兩種迴圈結構，分別用於無限迴圈和指定次數迴圈。數位電路的邏輯功能描述常常使用到這些流程控制結構，例如，case 結構可以清晰地描述一個數據選擇器。

3.4 運算子

Verilog 的許多運算子和 C 語言類似，但是有一部分運算子是特有的，例如拼接運算子、縮減運算子、帶有無關位的相等運算子等。

按位
按位取反（~）：1 個多位運算元按位取反。例如：a=4'b1011，則～a 的結果為 4'b0100
按位與（&）：2 個多位運算元按位進行與運算，各位的結果按順序組成一個新的多位數。例如：a=2'b10，b=2'b11，則 a&b 的結果為 2'b10
按位或：2 個多位運算元按位進行或運算，各位的結果按順序組成一個新的多位數。例如：a=2'b10，b=2'b11，則 a|b 的結果為 2'b11
按位異或（^）：2 個多位運算元按位進行異或運算，各位的結果按順序組成一個新的多位數。例如：a=2'b10，b=2'b11，則 a^b 的結果為 2'b01
按位同或（~^ 或 ^~）：2 個多位運算元按位進行同或運算，各位的結果按順序組成一個新的多位數。例如：a=2'b10，b=2'b11，則 a~^b 的結果為 2'b10
邏輯
邏輯取反（!）：對 1 個運算元進行邏輯取反，如果這個運算元為 0，則結果為 1；如果這個運算元不為 0，則結果為 0
邏輯與（&&）：對 2 個運算元進行邏輯與，如果二者同不為 0，則結果為 1，否則為 0。例如：3 && 0 的結果為 0。
邏輯或：對 2 個運算元進行邏輯或，如果二者其中至少有一個不為 0，則結果為 1，否則為 0。例如：3

0 的結果為 1。

縮減
縮減與（&）：對一個多位運算元進行縮減與操作，先將它最高位與次高位進行與操作，其結果再與第二次高位進行與操作，直到最低位。例如：&(4'b1011) 的結果為 0
縮減與非（~&）：對一個多位運算元進行縮減與非操作，先將它最高位與次高位進行與非操作，其結果再與第二次高位進行與非操作，直到最低位。例如：~&(4'b1011) 的結果為 1
縮減或：對一個多位運算元進行縮減或操作，先將它最高位與次高位進行或操作，其結果再與第二次高位進行或操作，直到最低位。例如：|(4'b1011) 的結果為 1
縮減或非：對一個多位運算元進行縮減或非操作，先將它最高位與次高位進行或非操作，其結果再與第二次高位進行或非操作，直到最低位。例如：~|(4'b1011) 的結果為 0
縮減異或（^）：對一個多位運算元進行縮減異或操作，先將它最高位與次高位進行異或操作，其結果再與第二次高位進行異或操作，直到最低位。例如：^(4'b1011) 的結果為 1
縮減同或（~^or^~）：對一個多位運算元進行縮減同或操作，先將它最高位與次高位進行同或操作，其結果再與第二次高位進行同或操作，直到最低位。例如：~^(4'b1011) 的結果為 0
算術
加（+）：2 個運算元相加
減：2 個運算元相減或取 1 個運算元的負數（二進位制補碼錶示）
乘（*）：2 個運算元相乘
除（/）：2 個運算元相除
求冪}}：2 個運算元求冪，前一個運算元為底數，後一個運算元為指數
關係
大於（>）：比較 2 個運算元，如果前者大於後者，結果為真
小於：比較 2 個運算元，如果前者小於後者，結果為真
大於或等於（>=）：比較 2 個運算元，如果前者大於或等於後者，結果為真
小於或等於：比較 2 個運算元，如果前者小於或等於後者，結果為真
邏輯相等（==）：2 個運算元比較，如果各位均相等，結果為真。如果其中任何一個運算元中含有 x 或 z，則結果為 x
邏輯不等（!=）：2 個運算元比較，如果各位不完全相等，結果為真。如果其中任何一個運算元中含有 x 或 z，則結果為 x
case 相等（===）：2 個運算元比較，如果各位（包括 x 和 z 位）均相等，結果為真
case 不等（!==）：2 個運算元比較，如果各位（包括 x 和 z 位）不完全相等，結果為真
移位
邏輯右移（>>）：1 個運算元向右移位，產生的空位用 0 填充
邏輯左移：1 個運算元向左移位，產生的空位用 0 填充
算術右移（>>>）：1 個運算元向右移位。如果是無符號數，則產生的空位用 0 填充；有符號數則用其符號位填充
算術左移：1 個運算元向左移位，產生的空位用 0 填充
拼接（{,}）：2 個運算元分別作為高低位進行拼，例如：{2'b10,2'b11} 的結果是 a'b1011
重複（{n {m}}）：將運算元 m 重複 n 次，拼接成一個多位的數。例如：A=2'b01，則 {2 {A}} 的結果是 4'b0101
條件（?:）：根據？前的表示式是否為真，選擇執行後面位於：左右兩個語句。例如：(a>b)?(a=a-1):(b=b-2)，如果 a 大於 b，則將 a-1 的值賦給 a，否則將 b-2 的值賦給 b

3.5 系統任務

系統任務可以被用來執行一些系統設計所需的輸入、輸出、時序檢查、模擬控制操作。所有的系統任務名稱前都帶有美元符號 $ 使之與使用者定義的任務和函式相區分。例如，$display 用於顯示指定的字串，然後自動換行（用法類似 C 語言中的 printf 函式）；$monitor 用於監視變數，一旦被監視的變數發生變化，會顯示指定的字串；而 $time 可以提取當前的模擬時間。完整的列表請查閱參考工具、Verilog 手冊或標準文件。

3.6 編譯指令

Verilog 具有一些編譯指令，它們的基本格式為，注意第一個符號不是單引號，而是鍵盤上數字 1 左邊那個鍵對應的撇號。常用的編譯指令有文字宏預定義 `define、`include，它們的功能與 C 語言中類似，分別提供文字替換、檔案包含的功能。Verilog 還提供了 `ifdef、`ifndef 等一系列條件編譯指令，設計人員可以使得程式碼在滿足一定條件的情況下才進行編譯。此外，`timescale 指令可以對時間單位進行定義。詳細的編譯指令清單請參閱相關參考書籍。

暫存器傳輸級描述

4.1 兩種過程

在 Verilog 中，可以宣告兩種不同的過程：always 過程和 initial 過程。過程可以是包含時序的過程描述，而不包含時序的過程還可以表達組合邏輯。always 過程從關鍵字 always 開始，可以連續多次執行，當過程的最後一行程式碼執行完成後，再次從第一行程式碼開始執行。如果沒有使用系統任務 $finish，always 過程將不斷迴圈執行。initial 過程從關鍵字 initial 開始，它只能執行一次。

一個模組中可以包含多個過程，各個過程相互之間是併發執行的。不過，過程不能夠巢狀使用。如果過程中有多個語句，則需要使用關鍵字 begin、end 或 fork、join 將它們組成一個程式碼塊。這兩種關鍵字組合代表著順序程式碼塊和並行程式碼塊，後面的部分會講述這兩種結構。

例如，利用 always 過程迴圈執行的特點，可以為模組提供一個時間脈衝（注意第一個 initial 過程為時鐘的初始化，這個過程只需要進行一次）：

initial

a = 1'b0;

always

1 a=~a;

end

雖然，always 程式碼塊和 while 語句、forever 語句都能提供迴圈功能，但是 always 程式碼塊的迴圈更側重過程的迴圈執行，而後二者更側重程式碼的迴圈執行。因此，為了使程式碼更具條理，過程的迴圈應當用 always 語句描述。當然，在實際使用過程中，強制使用其中的某一種在功能實現上都是可行的。

4.2 暫存器變數的過程賦值

在 Verilog 中，有兩種賦值運算，一種叫做阻塞賦值（blocking assignment），其運算子為 =；另一種叫做非阻塞賦值（non-blocking assignment），其運算子為。在順序程式碼塊中使用阻塞賦值語句，如果這一句沒有執行完成，那麼後面的語句不會執行；如果在順序程式碼塊中使用非阻塞賦值，則執行這一句的同時，並不會阻礙下一句程式碼的執行。而且，如果後一個語句涉及前面一個非阻塞賦值語句中的變數，由於這兩個語句 “同時” 執行，因此後一個語句所用到的是前面一個語句執行前變數的數值。非阻塞賦值是 Verilog 作為硬體描述語言與普通程式語言的一個重大區別。

帶有兩個觸發器輸出端的簡單示例如下

always @ (posedge reset or posedge clock)

begin

a

上面的例子如果沒有使用非阻塞賦值，而使用阻塞賦值，那麼 flop1 和 flop2 的數值就不能被交換。flop1 和 flop2 在執行完畢後的數值都與之前 flop2 的數值相同。在傳統的程式語言中，可能需要一個臨時的變數，或者使用指標，才能夠達到交換兩個變數的目的。這裡使用了非阻塞賦值，相當於引入了一個隱含的臨時變數。第二個非阻塞賦值右邊的 a 是第一句賦值之前的數值，變數交換的目的得以實現。訊號邊緣敏感的過程語句塊內常使用非阻塞賦值，使語句塊的諸賦值語句同時進行，雖然功能上似乎可以用阻塞賦值實現，但是模擬時會產生不正常的結果。

通常的過程賦值語句往往只有在觸發或迴圈等情況，即賦值語句被執行到時候，才會使左邊的暫存器變數改變一次；而線網變數的連續賦值則一直 “監視” 右邊表示式的變化，一旦其結果發生變化，立即會左邊的線網變數更新為此結果。如果需要對暫存器變數進行過程連續賦值，則可以使用 Verilog 提供的 assign 或 force 關鍵字 “強制地” 將賦值運算子右邊表示式的結果連續不斷地施加在左邊的暫存器變數上。

4.3 線網變數的連續賦值

對線網型別變數的連續賦值是數位電路資料流建模的重要步驟，數字系統不含時序的組合邏輯部分可以使用線網的連續賦值描述。線網不能夠像暫存器那樣儲存當前數值，它需要驅動源提供訊號，這種驅動是連續不斷的，因此線網變數的賦值稱為連續賦值，這與暫存器變數在過程中的單次賦值不同，而且所用的運算子也有區別。在 Verilog 裡，線網連續賦值的關鍵字為 assign，下面為一個例子：

module and

wire out;

wire in1, in2;

assign out = in1 & in2;

在這個例子中，線網變數 out 在系統執行過程中總為兩個輸入線網變數 in1 和 in2 邏輯與的結果。

線網的連續賦值可以在關鍵字 assgin 附加延遲資訊，例如上面的程式碼可以改為：

assign #5 out = in1 & in2; //in1 和 in2 邏輯與的結果在 5 個時間週期後才施加在 out 上

4.4 時序控制

Verilog 能夠描述過程中的時序特性，這也是硬體描述語言與普通計算機程式語言的重要差別之一。過程的時序控制可以透過三種方式實現：延遲時序控制、事件時序控制以及電平敏感時序控制。

過程中的時序控制可以控制程式碼的執行時間。在 Verilog 中，除了過程中的時序控制，還可以定義元件、路徑的延遲。這些延遲請參見本條目後面有關邏輯閘級延遲的部分。

4.4.1 延遲時序控制

在程式碼中使用關鍵字 #和延遲的時間，就可以透過延遲來進行時序控制。延遲的時間可以是數字、變數或者表示式。延遲時序控制又分為兩種：常規延遲和內嵌延遲。

常規延遲在賦值語句的左邊，系統執行到這一行程式碼時，系統先進行延遲，延遲完成後，再計算表示式，並將結果賦值給左邊的變數；而內嵌延遲在賦值語句的右邊，系統執行到這一行程式碼時，系統先立即計算表示式，再進行延遲，最後把表示式的結果賦值給左邊的變數。在上述兩種延遲方式中，設計人員需要注意表示式的自變數在延遲過程中可能發生變化。常規延遲是先延遲再計算表示式，這時表示式的自變數可能已經發生了變化；而內嵌延遲在延遲前就已經進行了計算，表示式的自變數在延遲過程中發生的變化，對已經計算的表示式結果沒有影響，延遲只是指這個結果需要等待一段時間再賦值給左邊的變數。

下面的程式碼片段分別展示了常規延遲和內嵌延遲：

parameter latency = 8;

initial

begin

x = 1;

y = 2;

5 x = 3; // 使用常規延遲：等待 5 個系統週期後對 x 賦值
latency y = 4; // 使用變數進行常規延遲，再等待 8 個系統週期後對 y 賦值

z = #10 (x+y); // 使用內嵌延遲：先用當前時刻的 x、y 數值計算 (x+y)，再等待 10 個系統週期後對 z 賦值

end //z 的最終數值為 7

在順序語句塊（begin...end）中，由於語句是從上到下、一行一行地執行，而所有常規延遲時間都是實際執行時間相對於這一句本來應該開始執行的時間（也是上一句執行完成之時）的延遲值。因此，在上面的程式碼示例中，對變數 y 的賦值時間相對於上一句結束延遲了 8 個系統週期，而上一句相對系統零時刻已經延遲了 5 個系統週期，因此對 y 的賦值發生在第 13 個系統週期。不過，如果順序語句塊中存在非阻塞賦值，由於這個結構有著類似並行語句塊的特點，因此需要特別考慮。

在並行語句塊（fork...join）中，由於所有語句都是併發執行的，而所有常規延遲時間都是實際執行時間相對於這一句本來應該開始執行的時間（也是系統零時刻）的延遲值，因此各個常規延遲所指的時間都是相對於系統零時刻。

4.4.2 事件時序控制

事件時序控制的意思是，如果指定的事件發生，則程式碼被觸發執行。它的關鍵字為 @，後面可以加變數或者事件名稱。參見下面的例子：

@(clk) x = 1; // 當變數 clk 發生變化，則將 1 賦值給 x

@(posedge clk) y = 2; // 在變數 clk 的上升沿，將 2 賦值給 y

z = @(negedge clk) (x+y); // 先立即計算表示式 (x+y)，然後在變數 clk 下降沿，將表示式的結果賦值給 z

上面 @後面括號裡的是常規事件。Verilog 允許設計人員透過關鍵字 event 和觸發符號 -> 定義自己所需要的命名事件觸發：

event biggerthantwo;

always @(posedge clock)

begin

if (a> 2)

->biggerthantwo; // 如果 a 大於 2，則事件 biggerthantwo 被觸發

end

always @(biggerthantwo) // 當 biggerthantwo 被觸發，執行下面的過程

begin

// 過程的程式碼

end

一種經典的用法結構如下，可以理解為 “在整個模擬過程中，一旦某變數發生變化，就執行某操作”：

always @(a)

begin

x = x+1;

end

另一種用法稱為 OR 事件時序控制，其程式碼結構為 @(a or b) 或 @(a, b)，即當 a 或 b 其中任意一個變數發生變化時，程式碼或程式碼塊才被觸發執行。監視的變數如果有 3 個，則其程式碼結構變為 @(a or b or c) 或 @(a, b, c)，以此類推。如果需要監視的變數很多，則可以使用 @ 或 @，它表示對之後程式碼塊中的所有輸入變數敏感。此外，敏感列表中除了變數，還可以是前面所提到過的常規事件、命名事件。

4.4.3 電平敏感時序控制

Verilog 中還有一種電平敏感時序控制方式，即使用 wait (a)，當變數 a 為真，則執行後面的程式碼塊。

4.5 順序程式碼塊與並行程式碼塊

begin、end 組合代表了這個程式碼塊的各行程式碼是順序執行的，這種程式碼塊稱為順序程式碼塊；後面的 fork、join 代表了這個程式碼塊的各行程式碼是併發執行的，這種程式碼塊稱為並行程式碼塊。與模組、過程不同，兩種程式碼塊是可以巢狀，即順序程式碼塊中可以包含並行程式碼塊。下面的例子展示了這兩種程式碼塊巢狀使用的效果：

initial

fork

x = 1;

y = 2;

begin

z = 3;

w = 4;

end

join

由於這個 initial 過程使用了關鍵字 fork、join，其中 x、y、z 的賦值同時於系統零時刻發生，而 z 和 w 由於位於一個順序程式碼塊中，因此 w 的賦值在 z 的賦值後才進行。

在使用並行程式碼塊的時候，有可能引起程式碼的競爭，例如兩個語句對一個變數同時進行賦值。雖然理論上兩個語句同時執行，但是具體的情況是必然有一句先執行，但這與順序語句塊的 “先後” 有本質區別。實際的先後順序取決於所用的模擬系統。這並不是 Verilog 硬體描述語言本身的缺陷，並行語句塊是一種人為設定的功能，這可以讓設計人員更容易地描述某些過程，當然他們必須認真考慮競爭帶來的潛在問題。

4.6 任務和函式

如果某部分程式碼需要在不同地方多次使用，可以在模組中定義任務或函式。

任務透過關鍵字 task 來宣告。任務可以有零個或者多個輸入變數，但是沒有輸出返回值。呼叫任務時，將按照任務內指定的方式處理這些變數。由於它相當於一個子過程，因此任務中賦值的變數只能是暫存器型別的，而且只能使用過程賦值語句。任務可以具有時序結構，例如延遲、非阻塞賦值等。任務中可以呼叫任務和函式。與模組的宣告不同，任務的宣告沒有類似模組埠列表的輸入變數列表。儘管如此，呼叫任務的時候，還是需要在括號裡按照任務宣告時的順序羅列輸入變數。在某種程度上，任務和 C 語言中沒有返回值的函式有些類似。

函式透過關鍵字 function 來宣告。函式不僅有輸入變數，還有一個返回值作為輸出變數，這個返回值的名稱與函式的名稱相同。函式與任務不同，它是一個只有邏輯功能的部分，不能包含時序結構。函式中只能呼叫函式。Verilog 中的函式與 C 語言中有返回值的函式有些類似。通常將函式放在賦值運算子的右邊，它的返回值被賦值給左邊的變數。

如果任務或函式同時在多個地方被呼叫，則需要使用 automatic 關鍵字宣告，這樣系統可以為不同地方的呼叫分配獨立的記憶體空間。

邏輯閘級描述

邏輯閘級描述的抽象級別較低，僅次於電晶體級。實際的硬體電路往往都是以邏輯閘級網表作為基礎構建的，而設計人員常常會在進行更高抽象級別的設計。儘管如此，邏輯閘級的設計還是更接近真實電路形式。Verilog 提供了一系列邏輯閘原語（Primitive）供使用者使用。例如，非（not）、與門（and）、或門（or）、與非門（nand）、或非（nor）、異或（xor）、同或（xnor）。邏輯閘原語和模組類似，可以透過例項引用的方式使用。

電晶體級描述

Verilog 能夠在低抽象級別對電路進行描述，是它的一個重要特點。Verilog 中提供了多種電晶體級（也稱開關級）元件型別，包括 N 型金氧半導體場效電晶體（關鍵字為 nmos）、P 型金氧半導體場效電晶體（關鍵字為 pmos）、互補式金氧半導體（關鍵字為 cmos）、帶阻抗的互補式金氧半導體（關鍵字為 rcmos）、電源單元（關鍵字為 supply1）、接地單元（關鍵字為 supply0）等。所有的電晶體都可以設定延遲屬性。設計人員可以利用這些低抽象級元件構建所需要的邏輯閘或直接構成其他高階元件。

延遲

7.1 邏輯閘和電晶體的延遲

真實的硬體電路不可避免地都存在延遲現象。在 Verilog 中，可以對邏輯閘、電晶體這些元件的延遲資訊進行描述。可以為元件的延遲指定一個時間，則上升、下降、關斷的延遲都使用這個時間；也可以按照先後順序分別指定上升延遲、下降延遲，而關斷延遲取二者較小值；當然也可以為上升、下降、關斷各指定一個時間。例如，下面的程式碼為與門例項添加了三個延遲時間，分別對應上升、下降、關斷：

and #(1, 2, 3) my_and (out, in1, in2);

邏輯閘和電晶體的延遲屬於 “慣性延遲”。它的意思是，邏輯閘和電晶體獲得外部輸入之後，延遲指定的時間後，才會將結果呈現在輸出端上。在延遲期間，如果輸入改變，但是這個訊號的持續時間小於指定延遲的時間，則不會影響邏輯閘和電晶體的輸出；如果這個訊號的持續時間大於指定延遲的時間，則之前的結果將不會呈現在輸出端，改變輸入訊號後的結果將經過延遲後將呈現在輸出端

Verilog 還允許設計人員為每個延遲時間設定最大值、典型值、最小值，在編譯階段可以透過編譯程式碼選擇其中一個。

7.2 線網延遲

在宣告線網或對線網進行連續賦值的時候，可以為線網新增延遲資訊。這樣，所有連續賦值給線網的表示式都會立即計算出結果，但是這個結果在延遲時間後才會賦值給線網。如果在這段延遲時間內，右側表示式的結果發生變化，則用於賦值的表示式結果取變化後的。另外，如果如果輸入變數變化的脈衝寬度小於延遲的時間，其變化不會對輸出造成影響。這種延遲被稱為 “慣性延遲”，邏輯閘和電晶體的延遲也是這種情況。

7.3 過程延遲

過程延遲在前面的延遲時序控制一部分講述過。過程賦值語句中的延遲主要分為常規延遲（又稱為外部延遲）和內嵌延遲（又稱為內部延遲）兩種，其中前者先延遲，再計算表示式、賦值給左邊的變數；而後者先立即計算表示式，經過延遲後再將結果賦值給左邊的變數。

7.4 路徑延遲

設計人員可以在模組中關鍵字 specify、endspecify 之間對路徑延遲進行描述。與元件的延遲不同，路徑延遲是指訊號在某兩個暫存器型別或線網型別變數之間傳遞所需的延遲時間。在 specify 程式碼塊中可以使用條件結構來根據情況選擇所需的延遲時間值。與元件延遲相同的是，延遲的時間值可以指定上升、下降、關斷的情況，同時也可以包含最大值、典型值、最小值。

邏輯綜合

8.1 概念簡介

設計人員編寫的 Verilog 程式碼通常是在較高抽象級別的，例如暫存器傳輸級。這一抽象級別包含了對電路訊號在暫存器之間傳輸情況的描述。但是邏輯閘級的網表，即邏輯閘的相互連線形式，才最接近真實的硬體電路。這一形式與暫存器傳輸級的描述，在功能上是等效的。為了給後續硬體製造人員提供這種低抽象級別的描述，需要將高抽象級別的 Verilog 程式碼轉換為低抽象級別的邏輯閘級網表。這一過程稱為邏輯合成（Logic Synthesis）。

在自動化邏輯合成工具出現之前，儘管人們可以用硬體描述語言進行設計，但是還是需要人工進行邏輯合成。例如，如果電路模組只有少數幾個輸入端，我們可以使用類似卡諾圖的方法來對邏輯函式進行化簡。隨著電路規模不斷增加，人工邏輯合成的容易出錯、耗費大量時間的缺點逐漸凸顯。同時，在某種特殊器件製程下最最佳化的綜合結果不一定在另一種製程下還合適，如果需要採用另外的製程，設計人員需要花費很長時間重新進行邏輯合成。隨著自動化邏輯合成工具的出現，硬體描述語言、所需器件製程資訊（製程庫）可以直接被邏輯合成工具讀取，透過其內部的自動合成演演算法，輸出符合設計約束（通常包括時序、功耗、面積的約束）的邏輯閘級網表。藉助自動合成工具，設計人員可以將更多的精力放在高抽象級別的硬體描述語言設計。

8.2 可合成程式碼

邏輯合成工具不能接受所有的 Verilog 程式碼。設計人員需要確保硬體描述語言程式碼是週期到週期的暫存器傳輸級描述。諸如 while 的迴圈結構必須透過訊號邊緣的形式（如 @(posedge clock)）提供終止條件；initial 結構可能也不能被轉換。如果不指明數字的位寬，那麼系統可能預設它為一個較大的值（如 32 位），這就可能產生規模非常龐大的邏輯閘級網表，其中一部分是不必要的，這將造成資源的浪費。與未知邏輯 x、高阻態 z 有關的運算子不能被轉換，例如 ===、!==。此外，條件結構如果只有 if 而沒有對 else 的情況進行設計，或者選擇結構缺少預設情況 default，很可能產生預期之外的鎖存器。由於需要使用與製程相關的邏輯閘，因此使用者自定義的原語很可能不能被轉換。設計人員需要採取良好的程式碼風格，以獲得更最佳化的邏輯合成結果。為了適應符合可重用設計思想的系統晶片、IP 核設計，設計人員還應該遵循更嚴格的編碼規範。

高階功能

9.1 使用者自定義原語

除了系統提供的 26 種邏輯閘、電晶體原語，Verilog 也提供使用者自定義原語（User Defined Primitive, UDP）。原語與模組的層次結構類似，但是原語的輸入輸出關係是完全透過查表實現的。組合邏輯的使用者自定義原語的核心是真值表，時序邏輯的使用者自定義原語的核心是激勵表。設計人員需要在狀態表中羅列可能出現的輸入和輸出情況。如果在實際使用過程中，遇到狀態表中沒有定義的情況，則輸出不確定值 x。使用自定義原語很直觀，但是如果輸入變數較多，狀態表就會變得很複雜。在很多情況中，使用者自定義原語並不能被邏輯綜合工具轉換。

9.2 程式語言介面

程式語言介面（Program Language Interface, PLI）提供了透過 C 語言函式對 Verilog 資料結構進行儲存、讀取操作的途徑。

Verilog 程式語言介面的發展先後經過了三代，其中第一代為任務或函式子程式，它可以在 C 程式和 Verilog 設計之間傳遞資料；第二代為存取子程式，它可以在使用者自定義 C 程式和 Verilog 的內部資料表示的介面上被使用；第三代為 Verilog 過程介面，它進一步擴充套件了前兩代程式語言介面的功能。

透過使用程式語言介面，設計人員可以自定義介面的功能，然後透過類似呼叫系統任務的方式呼叫這些自定義功能。這樣，設計人員可以很大程度地擴充套件他們能使用的功能，例如監視、激勵、除錯功能，或者用它來提取設計資訊、顯示輸出等。

相關電子設計自動化工具

Verilog 作為業界使用最廣泛的硬體描述語言之一，有大量的電子設計自動化工具對它予以支援。透過使用整合開發環境，設計人員可以在常見的 Windows 或其他圖形化系統中進行設計、模擬、驗證，例如 Cadence 和 Synopsys 等公司提供的積體電路計算機輔助設計系統。

與 VHDL 的比較

Verilog 硬體描述語言（Verilog Hardware Description Language）的英語縮寫 Verilog HDL 和另一種類似的硬體描述語言 VHDL 在名稱上容易混淆，實際上二者是兩種不同的硬體描述語言。一些高階的電子設計自動化工具支援使用者在專案內同時使用 Verilog 和 VHDL 來進行硬體設計。

VHDL 是由美國國防部主持研發的硬體描述語言，成為了第一個成為電氣電子工程師學會標準的硬體描述語言，美國政府相關的專案都是基於 VHDL；而 Verilog 由民間商業公司的私有產品發展為 IEEE 標準的，因此在商用領域的市場佔有量更大，設計人員和支援資源比 VHDL 更廣。在美國大約有 10 萬設計人員、200 所大學教授採用 Verilog 硬體描述語言。

Verilog 和 VHDL 作為業界廣泛認可、同為電氣電子工程師學會標準的硬體描述語言，有著各自的特點。VHDL 的設計之初就更加針對標準化進行設計，Verilog 則具有簡明、高效的程式碼風格。兩種語言都能夠在多個抽象層次對數位電路建模，並且可以與驗證、模擬、綜合工具協同工作。其中，Verilog 的邏輯閘級、電晶體級級電路描述能力更強，VHDL 不具備這樣低階的描述能力，但是另一方面，VHDL 的系統級抽象描述能力則比 Verilog 強。另外，由於 Verilog 與 C 語言在語法上有相似之處，因此具有 C 語言基礎的設計人員更容易掌握它，而 VHDL 設計人員需要具有 Ada 語言程式設計基礎，並且學習週期比 Verilog 更長。相關學術文獻顯示，在美國的高階數字系統設計領域，Verilog 和 VHDL 的使用比率大約分別為 80% 和 20%，這項比率在日本和臺灣和美國相似。隨著 Verilog-A 被合併到 Verilog 標準之中，而該部分後來成為了 Verilog-AMS 的一部分，該語言增加了對模擬電子系統的描述能力，因此它在混合訊號積體電路中有著更廣泛的應用。