1. 程式人生 > >關於verilog的一些基礎知識整理

關於verilog的一些基礎知識整理


*作者: Ian11122840 時間: 2010-9-27 09:04 *
*標題: 菜鳥做設計必看!有關如何做設計的整體思路,以及能否綜合的筆記 *
*所謂綜合,就是把描述語言轉化成能硬體實現的電路,學verilog的時候,沒有人給我說要不要考慮能否綜合的問題~~~ *
*看了5本書,居然沒有一本書講到能否綜合,所以設計出來的程式完全不能用~~~ *
而且,書中都是講語句的具體使用辦法,例如always @(),但是什麼時候用always,幾個always之間、時序電路、邏輯電路、任務與函式什麼時候用,卻沒有一本書能講清楚。
*這個筆記詳細寫了這些思路的問題,分享給新手看看,學習一種思路~~
*點選此處下載 ourdev_585849OJ54KV.doc(檔案大小:720K) (原檔名:verilog_經驗(適合初學者).doc) *


先記下來:
1、不使用初始化語句;
2、不使用延時語句;
3、不使用迴圈次數不確定的語句,如:forever,while等;
4、儘量採用同步方式設計電路;
5、儘量採用行為語句完成設計;
6、always過程塊描述組合邏輯,應在敏感訊號表中列出所有的輸入訊號;
7、所有的內部暫存器都應該可以被複位;
8、使用者自定義原件(UDP元件)是不能被綜合的。
一:基本
Verilog中的變數有線網型別和暫存器型別。線網型變數綜合成wire,而暫存器可能綜合成WIRE,鎖存器和觸發器,還有可能被優化掉。
二:verilog語句結構到門級的對映
1、連續性賦值:assign
連續性賦值語句邏輯結構上就是將等式右邊的驅動左邊的結點。因此連續性賦值的目標結點總是綜合成由組合邏輯驅動的結點。Assign語句中的延時綜合時都將忽視。
2、過程性賦值:
過程性賦值只出現在always語句中。
阻塞賦值和非阻塞賦值就該賦值本身是沒有區別的,只是對後面的語句有不同的影響。
建議設計組合邏輯電路時用阻塞賦值,設計時序電路時用非阻塞賦值。
過程性賦值的賦值物件有可能綜合成wire, latch,和flip-flop,取決於具體狀況。如,時鐘控制下的非阻塞賦值綜合成flip-flop。
過程性賦值語句中的任何延時在綜合時都將忽略。
建議同一個變數單一地使用阻塞或者非阻塞賦值。
3、邏輯操作符:
邏輯操作符對應於硬體中已有的邏輯閘,一些操作符不能被綜合:=、!


4、算術操作符:
Verilog中將reg視為無符號數,而integer視為有符號數。因此,進行有符號操作時使用integer,使用無符號操作時使用reg。
5、進位:
通常會將進行運算操作的結果比原運算元擴充套件一位,用來存放進位或者借位。如:
Wire [3:0] A,B;
Wire [4:0] C;
Assign C=A+B;
C的最高位用來存放進位。
6、關係運算符:
關係運算符:<,>,<=,>=
和算術操作符一樣,可以進行有符號和無符號運算,取決於資料型別是reg,net還是integer。
7、相等運算子:==,!=
注意:===和!==是不可綜合的。
可以進行有符號或無符號操作,取決於資料型別
8、移位運算子:
左移,右移,右邊運算元可以是常數或者是變數,二者綜合出來的結果不同。
9、部分選擇:
部分選擇索引必須是常量。
10、BIT選擇:
BIT選擇中的索引可以用變數,這樣將綜合成多路(複用)器。
11、敏感表:Always過程中,所有被讀取的資料,即等號右邊的變數都要應放在敏感表中,不然,綜合時不能正確地對映到所用的門。
12、IF:
如果變數沒有在IF語句的每個分支中進行賦值,將會產生latch。如果IF語句中產生了latch,則IF的條件中最好不要用到算術操作。Case語句類似。Case的條款可以是變數。
如果一個變數在同一個IF條件分支中先贖值然後讀取,則不會產生latch。如果先讀取,後贖值,則會產生latch。
13、迴圈:
只有for-loop語句是可以綜合的。
14、設計時序電路時,建議變數在always語句中賦值,而在該always語句外使用,使綜合時能準確地匹配。建議不要使用區域性變數。
15、不能在多個always塊中對同一個變數贖值
16、函式
函式代表一個組合邏輯,所有內部定義的變數都是臨時的,這些變數綜合後為wire。
17、任務:
任務可能是組合邏輯或者時序邏輯,取決於何種情況下呼叫任務。
18、Z:
Z會綜合成一個三態門,必須在條件語句中賦值
19、引數化設計:
優點:引數可過載,不需要多次定義模組
四:模組優化
1、資源共享:
當程序涉及到共用ALU時,要考慮資源分配問題。可以共享的操作符主要有:關係操作符、加減乘除操作符。通常乘和加不共用ALU,乘除通常在其內部共用。
2、共用表示式:
如:C=A+B;
D=G+(A+B);
兩者雖然有共用的A+B,但是有些綜合工具不能識別.可以將第二句改為:D=G+C;這樣只需兩個加法器.
3、轉移程式碼:
如迴圈語句中沒有發生變化的語句移出迴圈.
4、避免latch:
兩種方法:1、在每一個IF分支中對變數賦值。2、在每一個IF語句中都對變數賦初值。
5:模組:
綜合生成的儲存器如ROM或RAM不是一種好方法,只是成堆的暫存器,很費資源。最好用庫自帶的儲存器模組。
五、驗證:
1、敏感表:
在always語句中,如果敏感表不含時鐘,最好將所有的被讀取的訊號都放在敏感表中。
2、非同步復位:
建議不要在非同步時對變數讀取,即非同步復位時,對訊號賦以常數值。

Averilog的流行,有兩方面的原因;
B verilog與VHDL相比的優點
C典型的verilog模組
D verilog語法要點

