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中可以表示為：

```verilog
module mux (out, select, in0, in1, in2, in3);
output out;
input [1:0] select;
input in0, in1, in2, in3;
//具體的寄存器传输级代碼
endmodule
```

設計人員可以使用一個頂層模組，通過實例調用上面這個模組的方式來進行測試。這個頂層模塊常被稱為「測試平台（Testbench）」。

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

```verilog
module tester;
reg [1:0] SELECT;
reg IN0, IN1, IN2, IN3;
wire OUT;
mux my_mux (OUT, SELECT, IN0, IN1, IN2, IN3); //实例调用mux模块，这个实例被命名为my_mux
initial //需要仿真的激励代码
  begin
  end
endmodule
```

在這個測試平台模塊里，設計人員可以設定仿真時的輸入信號以及信號監視程序，然後觀察仿真時的輸出情況是否符合要求，這樣就可以了解設計是否達到了預期。

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

```verilog
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硬體描述語言。

### 基本規範

#### 空白符

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

#### 注釋

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

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

#### 大小寫敏感性

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

#### 標識符及保留字

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

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

#### 轉義標識符

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

### 數據類型

#### 四值邏輯

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

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

#### 線網與寄存器

Verilog所用到的所有變量都屬於兩個基本的類型：線網類型和寄存器類型。

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

```verilog
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`過程代碼塊中賦值的變量必須是寄存器類型的，而連續賦值的對象只能是線網類型的變量。

#### 數字的表示

在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來填充沒有占據的更高位。

#### 向量

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

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

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

```verilog
wire [3:0] input_add; //声明名为input_add的4位wire型向量
wire [4:1] input_add1; //也是4位wire型向量，但是分量序号从4到1
wire [0:3] input_add2; //也是4位wire型向量，但是分量序号从0到3
```

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

```verilog
input_add [3] = 1'b1; //将1赋值给input_add向量的第三位（最高位）
input_add [1:0] = 2'b01; //将0和1分别赋值给input_add向量的第1、0位（最低两位）
```

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

#### 陣列

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

```verilog
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:3][0:4]; //声明一个具有20个元素的二维寄存器数组
my_reg [1][2] = 1'b1; //将1赋值给上述二维数组的第2行、第3列元素
```

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

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

#### 參數

可以通過`parameter`關鍵字聲明參數。參數與常數的意義類似，不能夠通過賦值運算改變它的數值。在模塊進行實例化時，可以能夠通過`defparam`，即參數重載語句塊來改變模塊實例的參數。另一種方法是在模塊實例化時，使用`#()`將所需的實例參數覆蓋模塊的默認參數。局部參數可以用`localparam`關鍵字聲明，它不能夠進行參數重載。

在設計中使用參數，可以使得模塊代碼在不同條件下被重複利用，例如四位數全加器和十六位數全加器可以通過參數實例化同一個通用全加器模塊。

#### 字串

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

### 流程控制

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

### 運算符

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`

### 系統任務

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

### 編譯指令

Verilog具有一些編譯指令，它們的基本格式為`<keyword>，注意第一个符号不是单引号，而是键盘上数字1左边那个键对应的撇号。常用的编译指令有文本宏预定义`define、`include，它们的功能与C语言中类似，分别提供文本替换、文件包含的功能。Verilog还提供了`ifdef、`ifndef等一系列条件编译指令，设计人员可以使得代码在满足一定条件的情况下才进行编译。此外，`timescale指令可以對時間單位進行定義。詳細的編譯指令清單請參閱相關參考書籍。

## 寄存器傳輸級描述

### 兩種過程

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

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

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

```verilog
initial
  a = 1'b0;
always
  #1 a=~a;
end
```

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

### 寄存器變量的過程賦值

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

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

```verilog
always @ (posedge reset or posedge clock)
begin
  a <= b;
  b <= a;
