---
# System prepended metadata

title: 從0到有製作自己的CPU！！ 第一周第一組課後KIM教學 20220707
tags: [' 伴學松 第一組筆記', RISCV]

---

---
tags: RISCV, 伴學松 第一組筆記
---
# 從0到有製作自己的CPU！！ 第一周第一組課後KIM教學 20220707
[TOC]

# 講解 CPU 結構
> [name=Kim Weng]
- 所謂的computer，其學名叫做計算機，不外乎計算加減乘除法或是一些邏輯、移位的運算
- 早期要做機器運算做加減乘除就是要拿這個ALU做處理，而將資料放進左邊，再由中間的ALU做運算後再從右邊輸出結果但基於較沒效率的關係,有一個人叫做約翰·馮紐曼發明一個馮·紐曼模型（Von Neumann model）或普林斯頓架構（Princeton architecture），他將右邊的ab輸入額外做出一個儲存資料的記憶體,這樣一來右邊不需要一直有個輸入裝置去馬不停蹄地送資料，而這個記憶體具有儲存資料的電腦,學名叫做儲存程式電腦, 意思是指將儲存裝置與中央處理器分開的概念。
- 然而，由於馮紐曼架構將指令與數據放在同一記憶體的關係，會導致CPU在讀取instruction和存取data會互相競爭，造成的CPU利用率（吞吐率）受到限制，也就是所謂的馮紐曼瓶頸。為了改善此一缺點，因此instruction和data分開存取的哈佛架構(Harvard architecture)就相繼被提出
- 原本純馮紐曼架構下的CPU可以讀取指令或讀/寫記憶體資料，它們都不能使指令和資料同時使用同一個的匯流排系統。而使用哈佛架構的電腦中CPU，即使沒有快取的情況下也可以在讀取指令的同時進行資料存取。由於指令和資料存取不使用同一個記憶體通道，因此哈佛結構的電腦可以在相同的電路複雜度下有更好的表現。而目前即將要實現的RV32I即屬於哈佛架構。
![](https://i.imgur.com/NeeB5Mg.png)
- 由於RV32I採用哈佛架構，所以基本上包括從instruction memory提取指令、ALU執行、把計算的結果存入data memory，這樣一來就包括三個stage了。由於RISC-V屬於精簡指令集，即會把一個比較複雜的操作拆成兩步驟來完成，即第一個指令先用ALU計算結果，下一指令再把結果存入data memory，而第一個指令計算完的結果需要先放入某一處「暫存」，下一指令再從這個暫存的地方把結果store進data memory，因此我們還需要實現register，即所謂的暫存器。而CPU提取指令後，在進入ALU執行之前，需要知道目前提取出來的指令需要做什麼事情，所以需要有一個Decode模塊來負責解碼。讀取暫存器和寫入暫存器各暫一個stage，而解碼器解碼則與讀取暫存器在同一stage完成，因此暫存器和解碼器的操作又佔了兩個stage，再加上原先的三個stage，就構成了5 stage的CPU架構，如下圖所示:
![](https://i.imgur.com/2yW1q3o.png)


# 講解 ALU 與 verilog 程式結構
> [name=bill503084699]
1. 這是CPU裏頭某一個很像多工器形狀的一個在計算機中，算術邏輯單元（ALU）是專門執行算術和邏輯運算的數字電路。 ALU是計算機中央處理器的最重要組成部分，甚至連最小的微處理器也包含ALU作計數功能。 在現代CPU和GPU處理器中已含有功能強大和複雜的ALU; 一個單一的元件也可能含有ALU。
![](https://i.imgur.com/Ge7u6aE.png)
2. 介紹以ALU的加減法架構所呈現出來的圖
![](https://i.imgur.com/L1jjDeh.png)
![](https://i.imgur.com/xAvIo7F.png)
3. 依照慣例我們先把模組名稱定義出來, 接下來就是小括號" 埠列信號 "的部分, 將輸入跟輸出以input 跟output 給列出來
```verilog= 
module alu(input[31:0] a,
           input[31:0] b,
           input[31:0] op,
           output reg[31:0] y );
endmodule
```
![]( https://i.imgur.com/t0dPVi1.png)

4. 設定一個always 來指定一個特定事件觸發的基本型 (組合邏輯語法)
    - always語句有兩種觸發的方式 
    - 第一種是"電平觸發"，
      - 例如always @（a or b or c），a、b、c均為變數，當其中一個發生變化時，下方的語句將被執行。
    - 第二種是"沿觸發"，沿觸發就是"相當於posedge之類的語言約束的信號" ,如果沒有如posedge之類的約束的話就是電平變化觸發
      - 例如always @（posedge clk or negedge rstn），即當時鍾處在上升沿或下降沿時，語句被執行 。
    - 而對於"always@（*），意思是以上兩種觸發方式都包含在內，任意一種發生變化都會觸發該語句,就是因為敏感清單過多的情況下太麻煩
      - 補充第*種 : verilog 2001標準說可以使用替換敏感清單，表示"缺省，"編譯器會根據always塊內部的內容自動識別敏感變數。
     

|  設定一個always 來指定一個特定事件觸發的基本型 (組合邏輯語法) |  always語句有兩種觸發的方式 | 
| ---- |:----:|
|    - 第一種是"電平觸發"|例如always @（a or b or c），a、b、c均為變數，當其中一個發生變化時，下方的語句將被執行 |  | |  | |  || ---- |:----:|:----:|:---------:|:----:|:-------:| -------- |
|  第二種是"沿觸發"，沿觸發就是"相當於posedge之類的語言約束的信號" ,如果沒有如posedge之類的約束的話就是電平變化觸發 |例如always @（posedge clk or negedge rstn），即當時鍾處在上升沿或下降沿時，語句被執行| | ---- |:----:|
|     - 而對於"always@（*），意思是以上兩種觸發方式都包含在內，任意一種發生變化都會觸發該語句,就是因為敏感清單過多的情況下太麻煩
|補充第*種 : verilog 2001標準說可以使用替換敏感清單，表示"缺省，"編譯器會根據always塊內部的內容自動識別敏感變數。|  | |  | |  || ---- |:----:|:----:|:---------:|:----:|:-------:| -------- |


```verilog= 
always@(a or b or c) begin
```
```verilog= 
always@(posedge clk or negedge rstn) begin
```

```verilog= 
always@(*) begin
```
------------
    
```verilog= 
module alu(input[31:0] a,
           input[31:0] b,
           input[31:0] op,
           output reg[31:0] y );
    always@(*)// 這邊我是用@(*)

endmodule
```
  * ![](https://i.imgur.com/fehi4Hu.png)   

5. 再以多路分支的結構下去描寫電路"多則一" 多工器?的情況但最後結果會把它變成更簡潔的一種表示法

     - 像是if...else...多則一的描述語法
     - 像是c語言的 switch...case
     - 以case做關鍵字 以endcase做收尾
     - 剩餘未達條件的項目用default 一網打盡
     - 行為描述區塊的行數如果是多行的表達方式需要用begin end的方式下去把它包覆起來
```verilog=
always@(?) begin
          case(訊號(變數))
              條件值1:   行為描述區塊 // 多行
              條件值2:   行為描述區塊
              條件值3:   行為描述區塊
              ...
              default : 剩餘條件行為描述區塊
          endcase
      end
```
```verilog= 
module alu(input[31:0] a,
           input[31:0] b,
           input[31:0] op,
           output reg[31:0] y );
    always@(*) begin
        case(op)// 括號裏頭代表訊號,而在case裏頭的值是表達訊號的輸出值可以去決定
                // case到endcase之間列的條件(條件值x:行為描述區塊)
          
        endcase
    end
endmodule
```

6. 而在題目上我們要表達 reg[31:0]表達的是一個32值得比特向量(vector) 而我們又該怎麼表達行為區塊的表現式
     - 條件值1:   行為描述區塊
     - 條件值 x 最前面的數字為這個串的長度
        - 例如: 001 為 3'001 
               0001為 4'0001 
     - x'一個英文數字 代表示是哪種位數的進位表現式
        - b說明這是二進位串 
        - o說明這是八進位串
        - d說明這是十進位串 
        - h說明這是十六進位串 
          - 例如:1'h0 , 3'b001 
     
                 

```verilog=
module alu(input[31:0] a,
           input[31:0] b,
           input[31:0] op,
           output reg[31:0] y );
    always@(*)begin 
        case(op)
          3'h0: y = a+b; //當op=3'h0時,out為a+b
          3'h1: y = a-b; //當op=3'h1時,out為a-b
          3'h2: y = a|b; //當op=3'h2時,out為a|b
          3'h3: y = a&b; //當op=3'h3時,out為a&b 
          3'h4: y = a^b; //當op=3'h4時,out為a^b 
          default : y = 0;
        endcase
    end
endmodule
```                         


7. 行為描述條件若剩剩餘未達條件的項目則用default 一網打盡,至於default要不要寫，則取決於case條件是否完備啦
如果你的case條件已經完備，那default不寫也無所謂，一定要寫的話，隨便賦值都可以
如果case條件不完備，default肯定要寫一個確定的值。
```verilog=     
          3'h3: y = a&b; 
          3'h4: y = a^b; 
          default : y = 0; //行為描述條件若剩剩餘未達條件的項目則用 default 一網打盡
``` 
8. 最終呈現 ALU
![](https://i.imgur.com/WK0UJ90.png)
    
    
## 參考資料
* [FPGA系統設計實務_蕭宇宏_Verilog 硬體描述語言介紹](https://youtube.com/playlist?list=PLI6pJZaOCtF3_-vE7VUn9RdhQ6KXpcFQD)
* [Verilog初級教程（10）Verilog的always塊](https://blog.csdn.net/Reborn_Lee/article/details/107052261)   
* [在Verilog裡邊 always@（*）語句是什麼意思？](https://zhidao.baidu.com/question/261901454.html?fr=iks&word=ALU+ALWAY%40&ie=gbk) 
* [HDLbits刷題中文完整版，按照刷題網站順序每日更新一道](https://blog.csdn.net/wszwszwszqwer/article/details/123764784)
* [yf869778412 verilog中的default應該賦捨麼樣的值](https://www.cnblogs.com/chengqi521/p/6721276.html)
* 名名在群組上寫的答案
```verilog
module alu(input [31:0] a, input [31:0] b, input [31:0] op, output reg [31:0] y);
  always@(*) begin
    case(op)                  
      3'b000: y = a + b;     
      3'b001: y = a - b;      
      3'b010: y = a * b;      
      3'b011: y = a / b;      
      3'b100: y = a & b;      
      3'b101: y = a | b;      
      3'b110: y = ~a;         
      3'b111: y = a ^ b;
      default: y = 0;      
    endcase
  end
endmodule
```