A) verilog的流行,有兩方面的原因:
1它是cadence的模擬器verilog-XL的基礎,cadence的廣泛流行使得verilog在90年代深入人心;
2它在矽谷獲得廣泛使用;
B) verilog與VHDL相比的優點二者的關係彷彿C與FORTRAN,具體而言:
1 verilog的程式碼效率更高:
比較明顯的對比:
VHDL在描述一個實體時採用entity/architecture模式,
verilog在描述一個實體時只需用一個"module/edumodule"語句塊.
此外verilog的高效性還在很多地方體現出來;
2 verilog支援二進位制的加減運算:
VHDL在進行二進位制的加減運算時使用conv_***函式或者進行其他的定義,總之必須通知編譯器;verilog直接用形如"c=a+b"的表示二進位制的加減運算;
3綜合時可控制性好:
VHDL對訊號不加區分地定義為"signal",
而verilog區分為register型別的和wire型別的;
但是也有人支援VHDL,認為verilog和VHDL的關係彷彿C和C++.C)典型的verilog模組
討論以下典型電路的verilog描述:
*與非門;
*加法器; //即全加器

  • D觸發器;
    *計數器; //**分頻的counter
  • latch;
    *時序機;
    *RAM; //用synopsys的
    *模組引用;
    *預編譯;
    *與非門的verilog描述如下:
    //verilog使用和C語言相同的註釋方法
    module nd02(a1,a2,zn);//一個verilog模組總是以module開始,以endmodule 結束,nd02是模組名,a1,a2,zn是模組的3個輸入輸出訊號
    input a1,a2; //告訴編譯器a1,a2對此模組而言是輸入,並且資料型別是"bit"
    output zn; //告訴編譯器zn對此模組而言是輸出,資料型別也是"bit"
    nand (zn,a1,a2); //我理解nand是運算子,我們不必深究verilog中的正式術語是什
    麼了吧,總之這種形式表示zn=~(a1 && a2);你一定已經想到類似的運算子還有"not",“and”,“or”,“nor”,“xor"了吧;除了"not”,括號裡的訊號數可以任意,例如or (z,f,g,h)表示z=f || g || h,並且延時是3個單位時間,#x表示延時x個單位時間;
    endmodule

*加法器的verilog描述如下:
module ad03d1(A,B,CI,S,CO) ;
input [2:0] A,B; //表示A,B是輸入訊號,並且是3位向量,上界是2,下界是0
input CI;
output [2:0] S;
output CO;
assign {CO,S}=A+B+CI;//一對"{“和”}"表示連結,即將CO和S合併成4位向量
endmodule

帶非同步清零端的D觸發器的verilog描述如下:
module dfctnb (d,cp,cdn,q,qn);
input d,cp,cdn;
output q,qn;
reg q,qn; //關鍵字"reg"表示q和qn是"register"型別的訊號;verilog中有兩種型別的訊號:“register"型別和"wire"型別.你可以簡單地把register型別的訊號想象為某個D觸發器的輸出,而wire型別的的訊號是組合邏輯的輸出.二者的最大區別在於:你可以對register型別的訊號進行定時賦值(用wait語句在特定時刻的賦值,詳見下面always語句),而對於wire型別的訊號則不可.
always wait (cdn0) //表示每當cdn=0時,將要對D觸發器清零,"always"和"wait"巢狀,“wait"和”@"是verilog的兩個關鍵字,表示一旦有某事發生;則執行下面的語句塊,“always"有點象C語言中的"if … then…”,“wait"和”@"的區別:請參考本模組.wait表示本語句塊的程序停止,直到"cdn=0"的條件出現才繼續;我理解在verilog中,每個最外層語句塊都是一個
**的程序;”@"(請看下個always語句)也表示本語句塊的程序停止,直到後面定義"posedge cp"(即出現cp的上升沿)的事件出現才繼續;也許wait和@可以合二為一吧,但至少到目前verilog中wait表示"條件",@表示"事件";具體運用中,wait總是用於類似"wait(xxx=1)“之類的場合,@總是用於類似”@(xxx)“或”@(posedge/negedge xxx)“之類的場合整句話的意思是"每當cdn等於0時,則作以下事情”
begin //begin…end結構的用法類似於pascal語言
q=0;
qn=1;
wait (cdn1);
end
always @ (posedge cp)//"@(posedge cp)“中有兩個關鍵字:”@ (x)“表示"每當事件x發
生”,“posedge x"表示"x的上升沿,“negedge x"表示"x的下降沿”,整句話的意思是"每當cp的上升沿,則作以下事情”
if (cdn) //如果cdn=1(意味著清零端無效)
begin
q=d;
qn=q;//""表示反相
end
endmodule
*計數器的verilog描述如下:
module count(in,set,cp,out) ;//此計數器,在cp的上升沿將輸入賦給輸出,在cp的上升沿使輸出加一
input [15:0] in;
input set,cp;
output [15:0] out;
reg [15:0] out;
always @ (posedge set)
out = in;
always @(posedge cp)
out = out+1; //verilog容許一個訊號同時出現在等號兩端,只要它是reg型別的
endmodule

*latch的描述如下:
always @(clk or d)
if (clk) q = d;

*時序機的verilog描述如下:
always @(posedge CLK) //D是下一個狀態,Q是當前狀態,e1,e2是輸入,a,b是輸出
Q=D;
always @(Q or othercase) begin //當Q變化或輸入e1,e2變化時D要相應變化
D = Q; //note1
a = 0;
b = 0;

case(Q)
q1:begin
q1 action;
if(e1)D=d1;
if(e2)D=d2;
else D=d3;
a = 1; //note 2
end
q2:begin
b = 1;

end
default:begin
a = 0;
b = 0;

end
end
—annotations—
note 1:
This is a custom expression,after reset,D should be equal to Q;
note 2:
In this state machine,a is only equal to 1 at state q1,in
other state,a is equal to 0;

  • RAM的verilog描述如下:
    module ram(din,ain,dout,aout,rd,wr);//這是一個雙口RAM,分別有:輸入端:輸入地址ain;輸入資料din;上升沿有效的寫訊號wr;/輸出端:輸出地址aout;輸出資料dout;高電平有效的讀訊號rd;
    inout [7:0] din;
    input [7:0] ain,aout;
    input rd,wr;
    output [7:0] dout;
    reg [7:0] memory [0:255]; //請注意這是儲存陣列的描述方法,描述了一個共有2
    56個字的儲存陣列,每個字是8位
    assign dout = rd ? memory[aout] : 8’bz; //“assign"關鍵字表示並行賦值語句的
    開始”?“運算子的作用和在C語言中一樣"8’bz"是一個常量,表示一個位元組的高阻態,其中8表示長度是8bit,”’"是固定分割符,"b"表示後面的資料是以位元形式給出的,“z"表示高阻;舉例:4’ha表示長4bit的數"1010”。類似的還可舉出5’b10111,6’o33等等
    always @(posedge wr)
    memory[ain] = din;
    endmodule
    *模組引用
    假設在前面(可以是別的模組)定義了module ram(din,ain,dout,aout,rd,wr),則引用此
    模組時只需寫
    ram myram(din_in_map,ain_in_map,dout_in_map,aout_in_map,rd_in_map,wr_in_map)
    ;
    //其中"ram"是所引用的module名,“myram"是你起的instance名,“din_in_map"等等是圖中的節點名,和器件(module)中的"din…“進行"虛實結合”;
    *預編譯
    類似C語言,只需寫
    include "<pathname:filename>",反上撇號"“是verilog的預編譯符,類似C中的”#”.
    D) verilog語法要點
    *基本原則
    設計時應該把你的系統劃分為計數器,觸發器,時序機,組合邏輯等等可綜合的單元,對此不同的IC公司和EDA開發商可能根據自己的見解和經驗提出不同的要求,並且對verilog程式的細節進行自己的規定,但有一點是對的:即寫硬體描述語言不象寫C語言那樣符合語法就行.單單符合verilog語法的程式可能被拒絕綜合,甚至被拒絕模擬;
    *最外層可以寫什麼?
    這裡所說的最外層是指module語句後的第一層,在這一層可以寫這些可執行語句:
    assign和nand等定義組合邏輯的語句,
    always語句,
    模組引用語句,
    一些以”$"開頭的系統定義語句.
    特別注意不可以寫if語句.if語句只能放在always內部.
    不推薦寫wait語句,因為不能綜合.
    *不可以在多個always語句中對一個訊號賦值.
  1.    強烈建議用同步設計
    