end
endmodule
```

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

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

### 線網變量的連續賦值

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

```verilog
module and
wire out;
wire in1, in2;
assign out = in1 & in2;
```

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

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

```verilog
assign #5 out = in1 & in2; //in1和in2逻辑与的结果在5个时间周期后才施加在out上
```

### 時序控制

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

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

#### 延遲時序控制

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

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

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

```verilog
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`）中，由於所有語句都是並發執行的，而所有常規延遲時間都是實際執行時間相對於這一句本來應該開始執行的時間（也是系統零時刻）的延遲值，因此各個常規延遲所指的時間都是相對於系統零時刻。

#### 事件時序控制

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

```verilog
@(clk) x = 1; //当变量clk发生变化，则将1赋值给x
@(posedge clk) y = 2; //在变量clk的上升沿，将2赋值给y
z = @(negedge clk) (x+y); //先立即计算表达式(x+y)，然后在变量clk下降沿，将表达式的结果赋值给z
```

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

```verilog
event bigger_than_two;
always @(posedge clock)
begin
  if(a > 2)
  ->bigger_than_two; //如果a大于2，则事件bigger_than_two被触发
end
always @(bigger_than_two) //当bigger_than_two被触发，执行下面的过程
begin
  //过程的代码
end
```

一種經典的用法結構如下，可以理解為「在整個仿真過程中，一旦某變量發生變化，就執行某操作」：

```verilog
always @(a)
begin
  x = x+1;
end
```

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

#### 電平敏感時序控制

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

### 順序代碼塊與並行代碼塊

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

```verilog
initial
fork
  x = 1; 
  y = 2; 
  begin
    z = 3;
    w = 4;
  end
join
```

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

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

### 任務和函數

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

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

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

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

## 邏輯門級描述

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

## 電晶體級描述

電晶體級也稱開關級，是描述器件中電晶體和存儲節點以及它們之間連接的模型。電晶體級元件類型有N型金屬氧化物半導體場效應管（關鍵字為`nmos`）、P型金屬氧化物半導體場效應管（關鍵字為`pmos`）、互補式金屬氧化物半導體（關鍵字為`cmos`）、帶阻抗的互補式金屬氧化物半導體（關鍵字為`rcmos`）、電源單元（關鍵字為`supply1`）、接地單元（關鍵字為`supply0`）等。所有的電晶體都可以設置延遲屬性。設計人員可以利用這些低抽象級元件構建所需要的邏輯門或直接構成其他高級組件。

## 延遲

### 邏輯門和電晶體的延遲

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

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

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

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

### 線網延遲

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

### 過程延遲

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

### 路徑延遲

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

## 邏輯綜合

### 概念簡介

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

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

### 可合成代碼

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

結構類型｜注

`initial`｜只用於仿真測試文件（test bench）

`events`｜`Events`對於同步測試文件的各個組件比較有意義

`real`｜`Real`數據類型不可合成

`time`｜`Time`數據類型不可合成

`force`和`release`｜`Force`和`release`不可合成

`assign`和`deassign`｜`reg`類型的`assign`和`deassign`操作不可合成，但是`wire`類型的`assign`操作可以合成

`fork join`｜使用非阻塞賦值可以獲得同樣效果

`primitive`｜只有門級的原語（primitives）可合成

`table`｜使用者自定義原語（UDP）及`table`不可合成

`#1`｜延遲只用於仿真，綜合器一般直接忽略延遲

## 高級功能

### 使用者自定義原語

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

### 程式語言接口

程式語言接口（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來進行硬體設計。兩者除了語法上的差異及限制之外，支持兩者的EDA工具幾乎相同。

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

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

## 基本資料

- 文件擴展名：.v, .vh
- 編程范型：結構化
- 發行日期：1984年
- 停產日期：併入SystemVerilog
- 軟體版本週期：IEEE 1364-2005
- 軟體版本週期：2005年11月9日
- 類型系統：靜態, 弱類型
- dialects：Verilog-AMS
- 受影響於：C, Fortran
- 施影響於：SystemVerilog
- wikibooks：Programmable Logic/Verilog