# Lab4 Review [TOC] ## Design Description 這次的 lab 要來實作 Matrix Operations。輸出的結果是 **Y = AX + B**。當中**A(n\*r)**、**X(r\*n)**、**B(n\*n)**。帶大家快速瀏覽一下的 opcode: | opcode | function | i,j | in_data | out_data | | ------ | -------- | ---------- | ------------ | ---------- | | 000 | get n | don't care | n | don't care | | 001 | get r | don't care | r | don't care | | 010 | read A | i,j | A[i][j] | don't care | | 011 | read X | i,j | X[i][j] | don't care | | 100 | read B | i,j | B[i][j] | don't care | | 101 | write Y | don't care | don't care | Y | ## Verilog Code 相信大家經過 Lab3、Lab4 的洗禮後對 sequential circuit design 的流程相當熟悉了:)以下會有助教 verilog 的寫法,供各位參考。 ### 分析 一切由簡而繁,從最抽象的層面開始做起,再去修整細部。 (一) **State** : 在思考 sequential circuit 的設計時,可以先從 state 的設計想起,也就是說,先將大目標切成小目標再一一擊破。而在這次的 Lab 當中,仔細的人應該有發現,opcode 的設計與 state transition 有很大的相關,因此就先將 STG 直接設定為 opcode。STG 如下圖: | ![](https://i.imgur.com/yNzLR3k.png) | | -------- | :point_right:這樣的好處是:**state 直接作為 opcode 輸出,不須另外判斷**。 (二) **Variables** : 分清楚哪些是需要記憶性的電路,那些只是 combinational 的操作。需要 DFF 的給予兩個變數(e.g. next_state、state),其餘則視功能而定。 ```verilog= reg [2:0] state, next_state; wire [2:0] next_state_DFF; reg [9:0] n, next_n, r, next_r;// size wire [9:0] next_n_DFF, next_r_DFF; reg [9:0] row, next_row, col, next_col;// position wire [9:0] next_row_DFF, next_col_DFF; reg [9:0] col_counter, next_col_counter;// i, j wire [9:0] next_col_counter_DFF; reg [19:0] sum, next_sum; wire [19:0] next_sum_DFF; reg [9:0] A_buf, next_A_buf; wire [19:0] next_A_buf_DFF; wire [19:0]p;//for the multiplication ``` (三) **Code** : 首先是根據不同 state 做不同輸出的變化。 ``` verilog= //Initialization //assign next_state_DFF = (reset == 1'b0)? ... : ...; //... //Non-blocking Assinment //DFF #(...) dff_0(...); //... always@(*)begin case(state) `GET_N: begin next_state = `GET_R; next_n = in_data; next_r = r; next_row = 10'd0; next_col = 10'd0; next_sum = 20'd0; next_A_buf = 10'd0; next_col_counter = 10'd0; end `GET_R: begin next_state = `READ_A; next_n = n; next_r = in_data; next_row = 10'd0; next_col = 10'd0; next_sum = 20'd0; next_A_buf = 10'd0; next_col_counter = 10'd0; end `READ_A: begin // n, r already next_state = `READ_X; next_n = n; next_r = r; next_row = row; next_col = col; next_sum = sum; next_A_buf = in_data; next_col_counter = col_counter; end `READ_X: begin // A already next_state = (col_counter == r-1)? `READ_B : `READ_A; next_n = n; next_r = r; next_row = row; next_col = col; next_sum = sum + p; next_A_buf = 10'd0; next_col_counter = (col_counter == r-1)? 10'd0 : col_counter + 1'b1; end `READ_B: begin // sum already next_state = `WRITE_Y; next_n = n; next_r = r; next_row = row; next_col = col; next_sum = sum + in_data; next_A_buf = 10'd0; next_col_counter = 10'd0; end `WRITE_Y: begin // sum already next_state = `READ_A; next_n = n; next_r = r; next_row = (col == n-1)? row + 1'b1 : row; next_col = (col == n-1)? 10'd0 : col + 1'b1; next_sum = 20'd0; next_A_buf = 10'd0; next_col_counter = 10'd0; end default: begin // don't care... next_state = `GET_N; next_n = n; next_r = r; next_row = 10'd0; next_col = 10'd0; next_sum = 20'd0; next_A_buf = 10'd0; next_col_counter = 10'd0; end endcase end ``` A、X 相乘: ```verilog= assign p = A_buf*in_data; ``` 再來就是 row、col index 的計算: ```verilog= always@(*)begin case(state) `READ_A: begin i = row; j = col_counter; end `READ_X: begin i = col_counter; j = col; end `READ_B: begin i = row; j = col; end `WRITE_Y: begin i = row; j = col; end default: begin i = 10'd0; j = 10'd0; end endcase end ``` 最後是把值 assign 給 output: ```verilog= assign opcode = state; assign out_data = sum; assign fin = (state == `READ_A && row == n)? 1'b1 : 1'b0; ``` :point_right:兩個 always blocks,結束這回合。 ## Report Grading policy * block diagram (0~20分) * nWave result (5分) * 問題討論 (5分) * 詳細描述實作內容與想法 (0~45分) * Other detail or stuff we like. (0~10分) * STG (0~15分) ## Q&A *有好多人忘了寫 block diagram | STG 還有人上傳word檔* 找些比較crucial的問題列出來,有些問題的敘述太長了,會佔很多版面, 或我看不懂你在工三,可能要請同學直接來問助教喔,有些前面lab有回答過的問題就不做回應囉。 ### MO: :::info 1. 一開始我在燒電路時我沒有注意到tb裡的clock cycle為100,所以我就把cycle設成30,但是我make syn卻有過,想知道確切原因是什麼? ::: 根據我的理解,燒得出來就代表DV沒有檢查到甚麼”不合理的”線路,像是同學的cyc設錯對DV來說可能是無關緊要的錯誤,但如果是在實際make syn的時候就不一定會過,可能你這次過是運氣好。 --- :::info 2. 我一開始做的時候沒有設FIN這個state,判斷是否要將fin設為1的條件一樣是I == N && J == N,但若我這樣做tb會測不到Y[n][n]的值,會是x,想請問會這樣是因為我的out_data跟fin同時送出,但是因為fin是1所以就不會再去看out_data是多少? ::: 沒錯喔,同學你可以這樣理解,因為當fin==1時我們的TB會break出迴圈所以他自然不會做把值assign的工作,所以你的Y[n][n]就會是x --- :::info 3. 請問助教,這一次的LAB是否不建議使用二維陣列來儲存矩陣資料?我在合成電路的時候跑模擬會出現Timing Violation和錯誤的答案,但是單純跑make sim的時候一切正常,全部的答案都是對的 ::: 不管有沒有用二維陣列都一樣喔,應該就是單純有些地方你coding style寫得不好。 --- :::info 4. 這次我遇到第一個最大的問題就是我不知道in_data進不來。如下圖,i、j、opcode在1500之後明明都有值,但in_data卻始終沒有進來。這個問題困擾我好幾天,問同學同學也沒想法,不知道為甚麼會這樣。最後我猜可能是我打了太多無意義的東西讓系統爆掉,所以選擇重新打一次,結果還真的莫名其妙in_data就有值了,所以我到現在還是不知道為甚麼會這樣,求解答。 ::: Opcode==0時TB會給你n的值,至於為甚麼你說的in_data會進不來,如果是根據你下面這張圖來解釋的話,那是因為我們TB會在negedge才會給值而你可以看到clk在你這整張圖沒有negedge的地方,所以in_data才沒有值在裡面。 ![](https://i.imgur.com/TmC1KAD.png) --- :::info 5. 除了讓y的結果一個一個output之外,有辦法將整個矩陣的結果算出來再一一輸出嗎? ::: 是可以做到的,但我們的TB設計是一個一個output喔,所以要follow助教給的spec來做。 --- :::info 6. 將所有的東西都放在同一個always block中,和分開放到不同的always block中為何會影響到時間?對於電路來說不都是同時發生的事情嗎? ::: 照理說就跟你描述的一樣是會同時發生的,但你沒有列出你碰到的例外狀況,所以我也沒辦法跟你解釋發生甚麼事,可以的話可以之後來找助教談: ) --- :::info 7. 這次的作業使我們可以用硬體表現軟體的功能,但我很好奇如果同樣的功能,例如 本次作業的計算 Y = AX + B,以軟體實作和以硬體實作會有什麼不同嗎?績效上是硬 體比較好還是軟體比較好? ::: 這個沒有定性解答喔,這牽扯到優化問題,之後你們會修到一門課是計算機結構,它裡面會講解到如何compiler是如何跟底層硬體做溝通的,而硬體也有牽涉到怎麼處理data hazard然後stall的問題,還有其他很多的學問,有興趣的話可以去修王廷基老師開的計結喔,人好教得細學的到很多東西。 --- :::info 8. 講到timing,我們在做lab的時候很常發⽣發⽣time violation的問題,這是因為在⼀個cycle內,因為coding style,導致輸出延遲的關係嗎︖⼀般來說⼀個電路跑 越快應該是越好,那上次考試範圍的hold time violation 和set time violation為什麼需要持續夠久的時 間︖這是什麼意思啊︖ ::: 是的,這是coding style的問題,所以可以的話盡量在一個cyc裡面做越少事越好。為甚麼會有hold time violation 和set time violation?因為在real-world中沒有一個DFF是完美生產出來的,意思就是說DFF的clk有可能會早一點trigger或晚一點,所以這時就需要有個容錯機制,即hold time violation 和set time violation,如果DFF會早trigger那我們就要將signal提早ready這樣DFF才不會”寫”到錯誤的signal,而如果較晚trigger,那麼你要”讀”到正確的資料,signal一樣要穩定住一定時間。 --- :::info 9. 同樣也是網路上找到的資料,如果同樣⼀件事⽤ case,就像⽤⼀個mux⼀樣,速度會比⽤if else判斷來 得快,這是否就是其中⼀種coding style導致time violation的緣故︖所以改善coding style除了穩定電路的 訊號,也可以改善電路的傳輸速度,這樣的說法正確 嗎︖ ::: 你的想法基本上沒有錯喔,if-else更慢是因為他的判斷是有順序性的如下圖,這是if else邏輯上長的樣子,而case基本上是只有一個mux組成 ![](https://i.imgur.com/jfguV2s.png) --- :::info 10. 矩陣裡的index的值是tb裡就設定好,只要控制適當ij就可以得到對應的值嗎?還是tb中並無設定,in_data會照順序輸入? ::: TB會根據opcode和i j(even n和r)做輸入所以建議會看懂TB來做的話會比較懂為甚麼in_data的規則是怎樣運作的喔。 --- :::info 11. 在default中給定的值,是所有state中沒賦值的reg都會被設為default中的值還是要在state都不符合時才會進入default?(假設我在default中設定next_a1=3’b111,在state_b中沒有寫出next_a1,那在state_b中,next_a1會等於111嗎?) ::: 你提到的兩種狀況都會。 --- :::info 12. verilog 和軟體語⾔言不同,變數不能同時在等號的兩兩邊,但為什什麼這種寫法不但在complie 時不會出錯,在synthesis 時,也不會燒不出電路路呢? ::: 這其實是verilog奇妙的地方,沒錯,一般來說變數不能同時在等號的兩遍 BUT,在DFF裡面則是例外(不過還是建議同學目前先不要用這種寫法),假如你要做一個counter的話有一種寫法是這樣寫的,這種寫法是OK的reg [10:0]cnt; always@(posedge clk)begin cnt<=cnt+1; end 不過我們一開始會叫同學不要這樣寫是怕你們會搞混硬體描述語言的核心精神,等同學們弄熟後也是可以這樣寫的: )。 --- :::info 13. 如果在合成電路後,若是有儲存MO_syn.v檔,但忘記儲存sdf檔或report就關閉GUI介面,那再次開啟合成電路介面時,一定要再重新合成一次才能存檔嗎?還是有其他更快的方法? ::: 是的,保險起見,還是要全部重用一次。 --- :::info 14. 請問助教DFF 的使用時機,問過很多人 大家使用DFF 的數量都不同,什麼時候可以不需要DFF? ::: DFF是為了儲存signal用的,所以最簡單的就是如果你該回合的signal要留到下回合的話,就需要DFF,另一種就是確保你的signal不會隨意跳動,這次lab也看到很多同學結果跑錯就是這個問題,因為DFF只會在posedge(或negedge) trigger,所以你會有整整一個cycle的時間來做你要做的事,如果只是單純的assign或事放在always@*裡面很容易信號就跑掉了。 --- :::info 15. 在用dv叫出初步的.v檔時,在Workstation介面上有時會看到 reg會發生latch的情形,想問到底latch是什麼? ::: 產生Latch最主要的原因是沒有把所有條件寫乾淨,這也是為甚麼助教一直強調要把default寫好寫滿。 --- :::info 16. 為了避免SYN後發生錯誤,我幾乎每個參數都用DFF避免跑掉,請問這是正確的作法嗎,如果不是,可以怎麼避免上面提到的變成ZZZ的問題呢? ::: 基本上就跟同學說的一樣,如果怕訊號跑掉的話,用DFF是個好選擇。 --- :::info 17. 在nWave的時候,會出現X與Z的時機為何?多是訊號還進入所以顯示don’t care和線路沒接好出現高阻抗,想問這兩種有實際應用效益嗎? ::: 時機就是當你verilog描述得不好時,有些線路沒有你預想的那樣接起來這是其中一種狀況。第二個問題,我在猜你是不是想問這裡的x(don’t care)跟學期初教的卡諾圖的關係,我可以說在語意上是一樣的(x就代表他可能是0或是1),但在應用上是迥異的,卡諾圖上的x是在design之後我們”刻意”要簡化電路才會有那個x,但如果是在design完的implement出現x那就是你verilog的寫法有問題,這兩個出現的時機在本質上是有差異的。 --- ### SA: :::info 1. 很明顯我可以不用再設state,直接做就好了。不過因為在前面為了訊號穩定我也會先將register的值設成先前的值(如下圖),這樣我可以直接把我在case內的做的事搬到case外面做嗎? ::: 我們這次原意就是要遵循systolic的核心原則,簡單快速,沒有要你們做FSM,直接單純用簡單(大量)的線路在短時間內完成task,所以回答你的問題,是,可以這樣做 --- :::info 2. Wired to nowhere ![](https://i.imgur.com/60e11PP.png) As above: the void wire is to wire to nowhere. Is there a better way to do it? ::: 我個人非常不建議你這樣做,原因在於我們是硬體描述語言,你這樣寫給我的感覺會變成下面這張圖,所以是我的話會把每條線分開instantiate (ex:void_1, void_2, void_3, etc.) ![](https://i.imgur.com/ZBNyhyr.png) --- :::info 3. Undeclared wire but goes well. In my circuit, if there is some undeclared wire, but I get through with it? Why? As an example. PE pe(clk, reset, a1, b1, a11, b11, c1); However, a11 and b11 is not declared. ::: 我不確定你的get through是DV get through還是make syn get through if(DV get through) 那是因為你直接用沒有宣告的wire的話,系統會自動把他默認為1 bit的wire所以不會有error的問題 else if(make syn get through) 那我只能說媽祖保佑,因為只有1 bit的線路是不可能做任何算術運算的,除非你的PE是在邊緣不會用到a11 b11。 :::info 4. 雖然systolic array好像很好用。但是如果只要做矩陣乘法的話,以這樣的方式給資料,我覺得沒有比較省時。用以下的方式給資料應該會快很多。 ![](https://i.imgur.com/kAIs3Q5.png) ::: 你可以去看我教材中的影片,如果照你這樣排的話,data在propagate的過程會配到錯誤的值。 --- --- # [HOMEPAGE](https://hackmd.io/s/HJdaLPTQV)