# Lab2 Review
[TOC]
## Design Description
主要的架構是使用多工器(MUX)去選取不同的資料,並依`Sel`信號的不同,選取相對應的功能。下表帶大家快速 review 一下功能:
| Sel value | Function | Sel value | Function |
| --------- | -------- | --------- | -------- |
| 0000 | priority encoder | 1000 | 2-to-4 decoder |
| 0001 | A&B | 1001 | 4-to-16 decoder |
| 0010 | A^B | 1010 | select the larger number|
| 0011 | A\*B | 1011 | select the smaller number|
| 0100 | A>>>1'b1 | 1100 | A+B | 2’s complement|
| 0101 | A<<<1'b1 | 1101 | A-B | 2’s complement |
| 0110 | A>>1'b1 | 1110 | \|A-B\| |
| 0111 | A<<1'b1 | 1111 | A\*B|
---
## Verilog Code
基本上,大家經過 Lab1、Lab2 的洗禮後應該對 design 的流程相當熟悉了:)以下會提供各功能 verilog 的寫法,供各位參考。
### 分析
這次 design 算是一個比較大的架構,所以事前分析是很重要的步驟,這會幫助你有計畫的安排線路名稱,之後寫出來的 code 會比較乾淨。
(一)數字有分 signed 跟 unsigned,所以可以分開宣告變數:
```verilog=
input [3:0] A;
input [3:0] B;
wire signed [3:0] A_s, B_s;
assign A_s = A;
assign B_s = B;
```
(二)主要架構是 mux,可以用 always block 實作:
```verilog=
wire [15:0] W_0000, W_0001, W_0010, W_0011;
wire [15:0] W_0100, W_0101, W_0110, W_0111;
wire [15:0] W_1000, W_1001, W_1010, W_1011;
wire [15:0] W_1100, W_1101, W_1110, W_1111;
always@(*)begin
case(Sel)
4'b0000: Out = W_0000;
4'b0001: Out = W_0001;
4'b0010: Out = W_0010;
4'b0011: Out = W_0011;
4'b0100: Out = W_0100;
4'b0101: Out = W_0101;
4'b0110: Out = W_0110;
4'b0111: Out = W_0111;
4'b1000: Out = W_1000;
4'b1001: Out = W_1001;
4'b1010: Out = W_1010;
4'b1011: Out = W_1011;
4'b1100: Out = W_1100;
4'b1101: Out = W_1101;
4'b1110: Out = W_1110;
4'b1111: Out = W_1111;
endcase
end
```
(三)每個功能所要選取的 bit 數不同,也依據功能看要做sign extension 或補0,利用 assign 的方式,把不同功能分別輸出的結果彙整,如下:
```verilog=
wire [3:0] OPand, OPxor;
wire signed [7:0] OPmul;
//variables declared here...
assign W_0000 = {13'd0, OPencode};
assign W_0001 = {12'd0, OPand};
assign W_0010 = {12'd0, OPxor};
assign W_0011 = {{8{OPmul[7]}}, OPmul};
assign W_0100 = {12'd0, OPsra};
assign W_0101 = {12'd0, OPsla};
assign W_0110 = {12'd0, OPsrl};
assign W_0111 = {12'd0, OPsll};
assign W_1000 = {12'd0, OPdecode_4};
assign W_1001 = OPdecode_16;
assign W_1010 = {12'd0, OPlag};
assign W_1011 = {12'd0, OPsml};
assign W_1100 = {{12{OPadd[3]}}, OPadd};
assign W_1101 = {{12{OPsub[3]}}, OPsub};
assign W_1110 = {12'd0, OPabs};
assign W_1111 = {{8{OPmul[7]}}, OPmul};
```
(四)偵測 overflow,針對三個 case 做處理
```verilog=
wire add_ov, sub_ov, abs_ov;
always@(*)begin
case(Sel)
4'b1100: Ovf = add_ov;
4'b1101: Ovf = sub_ov;
4'b1110: Ovf = abs_ov;
default: Ovf = 1'b0;
endcase
end
```
### 實作
以下就依各功能的 partial code 做說明:
`0000`:可以使用 always block。以 if、else 巢狀結構拼組。(也有看到同學使用`casex`的寫法,很好~)
```verilog=
always@(*)begin
if(A[3]==1'b1)begin
OPencode = 3'b111;
end else begin
if(A[2]==1'b1) begin
OPencode = 3'b110;
end else begin
if(A[1]==1'b1)begin
OPencode = 3'b101;
//...
```
`0001`、`0010`、`0011`:直接使用內建指令。A\*B記得要用 signed number。
```verilog=
//0001
assign OPand = A&B;
//0010
assign OPxor = A^B;
//0011
assign OPmul = A_s*B_s;
```
`0100`、`0101`、`0110`、`0111`:直接使用內建指令。算數右移需要用 signed number。
```verilog=
//0100
assign OPsra = A_s>>>1'b1;
//0101
assign OPsla = A_s<<<1'b1;
//0110
assign OPsrl = A>>1'b1;
//0111
assign OPsll = A<<1'b1;
```
`1000`、`1001`:自建 module ,比較主流的方式是讓input port 有 A、B、enable、out:
```verilog=
module two_four_decoder(A,B,enable,out);
input A, B, enable;
output [3:0]out;
wire not_A,not_B;
not na0(not_A, A);
not nb0(not_B, B);
and a0(out[0], not_A, not_B, enable);
and a1(out[1], not_A, B, enable);
and a2(out[2], A, not_B, enable);
and a3(out[3], A, B, enable);
endmodule
```
4-16 請參考附圖拼組:
||
| --- |
(圖片來源:http://www.exploreroots.com/dc23.html)
`1010`、`1011`:建 1-bit 的 comparator,最後使用 mux 將結果作為選擇信號:
```verilog=
//Comparator ... (do it by yourself)
module selector(A, B, Out);
input [3:0]A, B;
output [3:0]Out;
wire gt;
Comparator cp0(A, B, gt);
assign Out = (gt == 1'b1)? A : B;
endmodule
```
`1100`、`1101`、`1110`:adder 的部分應該大家都蠻熟悉的, **Cout = majority function, Sum = A xor B xor Cin**,而判別 overflow 的方式可以是 Cout[2] 跟 Cout[3] 做 xor 的結果,或是另外拉出來判斷都可以。而 subtractor 基本上是把 B 做convert 再 +1 的結果輸入 adder 並重新判別 overflow。最後就是 absolute value 的部分,這裡可能同學要注意的地方是,two's complement 的範圍在 -8~+7 之間,所以 -8 要翻過來時會 overflow。
```verilog=
//1-bit full adder
module adder(A, B, Cin, Cout, Sum);
input A, B, Cin;
output Sum, Cout;
wire w0, w1, w2;
and a0(w0,A,B);
and a1(w1,A,Cin);
and a2(w2,B,Cin);
or o1(Cout, w2, w1, w0);
xor x0(Sum, A, B, Cin);
endmodule
//Adder ... (do it by yourself)
//Subtractor
module Subtractor(A, B, Out);
input [3:0]A, B;
output [3:0]Out;
wire [3:0]B_c;
assign B_c = ~B + 1'b1;
Adder a0(A, B_c, Out);
endmodule
//absolute value
module absolute(A, B, Out);
input [3:0]A, B;
output [3:0]Out;
wire [3:0]out_tmp;
Subtractor s0(A, B, out_tmp);
assign Out = (out_tmp[3] == 1'b1)? ~out_tmp+1'b1 : out_tmp;
endmodule
```
`1111`:據說坊間有 two's complement 的乘法器,但我會都轉成正數相乘,再判斷正負號。
```verilog=
module Multiplier (a, b, p);
input [3:0] a, b;
output [7:0] p;
wire [3:0] mul0, mul1, mul2, mul3;
wire [3:0] out0, out1, out2, out3, out4;
wire q2, q3, q4, q6, q7, q8, q9, q10, q11, q12;
wire s3, s4, s5, s8, s9, s10;
and a01(mul0[0], a[0], b[0]);
and a02(mul0[1], a[1], b[0]);
and a03(mul0[2], a[2], b[0]);
and a04(mul0[3], a[3], b[0]);
//...
FullAdder z01(.a( mul0[1] ), .b( mul1[0] ), .cin( 1'b0 ), .cout( q2 ), .sum( p[1] )); // s2 == p[1]
FullAdder z02(.a( mul0[2] ), .b( mul1[1] ), .cin( q2 ), .cout( q3 ), .sum( s3 ));
FullAdder z03(.a( mul0[3] ), .b( mul1[2] ), .cin( q3 ), .cout( q4 ), .sum( s4 ));
FullAdder z04(.a( 1'b0 ), .b( mul1[3] ), .cin( q4 ), .cout( q5 ), .sum( s5 )); // q5 == s6
//...
endmodule
module Mul(A, B, Out);
input [3:0]A, B;
output [7:0]Out;
wire [3:0]A_p, B_p;
wire [7:0]out_t, out_p;
wire ab_x;
xor x0(ab_x, A[3], B[3]);
to_positive t0(A, A_p);//make A_p > 0
to_positive t1(B, B_p);//make B_p > 0
multiplier mul0(A_p, B_p, out_p);
two_s_converter tw0(out_t, out_p);//same as "assign out_t = ~out_p + 1'b1;"
mux mx0(Out, abx, out_t, out_p);//same as "assign Out = (ab_x == 1'b1)? out_t : out_p;"
//notice the special case -8*...
endmodule
```
## Grading policy
- 有交有圖片有描述 (40%)
- 對 0000~1110 各功能有詳述 (說明 design 如何根據限制實作...等) (45%)
- Other detail or stuff we like. (15%)
- Bonus 實作詳述(4%)
## Q&A
**Q:** `module module_name(.A(...), .B(...), ...);`當中`.A(...)`有無指定差別為何?
**A:** 主要是關於接進去的線路有沒有順序上的安排,例如:
```verilog=
module M(a, b, c);
//...
endmodule
module M_another(...);
wire a, b, c;
M m_0(a, b, c);
M m_1(.b(b), .a(a), .c(c));
module
```
---
**Q:** 如果我們寫的 verilog 就能執行並且解決許多問題,那為什麼要用我們寫完的 code 再合成電路用 synthesis 去解決問題勒?感覺很多此一舉。
**A:** verilog 的 code 當中包含我們所常用的三種描述方式,但是如果真的要被實際製造出來,就必須回歸 gate level 的形式,而 synthesis 的過程就是幫助我們把其他兩種的電路描述法轉成 gate level。或者反過來說,如果執行 verilog 只是要跑 simulation,倒可以直接寫 C,應該容易許多。
---
**Q:** if-else 的條件敘述一定要在 always blocks裡?module 可以放在 always blocks 裡面?
**A:** if-else 的條件敘述一定要在 always blocks 裡。module 不可以放在 always blocks 裡。以上都是 verilog 的語法。再次強調接自建 module 的方式:
```verilog=
module M(a, b, c);
//c is output port, and a, b are input ports.
//...
endmodule
module M_another(...);
wire a, b, c;
reg R0;
//...
M m_0(a, b, c);
always@(*)begin
if(...) R0=c;
else R0=1'b0;
end
module
```
---
**Q:** 用 always 還是 assign?
**A:** 請參考[型別介紹](https://hackmd.io/s/HkErL41NN)中 reg 比較。
---
**Q:** 因為 verilog 是硬體敘述語言,有時候我的寫法會有點「偏軟體」,比如用了 for 迴圈,那想請問這樣不好的原因是什麼呢?
**A:** 因為 synthesis 的時候會無法合成出 for 迴圈的電路。如果要模擬 for 迴圈的行為,可以在 sequential 電路中實現。(TB 中可以跑 for 是因為沒有要合成,只是用來驗證電路的正確性。)
---
**Q:** 在主要的 module AM 中呼叫我自己寫的module 時,在 output port 中使用的型別是wire,但在有些module 中使用Behavior Modeling 時,會將output 的變數設定為reg,但這樣不會有型別上的衝突嗎?
**A:** 不會喔~這邊舉個例子:
```verilog=
module M(a, b, c);
input a, b;
output reg c;
always@(*)begin //same as "assign c=a+b;".
c=a+b; //
end //
endmodule
module M_another(...);
//...
wire a, b, c;
M m_0(a, b, c); //OK~ no conflict.
module
```
# [HOMEPAGE](https://hackmd.io/s/HJdaLPTQV)