# 數位系統 第 7 次實驗報告
###### tags: `digital system`
姓名:高聖傑
系級:資工113
學號:B093040016
實驗日期:11/22
## 實驗一
### 內容
- 目標 & 要求
- 使用 Structural level modeling 模擬並驗證 8-bit ripple-carry adder
- 其內部由兩個 4-bit cla adder 所組成
### 過程
上次的 ripple-carry adder 在 bit 越多時,效能會越差。
原因是在算第 N 位時,必須等待第 N-1 位的 Cout 進位輸出。
而 carry-look ahead adder 可以把加法的邏輯展開,
直接使用 $A_i\oplus B_i$ 和 $A_i\cdot B_i$ 的組合來算 carry 輸出。
- 比較兩者:
- ripple-carry adder
- 優點:構造簡單、模組化,可以像積木一個一個拼起來
- 缺點:在 bit 越多時,效能會越差
- 時間複雜度 : 線性時間 $O(2n+1) = O(n)$
- carry-look ahead adder
- 優點:很快。
- 時間複雜度 : 常數時間 $O(4) = O(1)$
- 缺點:把加法的邏輯展開需要大量 AND & OR gate,造成電路過於複雜
:::info
單獨使用其中一個都有其缺點,因此我們可以採取折衷做法,
例如使用 4 個 4-bit carry-look ahead adder 串連成共 16 bit 的加法器,
速度比 16 bit ripple-carry adder 快,在設計上也不至於太過複雜,
4 bit 版本的線路就已經夠複雜了,要是真的實做 16 bit 一定會很恐怖。
:::
- 公式
* carry propagate: $P_i = A_i\oplus B_i$
* carry generate: $G_i = A_iB_i$
* sum: $S_i = P_i\oplus C_i$
* carry: $C_{i+1} = G_i+P_iC_i$
* 展開以下第 n 項 carry 算式
* $C_0 =$ input carry
* $C_1 = G_0+P_0C_0$
* $C_2 = G_1+P_1C_1 = G_1+P_1(G_0+P_0C_0) = G_1+P_1G_0+P_1P_0C_0$
* $C_3 = G_2+P_2C_2 = G_2+P_2G_1+P_2P_1G_0+ P_2P_1P_0C_0$
* $C_4 = G_3+P_3C_3 = G_3+P_3G_2+P_3P_2G_1+P_3P_2P_1G_0+ P_3P_2P_1P_0C_0$
先做出一個 4-bit carry-look ahead adder。
小規模測試合格後,接著串接兩個 4-bit cla adder,分別處理前、後各 4 bit,
中間用一條傳遞 $C_4$ 的 wire 連起來,即組成一個 8-bit cla adder
#### module code
cla_4bit, cla_8bit 使用 Structural level modeling
```verilog=
module cla_4bit(
output [3: 0] sum, output ovfl,
input [3:0] A, B, input cin);
wire [3:0] g, p, c;
wire[10:0] w;
//wire c1, c2, c3
and a0(g[0],A[0],B[0]);
and a1(g[1],A[1],B[1]);
and a2(g[2],A[2],B[2]);
and a3(g[3],A[3],B[3]);
xor x0(p[0],A[0],B[0]);
xor x1(p[1],A[1],B[1]);
xor x2(p[2],A[2],B[2]);
xor x3(p[3],A[3],B[3]);
buf(c[0], cin);
and(w[0],p[0],c[0]);
or (c[1],g[0],w[0]);
and(w[1],p[1],p[0],c[0]);
and(w[2],p[1],g[0]);
or (c[2],g[1],w[2],w[1]);
and(w[3],p[2],p[1],p[0],c[0]);
and(w[4],p[2],p[1],g[0]);
and(w[5],p[2],g[1]);
or (c[3],g[2],w[5],w[4],w[3]);
and(w[6],p[3],p[2],p[1],p[0],c[0]);
and(w[7],p[3],p[2],p[1],g[0]);
and(w[8],p[3],p[2],g[1]);
and(w[9],p[3],g[2]);
or (ovfl,g[3],w[9],w[8],w[7],w[6]);
xor(sum[0],p[0],c[0]);
xor(sum[1],p[1],c[1]);
xor(sum[2],p[2],c[2]);
xor(sum[3],p[3],c[3]);
endmodule
module cla_8bit(
output [7: 0] sum, output ovfl,
input [7:0] A, B, input cin);
wire mid;
cla_4bit s0(sum[3:0], mid, A[3:0], B[3:0], cin);
cla_4bit s1(sum[7:4], ovfl, A[7:4], B[7:4], mid);
endmodule
```
#### testbench code
```verilog=
`timescale 1ns / 1ps
module tbGen();
reg [7:0] A;
reg [7:0] B;
reg cin;
wire ovfl;
wire [7:0] sum;
cla_8bit _tb(.A(A), .B(B), .cin(cin),
.sum(sum), .ovfl(ovfl));
initial begin
cin = 1'b0; A = 8'd8; B = 8'd7;
#10
cin = 1'b0; A = 8'd17; B = 8'd23;
#10
cin = 1'b1; A = 8'd0; B = 8'd9;
#10
cin = 1'b1; A = 8'd250; B = 8'd5;
#10
$finish;
end
endmodule
```
### 模擬結果
![](https://i.imgur.com/HfGJcTX.png =600x)
兩數字的加法運算結果正確,加入 carry in 的運算結果也正確
## 實驗二
### 內容
- 目標 & 要求
- 模擬並驗證 2-digit BCD adder
### 過程
需存下 A + B 的運算過程半成品 Z,以便判斷 > 9 及後續運算。
- 重點:「偵測 4-bit binary sum > 9」,有 3 種情況
- binary sum 發生溢位 (ovfl=1) >= 16
- binary sum 在 $Z_8Z_4$ 的情況 >= 12
- binary sum 在 $Z_8Z_2$ 的情況 >= 10
- 取聯集(OR),得到 $C = K +Z_8Z_4 + Z_8Z_2$
- 若 C > 9 ,則 +6(等效於 -10)
- 作用:記錄進位,且把超出 10 的部分壓回去 0 ~ 9 之間
- 實做:再加一個 4-bit adder 處理 +6 的部分,根據 C 決定是否 +6
- C = True : Sum = Z+6
- C = False : Sum = Z+0
先做出一個 1-digit BCD adder。
小規模測試合格後,接著串接兩個 1-digit BCD adder,分別處理前、後各 1 digit,
中間用一條傳遞 $C_1$ 的 wire 連起來,即組成一個 2-digit BCD adder
#### module code
dec_adder, dec_adder_v2 使用 Structural level modeling
```verilog=
module dec_adder(
output [3: 0] sum, output ovfl,
input [3:0] A, B, input cin);
wire k0, k1, k2, o1, p;
wire[3:0] w, z;
cla_4bit s0(z[3:0], k0, A[3:0], B[3:0], cin);
and(k1, z[3], z[2]);
and(k2, z[3], z[1]);
or(o1, k0, k1, k2);
buf(w[3], w[0], 0);
buf(w[2], w[1], o1);
cla_4bit s1(sum[3:0], p, z[3:0], w[3:0], 0);
or(ovfl, o1, p);
endmodule
module dec_adder_v2(
output [7: 0] sum, output ovfl,
input [7:0] A, B, input cin);
wire mid;
dec_adder s0(sum[3:0], mid, A[3:0], B[3:0], cin);
dec_adder s1(sum[7:4], ovfl, A[7:4], B[7:4], mid);
endmodule
```
#### testbench code
```verilog=
`timescale 1ns / 1ps
module tbGen();
reg [7:0] A;
reg [7:0] B;
reg cin;
wire ovfl;
wire [7:0] sum;
dec_adder_v2 _tb(.A(A), .B(B), .cin(cin),
.sum(sum), .ovfl(ovfl));
initial begin
cin = 1'b0;
A = 8'b00110010; B = 8'b01000001;
#10
A = 8'b00100101; B = 8'b01010111;
#10
A = 8'b10000110; B = 8'b01111001;
#10
$finish;
end
endmodule
```
### 模擬結果
![](https://i.imgur.com/SmFigyW.png =600x)
測資為簡報中範例數字,其加法運算結果正確。
## 實驗心得
> 這次的實驗主要是讓我們練習用 verilog 程式碼實做進階版的加法器 module。
> 在做實驗一時,我們知道 carry-look ahead 即是「用空間換時間」。
> 其原理是把邏輯展開,用大量的運算過程半成品($A_i\oplus B_i$ 和 $A_i\cdot B_i$)快速得出進位輸出。
> 我們同時也觀察到一個現象,carry-look ahead 把運算時長壓低的副作用,就是增加所佔用的面積,以及所使用的邏輯閘。
>
> 實驗二則是處理 10 進位的 BCD 編碼加法,我先完成「偵測 4-bit binary sum > 9」的部分,後半段卡了很久在思考怎麼在滿足條件時「+6」
> 我在測試時有試過能不能直接對 wire 賦予初始值,後來看了看網路上才知道有人使用 buf() 在最後的 argument 放入 0 或 1,且可以一個輸入對到多個輸出。
## Extra
網路上看到有人在使用 buf() 時,給了 2 個以上的 argument,這令我覺得非常困惑,
buf, not 居然不是單輸入、單輸出的嗎?
我可以理解 and, or, xor, nand, nor, xor, xnor 是屬於多輸入、單輸出
所以我先猜測 buf, not 是屬於單輸入、多輸出(不可能是多輸入、多輸出)
接著寫一個 module 實際驗證 / 測試
```verilog=
module test_buf(output[7:0] ans);
buf(ans[5], ans[2], ans[1], 1);
endmodule
```
![](https://i.imgur.com/vPlNDke.png =600x)
驗證結果:buf, not 確實是單輸入、多輸出。
不過我覺得這樣記很奇怪,就把他理解成 output 到很多根 wire 上吧