FPGA筆記之verilog語(yǔ)言(基礎語(yǔ)法篇) 寫(xiě)在前面: verilogHDL語(yǔ)言是面向硬件的語(yǔ)言,換句話(huà)說(shuō),就是用語(yǔ)言的形式來(lái)描述硬件線(xiàn)路。因此與C語(yǔ)言等軟件語(yǔ)言不同,如果想要在實(shí)際的電路中實(shí)現,那么在進(jìn)行verilog語(yǔ)言編寫(xiě)時(shí),就需要提前有個(gè)硬件電路的構思和想法,同時(shí),在編寫(xiě)verilog語(yǔ)言時(shí),應該采用可綜合的語(yǔ)句和結構。 1. verilog 的基礎結構 1.1 verilog設計的基本單元——module 在數字電路中,我們常常把一些復雜的電路或者具有特定功能的電路封裝起來(lái)作為一個(gè)模塊使用。以后在運用這種模塊化的封裝時(shí),我們只需要知道:1.模塊的輸入是什么;2.模塊的輸出是什么;3.什么樣的輸入對應什么樣的輸出。而中間輸入是經(jīng)過(guò)什么樣的電路轉化為輸出就不是我們在使用時(shí)需要特別重視的問(wèn)題。當很多個(gè)這樣的模塊相互組合,就能構成一個(gè)系統,解決一些復雜的問(wèn)題。verilog語(yǔ)言的基礎結構就是基于這種思想。verilog中最基本的模塊是module,就可以看做是一個(gè)封裝好的模塊,我們用verilog來(lái)寫(xiě)很多個(gè)基本模塊,然后再用verilog描述多個(gè)模塊之間的接線(xiàn)方式等,將多個(gè)模塊組合得到一個(gè)系統。 那么一個(gè)module應該具有哪些要素呢?首先對于一個(gè)module,我們應該設計好其各個(gè)I/O,以及每個(gè)I/O的性質(zhì),用于與模塊外部的信號相聯(lián)系,讓使用者知道如何連線(xiàn)。其次,作為開(kāi)發(fā)者,我們需要自己設計模塊內部的線(xiàn)路來(lái)實(shí)現所需要的功能。因此需要對模塊內部出現的變量進(jìn)行聲明,同時(shí)通過(guò)語(yǔ)句、代碼塊等實(shí)現模塊的功能。綜上所述,我們把一個(gè)module分成以下五個(gè)部分: 模塊名 端口定義 I/O說(shuō)明 內部信號的聲明 模塊功能實(shí)現 例: ///////////////////////////////////////////////////////////////// //module 模塊名 (端口1,端口2,端口3); //I/O說(shuō)明 //內部信號說(shuō)明 //模塊功能實(shí)現 //endmodule //////////////////////////////////////////////////////////////// //////////////////////////////////// //module 模塊名 (端口1,端口2,端口3); //////////////////////////////////// module FreDevider ( Clock, rst, Clkout ); //////////////////////////////////// //I/O說(shuō)明 //////////////////////////////////// input Clock; input rst; output Clkout; //////////////////////////////////// //內部信號說(shuō)明 //////////////////////////////////// reg Clkout; //////////////////////////////////// //模塊功能實(shí)現 //////////////////////////////////// always@(posedge Clock or posedge rst) begin if(rst) Clkout<=0; else Clkout<=~Clkout; end //////////////////////////////////// //endmodule //////////////////////////////////// endmodule 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 1.2 module的使用 當我們已經(jīng)寫(xiě)好了一種類(lèi)型的module,那我們在使用的時(shí)候,就可以直接調用module。使用方法是 模塊名+實(shí)例名+端口聲明+信號聲明。一個(gè)模塊可以定義多個(gè)實(shí)例。 例如:使用上述已經(jīng)寫(xiě)好的module //已經(jīng)定義過(guò)一個(gè)叫FreDevider的module FreDevider uut1( //模塊名:FreDevider 實(shí)例名:uut1 .Clock(clock_signal), // 端口聲明: .端口名 信號聲明: (信號名) .rst(rst_signal), .Clkout(clkout_signal) ); 1 2 3 4 5 6 這里涉及到了之前module定義和變量聲明等問(wèn)題,首先module的定義決定了模塊名和端口名,其次module中的信號的處理方式?jīng)Q定了這里的信號名是reg還是wire型。信號可以看做是一個(gè)大的模塊的輸入輸出或中間變量。 1.3 I/O的說(shuō)明 I/O的類(lèi)型共有3類(lèi):input,output,inout 前兩個(gè)比較好理解,分別是輸入和輸出 而inout則是雙向端口,既可以當做輸入,也可以當做輸出,這種雙向端口具有雙向傳輸的能力,比較節約端口,同時(shí)適合作為總線(xiàn)等需要但是要保證在某一時(shí)刻,其只進(jìn)行某一方向的傳輸,也就是說(shuō),應避免兩個(gè)方向由需要傳輸這種情況。因此在實(shí)際工程實(shí)例中,我們在比較底層的模塊編寫(xiě)時(shí),是比較少的用inout端口,而是在較高一層的模塊,通過(guò)控制信號來(lái)實(shí)現inout端口的使用。同時(shí)inout端口在實(shí)際使用時(shí)還有許多問(wèn)題,對于初學(xué)者,先理解整個(gè)架構,再對細節補充,所以這部分內容放在高級語(yǔ)法篇講述。 I/O說(shuō)明的形式: I/O的說(shuō)明可以放在端口定義后,也可以在端口定義時(shí)同時(shí)說(shuō)明。例: module FreDevider ( input Clock, input rst, output Clkout ); 1 2 3 4 5 同時(shí),也可以在說(shuō)明I/O口的時(shí)候,同時(shí)說(shuō)明信號的位數 例: module FreDevider ( input Clock, input rst, input [15:0] x; //16位輸入 output [31:0] y; //32位輸出 ); 1 2 3 4 5 6 7 1.3 內部信號的聲明 如果說(shuō)端口的定義連接了模塊與外部世界,是一個(gè)模塊世界中可進(jìn)可出的若干個(gè)大門(mén),那么信號就是一個(gè)個(gè)行人。要實(shí)現模塊的功能,就要對各個(gè)信號進(jìn)行處理和變換。有些信號是外部輸入的,有些信號是要輸出給外部的,還有些信號是信號變換的中間變量。就像行人有些是外面進(jìn)來(lái)的,有些是要去往外面世界的,有些是住在模塊世界里的。這些參與到模塊功能實(shí)現的所有信號,都要對其信號的屬性進(jìn)行規定,就像每個(gè)行人都有各自的身份?偠灾,分清楚端口和信號的區別,端口是固定的,信號是變化的。端口是門(mén),信號是人。 信號的屬性有reg、wire、paramater三種種,其中reg又稱(chēng)為寄存器型,wire又稱(chēng)為線(xiàn)性,每個(gè)信號都要定義其屬性,但是對于模塊的輸入信號,其屬性必須不是reg型,一般為wire型。又因為對于沒(méi)有聲明的信號,其默認為wire型,因此在定義時(shí),我們只需要定義輸出信號的類(lèi)型和中間變量的類(lèi)型即可。 reg a; wire b; 1 2 1.4 模塊功能的實(shí)現 對于一個(gè)硬件電路來(lái)說(shuō),已經(jīng)有了模塊名,端口和端口名,信號與信號屬性,剩下的就是通過(guò)硬件電路來(lái)實(shí)現各個(gè)信號之間的邏輯功能。這比部分的知識就和我們在大學(xué)時(shí)期學(xué)的數電的知識聯(lián)系緊密,通?梢苑譃闆](méi)有時(shí)間,只有邏輯轉換的邏輯電路和有時(shí)間和狀態(tài)轉換的時(shí)序邏輯電路兩種形式,再加上通過(guò)調用已經(jīng)模塊化的實(shí)例元件來(lái)參與更高一級的模塊設計。所以在一個(gè)模塊功能的實(shí)現方法中,通常有三種類(lèi)型: 用assign聲明語(yǔ)句 assign語(yǔ)句用于驅動(dòng)線(xiàn)網(wǎng)型的變量,聲明語(yǔ)句右邊表達式的變量是敏感信號,當右邊的值發(fā)生改變時(shí),立即計算左邊的結果,并進(jìn)行表達。也就是說(shuō),當輸入變化時(shí),輸出也隨之變化。這種特性就像是數字電路里的組合邏輯電路。 assign a_not=~a; assign c=a&b; 1 2 采用實(shí)例化的元件 采用實(shí)例的元件方法已經(jīng)在前面講過(guò),除了采用module的實(shí)例化元件以外,還可以采用IP核的形式來(lái)實(shí)現。 采用always語(yǔ)句塊 always語(yǔ)句塊既能描述組合邏輯電路,又能描述時(shí)序邏輯電路。與assign不同的是,always語(yǔ)句后面的觸發(fā)條件是持續敏感,也就是每時(shí)每刻都在執行或者判斷的。后面也會(huì )更加詳細的區別assign和always語(yǔ)句塊。 //生成時(shí)鐘信號 always #5 clk=~clk; //組合邏輯電路:二選一多路器 reg c_out; always @(a_in or b_in or sel) if(sel) c_out=a_in; else c_out=b_in; //時(shí)序邏輯電路:二分頻模塊 reg d; always @(posedge clk or posedge rst) begin if(rst) d<=1'b0; else d=~d; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1.5 第一章學(xué)習筆記 verilog是一門(mén)描述硬件電路的語(yǔ)言,在語(yǔ)言結構上和C語(yǔ)言有相同的地方,所以學(xué)習起來(lái)比較容易,但是verilog終究還是要最終用硬件的方法去實(shí)現,因此在編程時(shí),不能空想內部的邏輯,而是通過(guò)電路圖、時(shí)序圖等方式,將功能先用硬件的方法表示出來(lái),然后再用軟件的語(yǔ)言來(lái)實(shí)現。 verilog的使用離不開(kāi)對數字電路基礎知識的掌握,所以在進(jìn)行verilog的學(xué)習的同時(shí),應該建立在對數字電路基礎知識的學(xué)習之上。語(yǔ)言是描述思想的工具,思想才是解決問(wèn)題的關(guān)鍵。 … 2. verilog 的數據類(lèi)型 verilog的數據類(lèi)型主要分為四類(lèi),整數型、實(shí)數型、字符串型、參數型,這四種類(lèi)型都可以在編程中出現,但是在實(shí)際硬件層面綜合時(shí),卻并不能保證我們所寫(xiě)的數據類(lèi)型能夠被表達。因為在實(shí)際的電路中,無(wú)論是哪種類(lèi)型的數據,最后都要轉化為1、0、X、Z(X、Z分別是未知態(tài)和高阻態(tài))這四種信號狀態(tài)。因此掌握verilog的數據類(lèi)型,最根本的就是掌握各種數據類(lèi)型在實(shí)際電路中的表達。 2.1 整數型 2.1.1 數制 在verilog中,整數有四種數制,分別是 十進(jìn)制:代號d/D 二進(jìn)制:代號b/B 八進(jìn)制:代號o/O 十六進(jìn)制:代號h/H 一個(gè)整數對于進(jìn)制的不同表達,只是數的表達形式不同,但是數字本身代表的數學(xué)含義是相同的。而在硬件電路中,所有的數字都是通過(guò)二進(jìn)制來(lái)進(jìn)行表達的。因此在編程過(guò)程中,對于數字的數學(xué)表示,我們可以采用不同的進(jìn)制,但是對于數字的物理實(shí)現,我們一定是采用二進(jìn)制的方式來(lái)進(jìn)行硬件上的實(shí)現。因此在實(shí)際編程過(guò)程中出現的數字,我們應該從二進(jìn)制的表達中理解其變化的規律。 這種思想可以在verilog的數字表達中得到體現,一般來(lái)說(shuō),我們通過(guò)以下的方式來(lái)表示一個(gè)整數: <位寬><′><進(jìn)制><數字> <位寬><'><進(jìn)制><數字> <位寬>< ′ ><進(jìn)制><數字> 舉例說(shuō)明: 8'd23 //位寬8 十進(jìn)制 數字23 8'b00010111 //位寬8 二進(jìn)制 數字23 8'o27 //位寬8 八進(jìn)制 數字23 8'h17 //位寬8 十六進(jìn)制 數字23 1 2 3 4 對于任何一個(gè)數字,無(wú)論其后面是什么進(jìn)制,什么數字,最后都要轉化為指定位寬的二進(jìn)制數字來(lái)表達。因此有可能在進(jìn)制表達時(shí),出現實(shí)際的二進(jìn)制數字和我們編程所寫(xiě)的二進(jìn)制數字不同;蛘咂渌菢藴实谋磉_方法。下面我們說(shuō)明幾種常見(jiàn)的情況: 位寬大于實(shí)際數字二進(jìn)制的寬度 位寬大于實(shí)際二進(jìn)制的寬度時(shí),有四種可能: 1.二進(jìn)制數字最高位為1,高位部分用0補全 2.二進(jìn)制數字最高位為0,高位部分用0補全 3.二進(jìn)制數字最高位為X,高位部分用X補全 4.二進(jìn)制數字最高位為Z,高位部分用Z補全 例如: 6'b101 //實(shí)際上是 000101 6'b001 //實(shí)際上是 000001 6'bx11 //實(shí)際上是 xxxx11 6'bz11 //實(shí)際上是 zzzz11 1 2 3 4 位寬小于實(shí)際二進(jìn)制的寬度 當位寬小于實(shí)際二進(jìn)制寬度時(shí),一般情況下是將超過(guò)的位數舍去,只保留給定位寬內的數,例如: 4'b101011 //實(shí)際上是 1011 1 位寬省略不寫(xiě) 一般情況下,位寬省略不寫(xiě),默認為是32位寬的數,或者根據操作系統和編程軟件的設定來(lái)定義。例如: 'b10111 //位寬為32的二進(jìn)制數 代表數字23 1 數制位寬都省略不寫(xiě) 這種情況下,默認為數字是32位寬的十進(jìn)制數。例如 23 //位寬為32的十進(jìn)制數 代表數字23 1 下劃線(xiàn)表達法 當需要表達的數字位數較多時(shí),為了書(shū)寫(xiě)和觀(guān)察方便,常常使用下劃線(xiàn)符號來(lái)輔助表達。采用下劃線(xiàn)將相鄰若干個(gè)數字分隔開(kāi),使其表達更清晰。但是注意下劃線(xiàn)只能出現在數字中,不能出現在位寬和進(jìn)制中,并且數字的第一位不能是下劃線(xiàn)。例如: 16'b0100_1011_1101_0110 //正確 16'b_1110_1101_0101_0001 //錯誤 1 2 2.1.2 負數 負數在verilog中的表達是很簡(jiǎn)單的,直接在數字的前面添加符號就表示負數。但是在實(shí)際的二進(jìn)制編碼中,負數是通過(guò)其正數的補碼的形式進(jìn)行實(shí)現的。 -8'd23 //就是-23,但是在二進(jìn)制代碼中 是用8'b11101001來(lái)表示 1 此時(shí)對于一個(gè)8位的二進(jìn)制數,其第一位為符號位,后7位才為有效的數字位。 由此可見(jiàn),一個(gè)二進(jìn)制,當首位為1時(shí),有可能表示的是一個(gè)正數,也有可能表示的是一個(gè)負數,那么在實(shí)際硬件電路中,如何區分這兩種情況呢? 一般來(lái)說(shuō),對于硬件中的信號,硬件處理時(shí)是默認為正整數的,因此當我們輸入一個(gè)負值,實(shí)際上產(chǎn)生的是我們輸入的值的正數部分的補碼所表示的正值(這里有點(diǎn)繞,距離來(lái)說(shuō),我輸入-8’d23,得到的是23的補碼8’b11101001,這個(gè)補碼被硬件默認是正數,即8’d233)。那么我們在進(jìn)行二進(jìn)制的數據的變換時(shí),就應該回歸其硬件中的表達,然后在此基礎上通過(guò)自己的程序來(lái)實(shí)現當為負數時(shí)的轉化。 例如:我之前寫(xiě)過(guò)的粗插補算法 if(x_rough[15]==0) //當插補數據為正數時(shí),將粗插補數據進(jìn)行四分,并將余數添加到第一個(gè)精插補結果中 begin lx[1:0]=x_rough[1:0]; x4=(x_rough>>2); x3=(x_rough>>2); x2=(x_rough>>2); x1=((x_rough>>2)+lx); end else //當插補數據為負數時(shí),將插補數據求補碼,進(jìn)行四分等操作后,再將結果求補碼得到負數(對絕對值進(jìn)行操作) begin x_complement[15:0]=(~(x_rough[15:0]-8'b0000000000000001)); lx[1:0]=x_complement[1:0]; x4=((~(x_complement>>2))+8'b0000000000000001); x3=((~(x_complement>>2))+8'b0000000000000001); x2=((~(x_complement>>2))+8'b0000000000000001); x1=((~((x_complement>>2)+lx))+8'b0000000000000001); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 這里就是根據最高位是1和0來(lái)決定進(jìn)行什么樣的數據處理?偠灾,我們要明白,數據在硬件中的儲存是默認為正整數的二進(jìn)制儲存方式,但是如何取理解這個(gè)數,卻是使用這個(gè)數據的人自己規定的。這種思想不僅在處理負數時(shí)是這樣的,在碰到小數、字符等都是這么一個(gè)思想。0和1的世界是死的,但是解讀他們的規則卻是活的。 2.1.3 X和Z 我們都知道0和1代表的含義,但是對于verilog語(yǔ)言來(lái)說(shuō),還有X和Z兩種獨立于0和1之外的狀態(tài)。這兩種狀態(tài)分別是未知態(tài)和高阻態(tài)。 未知態(tài)X是指,無(wú)法確定此時(shí)信號的狀態(tài)是1還是0,但是能確定信號是有狀態(tài)的,不是1就是0,且這個(gè)狀態(tài)是能夠影響到與其相連的后續電路的,當我們用電表測量時(shí),其值可能是1,可能是0,取決于被測當時(shí)硬件電路的當前狀態(tài)。而高阻態(tài)Z是指,當前的信號狀態(tài)既不是1,也不是0,而是沒(méi)有狀態(tài),或者可以認為是斷開(kāi),即此時(shí)信號的狀態(tài)已經(jīng)無(wú)法再影響到后續的電路。 而在實(shí)際電路中,某一時(shí)刻時(shí)只有1、0、高阻態(tài)三種狀態(tài)。 盡管X和Z表示的狀態(tài)不是傳統的1和0,但是X和Z也能參與到二進(jìn)制的邏輯運算中來(lái)。在邏輯運算中,X和Z滿(mǎn)足如下的規律: 0 && X = 0 ; 1 && X = X ; 0 || X = X ; 1 || X = 1 ; 0 && Z = 0 ; 1 && Z = X ; 0 || Z = X ; 1 || Z = 1 ; 1 2 3 4 5 6 7 8 9 2.2 實(shí)數型和字符型 實(shí)數型和字符型放在一起,因為這兩種數據的表達有相似的地方。那就是實(shí)數型或者字符型的數據可以在verilog語(yǔ)言中出現,但是卻不能通過(guò)硬件表達出來(lái)。而是常常出現在命令、顯示等非硬件參與的操作中。這與上面的整數的表達方式和意義是不同的。 2.2.1 實(shí)數 實(shí)數可以用十進(jìn)制來(lái)表示,也可以用科學(xué)計數法來(lái)表示,例如: 12 //表示12 23e-3 //表示0.0023 23E4 //表示230000 1 2 3 雖然我們可以在verilog中寫(xiě)出這些數據,但是并不是所有我們寫(xiě)出來(lái)的數據都能在硬件中表達。在實(shí)際的仿真過(guò)程中,所有的科學(xué)計數的表達例如23e-3或者23E4,在硬件中都是0,而硬件中也不能直接表達小數。也就是直接在verilog中寫(xiě)出來(lái)的小數,會(huì )被硬件識別為0。 但是小數或者指數的表達,可以通過(guò)使用者自己規定來(lái)獲得。例如我們常聽(tīng)說(shuō)的浮點(diǎn)數等,就是通過(guò)一些算法轉換,來(lái)將二進(jìn)制的代碼表達成帶有小數或者指數的數字。 3. verilog 的變量類(lèi)型 4. verilog 的運算符 5. verilog 的語(yǔ)句塊 點(diǎn)贊 10 ———————————————— 版權聲明:本文為CSDN博主「Dobolong」的原創(chuàng )文章,遵循 CC 4.0 BY-SA 版權協(xié)議,轉載請附上原文出處鏈接及本聲明。 原文鏈接:https://blog.csdn.net/DBLLLLLLLL/article/details/84306098 |