# NYCU ICLAB POV-2 ## 前言 本周的課程正式進入到循序電路(Sequntial Circuit)的環節。當時大一的時候教授用了"在電路中加入時間觀念"這樣的方式帶過 Sequential Circuit,不過我認為這種說法有點太過抽象,所以我想用我自己的講法給出我對 Sequnential Circuit 的理解。 另外在更新文章之餘,我也會和同學慢慢整理lab的完整檔案還有各家好手的程式碼,如果有時間也可能針對design進行講解,還請期待~ ## Lecture 02 --- Finite State Machine ### 電路中的儲存元件 基礎邏輯設計都有介紹到電路當中的儲存結構,如 Latch、Flip-Flop等,基本上都是透過穩定的**回授電路**實現"儲存"這樣的行為,或者加上一些其他的控制機關做出"Set"、"Reset"等動作,當然這些都是維持通電才能保持狀態的元件,和真正生活上會用來"儲存"資料 USB / SSD 的物理原理有很大的不同。 但我認為最重要的概念是:為什麼我們需要這種暫時儲存資料的單位? 思考一下,其實任何電路功能都可以用組合邏輯的方式就可以實現,比方說做 10 個數字的加減,大可以製作 9 個加法器並提供 10 個接口給那些數字的來源,但這樣會消耗很可觀的元件數目對吧?一個比較合理的電路,應該只會有**有限的輸入輸出 pin 腳**,這樣就不可能只使用組合邏輯來實現功能了,因為組合邏輯總會在輸入改變時做出改變,一旦輸入電位不穩定,輸出電位也不會穩定。這是什麼意思?以上述例子來說,組合邏輯**永遠只能輸出 10 個數字的總和**,但循序電路卻可以在**某段時間輸出 1+2、某段時間輸出 1+2+3** ......甚至循序電路也不需要一次接收 10 個數字那麼多輸入,**可以一段時間給它 1、再下一段時間給它 2**...... ### 有限狀態機與資料通路(Finite State Machine & Datapath) 循序電路其實就是在原本的組合邏輯中加入可以"**儲存**"及"**維持一段時間**"的元件,作為其內部組合邏輯"**隱藏**"**的額外輸入**。我們可以把所有的儲存元件抓出來,它們的數量必定是有限的($n$),所以產生的 0 / 1 組合必定是有限的($2^n$),我們可以把每一個 0 / 1 組合稱為一個**狀態(State)**,由於狀態機的下一個狀態是將現在的狀態經過組合邏輯產生,所以**同樣的狀態必定會產生相同的下個狀態**(組合邏輯特性),這就是狀態機之所以有限的原因。資料通路(Datapath)就是指那些組合邏輯的部分。 講了很多抽象的東西,總結起來就是 **1. 所有儲存單位的值代表著一個 State 2. 由 State 和輸入經過組合邏輯後會產生新的 State 和輸出 3. 儲存單位在某些時刻會把新的 State 更新到自身內部(回到1)** 第 3 點中的**"某些時刻"**,其實就是驅動 Latch 和 Flip-Flop 的 clk 訊號 ### 呼叫時序元件 上一篇筆記當中也有大概提到怎麼呼叫時序元件了,以下直接給出範例程式碼 ```verilog= //calling edge triggered Flip-Flop reg d,e; always@(posedge clk)begin d <= e; end //calling edge triggered asynchronus reset Flip-Flop reg d,rst; always@(posedge clk,negedge rst)begin if(!rst) d<=0; else d<=e; end ``` 這兩種寫法是在合成階段唯二能被 tool 辨識的 Flip-Flop 寫法,其他如同時被 poedge clk 和 negedge clk 觸發的 Flip-Flop、使用initial block 做初始化的 register 等通通都是旁門左道不可合成。 那為什麼我們需要外接訊號來做 asychronus reset 呢?不是因為開機後Flip-Flop 的初始值是 X(Unknown),而是因為實際上電路在運作的**任何狀態都有可能被 reset**,所以我們不能依賴 reset 訊號來之前的 Flip-Flop 狀態來重啟電路,才把它們設為 X(任何值都要能夠被 reset)。 ### Set up & Hold 在剛學習到 Flip-Flop 的特性時,我曾經有這樣子的疑問,為什麼使用 clk 控制所有 Flip-Flop 時,**某顆 Flip-Flop 在 edge 上切換數值時不會因此剛好擾亂下一顆連接的 Flip-Flop呢**(意即資料穿透好幾層Flip-Flop)? 答案是:有可能發生,有可能不會發生,而 tool 會想辦法幫助你讓這種情況不會發生。 電路的訊號傳播需要一定時間的延遲,從輸入穩定到輸出穩定需要時間,反過來說從輸入不穩定到輸出不穩定也需要時間,對於 Flip-Flop 來說,需要輸入在時鐘觸發緣前後一定時間內保持穩定,所以只要滿足兩個原則就可以保證時序電路正常運作。 ::: info clk edge 來臨前,任何 FF 的輸入已經穩定下來 **(Setup time check)** clk edge 來臨後,任何上一級 FF 改變造成的不穩定輸出不會太快影響到本級 FF 保持穩定 **(Hold time check)** ::: 抓住這樣的概念跟靜態時序分析當中一些名詞的解釋,就能輕鬆搞清楚~~邏設/計組/VLSI/數電/ICLAB各學過一次的~~ **Setup time** 和 **Hold time** 是什麼意思囉! ~~同樣學了很多次的東西們:~~ 1. ~~捲積@訊號與系統/DSP/機率/微方/數電/機器學習/深度學習/ICLAB/影像處理~~ 2. ~~Laplace & Fourier Transform@訊號與系統/DSP/微方/控制~~ **與 Flip-Flop 間的組合邏輯有關** $T_{pd}$:表示一個邏輯區塊輸入穩定後,達到輸出穩定所需的時間(propagation delay) $T_{cd}$:表示一個邏輯區塊輸入不穩後,達到輸出不穩定的時間。(contamination delay) **與 Flip-Flop 有關** $T_{setup}$:clk edge 來臨前,FF 要求輸入達到穩態的時間 $T_{hold}$:clk edge 來臨後,FF 要求輸入維持穩定的時間 $T_{pcq}$:clk edge 來臨後,FF 輸出達到穩定的時間(clk to q propagation delay) $T_{ccq}$:clk edge 來臨後,FF 輸出開始不穩的時間(clk to q contamination delay) **與時鐘有關** $\Delta t_{skew}$:不同的 FF 接收的時鐘訊號**不一定完全同步**,這個差異(可正可負)就是用這一項表示。 $T_{C}$:Cycle time,表示兩個正緣之間的時間長度。 ![](https://hackmd.io/_uploads/B1V8Trtf6.png =80%x) ![](https://hackmd.io/_uploads/BJFhsBYM6.png =125%x) 有了範例電路和說明圖片,我們就可以來仔細看看常見的 Setup 和 Hold 的公式代表什麼意思了。 $$ T_C \ge T_{pcq}+T_{pd}+T_{setup}+\Delta t_{skew} $$ **從 clk edge 出發、FF1 輸出穩定($T_{pcq}$)(COMB1輸入穩定)、到 COMB1 輸出穩定前($T_{pd}$),必須要在下個 clk edge 前的 Setup time 達到穩定** $$ T_{ccq}+T_{cd} - \Delta t_{skew} \ge T_{hold} $$ **從 clk edge 出發、FF1 輸出不穩($T_{ccq}$)(COMB1輸入不穩)、到 COMB1 輸出不穩前($T_{cd}$),必須撐過 FF2 的 Hold time,避免干擾到 FF2。** 現在我們可以詳細地回答開頭的提問了 ::: info Q:某顆 Flip-Flop 在 edge 上切換數值時不會因此剛好擾亂下一顆連接的 Flip-Flop呢? A:edge 切換後,保持在穩態的 Flip-Flop 輸入需要經過一汙染延遲(Contamination Delay)時間才會變得不穩,只要該 Flip-Flop 能在 Hold time 內不受干擾,就不用擔心此問題。 ::: 在這邊也補充一下到了實體布局和繞線階段 tool 會怎麼修 setup 和 hold violation。Setup 可以透過減少共用邏輯、選用高驅動力的元件、複製多個相同邏輯提高驅動力、或甚至做 retiming 來調整 pipeline 架構。Hold 則可以透過加入 buffer 增加延遲,或刻意讓 clk 產生足夠的 skew 保證不會有問題。 **ref:** https://www.edn.com/ways-to-solve-the-setup-and-hold-time-violation-in-digital-logic/ ### for & generate 有撰寫過 C++ 程式碼的讀者想必已經用```for```用到快要爛掉了,~~因為確實好用~~。```for```的行為在模擬階段和在 C++ 裡面時很像,但合成階段會有很大的不同:**```for```會被 Desgin Compiler 依序一行一行展開**,例如。 ```verilog= integer i; always @(posedge clk)begin for(i = 0;i<4;i=i+1)begin image_reg[i]<=image_in[i]; end end /* The following code are the format analyzed by design copiler always @(posedge clk)begin image_reg[0]<=image_in[0]; image_reg[1]<=image_in[1]; image_reg[2]<=image_in[2]; image_reg[3]<=image_in[3]; image_reg[4]<=image_in[4]; end */ ``` 拜這項性質所賜,```for``` 多了非常多神奇的限制,我自己有遇過的就有 1. 不要在不同```always```內的```for```共用 integer index,有可能產生 race condition。 2. 不能使用 integer index 存取一個多 bits 變量的部分 bits ```verilog= reg [2:0]a; reg [2:0]b_reg[0:7],b_in[0:7]; integer i; always @(posedge clk)begin for(i=0;i<8;i=i+1)begin b_reg[i] <= b_in[i][i:0];//[i:0] is illegal end end ``` 3. 只能使用可確定的數字當作中止條件,不然合成會失敗 ```generate```則是另一項特殊的關鍵字,可以用來一次描述多個相同結構的電路。例如現在有一系列的```input[i]```和```output[i]```需要用多個相同的```example_module```連接,這時就可以讓```generate```登場了。 ```verilog= wire [63:0]in[0:31],out[0:31]; genvar i; //only genvar can iterate through generate generate for(i = 0;i<=31;i = i+1)begin //genvar must be step-incremental with constant upperbound example_module em(.in(in[i]),.out(out[i])); end endgenerate ``` 在 generate block 會使用```assign/always/module``` 等關鍵字,複製多個相同的電路;另外 generate block 內迭代的變數必須是 ```genvar``` 型別,只能遞增且須有常數上界;generate block 也可以有```generate if```和```generate case```等變形,這些就不多做說明了。 ### Sample Code 以下我提供實際存在我作業內的一些代碼,說明一下我做 Flip-Flop 給值時會用的 Coding Style。 1. **always @(posedge clk) 只做給值**->用在複雜的 FSM 或資料暫存器上 ```verilog= //decribing the DFF of FSM, only do assignment always @(posedge clk,negedge rst_n) begin if(!rst_n) cur_state<=0; else cur_state<=next_state; end //decribing the Combinational part of FSM parameter IDLE='d0, INPUT = 'd1, OPERATION = 'd2,OUTPUT = 'd3; always @(*) begin case (cur_state) IDLE: if(in_valid) next_state = INPUT; else next_state = IDLE; INPUT: if(in_valid) next_state = INPUT; else next_state = OUTPUT; OUTPUT:case (Mode) 'd0: begin next_state = (output_is_over)?IDLE:OUTPUT; end 'd1: next_state = IDLE; 'd2: next_state = IDLE; default: next_state = 0; endcase default: next_state = IDLE; endcase end ``` 2. **always @(posedge clk) 加入一些COMB語句**->通常用在描述規則簡單的 shift register 上 ```verilog= reg [31:0] Img_reg [13:0]; integer i; always @(posedge clk) begin //the assignment involves some combinational elemets Img_reg[0]<={32{in_valid}}&Img; for(i=1;i<=13;i = i+1) Img_reg[i]<=Img_reg[i-1]; end ``` ## Lab02 Cordinates Calculation ### Description 本周的 Lab 要撰寫一個座標處理器,依序給定一些座標點和選擇訊號 mode 後要計算圍成四邊形包含的整數座標點、面積、或其中兩點圍成的直線能不能與另外兩點決定的圓相切/交/割。Pin腳如下 :::success **Input** xi, yi (8bit 2補數) mode (2'b0->梯形渲染 2'b1圓與直線 2'b2算梯形面積) in_valid, rst_n, clk (基礎控制訊號,未來會不斷出現,rst_n出現在最開頭提供asynchronus active low reset,input 和 invalid 則會在 clk negedge 給值) **Output** out_valid (通知 pattern/下級電路本級完成的訊號) xo, yo (8bits 不同 mode 有不同含意) ::: in_valid 拉起後,Cycle 1會給 mode 訊號,Cycle 1~4 會依序給 4 組 8bit signed number 的座標點。依據 mode 的不同,需要對座標做不同處理。 ### Strategy 本次計算 Performance 的方式是 $Latency\times Area$,因為 Cycle time 被鎖定在 12ns,所以只要注意 in_valid 落下到 out_valid 升起的Cycle 數目就好。未來會有一些 lab 可以調整 Cycle time。 我自己通常會把 **Latency 的最佳化順序設定在最前面**,拿這次 Lab 當例子,從 1 cycle 開始,多一個 cycle performance 直接被翻倍計算。之後 開始設定 Cycle time時,雖然會有「多一個 Cycle 但換取 Cycle time 減少划不划算?」的問題,但不管怎樣,**拿面積換時間通常是個划算的交易**。 --- #### Mode 0 Mode0 保證 4 個座標點會形成一個矩形,我們則要想辦法判定矩形"**圍**"住哪些網格,並將這些網格的座標由左下角往右上輸出。 ![](https://hackmd.io/_uploads/B1Tdje9Mp.png =50%x) ###### 例圖中的橘色網格的左下角座標就是應該要輸出的座標點 邊界條件(例如壓線要不要輸出、最上面一排要不要輸出......)在這個 Mode 其實不是最重要的一環,重要的是怎麼計算每一列列首和列尾的座標點,接著讓輸出從列首累進到列尾,再切換到下個列首列尾就好了。 注意到 y 座標每 +1,x 就會位移一個斜率。斜率可由兩個 8 bits 數字相除得到,我們假設商是$Q$、餘是$R$、只考慮正斜率,從左下的網格迭代每一個行首的公式是。(負斜率也只需要稍作控制就可以用正數除法的結果迭代) $X_0 = X_{grid}$ $R_0 = 0$ $X_i = X_{i-1}+Q+Indicator((R{i-1}+R )>\Delta y)$ $R_i \equiv R_{i-1}+R \ (mod\ \ \Delta y)$ 值得注意的是,因為測資保證四個座標點都不同(不會退化成三角形),所以我可以在輸出列中間網格時->迭代行首;輸出走到行尾時->迭代行尾來,進一步共用除法器 ::: warning Tips: 除法元件通常會是電路中最關鍵的部分,面積大、$T_{pd}大$,非使用不可時請盡量嘗試共用或將它 pipeline ::: 最後,我在看合成訊息時發現到 compiler 幫我除法取餘各用了一個除法器,所以我就自己刻了一顆除法器,下方有範例程式碼。 #### Mode1 $a(x_0 \ y_0)$:決定直線的一個點 $b(x_1 \ y_1)$:決定直線的第二個點 $c(x_2 \ y_2)$:圓心 $d(x_3 \ y_3)$:圓周上某點 測資會稍微限制大小保證輸入在 6bits 有號數可表示的範圍內。若圓與直線相切則 yo 輸出 2,相割則輸出 1,不相交則輸出 0。 我自己有稍微先重新改寫公式,總之要知道直線是否與圓相切,等效於判斷以下兩個運算式的大小關係 $${ \left| \begin{array}{cccc} \vec{ac} \\ \vec{ab} \\ \end{array} \right|}^2 ?\ \left| \vec{ab} \right|^2 \left| \vec{cd} \right|^2 $$ c 到 ab 直線的距離可由平行四邊形面積除以ab線段長求得,把這個數值和圓半徑比較就能得到結果 ![](https://hackmd.io/_uploads/ryVfehqfT.png =90%x) 這張圖代表我們可以在不同的時間內利用相同的運算單元,橘色代表 4 個 6bits 數字先兩兩互乘再相加的單元,綠色則代表兩個 13bits 數字的乘法,至於為什麼是這樣設計,就留給讀者自行解讀 #### Mode2 Mode2 保證四個座標點是一個四邊形座標點逆時鐘或順時鐘排序後的結果,所以我們可以利用 Surveyor 公式求面積: $$ Area =|{1\over 2}( \left| \begin{array}{cccc} x_0 & x_1 \\ y_0 & y_1 \\ \end{array} \right| + \left| \begin{array}{cccc} x_1 & x_2 \\ y_1 & y_2 \\ \end{array} \right| + \left| \begin{array}{cccc} x_2 & x_3 \\ y_2 & y_3 \\ \end{array} \right| + \left| \begin{array}{cccc} x_3 & x_0 \\ y_3 & y_0 \\ \end{array} \right| )| $$ ![](https://hackmd.io/_uploads/By5bX3czp.png =90%x) 同樣地,我們可以利用累加器和一個行列式單元來實現 mode2。 最後,將輸入同時送到 3 個 mode 的輸入端,最後再用一個控制訊號接出來到 out 就可以完成了 ### Performance $Area$:79.5k $um^2$ $Latency$:1 $cycle/pattern$ $Ranking$:4/134