owned this note
owned this note
Published
Linked with GitHub
# NYCU ICLAB 第一人稱修課體驗
## CREDITS TO
https://medium.com/mirkat-x-blog/iclab-交大修課心得-b60f272c2f32
~~我拿到同學的md id後就會慢慢補上~~
## 前言
哈囉大家好,這裡是傑克麻煩。交大電子研究所李鎮宜教授授課的**積體電路設計實驗**(**又稱 ICLAB**),一直是一門傳說中的課程,已經不知多少人午夜在 LAB 當中揮霍,也不知多少個 IC 設計的美夢葬送在一大疊退選單中。身為一個合格的交大電機學生,不修個 ICLAB ~~就跑去臺大~~簡直是浪費青春,加上剛好有機會看到學長過去的修課心得,而且有幸找了一群最頂尖的同屆組隊討論,有感而發想做一個第一視角回顧 ICLAB,修課內容和最詳細設計講解的系列筆記。
## 那開始囉!
**積體電路設計實驗 2023 Autum 教學大綱**
| | Lecture | Lab |
| ----- | ----------------------------- | --------------------- |
| [Week1](https://hackmd.io/@jackmafan/HyvZkizZ6) | Cell Based Design Methodology | Combinational Circuit|
| [Week2](https://hackmd.io/@jackmafan/iclabhacker2) | Finite State Machine | Sequential Circuit |
| Week3 | Verification & Simulation | Protocol & Pattern|
| Week4 | Sequential Circuit II (STA + Pipeline) | AI Chip (Float op) |
| Week5 | Memory & Memory Compiler | Image Processing (Memory) |
| Week6 | Synthesis Methodology | Network IP Design|
| Midterm Project | \ | Algorithm Based ASIC|
| Week7 | Timing: Cross Clock Domain + Synthesis Static Time Analysis | FIFO Design |
| Week8 | Power: Low Power Design | Clock/Power Gating|
| Week9 | System Verilog I (Design) | OOP Design|
| Week10 | System Verilog II (Verification) | Checker|
| Week11 | System Verilog (Formal Verification) | ABVIP|
| Week12 | APR I : From RTL to GDSII | Physical Design|
| Week13 | APR II : IR-Drop Analysis +Advanced IC Design Methodology| IR Drop Analysis|
| Final Project | \ | General Purpose IC|
|==ADFP==| ==FinFET Design== | ==FinFET Design== |
上半學期從 verilog 開始,囊括組合電路(Combinational Circuit)、時序電路(Sequential Circuit)、模擬及驗證(Verification)的基本語法,還有實體 RAM/ROM 的呼叫語法(Memory Compiler)、以及合成設定(Synthesis)。下半學期則包含多時脈電路分析、低功耗設計、Syetemverilog語法、甚至還要做確定佈局後的電路分析(APR)、以及最後的重頭戲:使用先進製程檔案操作的 FinFET Design(因保密協定、此系列文章將不會講解該堂課程),紮紮實實地訓練數位積體電路設計端的操作流程。
接下來的講解將預設讀者們已經有基礎的數位邏輯設計概念和 C/C++ 撰寫能力,避免過長的篇幅及冗餘的講解。
## Lecture 01 --- Cell Based Design Methodology
### 從抽象邏輯閘到實體電路
在學習邏輯設計的一開始,我們可能會用真值表、基礎的 AND 閘 OR 閘、還有反向器來描述一個電路。例如下面的布林函數 C=~(AB),對你而言可能是長這個樣子的:

| A | B| C|
| -------- | -------- | -------- |
| 0 | 0 | 1 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
但實體層面來說,製造廠(~~就是那家TS什麼C的~~)要做的卻是以下四個電晶體的電路,這個電路會在輸入端是高電位(Vdd、對應邏輯1)或低電位(GND,對應邏輯0)時,讓輸出端產生如真值表的輸出。
##### schematic&Layout of NAND

看到右上那張 layout 裡面花花綠綠的矩形了嗎?如果你修過VLSI實驗或類似名字的課程,肯定是對這些長方形刻骨銘心,因為修課學生會像在設計晶片時採用**全客製化方式設計(Full Custom Design)** 的工程師一樣,一個一個拉那些長方形,並產生右上這樣的圖層檔
### 數位電路設計方法
可能有些人覺得,拉個長方形又沒什麼,但如果想像一下你是要自己用長方形拼出以下 layout 呢?
https://medium.com/@akurian.2001/ic-layout-6232a0144629

顯然是天方夜譚對吧,所以現在主流的數位晶片設計方式是使用所謂 **Cell based** 的設計流程,也就是
1. **使用硬體描述語言描述電路的行為**
2. **交由設計自動化軟體將所描述的電路映射至製程廠提供的標準邏輯單元(Standard Cell)**
3. **設計自動化軟體會接著產生如上圖的layout**
4. **使用產生的實際電路做後續驗證**
5. .......
以下就來實際比較不同設計流程以及各自的優缺點
#### FPGA
FPGA 是一種比較特別的設計流程,當向製程廠下線晶片並不是很划算時,就可以採用 FPGA。本人並不是很熟悉相關的步驟,但一樣透過特定的軟體和各大硬體商提供的 FPGA 開發板,就可以在上面客製化屬於自己的電路。優點是不用等待極久極繁瑣的下線過程就可以得到一個可做測試的電路,缺點是電路的複雜性有其侷限,而且工作頻率最多只能達到百萬赫茲等級
**(note:目前個人電腦的工作頻率普遍可以到4G左右)**
#### Full Custom vs Cell Based
如同前述的優缺點,Full Custom 就像是裝潢時房子時從原木開始打造家具,優點是格局、雕飾、選材都可以客製化,但缺點是耗時極長;另一方面,Cell Based 就有點像是去某知名瑞典家具~~玩偶~~商買來自己組合和搭配,缺點就是多少有些缺點,有時候色澤差一點、有時候少一個夾層......但設計速度相對快非常多,而且效能不會比 Full Custom 差到非常多,~~當然是指手刻layout還是可行的那個年代,現在已經不可能這樣做了~~。
所以本門課程的一大重點就是學習如何使用 verilog 描述欲合成之電路行為(**RTL level design & Gate level synthesis**),以及怎麼去驗證這個電路的行為是符合預期的(**Pattern & Verification**)。
### Verilog & Combinational Circuit
在開始學習 verilog 之前,請讀者務必注意以下重點
:::info
verilog 是用來"描述"你心中想好的電路,而不是想辦法寫到讓模擬跑過,剩下交給EDA tool把電路通靈出來
:::
:::info
verilog 一開始發展的契機真的是用來描述電路並且做驗證,讓設計者用 c-style 寫 pattern 再對 gate level 的 netlist 進行驗證,所以從 verilog 程式碼合成電路的規範是後來才慢慢發展出來的,因此如果你對 verilog 那莫名其妙的變數型別和時序元件呼叫方法感到非常疑惑,純屬正常現象。
:::
#### reg & wire
verilog 中有以下兩種型別的變量,reg 和 wire,宣告時必須一同宣告其bit數,如下述代碼。
```verilog=
reg [31:0] my_first_reg; //32 bits width
wire [31:0] my_first_wire; //32 bits width
```
這兩種型別的變數具體而言差在哪裡呢?絕對不是合程階段保證合成出節點和暫存器的差別,而是
:::info
能夠在always敘述句中被放在左值賦予變量的只有reg型別的變數,能夠在assign敘述句中放在左值的只有wire型別的變數
:::
如下代碼
```verilog=
assign my_first_wire = 32'd0;
always(*)begin
my_first_reg = my_first_wire;
end
```
#### constant
在verilog中表示常數需要使用如下的表示式 *位元數* '*進制*_*值*
```verilog=
wire [7:0] constant_wire;
assign constant_wire = 8'b00100001; //代表binary的00100001
assign constant_wire = 8'd12;//代表decimal的12 自動補0到滿足8個bits
assign constant_wire = 6'b100001;//代表binary的100001
//assign給左值時會補0補到8個bits
assign constant_wire = -8'd12;//將-12的2補數assign給左值 也就是8'b11110100
```
#### assign
assign 是一個常用於組合電路的關鍵字,用法如下
```verilog=
wire [7:0] assign_example;
wire [7:0] r_value1;
reg [7:0]r_value2;
assign assign_example/*wire型別*/ = r_value1 & r_value2;
//可以看到不論變數是reg/wire,其實並不影響右值運算的合法性
assign assign_example = r_value1 | r_value2;
//Error: one wire cannot be assigned by two drivers
```
assign 會讓左值像是被放在一個組合邏輯單元的輸出端一樣,每當右值出現變動時也會出現相對應的變動。
#### assignment in always block
always 完整的語法的語法如下
```verilog=
always @(/*sensitivity list*/)
begin
//event
end
```
sensitivity list 可以寫入一個訊號,或者某個訊號的正負緣,這樣每當沒有指定正負緣的訊號發生改變,或指定的訊號來到正負緣時,就會執行always內的敘述。但在撰寫組合邏輯的時候,建議一律填入*,這代表所有在該 always block 右值出現的訊號發生改變時,都會觸發此區塊——這也是組合邏輯的特性:每當輸入發生改變,輸出就會嘗試改變。
另外 always 當中,有所謂的non-blocking assignment 和 blocking assignment,這是什麼意思呢?我們先來看看下方的程式碼
```verilog=
//this is a blocking assignment block
//remember only use blocking assignment in combinational circuit
reg a,b,c;
always@(*)begin
a = ~b;
a = a&c;
end
//this is a non-blocking assignment block
//remember only use blocking assignment on Flip-Flop
reg d,e,f;
always@(posedge clk)begin
d <= e;
e <= f;
end
```
首先,雖然我們在第一個 block 當中寫了很像是 combinational loop 的東西,但它是一個**可以被合成**的電路。因為只有這個區塊中不論 b/c 誰先發生變動,電路都會先做 a=~b 再做 a=a&c,所以它等效於

而第二個 block 則會在 clk 的正緣來臨時,將 e/f 在正緣前的值,更新到d/e 在正緣後的值,所以其實是如下 2 個 DFF 的結構。

其實從這樣的敘述中也可以看的出 non-blocking assignment 和 blocking assignment 的差別了。我自己的理解是,**non-blocking assignment 會將 r-value 在 trigger 前的值送到 trigger 後的 l-value,而 blocking assignment 則會尋找最"新"的 r-value 送到 l-value**。所以兩種 assignment style 當然不能用在同一個 l-value 上,甚至有些編譯器會禁止在同一個 always block 同時出現 "=" 和 "<="。(**即使不禁止你也應該避免使用**)
### Condition: If & Case
有時候在輸出並不只是輸入彼此之間的簡單運算,例如現在有 ctrl、a、b 訊號,當 ctrl 不同時,要輸出諸如 a+b、a-b、a&b,要描述這種 branching 的行為,在 verilog 中可以用 If 或者 Case 完成,範例如下。
```verilog=
//Case
reg a,b,c,select;
always@(*)begin
case(select)
1'b0:begin
c = a&b;
end
1'b1:begin
c = a|b;
end
//when the value before ':' is the same as value in (),
//the statement after ':' will be executed
endcase //using 'case'-'endcase' as begin-end token
end
//If else
reg d,e,f;
always@(*)begin
if(select == 1'b0)begin
f = d&e;
end
else begin
f = d|e;
end
end
```
要注意的是,If 代表的是一個不同條件有階級之分的電路,Case 裡面的不同條件則是平等的。這個差別通常在合成階段才會明顯表現出來,因為 If 所描述的電路的長相感覺會是這樣

而 Case 的電路則會感覺像這樣

If 電路的**關鍵路徑 (critical path)** 在合成初階段步看下來是會比較大的。雖然很多時候If之間彼此的條件是互斥的,或者一個本質上是平行條件的電路硬用If-style寫出來,合成器這時會去做適當的化簡,但是如果這些限制是使用者知道但合成器看不出來的話,最後還是會合出一個有階級條件的電路。
### 其他常見眉眉角角
礙於篇幅,其他 verilog 的基本語法(如 module declaration 和 port connection、signed/unsigned arithmetic)就不在此贅述。這一小段著重於分享我在撰寫 Combinational Logic 時的常見錯誤和debug方法
#### 意外產生 latch
Latch 是一種由控制訊號鎖存訊號的電路結構。當控制訊號 HIGH,輸出會等於輸入;控制訊號 LOW,輸出則會被"鎖"在控制訊號仍為 HIGH 的值,是最基本的"維持"狀態的電路單位。在學校的課程當中通常不希望出現 Latch 的原因如下
::: info
1. 難以進行靜態時序分析。對於時序元件,都需要在控制訊號切換前後要求輸入保持穩定狀態,如果 Latch 的控制訊號是由組合電路產生,此時會很難分析輸入該在什麼時候之前保持穩定。
2. 即使使用時鐘訊號做控制,實際運行時的 Latch 本身仍是一個容易產生 glitch 的結構。
3. 更多時候是因為在我們預期根本不應該出現 Latch 的地方被合成出 Latch —— 例如組合邏輯。
:::
要確認有沒有合成出 Latch 只要去看 syn.log 就可以了。如果某個變量被判定有出現 **"Latch Inference"** ,就記得去修改賦值該變量代碼的 always block 即可。
通常出現 Latch 的地方會在帶有條件判斷的變量上,**當經過一系列的條件判斷,某個 reg 變量有可能沒有被賦值**,就會被合成 tool 判定為你在寫一個 Latch 的行為,**因為不給值它就只好維持舊有的值嘛!**
拿上面的代碼來說
```verilog=
//Case
reg a,b,c,select;
always@(*)begin
case(select)
1'b0:begin
c = a&b;
end
//1'b1:begin
//c = a|b;
//end
//Latch Inference!! when select = 1'b1
//c will not be assigned
endcase
end
//If else
reg d,e,f;
always@(*)begin
if(select == 1'b0)begin
f = d&e;
end
//else begin
//f = d|e;
//end
//Latch Inference!! when select = 1'b1
//f will not be assigned
end
```
要去掉這種簡單或更複雜代碼的 Latch 也很簡單。
```verilog=
//Case
reg a,b,c,select;
always@(*)begin
case(select)
1'b0:begin
c = a&b;
end
default:begin
c = a|b;
end
//use default statement to catch the exceptional cases
//so that you dont have to enumerate all exceptional cases
endcase
end
//If else: style 1
reg d,e,f;
always@(*)begin
if(select == 1'b0)begin
f = d&e;
end
else begin //always append a else block after if-else-if structure
f = d|e;
end
end
//If else: style 2
reg f2;
always@(*)begin
f2 = d|e;//assign the value at initial
//so that the value is still assigned when condition is not met
if(select == 1'b0)begin
f2 = d&e;
end
end
```
#### 接錯port
其實通常在模擬階段就能抓出呼叫 module 時接錯線的錯誤了。為了避免此類錯誤,請盡量使用 **naming mapping** 的方式呼叫 module。此外有時候仔細看合成訊息也能發現一些 debug 方法,例如有時候我會接收到某些 module 的 pin 腳只被接了 1 bit wire上去,這時我就大概可以知道我這個變量沒改到名字(因為合成器會自動判定沒被宣告過的變量為 1bit 寬)。
此外我在後續 Lab 也有發生非常離奇的錯誤,在合成階段時因為 RTL 沒有使用naming mapping接線(但線寬順序都對),導致合出錯誤電路的結果。
PS:附上助教回應

## Lab01 MOS Caculator
### Description
本週的 Lab 是要撰寫一個簡單的邏輯電路,在輸入給定一系列關於 MOS 的參數後以及控制訊號後,需要計算出 MOS 們的 IDS 或者 gm 並排序,再選擇對大三數取加權平均或小三數取平均並輸出。Pin腳大致如下
::: success
**Input**
W_0, V_GS_0, V_DS_0,
...
W_5, V_GS_5, V_DS_5,(6顆MOS的參數)
Mode (bit0選擇計算ids或gm,bit1選擇計算大三數加權平均或小三數加權平均)
**Output**
out (計算結果)
:::
### Strategy
根據助教在講義中指定的的公式,在 saturation 和 triode region 下 Ids 和 gm 的公式分別是
$$
Triode(V_{gs}-1>V_{ds})
$$
$$
I_{ds}={W \over 3}[2(V_{gs}-1)V_{ds}-V_{ds}^2]
$$
$$
g_{m}={2WV_{ds} \over 3}
$$
$$
Saturation(V_{gs}-1 \le V_{ds})
$$
$$
I_{ds}={W \over 3}(V_{gs}-1)^2
$$
$$
g_{m}={2W(V_{gs}-1) \over 3}
$$
但如果在 Vds 進來後對它做所謂的限制,壓縮到不比 Vgs-1 大,就可以把公式變成
$$
I_{ds}={W \over 3}[2(V_{gs}-1)V_{ds'}-V_{ds'}^2]
$$
$$
g_{m}={W(2V_{ds'}) \over 3}
$$
注意到其實可以對W後的第二個運算單元做多工,這樣就只需要一組乘法器,要壓縮面積的話,甚至可以直接根據 mode+Vgs+Vds 寫一個 case 的電路,給出乘法單元的第二個運算元(也就是要算電流的話,不用乘法器跟減法器去做2(Vgs-1)Vds....這件事情)。
### Sample Code
以下這段程式碼則可以給出一個排序 6 個 unsigned input 的電路,欲了解具體架構可以搜尋關鍵字 **sorting network**,應該就能找到容易參數化或最佳化的各種排序網絡。可以注意到"唯一"的 always block內有很多變量都被重複給值,這就是應用到 always 依序執行的特性,讓我可以重複利用相同名字的變量堆出複雜結構。
```verilog=
module sorter( //using odd even mergesort algorithm
//input
mode,
i0,i1,i2,i3,i4,i5,
//output
o0,o1,o2
);
input mode;
input [7:0] i0,i1,i2,i3,i4,i5;
output reg [7:0] o0,o1,o2;
reg [7:0] s0,s1,s2,s3,s4,s5; //eventually, s0 is largest, and s5 is smallest
always@(*) begin
{s0,s1,s2,s3,s4,s5} = {i0,i1,i2,i3,i4,i5};
//level1
{s0,s1} = (s0>s1)?{s0,s1}:{s1,s0};
{s2,s3} = (s2>s3)?{s2,s3}:{s3,s2};
{s4,s5} = (s4>s5)?{s4,s5}:{s5,s4};
//level2
{s0,s2} = (s0>s2)?{s0,s2}:{s2,s0};
{s1,s3} = (s1>s3)?{s1,s3}:{s3,s1};
//level3
{s1,s2} = (s1>s2)?{s1,s2}:{s2,s1};
//level4
{s0,s4} = (s0>s4)?{s0,s4}:{s4,s0};
{s1,s5} = (s1>s5)?{s1,s5}:{s5,s1};
//level5
{s2,s4} = (s2>s4)?{s2,s4}:{s4,s2};
{s3,s5} = (s3>s5)?{s3,s5}:{s5,s3};
//level6
{s1,s2} = (s1>s2)?{s1,s2}:{s2,s1};
{s3,s4} = (s3>s4)?{s3,s4}:{s4,s3};
//output
{o0,o1,o2} = (mode)?{s0,s1,s2}:{s3,s4,s5}; //mode[1]: 0-> work on smaller results 1->work on larger result
end
endmodule
```
### Performance
Area:36k $um^2$
Rank:22/149