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