2.在設計時總是記住時序問題
3.在一個設計開始就要考慮到地電平或高電平復位、同步或非同步復位、上升沿或下降沿觸發等問題,在所有模組中都要遵守它
4.在不同的情況下用if和case,最好少用if的多層巢狀(1層或2層比較合適,當在3層以上時,最好修改寫法,因為這樣不僅可以reduce area,而且可以獲得好的timing)
5.在鎖存一個訊號或匯流排時要小心,對於整個design,儘量避免使用latch,因為在DFT時很難test。
6.確信所有的訊號被複位,在DFT時,所有的FlipFlop都是controllable,
7.永遠不要再寫入之前讀取任何內部儲存器(如SRAM)
8.從一個時鐘到另一個不同的時鐘傳輸資料時用資料緩衝,他工作像一個雙時鐘FIFO(是非同步的),可以用Async SRAM搭建Async FIFO。
9.在VHDL中二維陣列可以使用,它是非常有用的。在VERILOG中他僅僅可以使用在測試模組中,不能被綜合
10.遵守register-in register-out規則
11.像synopsys的DC的綜合工具是非常穩定的,任何bugs都不會從綜合工具中產生
12.確保FPGA版本與ASIC的版本儘可能的相似,特別是SRAM型別,若版本一致是最理想的,但是在工作中FPGA版本一般用FPGA自帶的SRAM,ASIC版本一般用廠商提供的SRAM。
13.在嵌入式儲存器中使用BIST
14.虛單元和一些修正電路是必需的
15.一些簡單的測試電路也是需要的,經常在一個晶片中有許多測試模組
16.除非低功耗不要用門控時鐘,強烈建議不要在design中使用gate clock
17.不要依靠指令碼來保證設計。但是在指令碼中的一些好的約束能夠起到更好的效能(例如前向加法器)
18.如果時間充裕,通過時鐘做一個多鎖存器來取代用MUX
19.不要用內部tri-state, ASIC需要匯流排保持器來處理內部tri-state,如IO cell。
20.在top level中作pad insertion
21.選擇pad時要小心(如上拉能力,施密特觸發器,5伏耐壓等),選擇合適的IO cell
22.小心由時鐘偏差引起的問題
23.不要試著產生半週期訊號
24.如果有很多函式要修正,請一個一個地作,修正一個函式檢查一個函式
25.在一個計算等式中排列每個訊號的位數是一個好習慣,即使綜合工具能做
26.不要使用HDL提供的除法器
27.削減不必要的時鐘。它會在設計和佈局中引起很多麻煩,大多數FPGA有1-4個專門的時鐘通道

良好程式碼編寫風格可以滿足信、達、雅的要求。在滿足功能和效能目標的前提下,增強程式碼的可讀性、可移植性,首要的工作是在專案開發之前為整個設計團隊建立一個命名約定和縮略語清單,以文件的形式記錄下來,並要求每位設計人員在程式碼編寫過程中都要嚴格遵守。良好程式碼編寫風格的通則概括如下:
(1) 對所有的訊號名、變數名和埠名都用小寫,這樣做是為了和業界的習慣保持一致;對常量名和使用者定義的型別用大寫;
(2) 使用有意義的訊號名、埠名、函式名和引數名;
(3) 訊號名長度不要太長;
(4) 對於時鐘訊號使用clk 作為訊號名,如果設計中存在多個時鐘,使用clk 作為時鐘訊號的字首;
(5) 對來自同一驅動源的訊號在不同的子模組中採用相同的名字,這要求在晶片總體設計時就定義好頂層子模組間連線的名字,埠和連線埠的訊號儘可能採用相同的名字;
(6) 對於低電平有效的訊號,應該以一個下劃線跟一個小寫字母b 或n 表示。注意在同一個設計中要使用同一個小寫字母表示低電平有效;
(7) 對於復位訊號使用rst 作為訊號名,如果復位訊號是低電平有效,建議使用rst_n;
(8) 當描述多位元匯流排時,使用一致的定義順序,對於verilog 建議採用bus_signal[x:0]的表示;
(9) 儘量遵循業界已經習慣的一些約定。如*_r 表示暫存器輸出,_a 表示非同步訊號,_pn 表示多週期路徑第n 個週期使用的訊號,_nxt 表示鎖存前的訊號,_z 表示三態訊號等;
(10)在原始檔、批處理檔案的開始應該包含一個檔案頭、檔案頭一般包含的內容如下例所示:檔名,作者,模組的實現功能概述和關鍵特性描述,檔案建立和修改的記錄,包括修改時間,修改的內容等;
(11)使用適當的註釋來解釋所有的always 程序、函式、埠定義、訊號含義、變數含義或訊號組、變數組的意義等。註釋應該放在它所註釋的程式碼附近,要求簡明扼要,只要足夠說明設計意圖即可,避免過於複雜;
(12)每一行語句獨立成行。儘管VHDL 和Verilog 都允許一行可以寫多個語句,當時每個語句獨立成行可以增加可讀性和可維護性。同時保持每行小於或等於72 個字元,這樣做都是為了提高程式碼得可讀性;
(13)建議採用縮排提高續行和巢狀語句得可讀性。縮排一般採用兩個空格,如西安交通大學SOC 設計中心2 如果空格太多則在深層巢狀時限制行長。同時縮排避免使用TAB 鍵,這樣可以避免不同機器TAB 鍵得設定不同限制程式碼得可移植能力;
(14)在RTL 原始碼的設計中任何元素包括埠、訊號、變數、函式、任務、模組等的命名都不能取Verilog 和VHDL 語言的關鍵字;
(15)在進行模組的埠申明時,每行只申明一個埠,並建議採用以下順序:
輸入訊號的clk、rst、enables other control signals、data and address signals。然後再申明輸出訊號的clk、rst、enalbes other control signals、data signals;
(16)在例化模組時,使用名字相關的顯式對映而不要採用位置相關的對映,這樣可以提高程式碼的可讀性和方便debug 連線錯誤;
(17)如果同一段程式碼需要重複多次,儘可能使用函式,如果有可能,可以將函式通用化,以使得它可以複用。注意,內部函式的定義一般要添加註釋,這樣可以提高程式碼的可讀性;
(18)儘可能使用迴圈語句和暫存器組來提高原始碼的可讀性,這樣可以有效地減少程式碼行數;
(19)對一些重要的always 語句塊定義一個有意義的標號,這樣有助於除錯。注意標號名不要與訊號名、變數名重複;
(20)程式碼編寫時的資料型別只使用IEEE 定義的標準型別,在VHDL 語言中,設計者可以定義新的型別和子型別,但是所有這些都必須基於IEEE 的標準;
(21)在設計中不要直接使用數字,作為例外,可以使用0 和1。建議採用引數定義代替直接的數字。同時,在定義常量時,如果一個常量依賴於另一個常量,建議在定義該常量時用表示式表示出這種關係;
(22)不要在原始碼中使用嵌入式的dc_shell 綜合命令。這是因為其他的綜合工具並不認得這些隱含命令,從而導致錯誤的或較差的綜合結果。即使使用Design Compiler,當綜合策略改變時,嵌入式的綜合命令也不如放到批處理綜合檔案中易於維護。這個規則有一個例外的綜合命令,即編譯開關的開啟和關閉可以嵌入到程式碼中;
(23)在設計中避免例項化具體的門級電路。門級電路可讀性差,且難於理解和維護,如果使用特定工藝的閘電路,設計將變得不可移植。如果必須例項化閘電路,我們建議採用獨立於工藝庫的閘電路,如SYNOPSYS 公司提供的GTECH 庫包含了高質量的常用的門級電路;
(24)避免冗長的邏輯和子表示式;
(25)避免採用內部三態電路,建議用多路選擇電路代替內部三態電路。

