VHDL:中文版Verilog HDL簡(jiǎn)明教程:第2章 HDL指南
模塊是Verilog 的基本描述單位,用于描述某個(gè)設計的功能或結構及其與其他模塊通信的外部端口。一個(gè)設計的結構可使用開(kāi)關(guān)級原語(yǔ)、門(mén)級原語(yǔ)和用戶(hù)定義的原語(yǔ)方式描述; 設計的數據流行為使用連續賦值語(yǔ)句進(jìn)行描述; 時(shí)序行為使用過(guò)程結構描述。一個(gè)模塊可以在另一個(gè)模塊中使用。
一個(gè)模塊的基本語(yǔ)法如下:
module module_name (port_list);
Declarations:
reg, wire, parameter,
input, output, inout,
function, task, . . .
Statements:
Initial statement
Always statement
Module instantiation
Gate instantiation
UDP instantiation
Continuous assignment
endmodule
說(shuō)明部分用于定義不同的項,例如模塊描述中使用的寄存器和參數。語(yǔ)句定義設計的功能和結構。說(shuō)明部分和語(yǔ)句可以散布在模塊中的任何地方;但是變量、寄存器、線(xiàn)網(wǎng)和參數等的說(shuō)明部分必須在使用前出現。為了使模塊描述清晰和具有良好的可讀性, 最好將所有的說(shuō)明部分放在語(yǔ)句前。本書(shū)中的所有實(shí)例都遵守這一規范。
以下為建模一個(gè)半加器電路的模塊的簡(jiǎn)單實(shí)例。
module HalfAdder (A, B, Sum, Carry);
input A, B;
output Sum, Carry;
assign #2 Sum = A ^ B;
assign #5 Carry = A B;
endmodule
模塊的名字是HalfAdder。 模塊有4個(gè)端口: 兩個(gè)輸入端口A(yíng)和B,兩個(gè)輸出端口Sum和Carry。由于沒(méi)有定義端口的位數, 所有端口大小都為1位;同時(shí), 由于沒(méi)有各端口的數據類(lèi)型說(shuō)明, 這四個(gè)端口都是線(xiàn)網(wǎng)數據類(lèi)型。
模塊包含兩條描述半加器數據流行為的連續賦值語(yǔ)句。從這種意義上講,這些語(yǔ)句在模塊中出現的順序無(wú)關(guān)緊要,這些語(yǔ)句是并發(fā)的。每條語(yǔ)句的執行順序依賴(lài)于發(fā)生在變量A和B上的事件。
在模塊中,可用下述方式描述一個(gè)設計:
1) 數據流方式;
2) 行為方式;
3) 結構方式;
4) 上述描述方式的混合。
下面幾節通過(guò)實(shí)例講述這些設計描述方式。不過(guò)有必要首先對Verilog HDL的時(shí)延作簡(jiǎn)要介紹。
2.2 時(shí)延
Verilog HDL模型中的所有時(shí)延都根據時(shí)間單位定義。 下面是帶時(shí)延的連續賦值語(yǔ)句實(shí)例。
assign #2 Sum = A ^ B;
#2指2個(gè)時(shí)間單位。
使用編譯指令將時(shí)間單位與物理時(shí)間相關(guān)聯(lián)。這樣的編譯器指令需在模塊描述前定義,如下所示:
` timescale 1ns /100ps
此語(yǔ)句說(shuō)明時(shí)延時(shí)間單位為1ns并且時(shí)間精度為100ps (時(shí)間精度是指所有的時(shí)延必須被限定在0.1ns內)。 如果此編譯器指令所在的模塊包含上面的連續賦值語(yǔ)句, #2 代表2ns。
如果沒(méi)有這樣的編譯器指令, Verilog HDL 模擬器會(huì )指定一個(gè)缺省時(shí)間單位。IEEE Verilog HDL 標準中沒(méi)有規定缺省時(shí)間單位。
2.3 數據流描述方式
用數據流描述方式對一個(gè)設計建模的最基本的機制就是使用連續賦值語(yǔ)句。在連續賦值語(yǔ)句中,某個(gè)值被指派給線(xiàn)網(wǎng)變量。 連續賦值語(yǔ)句的語(yǔ)法為:
assign [delay] LHS_net = RHS_ expression;
右邊表達式使用的操作數無(wú)論何時(shí)發(fā)生變化, 右邊表達式都重新計算, 并且在指定的時(shí)延后變化值被賦予左邊表達式的線(xiàn)網(wǎng)變量。時(shí)延定義了右邊表達式操作數變化與賦值給左邊表達式之間的持續時(shí)間。如果沒(méi)有定義時(shí)延值, 缺省時(shí)延為0。
下面的例子顯示了使用數據流描述方式對2-4解碼器電路的建模的實(shí)例模型。
`timescale 1ns/ 1ns
module Decoder2x4 (A, B, EN, Z);
input A, B, EN;
output [ 0 :3] Z;
wire Abar, Bbar;
assign #1 Abar = ~ A; / / 語(yǔ)句 1。
assign #1 Bbar = ~ B; / / 語(yǔ)句 2。
assign #2 Z[0] = ~ (Abar Bbar EN) ; / / 語(yǔ)句 3。
assign #2 Z[1] = ~ (Abar B EN) ; / / 語(yǔ)句 4。
assign #2 Z[2] = ~ (A Bbar EN) ; / / 語(yǔ)句 5。
assign #2 Z[3] = ~ (A B EN) ; / / 語(yǔ)句 6。
endmodule
以反引號“ ` ”開(kāi)始的第一條語(yǔ)句是編譯器指令, 編譯器指令`timescale 將模塊中所有時(shí)延的單位設置為1 ns,時(shí)間精度為1 ns。例如,在連續賦值語(yǔ)句中時(shí)延值#1和#2分別對應時(shí)延1 ns和2 ns。
模塊Decoder2x4有3個(gè)輸入端口和1個(gè)4位輸出端口。線(xiàn)網(wǎng)類(lèi)型說(shuō)明了兩個(gè)連線(xiàn)型變量Abar和Bbar (連線(xiàn)類(lèi)型是線(xiàn)網(wǎng)類(lèi)型的一種)。此外,模塊包含6個(gè)連續賦值語(yǔ)句。
當EN在第5 ns變化時(shí),語(yǔ)句3、4、5和6執行。這是因為EN是這些連續賦值語(yǔ)句中右邊表達式的操作數。Z[0]在第7 ns時(shí)被賦予新值0。當A在第15 ns變化時(shí), 語(yǔ)句1、5和6執行。執行語(yǔ)句5和6不影響Z[0]和Z[1]的取值。執行語(yǔ)句5導致Z[2]值在第17 ns變?yōu)?。執行語(yǔ)句1導致Abar在第16 ns被重新賦值。由于A(yíng)bar的改變,反過(guò)來(lái)又導致Z[0]值在第18 ns變?yōu)?。
請注意連續賦值語(yǔ)句是如何對電路的數據流行為建模的;這種建模方式是隱式而非顯式的建模方式。此外,連續賦值語(yǔ)句是并發(fā)執行的,也就是說(shuō)各語(yǔ)句的執行順序與其在描述中出現的順序無(wú)關(guān)。
2.4 行為描述方式
設計的行為功能使用下述過(guò)程語(yǔ)句結構描述:
1) initial語(yǔ)句:此語(yǔ)句只執行一次。
2) always語(yǔ)句:此語(yǔ)句總是循環(huán)執行, 或者說(shuō)此語(yǔ)句重復執行。
只有寄存器類(lèi)型數據能夠在這兩種語(yǔ)句中被賦值。寄存器類(lèi)型數據在被賦新值前保持原有值不變。所有的初始化語(yǔ)句和always語(yǔ)句在0時(shí)刻并發(fā)執行。
下例為always語(yǔ)句對1位全加器電路建模的示例。
module FA_Seq (A, B, Cin, Sum, Cout);
input A, B, Cin;
output Sum, Cout;
reg Sum, Cout;
reg T1, T2, T3;
always
@ ( A or B or Cin ) begin
Sum = (A ^ B) ^ Cin;
T1 = A Cin;
T2 = B Cin;
T3 = A B;
Cout = (T1| T2) | T3;
end
endmodule
模塊FA_Seq 有三個(gè)輸入和兩個(gè)輸出。由于Sum、Cout、T1、T2和T3在always 語(yǔ)句中被賦值,它們被說(shuō)明為 reg 類(lèi)型(reg 是寄存器數據類(lèi)型的一種)。always 語(yǔ)句中有一個(gè)與事件控制(緊跟在字符@ 后面的表達式)。相關(guān)聯(lián)的順序過(guò)程(begin-end對)。這意味著(zhù)只要A、B或Cin 上發(fā)生事件,即A、B或Cin之一的值發(fā)生變化,順序過(guò)程就執行。在順序過(guò)程中的語(yǔ)句順序執行,并且在順序過(guò)程執行結束后被掛起。順序過(guò)程執行完成后, always 語(yǔ)句再次等待A、B或Cin上發(fā)生的事件。
在順序過(guò)程中出現的語(yǔ)句是過(guò)程賦值模塊化的實(shí)例。模塊化過(guò)程賦值在下一條語(yǔ)句執行前完成執行。過(guò)程賦值可以有一個(gè)可選的時(shí)延。
時(shí)延可以細分為兩種類(lèi)型:
1) 語(yǔ)句間時(shí)延: 這是時(shí)延語(yǔ)句執行的時(shí)延。
2) 語(yǔ)句內時(shí)延: 這是右邊表達式數值計算與左邊表達式賦值間的時(shí)延。
下面是語(yǔ)句間時(shí)延的示例:
Sum = (A ^ B) ^ Cin;
#4 T1 = A Cin;
在第二條語(yǔ)句中的時(shí)延規定賦值延遲4個(gè)時(shí)間單位執行。就是說(shuō),在第一條語(yǔ)句執行后等待4個(gè)時(shí)間單位,然后執行第二條語(yǔ)句。下面是語(yǔ)句內時(shí)延的示例。
Sum = #3 (A^ B) ^ Cin;
這個(gè)賦值中的時(shí)延意味著(zhù)首先計算右邊表達式的值, 等待3個(gè)時(shí)間單位,然后賦值給Sum。
如果在過(guò)程賦值中未定義時(shí)延,缺省值為0時(shí)延,也就是說(shuō),賦值立即發(fā)生。這種形式以及在always 語(yǔ)句中指定語(yǔ)句的其他形式將在第8章中詳細討論。
下面是initial語(yǔ)句的示例:
`timescale 1ns / 1ns
module Test (Pop, Pid);
output Pop, Pid;
reg Pop, Pid;
initial
begin
Pop = 0; // 語(yǔ)句 1。
Pid = 0; // 語(yǔ)句 2。
Pop = #5 1; // 語(yǔ)句 3。
Pid = #3 1; // 語(yǔ)句 4。
Pop = #6 0; // 語(yǔ)句 5。
Pid = #2 0; // 語(yǔ)句 6。
end
endmodule
initial語(yǔ)句包含一個(gè)順序過(guò)程。這一順序過(guò)程在0 ns時(shí)開(kāi)始執行,并且在順序過(guò)程中所有語(yǔ)句全部執行完畢后, initial語(yǔ)句永遠掛起。這一順序過(guò)程包含帶有定義語(yǔ)句內時(shí)延的分組過(guò)程賦值的實(shí)例。語(yǔ)句1和2在0 ns時(shí)執行。第三條語(yǔ)句也在0時(shí)刻執行,導致Pop 在第5 ns時(shí)被賦值。語(yǔ)句4在第5 ns執行,并且Pid 在第8 ns被賦值。同樣,Pop在14 ns被賦值0,Pid在第16 ns被賦值0。第6條語(yǔ)句執行后,initial語(yǔ)句永遠被掛起。
2.5 結構化描述形式
在Verilog HDL中可使用如下方式描述結構:
1) 內置門(mén)原語(yǔ)(在門(mén)級);
2) 開(kāi)關(guān)級原語(yǔ)(在晶體管級);
3) 用戶(hù)定義的原語(yǔ)(在門(mén)級);
4) 模塊實(shí)例 (創(chuàng )建層次結構)。
通過(guò)使用線(xiàn)網(wǎng)來(lái)相互連接。下面的結構描述形式使用內置門(mén)原語(yǔ)描述的全加器電路實(shí)例。
module FA_Str (A, B, Cin, Sum, Cout);
input A, B, Cin;
output Sum, Cout;
wire S1, T1, T2, T3;
xor
X1 (S1, A, B),
X2 (Sum, S1, Cin);
and
A1 (T3, A, B),
A2 (T2, B, Cin),
A3 (T1, A, Cin),
or
O1 (Cout, T1, T2, T3);
endmodule
在這一實(shí)例中,模塊包含門(mén)的實(shí)例語(yǔ)句,也就是說(shuō)包含內置門(mén)xor、and和or 的實(shí)例語(yǔ)句。門(mén)實(shí)例由線(xiàn)網(wǎng)類(lèi)型變量S1、T1、T2和T3互連。由于沒(méi)有指定的順序, 門(mén)實(shí)例語(yǔ)句可以以任何順序出現;圖中顯示了純結構;xor、and和or是內置門(mén)原語(yǔ);X1、X2、A1等是實(shí)例名稱(chēng)。緊跟在每個(gè)門(mén)后的信號列表是它的互連;列表中的第一個(gè)是門(mén)輸出,余下的是輸入。例如,S1與xor 門(mén)實(shí)例X1的輸出連接,而A和B與實(shí)例X1的輸入連接。
4位全加器可以使用4個(gè)1位全加器模塊描述。下面是4位全加器的結構描述形式。
module FourBitFA (FA, FB, FCin, FSum, FCout );
parameter SIZE = 4;
input [SIZE:1] FA, FB;
output [SIZE:1] FSum
input FCin;
input FCout;
wire [ 1: SIZE-1] FTemp;
FA_Str
FA1( .A (FA[1]), .B(FB[1]), .Cin(FCin),
.Sum(FSum[1]), .Cout(FTemp[2])),
FA2( .A (FA[2]), .B(FB[2]), .Cin(FTemp[1]),
.Sum(FSum[2]), .Cout(FTemp[2])),
FA3(FA[3], FB[3], FTemp[2], FSum[3], FTemp[3],
FA4(FA[4], FB[4], FTemp[3], FSum[4], FCout);
endmodule
在這一實(shí)例中,模塊實(shí)例用于建模4位全加器。在模塊實(shí)例語(yǔ)句中,端口可以與名稱(chēng)或位置關(guān)聯(lián)。前兩個(gè)實(shí)例FA1和FA2使用命名關(guān)聯(lián)方式,也就是說(shuō),端口的名稱(chēng)和它連接的線(xiàn)網(wǎng)被顯式描述(每一個(gè)的形式都為“.port_name (net_name))。最后兩個(gè)實(shí)例語(yǔ)句,實(shí)例FA3和FA4使用位置關(guān)聯(lián)方式將端口與線(xiàn)網(wǎng)關(guān)聯(lián)。這里關(guān)聯(lián)的順序很重要,例如,在實(shí)例FA4中,第一個(gè) FA[4]與FA_Str 的端口A(yíng)連接,第二個(gè)FB[4]與FA_Str 的端口B連接,余下的由此類(lèi)推。
2.6 混合設計描述方式
在模塊中,結構的和行為的結構可以自由混合。也就是說(shuō),模塊描述中可以包含實(shí)例化的門(mén)、模塊實(shí)例化語(yǔ)句、連續賦值語(yǔ)句以及always語(yǔ)句和 initial語(yǔ)句的混合。它們之間可以相互包含。來(lái)自always語(yǔ)句和initial語(yǔ)句(切記只有寄存器類(lèi)型數據可以在這兩種語(yǔ)句中賦值)的值能夠驅動(dòng)門(mén)或開(kāi)關(guān),而來(lái)自于門(mén)或連續賦值語(yǔ)句(只能驅動(dòng)線(xiàn)網(wǎng))的值能夠反過(guò)來(lái)用于觸發(fā)always語(yǔ)句和initial語(yǔ)句。
下面是混合設計方式的1位全加器實(shí)例。
module FA_Mix (A, B, Cin, Sum, Cout);
input A,B, Cin;
output Sum, Cout;
reg Cout;
reg T1, T2, T3;
wire S1;
xor X1(S1, A, B); // 門(mén)實(shí)例語(yǔ)句。
always
@ ( A or B or Cin ) begin // always 語(yǔ)句。
T1 = A Cin;
T2 = B Cin;
T3 = A B;
Cout = (T1| T2) | T3;
end
assign Sum = S1 ^ Cin; // 連續賦值語(yǔ)句。
endmodule
只要A或B上有事件發(fā)生,門(mén)實(shí)例語(yǔ)句即被執行。只要A、B或Cin上有事件發(fā)生,就執行always 語(yǔ)句,并且只要S1或Cin上有事件發(fā)生,就執行連續賦值語(yǔ)句。
2.7 設計模擬
Verilog HDL不僅提供描述設計的能力,而且提供對激勵、控制、存儲響應和設計驗證的建模能力。激勵和控制可用初始化語(yǔ)句產(chǎn)生。驗證運行過(guò)程中的響應可以作為“變化時(shí)保存”或作為選通的數據存儲。最后,設計驗證可以通過(guò)在初始化語(yǔ)句中寫(xiě)入相應的語(yǔ)句自動(dòng)與期望的響應值比較完成。
下面是測試模塊Top的例子。該例子測試2.3節中講到的FA_Seq模塊。
‘timescale 1ns/1ns
module Top; // 一個(gè)模塊可以有一個(gè)空的端口列表。
reg PA, PB, PCi;
wire PCo, PSum;
// 正在測試的實(shí)例化模塊:
FA_Seq F1(PA, PB, PCi, PSum, PCo); // 定位。
initial
begin: ONLY_ONCE
reg [3:0] Pal;
//需要4位, Pal才能取值8。
for (Pal = 0; Pal 8; Pal = Pal + 1)
begin
{PA, PB, PCi} = Pal;
#5 $display (“PA, PB, PCi = %b%b%b”, PA, PB, PCi,
“ : : : PCo, PSum=%b%b”, PCo, PSum);
end
end
endmodule
在測試模塊描述中使用位置關(guān)聯(lián)方式將模塊實(shí)例語(yǔ)句中的信號與模塊中的端口相連接。也就是說(shuō),PA連接到模塊FA_Seq的端口A(yíng),PB連接到模塊 FA_Seq的端口B,依此類(lèi)推。注意初始化語(yǔ)句中使用了一個(gè)for循環(huán)語(yǔ)句,在PA、PB和PCi上產(chǎn)生波形。for 循環(huán)中的第一條賦值語(yǔ)句用于表示合并的目標。自右向左,右端各相應的位賦給左端的參數。初始化語(yǔ)句還包含有一個(gè)預先定義好的系統任務(wù)。系統任務(wù)$ display將輸入以特定的格式打印輸出。
系統任務(wù)$display調用中的時(shí)延控制規定$display任務(wù)在5個(gè)時(shí)間單位后執行。這5個(gè)時(shí)間單位基本上代表了邏輯處理時(shí)間。即是輸入向量的加載至觀(guān)察到模塊在測試條件下輸出之間的延遲時(shí)間。
這一模型中還有另外一個(gè)細微差別。Pal在初始化語(yǔ)句內被局部定義。為完成這一功能,初始化語(yǔ)句中的順序過(guò)程(begin-end)必須標記。在這種情況下, ONLY_ONCE是順序過(guò)程標記。如果在順序過(guò)程內沒(méi)有局部聲明的變量,就不需要該標記。下面是測試模塊產(chǎn)生的輸出。
PA, PB, PCi = 000 ::: PCo, PSum = 00
PA, PB, PCi = 001 ::: PCo, PSum = 01
PA, PB, PCi = 010 ::: PCo, PSum = 01
PA, PB, PCi = 011 ::: PCo, PSum = 10
PA, PB, PCi = 100 ::: PCo, PSum = 01
PA, PB, PCi = 101 ::: PCo, PSum = 10
PA, PB, PCi = 110 ::: PCo, PSum = 10
PA, PB, PCi = 111 ::: PCo, PSum = 11
驗證與非門(mén)交叉連接構成的RS_FF模塊的測試模塊如下例所示。
`timescale 10ns/1ns
module RS_FF (Q, Qbar, R, S);
output Q, Qbar;
input R, S;
nand #1 (Q, R, Qbar);
nand #1 (Qbar, S, Q,);
//在門(mén)實(shí)例語(yǔ)句中,實(shí)例名稱(chēng)是可選的。
endmodule
module Test;
reg TS, TR;
wire TQ, TQb;
//測試模塊的實(shí)例語(yǔ)句:
RS_FF NSTA (.Q(TQ), .S(TS), .R(TR), .Qbar(TQb));
//采用端口名相關(guān)聯(lián)的連接方式。
// 加載激勵:
initial
begin:
TR = 0;
TS = 0;
#5 TS = 1;
#5 TS = 0;
TR = 1;
#5 TS = 1;
TR = 0;
#5 TS = 0;
#5 TR = 1;
end
//輸出顯示:
initial
$monitor (At time %t , , $time,
TR = %b, TS=%b, TQ=%b, TQb= %b, TR, TS, TQ, TQb);
endmodule
RS_FF模塊描述了設計的結構。在門(mén)實(shí)例語(yǔ)句中使用門(mén)時(shí)延;例如,第一個(gè)實(shí)例語(yǔ)句中的門(mén)時(shí)延為1個(gè)時(shí)間單位。該門(mén)時(shí)延意味著(zhù)如果R或Qbar假定在T時(shí)刻變化,Q將在T+1時(shí)刻獲得計算結果值。
模塊Test是一個(gè)測試模塊。測試模塊中的RS_FF用實(shí)例語(yǔ)句說(shuō)明其端口用端口名關(guān)聯(lián)方式連接。在這一模塊中有兩條初始化語(yǔ)句。第一個(gè)初始化語(yǔ)句只簡(jiǎn)單地產(chǎn)生TS和TR上的波形。這一初始化語(yǔ)句包含帶有語(yǔ)句間時(shí)延的程序塊過(guò)程賦值語(yǔ)句。
第二條初始化語(yǔ)句調用系統任務(wù)$monitor。這一系統任務(wù)調用的功能是只要參數表中指定的變量值發(fā)生變化就打印指定的字符串。下面是測試模塊產(chǎn)生的輸出。請注意`timescale指令在時(shí)延上的影響。
At time 0, TR=0, TS=0, TQ=x, TQb= x
At time 10, TR=0, TS=0, TQ=1, TQb= 1
At time 50, TR=0, TS=1, TQ=1, TQb= 1
At time 60, TR=0, TS=1, TQ=1, TQb= 0
At time 100, TR=1, TS=0, TQ=1, TQb= 0
At time 110, TR=1, TS=0, TQ=1, TQb= 1
At time 120, TR=1, TS=0, TQ=0, TQb= 1
At time 150, TR=0, TS=1, TQ=0, TQb= 1
At time 160, TR=0, TS=1, TQ=1, TQb= 1
At time 170, TR=0, TS=1, TQ=1, TQb= 0
At time 200, TR=0, TS=0, TQ=1, TQb= 0
At time 210, TR=0, TS=0, TQ=1, TQb= 1
At time 250, TR=1, TS=0, TQ=1, TQb= 1
At time 260, TR=1, TS=0, TQ=0, TQb= 1
后面的章節將更詳細地講述這些主題。
習題
1. 在數據流描述方式中使用什么語(yǔ)句描述一個(gè)設計?
2. 使用`timescale 編譯器指令的目的是什么?舉出一個(gè)實(shí)例。
3. 在過(guò)程賦值語(yǔ)句中可以定義哪兩種時(shí)延?請舉例詳細說(shuō)明。
4. initial語(yǔ)句與always 語(yǔ)句的關(guān)鍵區別是什么?
5. 為2.3節中描述的模塊Decode2x4編寫(xiě)一個(gè)測試驗證程序。
6. 列出你在Verilog HDL模型中使用的兩類(lèi)賦值語(yǔ)句。
7. 在順序過(guò)程中何時(shí)需要定義標記?
8. 找出下面連續賦值語(yǔ)句的錯誤。
assign Reset = #2 ^ WriteBus;
評論