# 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 請參考附圖拼組: |![](https://i.imgur.com/Dvf0w8s.gif)| | --- | (圖片來源: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)