# 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[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
```
時序圖 :

可以看到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陳述裡,即可達成任意組合邏輯的描述
反正合成器會幫你化減,何必自己動手? **¯\\_(ツ)_/¯**
舉個例子:
真值表:

```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合成的東西:

紅底線為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 不勝感激