摘要:Verilog HDL硬件描述語言是在用途最廣泛的C語言的基礎上發展起來的一種硬件描述語言,具有靈活性高、易學易用等特點。Verilog HDL可以在較短的時間內學習和掌握,FPGA的Veilog HDL基礎語法總結,看完這些,FPGA的基本語法應該就沒啥問題了!
一、基礎知識1、邏輯值邏輯0:表示低電平,也就對應我們電路GND;
邏輯1:表示高電平,也就是對應我們電路的VCC;
邏輯X:表示未知,有可能是高電平,也有可能是低電平;
邏輯Z:表示高阻態,外部沒有激勵信號,是一個懸空狀態。
2、進制格式Verilog數字進制格式包括二進制、八進制、十進制和十六進制。
一般常用的為二進制、十進制和十六進制。
二進制表示如下:4b0101表示4位二進制數字0101
十進制表示如下:4‘d2表示4位十進制數字2(二進制0010)
十六進制表示如下:4ha表示4位十六進制數字a(二進制1010)
16’b1001 1010 1010 1001=16‘h9AA9
3、標識符標識符(identifier)用于定義模塊名、端口名、信號名等。
標識符可以是任意一組字母、數字、$符號和(下劃線)符號的組合;
但標識符的第一個字符必須是字母或者下劃線;
標識符是區分大小寫的;
4、標識符推薦寫法不建議大小寫混合使用;
普通內部信號建議全部小寫;
信號命名最好體現信號的含義,簡潔、清晰、易懂;
以下是一些推薦的寫法:
2、用下劃線區分詞,如cpu addr。
3、采用一些前綴或后綴,比如時鐘采用clk前綴:clk_50,clk_cpu;
二、數據類型在Verilog 語言中,主要有三大類數據類型。
從名稱中,我們可以看出,真正在數字電路中起作用的數據類型應該是寄存器數據類型和線網數據類型。
1、寄存器類型寄存器表示一個抽象的數據存儲單元,通過賦值語句可以改變寄存器儲存的值寄存器數據類型的關鍵字是reg,reg類型數據的默認初始值為不定值x。
reg類型的數據只能在always語句和initial語句中被賦值。
如果該過程語句描述的是時序邏輯,即always語句帶有時鐘信號,則該寄存器變量對應為觸發器;
如果該過程語句描述的是組合邏輯,即always語句不帶有時鐘信號,則該寄存器變量對應為硬件連線;
//計數器對系統時鐘計數,計時0.2秒
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
counter 《= 24’d0;
else if (counter 《 24‘d999_9999)
counter 《= counter + 1’b1;
else
counter 《= 24‘d0;
end
//通過移位寄存器控制IO口的高低電平,從而改變LED的顯示狀態
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
led 《= 4’b0001;
else if(counter == 24‘d999_9999)
led[3:0] 《= {led[2:0],led[3]};
else
led 《= led;
end
2、線網類型線網數據類型表示結構實體(例如門)之間的物理連線。
線網類型的變量不能儲存值,它的值是由驅動它的元件所決定的。驅動線網類型變量的元件有門、連續賦值語句、assign等。
如果沒有驅動元件連接到線網類型的變量上,則該變量就是高阻的,即其值為z。
線網數據類型包括wire型和tri型,其中最常用的就是wire類型。
3、參數類型參數其實就是一個常量,在Verilog HDL中用parameter定義常量。
我們可以一次定義多個參數,參數與參數之間需要用逗號隔開。
每個參數定義的右邊必須是一個常數表達式。
參數型數據常用于定義狀態機的狀態、數據位寬和延遲大小等。
采用標識符來代表一個常量可以提高程序的可讀性和可維護性。
在模塊調用時,可通過參數傳遞來改變被調用模塊中已定義的參數。
三、運算符1、算數運算符2、關系運算符3、邏輯運算符4、條件操作符result=(a》=b)?a:b;
5、位運算符6、移位運算符兩種移位運算都用0來填補移出的空位。
左移時,位寬增加;右移時,位寬不變。
4b1001 《《2 = 6’b100100;
4b1001 》》1 = 4b0100;
7、拼接運算符c={a,b[3:0];
8、優先級運算符四、模塊結構Verilog的基本設計單元是“模塊“(block)。
一個模塊是由兩部分組成的,一部分描述接口,另一部分描述邏輯功能。
使用quartusii軟件編寫出上圖左邊的硬件描述代碼,通過軟件編譯,就能生成最右邊組合邏輯電路圖來。每個Verilog程序包括4個主要的部分:端口定義、I0說明、內部信號聲明、功能定義。
流水燈代碼
上圖時流水燈的代碼,第一個always塊代碼的意思:
如果時鐘信號的上升沿或者復位信號的下降沿到來,就執行begin與end之間的代碼。
如果產生了復位信號(低電平),計數器清0,如果計數器的值小于10000000,計數器的值就+1,如果沒有產生復位信號和計數值不小于10000000,計數器的值就為0。在這個always塊中,邏輯是順序執行的。
第二個always塊代碼的意思:
如果時鐘信號的上升沿或者復位信號的下降沿到來,就執行begin與end之間的代碼。如果產生了復位信號(低電平),led0點亮,如果計數器的值小于10000000,led0-3順序點亮,如果沒有產生復位信號和計數值不小于10000000,led燈狀態保持不變。在這個always塊中,邏輯是順序執行的。
但是這個always塊代碼是并行執行的,也就是說時鐘信號一直在產生。
功能定義部分有三種方法:
1、assign語句描述組合邏輯
2、always語句描述組合/時序邏輯
3、例化實例元件
上述三種邏輯功能是并行執行的。
五、結構語句1、initial和always語句initial語句它在模塊中只執行一次。
它常用于測試文件的編寫,用來產生仿真測試信號(激勵信號),或者用于對存儲器變量賦初值。
always 語句一直在不斷地重復活動。但是只有和一定的時間控制結合在一起才有作用。
一般initial語句常用于測試文件,在測試文件中初始化使用。比如上面的代碼首先始終信號初始化為0,之后在always語句中讓其10個時鐘周期翻轉一次,就達到了時鐘的要求。
復位信號最開始為低電平,然后延時20個時鐘周期就拉高。觸摸按鍵信號最開始為低電平,延時10和時鐘周期后拉高,再延時30個時鐘周期再拉低,延時110個時鐘周期再拉高,再延時30個時鐘周期再拉低。
always的時間控制可以是沿觸發,也可以是電平觸發;可以是單個信號,也可以是多個信號,多個信號中間要用關鍵字or連接。always 語句后緊跟的過程塊是否運行,要看它的觸發條件是否滿足。
沿觸發的always塊常常描述時序邏輯行為。由關鍵詞or連接的多個事件名或信號名組成的列表稱為“敏感列表”。
電平觸發的always塊常常描述組合邏輯行為。
2、組合邏輯和時序邏輯電路根據邏輯功能的不同特點,可以將數字電路分成兩大類:
組合邏輯電路和時序邏輯電路。
組合邏輯電路中,任意時刻的輸出僅僅取決于該時刻的輸入,與電路原來的狀態無關。
時序邏輯電路中,任時刻的輸出不僅取決于當時的輸入信號,而且還取決于電路原來的狀態。或者說還與以前的輸入有關,因此時序邏輯必須具備記憶功能。
3、賦值語句Verilog HDL 語言中,信號有兩種賦值方式
1、阻塞賦值(blocking),如b=a
2、非阻塞賦值(Non_Blocking),如b《=a
3.1、阻塞賦值
阻塞賦值可以認為只有一個步驟的操作:即計算RHS(左側)并更新LHS(右側)。
所謂阻塞的概念是指,在同一個always塊中,后面的賦值語句是在前一句賦值語句結束后才開始賦值的。
module block_nonblock(Clk,Rst_n,a,b,c,out)
input Clk;
input Rst_n;
input a;
input b;
input c;
output reg [1:0] out;
// out a + b + c;最大值為3,所以應該定義為2位的位寬// d = a+b;// out = d+c;
reg [1:0]d;//定義一個中間變量
always @(posedge Clk or negedge Rst_n)
if (!Rst_n)
out = 2‘b0;
else begin
d = a+b;
out = d+c;
end
endmodule
現在我們改變一下d= a+b;out = d+c;的順序,就會發現綜合出來的電路是完全不同的。
module block_nonblock(Clk,Rst_n,a,b,c,out)
input Clk;
input Rst_n;
input a;
input b;
input c;
output reg [1:0] out;
reg [1:0]d;//定義一個中間變量
always @(posedge Clk or negedge Rst_n)
if (!Rst_n)
out = 2’b0;
else begin
out = d+c;
d = a+b;
end
endmodule
3.2、非阻塞賦值
非阻塞賦值的操作過程可以看作兩個步驟
(1)賦值開始的時候,計算RHS(左側);
(2)賦值結束的時候,更新LHS(右側)。
所謂非阻塞的概念是指,在計算非阻塞賦值的RHS以及更新LHS期間,允許其他的非阻塞賦值語句同時計算RHS和更新LHS。
非阻塞賦值只能用于對寄存器類型的變量進行賦值,因此只能用在initial塊和always塊等過程塊中。
還是用上面的例子
module block_nonblock(Clk,Rst_n,a,b,c,out)
input Clk;
input Rst_n;
input a;
input b;
input c;
output reg [1:0] out;
reg [1:0]d;//定義一個中間變量
always @(posedge Clk or negedge Rst_n)
if (!Rst_n)
out = 2‘b0;
else begin
d 《= a+b;
out 《= d+c;
end
endmodule
生成效果如下:
現在我們改變一下d= a+b;out = d+c;的順序,就會發現綜合出來的電路是完全相同的。這里由于采用的非阻塞賦值,因此交換語句的前后順序并不會對最終生成的邏輯電路有實際影響。
module block_nonblock(Clk,Rst_n,a,b,c,out)
input Clk;
input Rst_n;
input a;
input b;
input c;
output reg [1:0] out;
reg [1:0]d;//定義一個中間變量
always @(posedge Clk or negedge Rst_n)
if (!Rst_n)
out = 2’b0;
else begin
out 《= d+c;
d 《= a+b;
end
endmodule
1、在描述組合邏輯(電平觸發)的always 塊中用阻塞賦值=,綜合成組合邏輯的電路結構;這種電路結構只與輸入電平的變化有關系。
2、在描述時序邏輯(沿觸發)的always 塊中用非阻塞賦值=,綜合成時序邏輯的電路結構;這種電路結構往往與觸發沿有關系,只有在觸發沿時才可能發生賦值的變化。
“注意:在同一個always塊中不要既用非阻塞賦值又用阻塞賦值不充許在多個always塊中對同一個變量進行賦值!因為在多個always塊中代碼時并行執行的。”一般在設計中掌握以下六個原則,可解決在綜合后仿真中出現絕大多數的冒險競爭問題。
1)時序電路(沿觸發的always塊)建模時,用非阻塞賦值;
2)鎖存器電路建模時,用非阻塞賦值;
3)用always塊建立組合邏輯(電平觸發的always塊)模型時,用阻塞賦值;
4)在同一個always塊中建立時序和組合邏輯電路時,用非阻塞賦值:
5)在同一個always塊中不要既用非阻塞賦值又用阻塞賦值;
6)不要在一個以上的always塊中為同一個變量賦值。
4、條件語句條件語句必須在過程塊中使用。過程塊語句是指由initial語句和always語句引導的塊語句。
4.1 if_else語句
1、允許一定形式的簡寫,如:
if(a) 等同于if(a==1)
if(la)等同于if(a!=1)
2、if語句對表達式的值進行判斷,若為0,x,z,則按假處理;若為1,按真處理。
3、if和else后面的操作語句可以用begin和end包含多個語句。
4、允許if語句的嵌套。
4.1 case語句
case語句(多分支選擇語句)
1、分支表達式的值互不相同;
2、所有表達式的位寬必須相等;不能用’bx來代替n‘bx
3、casez比較時,不考慮表達式中的高阻值
4、casex不考慮高阻值z和不定值x
注意if_else需要配對,一個if語句就應該必須有一個else語句。好處是避免latch產生。latch是一個鎖存器,在數字電路中latch是一個電平觸發的存儲器,觸發器是一個邊沿觸發的存儲器。在編寫veilog語句中應避免產生無畏鎖存器,鎖存器只在組合邏輯電路中產成,而鎖存器會導致電路生成的毛刺比較多,還會影響我們對整個電路的時序分析。
什么樣的情況下會產生這個鎖存器呢?
首先在組合邏輯電路中,如果我們有if語句但是沒有相應的else語句,他就有可能產生鎖存器。第二點,比如case語句,如果我們的case語句沒有給完全,沒有列舉完所有應該的產生的case語句,就應該寫一個default,否則也會生成一個鎖存器。
編輯:jq
-
電路
+關注
關注
172文章
5959瀏覽量
172683 -
存儲器
+關注
關注
38文章
7525瀏覽量
164160 -
鎖存器
+關注
關注
8文章
907瀏覽量
41590 -
非阻塞賦值
+關注
關注
0文章
10瀏覽量
10017
原文標題:FPGA的Veilog HDL語法、框架總結
文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論