---
# System prepended metadata

title: 數位電路實驗暨 verilog 學習（一）Introduction
tags: [verilog, 數位邏輯電路練習]

---

# 數位電路實驗暨 verilog 學習（一）Introduction
本文為專題實作與自我練習紀錄筆記,望能幫助verilog入門
內容皆參考自以下網站
[南京大學數位電路實驗](https://nju-projectn.github.io/dlco-lecture-note/exp/01.html)
[YSYX 一生一芯網站](https://ysyx.oscc.cc/docs/2205/)

# 操作教學
前置作業參考YSYX網站
1. 創建一個.v
    EX : Switch.v (名稱不用跟module一樣）
    如果希望內部將各部份電路分隔為數個module進行實作,有以下方法 :
    1.個別創建 2. 直接在同一個檔案宣告
```     
        1.  創建檔案
            vim (filename).v

        2.  將各個module分別撰寫在個別的.v檔中 
            module XorGate(
            input wire a,
            input wire b,
            output wire y
            );

            assign y = a ^ b;

            endmodule

        3.  在最後的.v檔中的呼叫module 
            module TopModule(
            input wire a,
                input wire b,
                output wire xor_out,
                output wire and_out
            );

            // XorGate
            XorGate u1 (
                .a(a),
                .b(b),
                .y(xor_out)
            );

            // AndGate
            AndGate u2 (
                .a(a),
                .b(b),
                .y(and_out)
            );

            endmodule
        4.  或同一檔案...就把他們放在一起
    ``
        
    ```
```
2. 撰寫TestBench,使用.cpp檔以C++撰寫
* 1. vim
```
vim (Test bench file name) + .cpp 
```
```
#include "VTopModule.h"
#include "verilated.h"
#include "verilated_vcd_c.h"

int main(int argc, char **argv, char **env) {
    Verilated::commandArgs(argc, argv);
    VTopModule* top = new VTopModule;

    // 初始化變量
    Verilated::traceEverOn(true);
    VerilatedVcdC* tfp = new VerilatedVcdC;
    top->trace(tfp, 99);
    tfp->open("waveform.vcd");

    // input signal
    top->a = 0;
    top->b = 0;

    // 30個cycle
    for (int i = 0; i < 40; i++) {
        if (i == 10) top->a = 0, top->b = 1;
        if (i == 20) top->a = 1, top->b = 0;
        if (i == 30) top->a = 1, top->b = 1;

        top->eval();
        tfp->dump(i);
    }

    tfp->close();
    delete top;
    return 0;
}

```

3. 再來生成模擬檔案
* 指令 （1）：
```
verilator --cc --trace （希望模擬的module） --exe (Test bench 的.cpp檔）
```

* 指令 （2）：
```
 make -j -C obj_dir/ -f V +（.v檔的名字）+ .mk V +（.v檔的名字）
```
4. 運行檔案
* 指令 （3）：
```
./obj_dir/V +（.v檔的名字）
```
5. 查看波形
```
gtkwave waveform.vcd
```


# 南京大學數位電路實驗一：選擇器
* 2 to 1 mux
```
module m_mux21(a,b,s,y);
	input a;
	input b;
    // chosing signal
	input s;
    // final output
	output y;
    // assign 為賦予用function 像是把個logic gate 的結果接到某條output或logic gate
	assign y = (~s&a)|(s&b);
    // ~ : not , & : and gate , | : or gate
endmodule
    //module最後必打
```
> 由於verilator 內有辦法直接使用if 等function ,建議能用卡諾圖,簡化logic gate 的設計架構

```
*****************sim.cpp*****************
#include "verilated.h"
#include "verilated_vcd_c.h"
#include "obj_dir/Vmux21.h"

VerilatedContext* contextp = NULL;
VerilatedVcdC* tfp = NULL;

static Vmux21* top;

void step_and_dump_wave(){
  top->eval();
  contextp->timeInc(1);
  tfp->dump(contextp->time());
}
void sim_init(){
  contextp = new VerilatedContext;
  tfp = new VerilatedVcdC;
  top = new Vmux21;
  contextp->traceEverOn(true);
  top->trace(tfp, 0);
  tfp->open("dump.vcd");
}

void sim_exit(){
  step_and_dump_wave();
  tfp->close();
}
int main() {
  sim_init();

  top->s=0; top->a=0; top->b=0;  step_and_dump_wave();   // 将s，a和b均初始化为“0”
                      top->b=1;  step_and_dump_wave();   // 将b改为“1”，s和a的值不变，继续保持“0”，
            top->a=1; top->b=0;  step_and_dump_wave();   // 将a，b分别改为“1”和“0”，s的值不变，
                      top->b=1;  step_and_dump_wave();   // 将b改为“1”，s和a的值不变，维持10个时间单位
  top->s=1; top->a=0; top->b=0;  step_and_dump_wave();   // 将s，a，b分别变为“1,0,0”，维持10个时间单位
                      top->b=1;  step_and_dump_wave();
            top->a=1; top->b=0;  step_and_dump_wave();
                      top->b=1;  step_and_dump_wave();

  sim_exit();
}
```
> 每個logic circuit 中都需要一個模擬的C/C++程式碼進行模擬,以上為實驗網站提供,sim.cpp大多需要自己寫！！！所以要注意該格式為何（後續會專門提到）
> 但需要注意的是,如果只用if等lib中的function其實就跟在寫一般的C差不多,所以需要盡量使用基本的and,or等logic gate 進行實做
# 結構化module
> 此方式是用類似logic circuit的方式進行電路實現,因此需要用到大量的logic circuit design的基礎
```
// And gate module
module my_and(a,b,c);
  input  a,b;
  output c;

  assign c = a & b;
endmodule

// OR gate module
module my_or(a,b,c);
  input  a,b;
  output c;

  assign c = a | b;
endmodule

// Not gate module
module my_not(a,b);
  input  a;
  output b;

  assign b = ~a;
endmodule

//最終整合到頂部module中
module mux21b(a,b,s,y);
  input  a,b,s;
  output y;
    
  wire l, r, s_n;  // 宣告內部電路電線,注意語法！ 
  
  my_not i1(.a(s), .b(s_n)); 
  my_and i2(.a(s_n), .b(a), .c(l)); 
  my_and i3(.a(s),   .b(b), .c(r)); 
  my_or  i4(.a(l),   .b(r), .c(y)); 
endmodule
```
> 這裡要注意的是,跟C的語法不同,module的輸入參數設置模式與C中直接 my_not (s,s_n)是不同的,verilator中需要在module被call的過程中詳細寫出頂層module中哪條電路對應到該子module中的哪條input/output
> 範例如下 ： 
```
module my_not(a,b); //其中有兩參數a,b分別代表input/output
```
> 使用時,欲將頂層module的電路連入此module中,則寫作以下

```
module my_not(.a(頂層module的電線),.b（頂層module的電線）);
```
> 我的理解是,input/output為宣告一個port,但接入的signal線路則需要另行宣告,並在call的過程中以
```
.(input port name)(circuit name)
```
> 的方式寫出
# 行為module
> 據我的理解是像寫C一樣,使用if等function直接描述電路應該要做到的功能,此方式就不涉及logic gate等方面的知識背景
```
module mux21c(a,b,s,y);
  input   a,b,s;
  output reg  y;   
  // y在always块中被赋值，一定要声明为reg型的变量

  always @ (*)
    if(s==0)
      y = a;
    else
      y = b;
endmodule
```
> 這裡要注意的是,always/if-else的運用
```
alway @ (event) begin
(statement)
end
```
> event為觸發事件,簡而言之,當event發生變化時時,begin下述行為直到end語句
以下為範例
```
always＠ (a,b,s) begin
out = a*b*s;
end
```
這種情況下 a、b、s 三條訊號線只要有其中一條變化,則執行begin內部指令
但這有問題的點在於,內部的線程可能多到沒辦法列出,因此大多會改為以下方式
```
always＠ (*) begin
out = a*b*s;
end
```
> 此範例即為利用' * '代替所有線程
> 接下來將介紹if-else語法
```
//傳統C語法
if(s==0)
  y = a; x = b;
else
  y = b; x = a;
  
//verilog語法
if(s==0)
  begin 
  y = a; 
  x = b;
  end 
else
  begin
  y = b; 
  x = a;
  end
```
> 組合各線路進行運算 , 使用assign 
```
assign y = a*b;
```
> 需要注意的是，變數宣告的形式有wire/reg兩種,差別在於在assign 使用 wire, 在always內的變量則只能宣告為reg
> 
> 另一點要注意的是,對於一個module來  說,input/output/inout都被默認為wire型態,但如果該變量（如上一個範例中的y變量）在always中被'賦值'的話,就必須額外宣告reg
> 

> 再著,大多數event內都會使用clock或reset的變量,如以下
```
input rst;
input clk;
```
> 而我們可以使用verilog自身定義的posedge/negedge來描述此訊號是要在上升/下降時進行運算,如以下
```
always@ (posedge clk or posedge rst) begin
```
> 則代表若clk（0->1）or rst (0->1)則開始運算
# Case & Signal bits width

> 先由case語法開始,此類語法與if-else相似,只是在實例化電路時所給出的結構會不同,使用方式如下
```
case (s)
      0: y = a[0];
      1: y = a[1];
      2: y = a[2];
      3: y = a[3];
      default: y = 1'b0;
    endcase
```
> 在其中,可以得知輸入的判斷signal為s變量,當s分別等於0,1,2,3時則會相對應的將a[s]賦值給y
>另一例子如下
```
case (s)
      2'b00 : y = a[0];
      2'b01 : y = a[1];
      2'b10 : y = a[2];
      2'b11 : y = a[3];
      default: y = 2'b00;
    endcase
```
> 在其中我們可以發現一個表達方式,即2'b00這種。
> 此表達式所表達的意思為以下
```
（bits width）'(進位模式)+(在寬度中各個bits的狀態)
```
> 舉例而言
```
10 ' b 1000010000 ＝ 一個10位元寬度且為2進位信號
```
> 當然,中央的英文字母是可以更換的,各自代表如下
```
b = 二進位 / d = 10進位 / h = 16進位 / o = 8進位
```
# 最終實驗驗收 ： 實現二位四選一選擇器
Module code
```
module mux41(a,s,y);
	input [3:0] a;
	input [1:0] s;
	output reg y;
// output should using reg
	always@(*) begin
		case (s)
		2'b00 : y = a[0];
		2'b01 : y = a[1];
		2'b10 : y = a[2];
		2'b11 : y = a[3];
		default : y = 1'b0;
	endcase
	end
endmodule

```
Simulation code
```
#include "verilated.h"
#include "verilated_vcd_c.h"
#include "obj_dir/Vmux41.h"

VerilatedContext* contextp = NULL;
VerilatedVcdC* tfp = NULL;

static Vmux41* top;

void step_and_dump_wave(){
  top->eval();
  // eval函數調用則代表更新各訊號狀態
  contextp->timeInc(1);
  // 增加模擬時間,數字代表時間有多少個單位時間長
  tfp->dump(contextp->time());
  // 把波形丟到vcd檔
}
void sim_init(){
  contextp = new VerilatedContext;
  tfp = new VerilatedVcdC;
  top = new Vmux41;
  contextp->traceEverOn(true);
  //啟動追蹤
  top->trace(tfp, 0);
  tfp->open("dump.vcd");
}

void sim_exit(){
  step_and_dump_wave();
  tfp->close();
}

int main() {
  sim_init();
  // c++中 二進制表示法為0bxxxx
  top->s=0b00;  top->a=0b1110;  step_and_dump_wave();
                top->a=0b0001;  step_and_dump_wave();
  top->s=0b01;  top->a=0b1110;  step_and_dump_wave();
                top->a=0b0010;  step_and_dump_wave();
  top->s=0b10;  top->a=0b1010;  step_and_dump_wave();
                top->a=0b0100;  step_and_dump_wave();
  top->s=0b11;  top->a=0b0111;  step_and_dump_wave();
                top->a=0b1001;  step_and_dump_wave();
  sim_exit();
}
```
Terminal instruction
```
verilator --cc --trace mux41.v  --exe sim.cpp
```
```
make -j -C obj_dir/ -f Vmux41.mk Vmux41
```
```
./obj_dir/Vmux41
```
```
gtkwave dump.vcd
```
第一段落到此結束
下一回 ： [實驗二](https://hackmd.io/@1p0_VOUHTXGcHlBU9A0OEA/B1UjuRn9A)