# Verilog 學習紀錄 ## 2022/02/15 BEGIN 嘗試在 [HDLbits](https://hdlbits.01xz.net/) 刷Verilog的題目,剛學,是個菜雞 以下僅為個人見解 ### 數值表示法 布林代數(Boolean) : 0(False),1(True) 整數宣告語法 : **位元數'進制與資料** 如: 4'b0001,代表4位元的2進制資料為0001 常用進制: | 進制別 | 2進制 | 8進制 | 16進制 | |:------:|:-----:|:-----:|:------:| | 代號 | **B** | **O** | **H** | ### **向量(陣列或多條線)宣告語法 :** **型態** [index上限:index下限] **變數名稱** 型態可以是 **reg** 、 **wire**,index上下限可以是負的,順序由index上限為(MSB)至下限(LSB) ```verilog wire [7:0] W //宣告出來是W[7] 到 W[0],其中W7是MSB 最高有效位元 wire [-1:-5] W2 //宣告出來是W2[-1] 到 W2[-5],W2[-1]是最高有效位元 ``` 你也可以宣告很多包線(wire) Be like: ```verilog wire [7:0]W [3:0] ; //表示w[0],W[1],W[2],W[3]都有八條線 //總共宣告了8*4=32條 wire ``` 向量也能夠分段輸出或處理 : ```verilog=1 module vector( input wire [3:0]A, input wire [3:0]B, output wire [1:0]out_up, output wire [1:0]out_lo, ); assign out_up= A[3:2]|B[3:2]; //將輸入的A[3] or B[3] 放在out_up[1] , A[2] or B[2]放在out_up[0] assign out_lo = A[1:0]&B[1:0]; //將輸入的A[1] and B[1] 放在out_lo[1] , A[0] and B[0]放在out_up[0] endmodule ``` ### 遞迴算子用法 可以用來處理**很多輸入**的邏輯閘,舉個例子,我們需要把100個inputs AND在一起 EX: ```verilog=1 module And100(intput wire [99:0] in, output wire out ); assign out =&in;//這樣寫就可以了 做出來就是in[0]~in[99]全都AND在一起輸出至out //如果需要or就使用 assign out =|in; 以此類推 endmodule ``` 當然也可以只取一部分的資料出來做運算 ```verilog assign out =&in[15:30]; ``` 這只取出in的15~30出來AND,可搭配下面的[向量合成](###向量合成)服用,效果更加 ### 邏輯子與運算子 Verilog中有兩種邏輯運算子可以用,其中一種是與**C**邏輯判斷相同的符號: | AND | OR | XOR | |:------ |:------:|:-----:| | **&&** | **ll** | **^** | 另一種是Verilog原生的BitWise邏輯運算: | AND | OR | XOR | |:----- |:-----:|:-----:| | **&** | **l** | **^** | 兩種符號在使用上有些許不同,如果只有一個bit在做運算的話,兩者功能是相同的。 但假若是一個陣列與另一個陣列運算時,使用**C**的邏輯子會遵循**C**語言中的邏輯運算 EX: ```verilog=1 module logic_test( input wire [2:0] A, input wire [2:0] B, output wire [3:0]bitwise, output wire logical, ); assign logical =A&&B; //則若A或B值非全0(000)則logical輸出為1 //就像在C中的while(1)一樣 assign bitwise = A|B; //10 一行就帶表了以下程式碼: //assigne bitwise[2]=A[2]|B[2]; //assigne bitwise[1]=A[1]|B[1]; //assigne bitwise[0]=A[0]|B[0]; //請注意此處bitwise的index,bitwise 共有4位元,而A|B只會有3位元 //資料會由較低位元開始對齊,而最高的bitwise[3]則會為GND或空接 //視編譯器而定 endmodule ``` ### 條件運算子 用來判斷條件選擇的,看起來很醜,會被下面的if else或case取代掉 語法: **條件** ? **如果成立** : **不成立**; ```verilog assign out = a>=b?a:b; //如果a>=b成立則out=a;否則out=b ``` ### 向量合成 可使用合成符號 **{}** 來達成將多個小向量合成一個大的做處理,使用 **,** 區隔兩個向量 語法: ```verilog=1 module vector_combine( input wire [3:0] A, input wire [3:0] B, output wire [15:0] out, ); assign out = {A,B,4'b1010}; //out值會等於 {0,0,0,0,A[3],A[2],A[1],A[0],B[3],B[2],B[1],B[0],1,0,1,0} //共16bit endmodule ``` 也可以重複合成向量或數值 語法 : **{數量{欲重複的向量或數值}}** ```verilog=1 module vector_combine( input wire [3:0] A, output wire [15:0] out, ); assign out = {2{A}}; //out值會等於 {0,0,0,0,0,0,0,0,A[3],A[2],A[1],A[0],A[3],A[2],A[1],A[0]} //共16bit endmodule ``` 也可以 ```verilog assign out={{2{A,B}},4'b1010}; ``` 會得到out= ABAB1010 的結果(A,B是向量) ### Begin-end block 拿來包住程式區塊的,功能類似C中的大括弧。 在if或case或for中的單行可以直接執行,若要表示複數行則要使用begin-end包住程式碼 ### Always block Verilog有兩種Always用法 1. Always@(*) 2. Always@(pos(neg)edge 名稱) 前者用於組合邏輯時大致上與assign相同,但卻可以使用更多的語法,如條件判斷if-else、 Case-endcase,可使程式編輯更為簡潔 (其實也有Assign在用的[條件運算子](###條件運算子),但可讀性堪憂...) Always其含意為:當事件發生=>做事,沒事發生=>不動 為了達成 **沒事發生=>不動** 這個邏輯,帶表上個狀態需要被keep住,所以在Always的輸出只能是reg,且在做組合邏輯時若有使用if,請務必加上else,使用case時加上default, 不然容易產生意想不到的BUG(或生出栓鎖) 如果有寫上pos或negedge的Always,會在輸出上帶一個暫存器,時序上就會看到略為延遲 EX: ```verilog=1 module xor_module( input wire clk, input wire a, input wire b, output wire out_assign, output reg out_always_comb, output reg out_always_ff ); assign out_assign =a^b; //一般的assign輸出 always@(*)begin out_always_comb=a^b; //沒有暫存器輸出 end always@(posedge clk)begin out_always_ff=a^b; //帶有暫存器輸出的 end endmodule ``` 時序圖 : ![](https://i.imgur.com/xz7VenD.jpg) 可以看到out_always_ff輸出的整體偏移了一個clk週期,但輸出結果是對的。 ### For Loop 可使用begin-end包住,且其中不能使用 assign for中使用的變數(此處是i) 遞增(減)不能寫i++,只可使用 **i=i+1** 或 **i+=1** ==Quartus 更狠 只能使用i=i+1== system Verilog: ```verilog=1 always @(*) begin for (int i=0;i<8; i+=1)//for不能加上大括號 out[i]=in[7-i]; end ``` Verilog: ```verilog= integer i; always @(*) begin for (i=0;i<8; i=i+1) out[i]=in[7-i]; end ``` ### If-ElseIf-Else ==本句法必須被Always包住才能使用== ```verilog= if(x)begin //狀態一做事 end else if(y)begin //狀態二做事 end else begin //條件x,y都沒滿足時做的事 end ``` x與y是條件判斷,可以是布林式:**x==2'b01**或是一個變數**y**當條件達成時(y=1)就會觸發相對應的陳述 ### Case-EndCase ==本句法必須被Always包住才能使用== 用來取代很多種條件判斷的時候可以使用的語法且較為嚴格,需要條件變數 **完全等於** 時才會觸發 ```verilog= case(x) //x是條件變數,當它等於什麼:做什麼事; //變數x可以填入單純布林或是整個向量 1'b0:a=1; 1'b1:begin a=0; b=1; end default: //條件全不滿足時會觸發default下的敘述 endcase ``` 在使用Case-Endcase時**default**最好都要加上,避免出現奇怪的BUG 更懶的用法,把整個真值表照抄進case陳述裡,即可達成任意組合邏輯的描述 反正合成器會幫你化減,何必自己動手? **¯\\_(ツ)_/¯** 舉個例子: 真值表: ![](https://i.imgur.com/Ve6dPLo.jpg) ```verilog=1 always@(*)begin case({x3,x2,x1}) //沒錯!使用合成 3'd0:f=0; 3'd1:f=0; 3'd2:f=1; 3'd3:f=1; 3'd4:f=0; 3'd5:f=1; 3'd6:f=0; 3'd7:f=1; //夠簡單吧...? endcase end ``` ### Casez-EndCase ==本句法必須被Always包住才能使用== 有時候我們不需要**那麼嚴格**符合某些條件才觸發,只需要幾個滿足即可 就可使用CaseZ-endcase 舉個例子: 我們希望輸出一個向量最大權重,如x=4'b1100則out=**8**或x=4'b0010out=**2**以此類推 ```verilog=1 always@(*)begin casez(x) 4'b0001:out=1; 4'b001z:out=2; 4'b01zz:out=4; 4'b1zzz:out=8; default:out=0; endcase end ``` **請注意使用casez時,權重需要由低到高** Z格的意義有點像don't care 就不會納入條件判斷中 附註:另外有種寫法是Casex最好少用。 ### Generate For loop 用**generate-endgenerate**+For迴圈產生**重複實體**或**接線**(好讓你少打很多程式碼) ==請自己注意語法,細節頗多== 實例說明 : 模組"Fa"為一個1bit全加器 ```verilog=1 module Fa( input wire a, b, cin, output wire cout, sum ); assign sum=a^b^cin; assign cout=(a&b)|(b&cin)|(a&cin); endmodule ``` 利用4個Fa模組合成一個4bit連波進位全加器A1 ```verilog=1 module A1( input wire [3:0] a, b,//欲相加資料 input wire cin, //進位輸入 output wire [3:0] sum,//和(SUM)輸出 output wire cout //連波進位(CARRY)輸出 ); wire [3:0]temp;//在generate中不能實體wire,需要用到的線需要事先宣告 Fa Fadd(.a(a[0]),.b(b[0]),.cin(cin),.cout(temp[0]),.sum(sum[0])); //0號的加法器因接線沒有規則,故另外連接 /*------------------產生實體---------------*/ generate genvar i;//for 在跑的index,在generate中需要使用genvar關鍵字宣告 //這一個變數,每個使用的generate都要獨立宣告,不要混用 //容易產生錯誤 for( i=1;i<4;i=i+1)begin:TEST //"TEST"該字串不可省略(可以改其他喜歡的字串),為產生的實體冠名 Fa Fadder(.a(a[i]),.b(b[i]),.cin(temp[i-1]),.cout(temp[i]),.sum(sum[i])); //正常的引入模組動作 end endgenerate /*------------------產生實體---------------*/ assign cout=temp[3]; endmodule ``` RTL Viewer合成的東西: ![](https://i.imgur.com/MsKwrHK.png) 紅底線為generate產生的實體 ### Flip-Flop中的同步與非同步Reset 一般來說只會使用同步的重製(reset)或預設(preset) 以下以D型FF做範例 同步: ```verilog=1 module D_ff_reset( input wire d, output reg q, input wire reste ); always@(posedge clk)begin if(reset) q<=0;//暫存需使用bluck else q<=d; end ``` ### ModuleSim 相關 指令: Vsim +maxdelays [project_name].[testbenchname] add wave * run -all ###### tags: `Verilog` ###### 若有錯誤懇請:mailbox:Email至 : Wowhuglisme.mi@gmail.com 不勝感激