規則 #1: 建立時序邏輯模型時,採用非阻塞賦值語句。
規則 #2: 建立latch模型時,採用非阻塞賦值語句。
規則 #3: 在always塊中建立組合邏輯模型時,採用阻塞賦值語句。
規則 #4: 在一個always塊中同時有組合和時序邏輯時時,採用非阻塞賦值語句。
規則 #5: 不要在一個always塊中同時採用阻塞和非阻塞賦值語句。
規則 #6: 同一個變數不要在多個always塊中賦值。
規則 #7: 呼叫$strobe系統函式顯示用非阻塞賦值語句賦的值。
規則 #8: 不要使用#0延時賦值。
組合邏輯
1,敏感變數的描述完備性
Verilog中,用always塊設計組合邏輯電路時,在賦值表示式右端參與賦值的所有訊號都必須在always @(敏感電平列表)中列出,always中if語句的判斷表示式必須在敏感電平列表中列出。如果在賦值表示式右端引用了敏感電平列表中沒有列出的訊號,在綜合時將會為沒有列出的訊號隱含地產生一個透明鎖存器。這是因為該訊號的變化不會立刻引起所賦值的變化,而必須等到敏感電平列表中的某一個訊號變化時,它的作用才表現出來,即相當於存在一個透明鎖存器,把該訊號的變化暫存起來,待敏感電平列表中的某一個
訊號變化時再起作用,純組合邏輯電路不可能作到這一點。綜合器會發出警告。
Example1:
input a,b,c;
reg e,d;
always @(a or b or c)
begin
e=d&a&b; /d沒有在敏感電平列表中,d變化時e不會立刻變化,直到a,b,c中某一個變化/
d=e |c;
end
Example2:
input a,b,c;
reg e,d;
always @(a or b or c or d)
begin
e=d&a&b; /d在敏感電平列表中,d變化時e立刻變化/
d=e |c;
end
2,條件的描述完備性
如果if語句和case語句的條件描述不完備,也會造成不必要的鎖存器。
Example1:
if (a1’b1) q=1’b1;//如果a1’b0,q=? q將保持原值不變,生成鎖存器!
Example2:
if (a1’b1) q=1’b1;
else q=1’b0;//q有明確的值。不會生成鎖存器!
Example3:
reg[1:0] a,q;

case (a)
2’b00 : q=2’b00;
2’b01 : q=2’b11;//如果a
2’b10或a==2’b11,q=? q將保持原值不變,鎖存器!
endcase
Example4:
reg[1:0] a,q;

case (a)
2’b00 : q=2’b00;
2’b01 : q=2’b11;
default: q=2’b00;//q有明確的值。不會生成鎖存器!
endcase
Verilog中埠的描述
1,埠的位寬最好定義在I/O說明中,不要放在資料型別定義中;
Example1:
module test(addr,read,write,datain,dataout)
input[7:0] datain;
input[15:0] addr;
input read,write;
output[7:0] dataout; //要這樣定義埠的位寬!
wire addr,read,write,datain;
reg dataout;
Example2:
module test(addr,read,write,datain,dataout)
input datain,addr,read,write;
output dataout;
wire[15:0] addr;
wire[7:0] datain;
wire read,write;
reg[7:0] dataout; //不要這樣定義埠的位寬!!
2,埠的I/O與資料型別的關係:
埠的I/O 埠的資料型別
module內部 module外部
input wire wire或reg
output wire或reg wire
inout wire wire
3,assign語句的左端變數必須是wire;直接用"="給變數賦值時左端變數必須是reg!
Example:
assign a=b; //a必須被定義為wire!!


