---
# System prepended metadata

title: Verilog 學習紀錄
tags: [Verilog]

---

# 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 不勝感激