FPGA经验分享——时序收敛之路
首先感謝 coyoo 博主一直以來在 EDN 上分享他的經(jīng)驗(yàn),也感謝他這次慷慨拿出新作與我們分享。
非常希望能夠拜讀?coyoo 博主的大作,尤其希望對虛擬 JTAG 技術(shù)有一個深入的了解。 這里分享一下之前自己優(yōu)化設(shè)計時序時的經(jīng)驗(yàn)總結(jié)。相同的內(nèi)容之前已經(jīng)發(fā)在自己的 EDN 博客中了,不算違規(guī)吧? 內(nèi)容分為五個部分(其實(shí)只有三個啦)。- PART1 引子
- PART2 ISE 綜合選項(xiàng)設(shè)置
- PART3 代碼風(fēng)格
- PART4 高速電路的設(shè)計方法
- PART5 結(jié)語
布局布線以后,靜態(tài)時序分析的結(jié)果和自己的預(yù)期相差很遠(yuǎn),和綜合后XST的估值也相差很遠(yuǎn)。時延里面,route時延占了絕大部分(logic占20%,route占80%)。
惡補(bǔ)了一些資料,給自己的設(shè)計總結(jié)了三個可能的問題:有些控制信號的扇出太大,沒有做位置約束,不好的代碼風(fēng)格。
決定在這里記錄自己通向時序收斂的過程。這不是一條平坦的路吧。
PART2 綜合選項(xiàng)設(shè)置?
這里想說一下我對綜合選項(xiàng)的設(shè)置。設(shè)置的依據(jù)是ISE的幫助,網(wǎng)上的資料,以及自己的理解。請大家能為我指正設(shè)置得不合理的地方。沒有提及的選項(xiàng)采用缺省設(shè)置。采用的EDA軟件是ISE 13.2,綜合器為XST。
【Synthesis Options】
Use Synthesis Constraints File & Synthesis Constraints File:
一般來說,會在implement之前,采用UCF文件對設(shè)計進(jìn)行時序約束。實(shí)際上,在synthesis之前,可以先采用XCF文件對設(shè)計進(jìn)行時序約束,以使XST針對時序約束進(jìn)行synthesis,在synthesis時產(chǎn)生更好的網(wǎng)表。按照網(wǎng)上的說法,XCF中的時序約束應(yīng)當(dāng)要比實(shí)際需求更緊一些。
XCF文件的實(shí)際效果嘛。我談一下自己使用XST的情況吧。XST完成綜合(synthesis)后,會產(chǎn)生一個估計的最大工作頻率。某一次綜合后,我使用XCF文件將最大工作頻率約束的比XST的估值大一些,再重新進(jìn)行綜合后,XST給出的估值確實(shí)增大了少許。大部分時候,在重新綜合后是看不到效果的。至于對最終結(jié)果的影響,就不得而知了。
XCF文件的語法與UCF文件完全一致。與UCF不同,ISE沒有為XCF提供編輯的工具,只能自己用文本編輯器編輯。
Keep Hierarchy:
這個選項(xiàng)是設(shè)置是否在synthesis與implement中打破設(shè)計的層次結(jié)構(gòu)。選項(xiàng)【yes】和【no】很容易理解。選項(xiàng)【soft】的意思則是在綜合時不打破層次結(jié)構(gòu),而在之后打破層次結(jié)構(gòu)。
個人理解。打破層次結(jié)構(gòu)后,有些信號就變了,不利于分析與約束。而打破層次結(jié)構(gòu),更有利于電路的優(yōu)化。
【HDL Options】
FSM Encoding Algorithm:
有限狀態(tài)機(jī)的編碼方式。我采用了【One-Hot】(獨(dú)熱碼)。其優(yōu)缺點(diǎn)相信大家都非常清楚,不再贅述了。
Case Implementation Style:
case語句的實(shí)現(xiàn)方式。使用verilog時,缺省狀態(tài)下XST不會把case語句綜合成你想象的結(jié)構(gòu)。這點(diǎn),大家可以自己寫一段簡單的代碼試試。
以一個獨(dú)熱碼狀態(tài)機(jī)為例:
?
| 以下是代碼片段: reg [2:0] sta; case(1'b1) sta[2]: ...; sta[1]: ...; sta[0]: ...; endcase ? |
?
首先,綜合出來的電路不止判斷一個比特。XST不知道sta只會出現(xiàn)3'b100、3'b010、3'b001三種可能,它會把諸如3'b101這樣的狀態(tài)也考慮在內(nèi),大概把電路綜合成如下的樣子:
?
| 以下是代碼片段: reg [2:0] sta; case (sta) 3'b001: ...; 3'b010: ...; 3'b100: ...; default: ...; endcase ? |
這樣,采用獨(dú)熱碼似乎沒有什么意義。而且因?yàn)椴捎锚?dú)熱碼時,sta的比特數(shù)比采用格雷碼時更多,復(fù)雜度反而還增加了。
另外,XST也可能沒有把case語句轉(zhuǎn)換為并行結(jié)構(gòu),而是有優(yōu)先級的結(jié)構(gòu)。
【Case Implementation Style】中有三個選項(xiàng):【Full】、【Parallel】與【Full-Parallel】。其中,【Full】針對上述的第一點(diǎn),向XST說明有些狀態(tài)是不可能出現(xiàn)的,讓XST不要考慮太多;【Parallel】讓XST將case語句綜合為并行的電路結(jié)構(gòu);【Full-Parallel】則是兩者的結(jié)合。
對于狀態(tài)機(jī),這項(xiàng)設(shè)置的影響很大。器件為xc6vlx240t-1ff1156時,同樣代碼的8狀態(tài)獨(dú)熱碼狀態(tài)機(jī),缺省設(shè)置時占用11個寄存器、6個查找表,只能工作在575 MHz時鐘頻率下;改為【Full-Parallel】設(shè)置后,占用11個寄存器、3個查找表,可以工作在900 MHz時鐘頻率下。
【Xilinx Sepcific Options】
Max Fanout &?Register Duplication: 寄存器的最大扇出。扇出是一個門需要驅(qū)動的門數(shù)目。如果一個門的扇出很大,那么它的輸出布線將非常擁塞,布線的時延可能就會很長。在時序報告中,如果看到邏輯一條路徑的邏輯時延很短,但布線時延很長,很可能的原因就是信號的扇出太大。這樣,即使設(shè)計時保證了此處的組合邏輯很簡單,也無法減少時延。 一個有效的方法是寄存器復(fù)制。也就是說,像下圖一樣,為大扇出的門加入一些副本,讓這些完全相同的門來分擔(dān)扇出。寄存器復(fù)制可以通過代碼來實(shí)現(xiàn),不過顯然,這樣做是非常麻煩的。而【Max Fanout】這個選項(xiàng),能夠使XST自動實(shí)現(xiàn)寄存器復(fù)制。在綜合時,通過這個選項(xiàng)為所有寄存器都加上扇出的限制,一旦寄存器的扇出大于設(shè)置的值,XST會自動進(jìn)行寄存器復(fù)制。在我的設(shè)計中,將最大輸出設(shè)為20。 不過這么做沒法解決所有的問題。一方面,XST只能做寄存器復(fù)制,對于扇出大的組合邏輯,就沒有辦法了;另一方面,XST也無法跨越模塊進(jìn)行優(yōu)化。對于第一個問題,可以對大扇出的組合邏輯一級寄存器緩沖一下,這樣就可以復(fù)制了。對于第二個問題,采用扁平化的設(shè)計是一種方法,不過這樣會為設(shè)計帶來很大的困難。另一種方法,是在模塊的邊界處加入寄存器,即對模塊的輸入輸出都進(jìn)行緩存。這是比較推薦的做法。 順便提一句,在【Map Properties】中,同樣有【Register Duplication】的選項(xiàng)。該選項(xiàng)是根據(jù)時序約束(而非對扇出的限制)來進(jìn)行寄存器復(fù)制。 Equivalent Register Removal: 把設(shè)計中等效的寄存器去掉,合并為一個。顯然,這個選項(xiàng)讓人感覺與【Register Duplication】是互斥的。但是,在設(shè)置時,這兩個選項(xiàng)是能夠同時勾選的。因?yàn)椴恢劳瑫r勾選的效果會是怎樣的,我把這個選項(xiàng)前面的復(fù)選框去掉了。 要點(diǎn)總結(jié)- case 語句未必被綜合為并行電路。需要并行電路的話,需要設(shè)置【HDL Options】中的【Case Implementation Style】。
- 配合【Max Fanout】與【Register Duplication】來進(jìn)行全局的寄存器復(fù)制,以減小扇出。
- 同步復(fù)位電路能夠被綜合為性能更好的基于硬核(塊RAM、DSP等)的電路,異步復(fù)位電路則不能。
- 綜合器能夠?qū)⑼綇?fù)位信號與其它信號放在一起優(yōu)化,而異步復(fù)位信號只是復(fù)位信號。
| 以下是代碼片段: ? ? ?reg [0:3] sta; ? ? ?always@(posedge clk) ? ? ?if (sta[0]) ...; ? ? ?end else if (sta[1]) ...; ? ? ?end else if (sta[2]) ...; ? ? ?end else if (sta[3]) ...; |
| 以下是代碼片段: ? ? ?reg [0:3] sta; ? ? ?always@(posedge clk) ? ? ?case(1'b1) ? ? ?sta[0]: ...; ? ? ?sta[1]: ...; ? ? ?sta[2]: ...; ? ? ?sta[3]: ...; ? ? ?endcase |
- 能不用復(fù)位就不用復(fù)位;不能的話,采用同步復(fù)位。
- 盡量不要使用多層嵌套的條件判斷語句。
- 一般情況下,需要用寄存器對模塊的輸入與輸出進(jìn)行緩存。
- PART4 高速電路的設(shè)計方法 在上一個部分中,說明了幾種自己總結(jié)的利于設(shè)計出高速電路的代碼風(fēng)格。這里其實(shí)暗含著下面這樣的場景:電路已經(jīng)設(shè)計好了,而且已經(jīng)很棒,只是需要用風(fēng)格良好的 HDL 代碼進(jìn)行輸入,以保證綜合出的電路和設(shè)計的一樣好。 而在這一部分中,想討論一下怎樣設(shè)計高速電路。可以說,是進(jìn)行 HDL 輸入前的工作。 最初的時候,是沒有這個部分的。原因是這部分并非是自己總結(jié)的內(nèi)容,更多還是從前輩們那兒吸取的經(jīng)驗(yàn)。不過,少了這個部分,感覺有點(diǎn)缺乏完整性。畢竟 PART3 和 PART4 的內(nèi)容,只是一些小技巧。而且我想了一下,將獲取的知識再整理整理,也沒有什么不好的。 因此,這部分內(nèi)容是我對 Steve Kilts 的《高級 FPGA 設(shè)計:結(jié)構(gòu)、實(shí)現(xiàn)和優(yōu)化》第 1 章 1.3 節(jié)(4 至 13 頁)的總結(jié)。讀過這本書的朋友可以跳過這個部分了。其中的例子大都是我自己歸納后想出的簡單例子,可能還有些不妥之處,還請各位朋友指出。 這里強(qiáng)烈推薦這本書。一般的 FPGA 書中,講流程、接口的多,講電路結(jié)構(gòu)的感覺比較少,而這本書涉及的還挺多的;而比起 Parhi 的《VLSI 數(shù)字信號處理系統(tǒng):設(shè)計與實(shí)現(xiàn)》這種比較純粹的 IC 設(shè)計的專著(當(dāng)然這本也很好),這本書又和 FPGA 有著非常緊密的聯(lián)系。 1 增加寄存器層次 通過在組合邏輯中插入一級寄存器來優(yōu)化關(guān)鍵路徑。 考慮下圖所示的三輸入加法器。此時電路的關(guān)鍵路徑為兩個加法器的門延時。
通過在兩個加法器之間引入一級寄存器,能夠?qū)㈥P(guān)鍵路徑縮短為一個加法器的門延時。當(dāng)然,這會帶來一個周期的時延,同時也引入了多余的寄存器,增加了電路面積。以下是代碼片段:
always @ (posedge clk) begin
? ? ?// 第一層
? ? ?a_d <= a;
? ? ?b_d <= b;
? ? ?c_d <= c;
? ? ?// 第二層
? ? ?s <= m+c_d;
end
assign m = a_d+b_d;
2 并行結(jié)構(gòu) 與通常所說的通過增加并行路數(shù)從而提高吞吐量的并行架構(gòu)不同,這里指的是通過重新組織組合邏輯的結(jié)構(gòu),來優(yōu)化關(guān)鍵路徑。 考慮一個鏈狀的四輸入加法器電路。其關(guān)鍵路徑為三個加法器的門延時。以下是代碼片段:
always @ (posedge clk) begin
? ? ? ? ? // 第一層
? ? ?a_d <= a;
? ? ?b_d <= b;
? ? ?c_d <= c;
? ? ?// 第二層
? ? ?c_2d <= c_d;
? ? ?m <= a_d+c_d;
? ? ?// 第三層
? ? ?s <= m+c_2d;
end
如果采用樹狀結(jié)構(gòu),則關(guān)鍵路徑僅為兩個加法器的門延時。以下是代碼片段:
assign m1 = a+b;
assign m2 = c+m1;
assign s ?= d+m2;
3 去除電路中的優(yōu)先級編碼 這點(diǎn)就是 PART3 中所說的“盡量不要使用多層嵌套的條件判斷語句”。 考慮一個四狀態(tài)的獨(dú)熱碼狀態(tài)機(jī),采用優(yōu)先級編碼的電路有四層組合邏輯。以下是代碼片段:
assign m1 = a+b;
assign m2 = c+d;
assign s ?= m1+m2;
而如果使用 case 語句,并在【綜合選項(xiàng)】中設(shè)置好對應(yīng)的項(xiàng)目(見 PART2),得到的非優(yōu)先級編碼電路則是并行結(jié)構(gòu),速度更快。以下是代碼片段:
? ? ?reg [0:3] sta;
? ? ?always@(posedge clk)
? ? ?if (sta[0]) ...;
? ? ?end else if (sta[1]) ...;
? ? ?end else if (sta[2]) ...;
? ? ?end else if (sta[3]) ...;
這里需要強(qiáng)調(diào)的是,如果電路的邏輯確實(shí)需要優(yōu)先級,還應(yīng)該使用嵌套的條件判斷語句,而不該盲目采用無優(yōu)先級的電路。 4 寄存器平衡(重定時) 電路的速度,是由關(guān)鍵路徑?jīng)Q定的。因此在設(shè)計電路時,如果有一條路徑的門延時特別長,其它路徑的門延時再短,該電路的速度也仍然很慢。因此,設(shè)計電路時,可以通過改變寄存器的位置,使得各路徑的門延時變得平均,以達(dá)到提高電路速度的目的。 考慮下面的五輸入加法器。可以看到,第一級寄存器和第二級寄存器之間的路徑長度是一個加法器的門延時,而第二級寄存器和第三級寄存器之間的關(guān)鍵路徑長度是三個加法器的門延時。因此,整個電路的關(guān)鍵路徑長度是三個加法器的門延時。以下是代碼片段:
? ? ?reg [0:3] sta;
? ? ?always@(posedge clk)
? ? ?case(1'b1)
? ? ?sta[0]: ...;
? ? ?sta[1]: ...;
? ? ?sta[2]: ...;
? ? ?sta[3]: ...;
? ? ?endcase
經(jīng)過寄存器平衡后,輸入輸出之間的邏輯關(guān)系沒有改變,但兩條路徑的長度都變?yōu)閮蓚€加法器的時延,整個電路的關(guān)鍵路徑長度縮短為兩個加法器的時延。以下是代碼片段:
always @ (posedge clk) begin
? ? ?// 第一層
? ? ?a_d <= a;
? ? ?b_d <= b;
? ? ?c_d <= c;
? ? ?d_d <= d;
? ? ?e_d <= e;
? ? ?// 第二層
? ? ?m1 <= a_d+b_d;
? ? ?c_2d <= c_d;
? ? ?d_2d <= d_d;
? ? ?e_2d <= e_d;
? ? ?// 第三層
? ? ?s <= m4;
end
assign m2 = m1+c_2d;
assign m3 = m2+d_2d;
assign m4 = m3+e_2d;
在《高級 FPGA 設(shè)計:結(jié)構(gòu)、實(shí)現(xiàn)和優(yōu)化》中,還有一種“重新安排路徑”的策略。對于這點(diǎn),可能我的理解還不夠深,感覺這一策略和上面第二種策略類似:都是通過重新組織組合邏輯的結(jié)構(gòu),以提升電路速度。此處就不對這種策略進(jìn)行詳細(xì)的說明了。 總結(jié)以下是代碼片段:
always @ (posedge clk) begin
? ? ?// 第一層
? ? ?a_d <= a;
? ? ?b_d <= b;
? ? ?c_d <= c;
? ? ?d_d <= d;
? ? ?e_d <= e;
? ? ?// 第二層
? ? ?m2 <= m1+c_d;
? ? ?d_2d <= d_d;
? ? ?e_2d <= e_d;
? ? ?// 第三層
? ? ?s <= m4;
end
assign m1 = a_d+b_d;
assign m3 = m2+d_2d;
assign m4 = m3+e_2d;- 最基本的策略:在組合邏輯中插入寄存器,通過引入時延與增大電路面積來提升電路速度。
- 組合電路設(shè)計策略:提升組合邏輯的并行度,縮短門延時。
- 條件判斷電路設(shè)計策略:不需要優(yōu)先級編碼的話,采用無優(yōu)先級的電路。
- 整體設(shè)計策略:移動寄存器位置,平衡各組合邏輯路徑的門延時。
總結(jié)
以上是生活随笔為你收集整理的FPGA经验分享——时序收敛之路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: parameter与define 区别
- 下一篇: 用Quartus II Timeques