begin
a=b; //a必須被定義為reg!
end
VHDL中STD_LOGIC_VECTOR和INTEGER的區別
例如A是INTEGER型,範圍從0到255;B是STD_LOGIC_VECTOR,定義為8位。A累加到255時,再加1就一直保持255不變,不會自動反轉到0,除非令其為0;而B累加到255時,再加1就會自動反轉到0。所以在使用時要特別注意!
以觸發器為例說明描述的規範性
1,無置位/清零的時序邏輯
always @( posedge CLK)
begin
Q<=D;
end
2,有非同步置位/清零的時序邏輯
非同步置位/清零是與時鐘無關的,當非同步置位/清零訊號到來時,觸發器的輸出立即 被置為1或0,不需要等到時鐘沿到來才置位/清零。所以,必須要把置位/清零訊號 列入always塊的事件控制表示式。
always @( posedge CLK or negedge RESET)
begin
if (!RESET)
Q=0;
else
Q<=D;
end
3,有同步置位/清零的時序邏輯
同步置位/清零是指只有在時鐘的有效跳變時刻置位/清零,才能使觸發器的輸出分 別轉換為1或0。所以,不要把置位/清零訊號列入always塊的事件控制表示式。但是 必須在always塊中首先檢查置位/清零訊號的電平。
always @( posedge CLK )
begin
if (!RESET)
Q=0;
else
Q<=D;
end
結構規範性
在整個晶片設計專案中,行為設計和結構設計的編碼是最重要的一個步驟。 它對邏輯綜合和佈線結果、時序測定、校驗能力、測試能力甚至產品支援 都有重要的影響。考慮到模擬器和真實的邏輯電路之間的差異,為了有效的
進行模擬測試:
1,避免使用內部生成的時鐘
內部生成的時鐘稱為門生時鐘(gated clock)。如果外部輸入時鐘和門生時鐘同時驅動, 則不可避免的兩者的步調不一致,造成邏輯混亂。而且,門生時鐘將會增加測試的難度 和時間。
2,絕對避免使用內部生成的非同步置位/清零訊號
內部生成的置位/清零訊號會引起測試問題。使某些輸出訊號被置位或清零,無法正常 測試。
3,避免使用鎖存器
鎖存器可能引起測試問題。對於測試向量自動生成(ATPG), 為了使掃描進行,鎖存器需要置為透明模式(transparent mode), 反過來,測試鎖存器需要構造特定的向量,這可非同一般。
4,時序過程要有明確的復位值
使觸發器帶有復位端,在製造測試、ATPG以及模擬初始化時,可以對整個電路進行 快速復位。
5,避免模組內的三態/雙向
內部三態訊號在製造測試和邏輯綜合過程中難於處理.

近日讀 J.Bhasker 的 , 受益匪淺,理清了不少基礎電路知識 , 記下一些 tips :

  1. 過程賦值(always 中觸發賦值)的變數,可能會被綜合成連線 或觸發器 或鎖存器.
    2.綜合成鎖存器的規則:
    a. 變數在條件語句(if 或case)中,被賦值.
    b. 變數未在條件語句的所有分支中被賦值.
    c. 在always語句多次呼叫之間需要保持變數值 .
    以上三個條件必須同時滿足.
    3.綜合成觸發器的規則:
    變數在時鐘沿的控制下被賦值。
    例外情況:變數的賦值和引用都僅出現在一條always語句中,則該變數被視為中
    間變數而不是觸發器。
  2. 對於無時鐘事情的always語句(即組合邏輯建模),其時間表應包括該alwa語
    句引用的所有變數,否則會出現RTL與Netlist的不一致
    晶片外部引腳很多都使用inout型別的,為的是節省管腿。一般訊號線用做匯流排等雙向資料傳輸的時候就要用到INOUT型別了。就是一個埠同時做輸入和輸出。 inout在具體實現上一般用三態門來實現。三態門的第三個狀態就是高阻’Z’。 當inout埠不輸出時,將三態門置高阻。這樣訊號就不會因為兩端同時輸出而出錯了,更詳細的內容可以搜尋一下三態門tri-state的資料.
    1 使用inout型別資料,可以用如下寫法:
    inout data_inout;
    input data_in;
    reg data_reg;//data_inout的映象暫存器
    reg link_data;
    assign data_inout=link_data?data_reg:1’bz;//link_data控制三態門
    //對於data_reg,可以通過組合邏輯或者時序邏輯根據data_in對其賦值.通過控制link_data的高低電平,從而設定data_inout是輸出資料還是處於高阻態,如果處於高阻態,則此時當作輸入埠使用.link_data可以通過相關電路來控制.
    2 編寫測試模組時,對於inout型別的埠,需要定義成wire型別變數,而其它輸入埠都定義成reg型別,這兩者是有區別的.
    當上面例子中的data_inout用作輸入時,需要賦值給data_inout,其餘情況可以斷開.此時可以用assign語句實現:assign data_inout=link?data_in_t:1’bz;其中的link ,data_in_t是reg型別變數,在測試模組中賦值.
    另外,可以設定一個輸出埠觀察data_inout用作輸出的情況:
    Wire data_out;
    Assign data_out_t=(!link)?data_inout:1’bz;
    else,in RTL
    inout use in top module(PAD)
    dont use inout(tri) in sub module
    也就是說,在內部模組最好不要出現inout,如果確實需要,那麼用兩個port實現,到頂層的時候再用三態實現。理由是:在非頂層模組用雙向口的話,該雙向口必然有它的上層跟它相連。既然是雙向口,則上層至少有一個輸入口和一個輸出口聯到該雙向口上,則發生兩個內部輸出單元連線到一起的情況出現,這樣在綜合時往往會出錯。
    對雙向口,我們可以將其理解為2個分量:一個輸入分量,一個輸出分量。另外還需要一個控制訊號控制輸出分量何時輸出。此時,我們就可以很容易地對雙向埠建模。
    例子:
    CODE:
    module dual_port (

    inout_pin,

    );
    inout inout_pin;
    wire inout_pin;
    wire input_of_inout;
    wire output_of_inout;
    wire out_en;
    assign input_of_inout = inout_pin;
    assign inout_pin = out_en ? output_of_inout : 高阻;
    endmodule
    可見,此時input_of_inout和output_of_inout就可以當作普通訊號使用了。
    在模擬的時候,需要注意雙向口的處理。如果是直接與另外一個模組的雙向口連線,那麼只要保證一個模組在輸出的時候,另外一個模組沒有輸出(處於高阻態)就可以了。
    如果是在ModelSim中作為單獨的模組模擬,那麼在模組輸出的時候,不能使用force命令將其設為高阻態,而是使用release命令將匯流排釋放掉
    很多初學者在寫testbench進行模擬和驗證的時候,被inout雙向口難住了。模擬器老是提示錯誤不能進行。下面是我個人對inout埠寫testbench模擬的一些總結,並舉例進行說明。在這裡先要說明一下inout口在testbench中要定義為wire型變數。
    先假設有一原始碼為:
    module xx(data_inout , …);
    inout data_inout;

    assign data_inout=(! link)?datareg:1’bz;
    endmodule
    方法一:使用相反控制訊號inout口,等於兩個模組之間用inout雙向口互連。這種方法要注意assign 語句只能放在initial和always塊內。
    module test();
    wire data_inout;
    reg data_reg;
    reg link;
    initial begin

    end
    assign data_inout=link?data_reg:1’bz;
    endmodule
    方法二:使用force和release語句,但這種方法不能準確反映雙向埠的訊號變化,但這種方法可以反在塊內。
    module test();
    wire data_inout;
    reg data_reg;
    reg link;
    #xx; //延時
    force data_inout=1’bx; //強制作為輸入埠

    #xx;
    release data_inout; //釋放輸入埠
    endmodule
    很多讀者反映模擬雙向埠的時候遇到困難,這裡介紹一下雙向埠的模擬方法。一個典型的雙向埠如圖1所示。

其中inner_port與晶片內部其他邏輯相連,outer_port為晶片外部管腳,out_en用於控制雙向埠的方向,out_en為1時,埠為輸出方向,out_en為0時,埠為輸入方向。

用Verilog語言描述如下:
module bidirection_io(inner_port,out_en,outer_port);
input out_en;
inout[7:0] inner_port;
inout[7:0] outer_port;
assign outer_port=(out_en1)?inner_port:8’hzz;
assign inner_port=(out_en
0)?outer_port:8’hzz;
endmodule

用VHDL語言描述雙向埠如下:
library ieee;
use IEEE.STD_LOGIC_1164.ALL;
entity bidirection_io is
port ( inner_port : inout std_logic_vector(7 downto 0);
out_en : in std_logic;
outer_port : inout std_logic_vector(7 downto 0) );
end bidirection_io;
architecture behavioral of bidirection_io is
begin
outer_port<=inner_port when out_en=‘1’ else (OTHERS=>‘Z’);
inner_port<=outer_port when out_en=‘0’ else (OTHERS=>‘Z’);
end behavioral;

模擬時需要驗證雙向埠能正確輸出資料,以及正確讀入資料,因此需要驅動out_en埠,當out_en埠為1時,testbench驅動inner_port埠,然後檢查outer_port埠輸出的資料是否正確;當out_en埠為0時,testbench驅動outer_port埠,然後檢查inner_port埠讀入的資料是否正確。由於inner_port和outer_port埠都是雙向埠(在VHDL和Verilog語言中都用inout定義),因此驅動方法與單向埠有所不同。
驗證該雙向埠的testbench結構如圖2所示。

這是一個self-checking testbench,可以自動檢查模擬結果是否正確,並在Modelsim控制檯上打印出提示資訊。圖中Monitor完成訊號取樣、結果自動比較的功能。
testbench的工作過程為
1)out_en=1時,雙向埠處於輸出狀態,testbench給inner_port_tb_reg訊號賦值,然後讀取outer_port_tb_wire的值,如果兩者一致,雙向埠工作正常。
2)out_en=0時,雙向埠處於輸如狀態,testbench給outer_port_tb_reg訊號賦值,然後讀取inner_port_tb_wire的值,如果兩者一致,雙向埠工作正常。

用Verilog程式碼編寫的testbench如下,其中使用了自動結果比較,隨機化激勵產生等技術。

`timescale 1ns/10ps
module tb();
reg[7:0] inner_port_tb_reg;
wire[7:0] inner_port_tb_wire;
reg[7:0] outer_port_tb_reg;
wire[7:0] outer_port_tb_wire;
reg out_en_tb;
integer i;

initial
begin
out_en_tb=0;
inner_port_tb_reg=0;
outer_port_tb_reg=0;
i=0;
repeat(20)
begin
#50
i= r a n d o m ; o u t e n t b = i [ 0 ] ; / / r a n d o m i z e o u t e n t b i n n e r p o r t t b r e g = random; out_en_tb=i[0]; //randomize out_en_tb inner_port_tb_reg= random; //randomize data
outer_port_tb_reg=$random;
end
end

//**** drive the ports connecting to bidirction_io
assign inner_port_tb_wire=(out_en_tb1)?inner_port_tb_reg:8’hzz;
assign outer_port_tb_wire=(out_en_tb
0)?outer_port_tb_reg:8’hzz;

//instatiate the bidirction_io module
bidirection_io bidirection_io_inst(.inner_port(inner_port_tb_wire),
.out_en(out_en_tb),
.outer_port(outer_port_tb_wire));

//***** monitor ******
[email protected](out_en_tb,inner_port_tb_wire,outer_port_tb_wire)
begin
#1;
if(outer_port_tb_wire===inner_port_tb_wire)
begin
KaTeX parse error: Expected 'EOF', got '\n' at position 10: display("\̲n̲ ̲**** time=%t **…time);
$display(“OK! out_en=%d”,out_en_tb);
$display(“OK! outer_port_tb_wire=%d,inner_port_tb_wire=%d”,
outer_port_tb_wire,inner_port_tb_wire);
end
else
begin
KaTeX parse error: Expected 'EOF', got '\n' at position 10: display("\̲n̲ ̲**** time=%t **…time);
$display(“ERROR! out_en=%d”,out_en_tb);
$display(“ERROR! outer_port_tb_wire != inner_port_tb_wire” );
$display(“ERROR! outer_port_tb_wire=%d, inner_port_tb_wire=%d”,
outer_port_tb_wire,inner_port_tb_wire);
end
end
endmodule

今天重新回顧了一下阻塞賦值和非阻塞賦值的概念,感覺又有所收穫。
一、特點:
阻塞賦值:1、RHS的表示式計算和LHS的賦值更新,這兩個動作之間不能插入其他動作,即所謂計算完畢,立即更新。
2、所謂阻塞就是指在一個“begin…end”塊中的多個阻塞賦值語句內,只有上一句完全執行完畢後,才會執行下一語句,否則阻塞程式的執行。
非阻塞賦值:RHS的表示式計算和LHS的賦值更新分兩個節拍執行,首先,應該是RHS的表示式計算,得到新值後並不立即賦值,而是放在事件佇列中等待,直到
當前模擬時刻的後期才執行(原因下文會提到)。
二、Verilog的分層事件佇列:
在Verilog中,事件佇列可以劃分為5個不同的區域,不同的事件根據規定放在不同的區域內,按照優先順序的高低決定執行的先後順序,下表就列出了部分Verilog分層事件佇列。其中,活躍事件的優先順序最高(最先執行),而監控事件的優先順序最低,而且在活躍事件中的各事件的執行順序是隨機的(注:為方便起見,在一般的模擬器中,對同一區域的不同事件是按照排程的先後關係執行的)。

當前模擬
時間事件

活躍事件 阻塞賦值,非阻塞賦值的RHS計算……

非活躍事件

顯式0延時的阻塞賦值……

非阻塞賦值更新事件

由非阻塞語句產生的一個非阻塞賦值更新事件,並被調入當前模擬時刻。

監控事件

m o n i t o r monitor和 strobe等系統任務

將來模擬
時間事件 被排程到將來模擬時間的事件

三、結論:
由上表就可以知道,阻塞賦值屬於活躍事件,會立刻執行,這就是阻塞賦值“計算完畢,立刻更新”的原因。此外,由於在分層事件佇列中,只有將活躍事件中排在前面的事件調出,並執行完畢後,才能夠執行下面的事件,這就可以解釋阻塞賦值的第二個特點。
同樣是由上表知,非阻塞賦值的RHS計算屬於活躍事件,而非阻塞賦值更新事件排在非活躍事件之後,因此只有模擬佇列中所有的活躍事件和非活躍事件都執行完畢後,才輪到非阻塞賦值更新事件,這就是非阻塞賦值必須分兩拍完成的原因。

以上就是我今天的讀書筆記,寫得倉促,如有不對,敬請指出 。

一. 強調Verilog程式碼編寫風格的必要性。
強調Verilog程式碼編寫規範,經常是一個不太受歡迎的話題,但卻是非常有必要的。
每個程式碼編寫者都有自己的編寫習慣,而且都喜歡按照自己的習慣去編寫程式碼。與自己編寫風格相近的程式碼,閱讀起來容易接受和理解。相反和自己編寫風格差別較大的程式碼,閱讀和接受起來就困難一些。
曾有程式設計大師總結說,一個優秀的程式設計師,能維護的程式碼長度大約在1萬行數量級。程式碼的整潔程度,很大程度上影響著程式碼的維護難度。
遵循程式碼編寫規範書寫的程式碼,很容易閱讀、理解、維護、修改、跟蹤除錯、整理文件。相反程式碼編寫風格隨意的程式碼,通常晦澀、凌亂,會給開發者本人的除錯、修改工作帶來困難,也會給合作者帶來很大麻煩。
(實際上英文Coding Style有另一層涵義,更偏重的是,某一個電路,用那一種形式的語言描述,才能將電路描述得更準確,綜合以後產生的電路更合理。本文更偏重的是,編寫Verilog程式碼時的書寫習慣。)

二. 強調編寫規範的宗旨。
縮小篇幅
提高整潔度
便於跟蹤、分析、除錯
增強可讀性,幫助閱讀者理解
便於整理文件
便於交流合作
三. 變數及訊號命名規範。

  1. 系統級訊號的命名。
    系統級訊號指復位訊號,置位訊號,時鐘訊號等需要輸送到各個模組的全域性訊號;系統訊號以字串Sys開頭。

  2. 低電平有效的訊號後一律加下劃線和字母n。如:SysRst_n;FifoFull_n;

  3. 經過鎖存器鎖存後的訊號,後加下劃線和字母r,與鎖存前的訊號區別。如CpuRamRd訊號,經鎖存後應命名為CpuRamRd_r。
    低電平有效的訊號經過鎖存器鎖存後,其命名應在_n後加r。如CpuRamRd_n訊號,經鎖存後應命名為CpuRamRd_nr
    多級鎖存的訊號,可多加r以標明。如CpuRamRd訊號,經兩級觸發器鎖存後,應命名為CpuRamRd_rr。

  4. 模組的命名。
    在系統設計階段應該為每個模組進行命名。命名的方法是,將模組英文名稱的各個單詞首字母組合起來,形成3到5個字元的縮寫。若模組的英文名只有一個單詞,可取該單詞的前3個字母。各模組的命名以3個字母為宜。例如:
    Arithmatic Logical Unit模組,命名為ALU。
    Data Memory Interface模組,命名為DMI。
    Decoder模組,命名為DEC。

  5. 模組之間的介面訊號的命名。
    所有變數命名分為兩個部分,第一部分表明資料方向,其中資料發出方在前,資料接收方在後,第二部分為資料名稱。
    兩部分之間用下劃線隔離開。
    第一部分全部大寫,第二部分所有具有明確意義的英文名全部拼寫或縮寫的第一個字母大寫,其餘部分小寫。
    舉例:CPUMMU_WrReq,下劃線左邊是第一部分,代表資料方向是從CPU模組發向儲存器管理單元模組(MMU)。下劃線右邊Wr為Write的縮寫,Req是Request的縮寫。兩個縮寫的第一個字母都大寫,便於理解。整個變數連起來的意思就是CPU傳送給MMU的寫請求訊號。
    模組上下層次間訊號的命名也遵循本規定。
    若某個訊號從一個模組傳遞到多個模組,其命名應視訊號的主要路徑而定。

  6. 模組內部訊號:
    模組內部的訊號由幾個單詞連線而成,縮寫要求能基本表明本單詞的含義;
    單詞除常用的縮寫方法外(如:Clock->Clk, Write->Wr, Read->Rd等),一律取該單詞的前幾個字母( 如:Frequency->Freq, Variable->Var 等);
    每個縮寫單詞的第一個字母大寫;
    若遇兩個大寫字母相鄰,中間新增一個下劃線(如DivN_Cntr);
    舉例:SdramWrEn_n;FlashAddrLatchEn;
    四. 編碼格式規範。

  7. 分節書寫,各節之間加1到多行空格。如每個always,initial語句都是一節。每節基本上完成一個特定的功能,即用於描述某幾個訊號的產生。在每節之前有幾行註釋對該節程式碼加以描述,至少列出本節中描述的訊號的含義。

  8. 行首不要使用空格來對齊,而是用Tab鍵,Tab鍵的寬度設為4個字元寬度。行尾不要有多餘的空格。

  9. 註釋。
    使用//進行的註釋行以分號結束;
    使用/* */進行的註釋,//各佔用一行,並且頂頭;
    例:
    // Edge detector used to synchronize the input signal;

  10. 空格的使用:
    不同變數,以及變數與符號、變數與括號之間都應當保留一個空格。
    Verilog關鍵字與其它任何字串之間都應當保留一個空格。如:
    Always @ (……)
    使用大括號和小括號時,前括號的後邊和後括號的前邊應當留有一個空格。
    邏輯運算子、算術運算子、比較運算子等運算子的兩側各留一個空格,與變數分隔開來;單運算元運算子例外,直接位於運算元前,不使用空格。
    使用//進行的註釋,在//後應當有一個空格;註釋行的末尾不要有多餘的空格。
    例:
    assign SramAddrBus = { AddrBus[31:24], AddrBus[7:0] };
    assign DivCntr[3:0] = DivCntr[3:0] + 4’b0001;
    assign Result = ~Operand;

  11. 同一個層次的所有語句左端對齊;Initial、always等語句塊的begin關鍵詞跟在本行的末尾,相應的end關鍵詞與Initial、always對齊;這樣做的好處是避免因begin獨佔一行而造成行數太多;
    例:
    always @ ( posedge SysClk or negedge SysRst ) begin
    if( !SysRst ) DataOut <= 4’b0000;
    else if( LdEn ) begin
    DataOut <= DataIn;
    End
    else DataOut <= DataOut + 4’b0001;
    end

  12. 不同層次之間的語句使用Tab鍵進行縮排,每加深一層縮排一個Tab;

  13. 在endmodule,endtask,endcase等標記一個程式碼塊結束的關鍵詞後面要加上一行註釋說明這個程式碼塊的名稱;

  14. 在task名稱前加tsk以示標記。在function的名稱前加func以示標記。例如:
    task tskResetSystem;
    ……
    endtask //of tskResetSystem
    五.小結:
    以上列出的程式碼編寫規範無法覆蓋程式碼編寫的方方面面,還有很多細節問題,需要在實際編寫過程中加以考慮。並且有些規定也不是絕對的,需要靈活處理。並不是律條,但是在一個專案組內部、一個專案的程序中,應該有一套類似的程式碼編寫規範來作為約束。
    總的方向是,努力寫整潔、可讀性好的程式碼

二.reg型
在“always”塊內被賦值的每一個訊號都必須定義成reg型。
reg型資料的預設初始值是不定值。
reg型只表示被定義的訊號將用在“always”塊內,理解這一點很重要。並不是說reg型訊號一定是暫存器或觸發器的輸出。雖然reg型訊號常常是暫存器或觸發器的輸出,但並不一定總是這樣。

三.memory型
memory型資料是通過擴充套件reg型資料的地址範圍來生成的。其格式如下:

reg [n-1:0] 儲存器名[m-1:0];
或 reg [n-1:0] 儲存器名[m:1];

在這裡,reg[n-1:0]定義了儲存器中每一個儲存單元的大小,即該儲存單元是一個n位的暫存器。儲存器名後的[m-1:0]或[m:1]則定義了該儲存器中有多少個這樣的暫存器。
reg [7:0] mema[255:0];

這個例子定義了一個名為mema的儲存器,該儲存器有256個8位的儲存器。該儲存器的地址範圍是0到255。注意:對儲存器進行地址索引的表示式必須是常數表示式。
儘管memory型資料和reg型資料的定義格式很相似,但要注意其不同之處。如一個由n個1位暫存器構成的儲存器組是不同於一個n位的暫存器的。見下例:

reg [n-1:0] rega; //一個n位的暫存器
reg mema [n-1:0]; //一個由n個1位暫存器構成的儲存器組

一個n位的暫存器可以在一條賦值語句裡進行賦值,而一個完整的儲存器則不行。見下例:

rega =0; //合法賦值語句
mema =0; //非法賦值語句

如果想對memory中的儲存單元進行讀寫操作,必須指定該單元在儲存器中的地址。下面的寫法是正確的。
mema[3]=0; //給memory中的第3個儲存單元賦值為0。
3.3.1.基本的算術運算子

在Verilog HDL語言中,算術運算子又稱為二進位制運算子,共有下面幾種:

  1.  + (加法運算子,或正值運算子,如 rega+regb,+3)
    
  2.  -(減法運算子,或負值運算子,如 rega-3,-3)
    
  3.  ×(乘法運算子,如rega*3)
    
  4.  / (除法運算子,如5/3)
    
  5.  % (模運算子,或稱為求餘運算子,要求%兩側均為整型資料。如7%3的值為1)
    

注意: 在進行算術運算操作時,如果某一個運算元有不確定的值x,則整個結果也為不定值x。

  1.  ~          //取反
    
  2.  &          //按位與
    
  3.  |          //按位或
    
  4.  ^          //按位異或
    
  5.  ^~         //按位同或(異或非)
    

在Verilog HDL語言中存在三種邏輯運算子:

  1.  && 邏輯與
    
  2.  || 邏輯或
    
  3.  ! 邏輯非
    

關係運算符共有以下四種:

a < b a小於b
a > b a大於b
a <= b a小於或等於b
a >= b a大於或等於b

3.3.5.等式運算子
3.3.6.移位運算子
3.3.7.位拼接運算子(Concatation)
3.3.10.關鍵詞
在Verilog HDL中,所有的關鍵詞是事先定義好的確認符,用來組織語言結構。關鍵詞是用小寫字母定義的,因此在編寫原程式時要注意關鍵詞的書寫,以避免出錯。下面是Verilog HDL中使用的關鍵詞(請參閱附錄:Verilog語言參考手冊):

always, and, assign,begin,buf,bufif0,bufif1,case,casex,casez,cmos,deassign,default,defparam,disable,edge,else,end,endcase,endmodule,endfunction,endprimitive, endspecify, endtable, endtask, event, for, force, forever, fork, function,highz0, highz1, if,initial, inout, input,integer,join,large,macromodule,medium,module,nand,negedge,nmos,nor,not,notif0,notifl, or, output, parameter, pmos, posedge, primitive, pull0, pull1, pullup, pulldown, rcmos, reg, releses, repeat, mmos, rpmos, rtran, rtranif0,rtranif1,scalared,small,specify,specparam,strength,strong0, strong1, supply0, supply1, table, task, time, tran, tranif0, tranif1, tri, tri0, tri1, triand, trior, trireg,vectored,wait,wand,weak0,weak1,while, wire,wor, xnor, xor

(1).非阻塞(Non_Blocking)賦值方式( 如 b <= a; )

  1.  塊結束後才完成賦值操作。
    
  2.  b的值並不是立刻就改變的。
    
  3.  這是一種比較常用的賦值方法。(特別在編寫可綜合模組時)
    

(2).阻塞(Blocking)賦值方式( 如 b = a; )

  1.  賦值語句執行完後,塊才結束。
    
  2.  b的值在賦值語句執行完後立刻就改變的。
    
  3.  可能會產生意想不到的結果。
    

一.順序塊
順序塊有以下特點:

  1.  塊內的語句是按順序執行的,即只有上面一條語句執行完後下面的語句才能執行。
    
  2.  每條語句的延遲時間是相對於前一條語句的模擬時間而言的。
    
  3.  直到最後一條語句執行完,程式流程控制才跳出該語句塊。
    

順序塊的格式如下:
begin
語句1;
語句2;

語句n;
end
其中:
 塊名即該塊的名字,一個標識名。其作用後面再詳細介紹。
 塊內宣告語句可以是引數宣告語句、reg型變數宣告語句、integer型變數宣告語句、real型變數宣告語句。

二. 並行塊
並行塊有以下四個特點:

  1.  塊內語句是同時執行的,即程式流程控制一進入到該並行塊,塊內語句則開始同時並行地執行。
    
  2.  塊內每條語句的延遲時間是相對於程式流程控制進入到塊內時的模擬時間的。
    
  3.  延遲時間是用來給賦值語句提供執行時序的。
    
  4.  當按時間時序排序在最後的語句執行完後或一個disable語句執行時,程式流程控制跳出該程式塊。
    

並行塊的格式如下:
fork
語句1;
語句2;

語句n;
join
其中:
• 塊名即標識該塊的一個名字,相當於一個識別符號。
• 塊內說明語句可以是引數說明語句、reg型變數宣告語句、integer型變數宣告語句、real型變數宣告語句、time型變數宣告語句、事件(event)說明語句。

在fork_join塊內,各條語句不必按順序給出,因此在並行塊裡,各條語句在前還是在後是無關緊要的。見下例:

三. 塊名
在VerilgHDL語言中,可以給每個塊取一個名字,只需將名字加在關鍵詞begin或fork後面即可。這樣做的原因有以下幾點。

  1.  這樣可以在塊內定義區域性變數,即只在