今天跟大家分享的內(nèi)容很重要,也是我們調(diào)試FPGA經(jīng)驗(yàn)的總結(jié)。隨著FPGA對時序和性能的要求越來越高,高頻率、大位寬的設(shè)計(jì)越來越多。在調(diào)試這些FPGA樣機(jī)時,需要從寫代碼時就要小心謹(jǐn)慎,否則寫出來的代碼可能無法滿足時序要求。另外,最近跟網(wǎng)友聊天時,有談到公眾號壽命的問題,我覺得網(wǎng)絡(luò)交換FPGA公眾號應(yīng)該在這方面有優(yōu)勢,作為高校里從事FPGA開發(fā)的老師,又有興趣做公眾號的維護(hù),不會遇到其他公眾號做著做著就停更的問題,我們的公眾號也許將陪伴你十年甚至幾十年。同時,也請大家放心,我們今后每篇文章都將用心的去總結(jié)歸納,確保篇篇都是干貨!新的一年開始了,希望在新的一年里,繼續(xù)給大家分享平時遇到的FPGA調(diào)試中遇到的問題及經(jīng)驗(yàn),一起進(jìn)步,一起努力,加油!
問題一:沒有對設(shè)計(jì)進(jìn)行全面的約束導(dǎo)致綜合結(jié)果異常,比如沒有設(shè)置異步時鐘分組,綜合器對異步時鐘路徑進(jìn)行靜態(tài)時序分析導(dǎo)致誤報(bào)時序違例。
約束文件包括三類,建議用戶應(yīng)該將這三類約束文件分開寫在三個xdc/sdc文件中。
第一類是物理約束,它主要對設(shè)計(jì)頂層的輸入輸出引腳的分配約束、電平標(biāo)準(zhǔn)的約束,
如下圖所示:在quartus環(huán)境下,對pcie_rstn和pcie_refclk的電平標(biāo)準(zhǔn)和管腳進(jìn)行了約束。
如下圖所示:在vivado環(huán)境下,對rst_n和sys_clk_PCIe_p的電平標(biāo)準(zhǔn)和管教進(jìn)行了約束。
第二類是調(diào)試約束,用戶在使用ila調(diào)試時,Vivado會自動生成相關(guān)ila的調(diào)試約束。 如下圖所示,這是Vivado自動生成的相關(guān)ila的調(diào)試約束。
第三類是時序約束,這類約束的種類最多,它包括時鐘周期約束、輸入輸出延遲約束、跨時鐘域路徑約束、多周期路徑約束、偽路徑約束等。 時鐘周期約束:用戶需要將設(shè)計(jì)中的所有時鐘進(jìn)行約束后,綜合器才能進(jìn)行合理的靜態(tài)時序分析。一個設(shè)計(jì)中的時鐘主要分為兩類:主時鐘和生成時鐘。主時鐘包括由全局時鐘引腳接入的時鐘、高速收發(fā)器的輸出時鐘。生成時鐘包括由MMCM/PLL產(chǎn)生的時鐘、用戶邏輯分頻產(chǎn)生的時鐘,建議用戶不要使用后者,因?yàn)樗ǔJ怯山M合邏輯或觸發(fā)器生成的時鐘,這種時鐘的歪斜、抖動、驅(qū)動能力都很差。對時鐘進(jìn)行約束時,主要針對時鐘的頻率、占空比、抖動、不確定性等參數(shù)進(jìn)行約束。 全局時鐘引腳接入的時鐘約束舉例: 如下圖所示,在quatus環(huán)境下,對全局時鐘引腳接入的時鐘pcie_refclk進(jìn)行了約束,因?yàn)檎伎毡仁?0%,抖動和不確定性也采用默認(rèn)值,所以圖中只對頻率進(jìn)行了約束。
如下圖所示,在vivado環(huán)境下,對全局時鐘引腳接入的時鐘sys_clk_PCIe_p進(jìn)行了約束,因?yàn)檎伎毡仁?0%,抖動和不確定性也采用默認(rèn)值,所以圖中只對頻率進(jìn)行了約束。
高速收發(fā)器的輸出時鐘約束舉例:由于高速收發(fā)器通常是例化IP核來使用的,所以這種約束通常是IP核自帶的。 如下圖所示,在vivado環(huán)境下,PCIE IP核中對高速收發(fā)器的輸出時鐘進(jìn)行約束。
MMCM/PLL生成時鐘約束舉例: 如下圖所示,在quatus環(huán)境下,在sdc中加入以下命令,quatus會對所有PLL產(chǎn)生的時鐘進(jìn)行了約束。因?yàn)橛脩糁灰獙LL的輸入時鐘(通常情況下是主時鐘)進(jìn)行了約束,在sdc中加入以下命令后,quatus能夠根據(jù)輸入時鐘和輸出時鐘的關(guān)系自動推斷出PLL的輸出時鐘的時鐘周期、占空比、相位關(guān)系等。
如下圖所示,在vivado環(huán)境下,用戶對PCIE IP核中的MMCM的輸出時鐘進(jìn)行重命名,用戶只要確保對MMCM的輸入時鐘(通常情況下是主時鐘)進(jìn)行了約束,Vivado會自動能夠根據(jù)輸入時鐘和輸出時鐘的關(guān)系自動推斷出PLL的輸出時鐘的時鐘周期、占空比、相位關(guān)系等。
跨時鐘域約束:在介紹跨時鐘域之前,先介紹兩個概念:同步時鐘和異步時鐘。同步時鐘:當(dāng)兩個時鐘間的相位是固定的,則可以稱這兩個時鐘為同步時鐘。一般同源,如由同一個MMCM/PLL產(chǎn)生的兩個時鐘可以稱為同步時鐘。異步時鐘:無法判定兩個時鐘間相位時,則可以稱這兩個時鐘為異步時鐘。兩個來自不同晶振的時鐘,一定是異步時鐘。通常情況下設(shè)計(jì)中不同的主時鐘肯定是異步時鐘。
由不同的MMCM/PLL產(chǎn)生的兩個輸出時鐘即使頻率相同,但是由于相位關(guān)系不確定,所以也屬于異步時鐘。 用戶想要進(jìn)行跨時鐘域的約束,首先需要對設(shè)計(jì)中的所有時鐘進(jìn)行異步時鐘分組。若用戶沒有設(shè)置異步時鐘分組,綜合器在綜合時會認(rèn)為所有的時鐘都是相關(guān)的,從而對某些源時鐘與目的時鐘屬于異步時鐘關(guān)系的路徑進(jìn)行了靜態(tài)時序分析,由于源時鐘與目的時鐘的相位關(guān)系不確定,所以該路徑的建立時間或保持時間必定是存在違例的。若用戶設(shè)置了異步時鐘分組,Vivado在時序分析時,當(dāng)源時鐘和目的時鐘屬于同一個時鐘組時,才會分析此時序路徑;而源時鐘和目的時鐘屬于不同時鐘組時,則會略過此時序路徑的分析。 那么如何進(jìn)行時鐘的異步分組呢?首先要按照上面提到同步時鐘和異步時鐘的概念對設(shè)計(jì)中的所有時鐘進(jìn)行分類,屬于異步時鐘關(guān)系的時鐘必定要劃分在不同的分組中,屬于同步時鐘關(guān)系的時鐘可以分在同一個分組也可以分在不同的分組中,如何劃分要看具體情況而定。 ?
???如下圖所示,在quatus環(huán)境下,對時鐘進(jìn)行異步時鐘分組的劃分,圖中主要有3類時鐘, 由全局時鐘引腳輸入的pcie_refclk,PCIE IP核中的MMCM輸出的時鐘(圖中用通配符*表示) Flash_pll輸出的兩個不同頻率的時鐘outclk0和outclk1。首先要確定全局時鐘引腳輸入的時鐘和PCIE IP核中的MMCM輸出的時鐘以及Flash_pll輸出的兩個不同頻率的時鐘屬于異步時鐘關(guān)系,它們必須要劃分在不同的分組中。但是,F(xiàn)lash_pll輸出的兩個不同頻率的時鐘outclk0和outclk1如何進(jìn)行劃分呢?首先,考慮一下設(shè)計(jì)中,outclk0和outclk1之間有沒有進(jìn)行數(shù)據(jù)交互,如果沒有數(shù)據(jù)交互,那么就將這兩個時鐘劃分在同一個時鐘分組即可,圖中就屬于這種情況。
如果有數(shù)據(jù)交互,可以分為以下三種方案進(jìn)行操作: 1.將這兩個時鐘劃分在不同的時鐘分組,用戶在邏輯中進(jìn)行了跨時鐘域處理。(推薦使用) 2.將這個時鐘劃分在相同的時鐘分組,用戶在邏輯中進(jìn)行了跨時鐘域處理,在xdc/sdc中添加set_flase_path(偽路徑約束)禁止綜合器對跨時鐘域路徑(通常是雙觸發(fā)器同步的跨時鐘路徑)進(jìn)行靜態(tài)時序分析。 3.?將這個時鐘劃分在相同的時鐘分組,用戶邏輯中不需要進(jìn)行跨時鐘域處理,由后端保證時序。
偽路徑約束:偽路徑約束主要用于以下情況:1.上文提到的對通過雙觸發(fā)器同步的跨時鐘域路徑設(shè)置偽路徑。2.異步復(fù)位路徑
在vivado環(huán)境下,通過以下指令將異步復(fù)位路徑sys_rst_n 設(shè)置為偽路徑
set_false_path-from [get_ports sys_rst_n]
在quatus環(huán)境下,通過以下指令將異步復(fù)位路徑pcie_rstn 設(shè)置為偽路徑
set_false_path-from [get_ports pcie_rstn] -to *
判斷條件過長問題
問題二:一個always塊的判斷條件中的部分變量或賦值語句中的部分被賦值變量是直接由組合邏輯產(chǎn)生的。當(dāng)組合邏輯不是特別長時或FPGA的資源利用率比較低時,這種時序問題很可能會被綜合器優(yōu)化處理掉。但是當(dāng)組合邏輯鏈路過長時,尤其是大位寬的寄存器進(jìn)行邏輯運(yùn)算生成了較長的組合邏輯鏈路時,這種代碼風(fēng)格導(dǎo)致的時序問題就會比較明顯,最終導(dǎo)致always塊的輸入信號的延時變大,建立時間違例。
有時序問題的代碼如下圖所示,req_start_addr_reg信號和PC_LAST_ADDR信號屬于位寬較大的寄存器,圖中有三個典型問題需要注意。
第一個需要注意的問題:PC_LAST_ADDR信號是一個組合邏輯鏈路的輸出信號,該組合邏輯鏈路是由邏輯運(yùn)算導(dǎo)致的。錯誤的地方在于PC_LAST_ADDR信號直接被當(dāng)做always塊的輸入信號使用,導(dǎo)致always塊的輸入信號的延時變大,建立時間違例。正確的處理方式應(yīng)該是將PC_LAST_ADDR信號打一拍后,再將打一拍后的輸出信號作為always塊的輸入信號使用。
第二個需要注意的問題:在always塊的第三個和第四個的判斷條件中都是先進(jìn)行了邏輯運(yùn)算后再進(jìn)行邏輯判斷的。這個問題的本質(zhì)和第一個問題相同,都是將組合邏輯的輸出信號直接作為always塊的輸入信號使用,導(dǎo)致always塊的輸入信號的延時變大,建立時間違例。正確的處理方式應(yīng)該是將組合邏輯單獨(dú)拎出來,然后將組合邏輯的輸出結(jié)果打一拍后,再將打一拍后的輸出信號作為always塊的輸入信號使用。而且在always塊的第三個和第四個的判斷條件中使用了三段式狀態(tài)機(jī)的n_state作為了判斷條件,原因同上。正確的處理方式應(yīng)該盡量使用c_state作為判斷條件。
第三個需要注意的問題:在always塊的第四個賦值語句中,將兩個信號進(jìn)行邏輯運(yùn)算后進(jìn)行賦值操作。這個問題的本質(zhì)和第一個問題相同,都是將組合邏輯的輸出信號直接作為always塊的輸入信號使用,導(dǎo)致always塊的輸入信號的延時變大,建立時間違例。正確的處理方式應(yīng)該是將組合邏輯單獨(dú)拎出來,然后將組合邏輯的輸出結(jié)果打一拍后,再將打一拍后的輸出信號作為always塊的輸入信號使用。 修改之后的代碼如下圖所示:
總結(jié):在編寫代碼時,應(yīng)該注意以下三點(diǎn): 1.應(yīng)該盡量保證每一個always塊的判斷條件簡潔(判斷條件中盡量只進(jìn)行邏輯判斷,盡量避免邏輯運(yùn)算) 2.應(yīng)該盡量保證每一個always塊的判斷條件中的每一個變量都是直接來源于某個always塊輸出信號,盡量避免將組合邏輯的輸出直接作為某個always塊的判斷條件的一部分,這樣就可以保證每一個always塊的輸入信號的延時比較固定,有利于時序收斂。 3.應(yīng)該盡量保證每一個always塊的賦值語句中的被賦值變量都是直接來源于某個always塊輸出信號,盡量避免將組合邏輯的輸出直接作為某個always塊的賦值語句中的一部分,這樣就可以保證每一個always塊的輸入信號的延時比較固定,有利于時序收斂。
if else嵌套層數(shù)過多
問題三:always塊中的if…else…嵌套層數(shù)過多導(dǎo)致綜合問題 ????有時序問題的代碼如下圖所示,always塊中嵌套了三層if…else…,正確的處理方式應(yīng)該盡量減少always塊中if…else…的嵌套層數(shù)。
修改之后的代碼如下圖所示,
總結(jié):?在編寫代碼時,應(yīng)該盡量減少always塊中的if…else…嵌套層數(shù).
邏輯信號扇出過大
問題四:部分用戶邏輯信號扇出過大,導(dǎo)致驅(qū)動能力不足。
有時序問題的代碼如下圖所示,圖中三個always塊產(chǎn)生了一組RAM的寫信號,這組信號作為55個always塊的輸入信號,也就是說該信號驅(qū)動了55個always塊,普通的信號的扇出能力一般在15-20左右。這組信號作為普通的用戶邏輯,如果采用加BUFG的方式來增加驅(qū)動能力,這樣不僅浪費(fèi)BUFG的資源,而且BUFG會給信號加入極大的延時,這種延時對于有些高速設(shè)計(jì)來說也許是不可接受的,一般只有全局復(fù)位信號才會使用BUFG增加驅(qū)動能力。所以對于這種用戶邏輯信號,推薦采用復(fù)制寄存器的方式解決驅(qū)動能力不夠的問題。
修改之后的代碼如下圖所示:將原來的一組信號,復(fù)制了兩份,總共三組信號,且三組信號的邏輯相同,每組信號驅(qū)動的always塊的個數(shù)變成了原來的1/3。但是同時需要注意的問題是,這種復(fù)制的寄存器有極大的可能會被綜合器當(dāng)作等效寄存器優(yōu)化掉,為了防止綜合器多管閑事,在quatus下需要在聲明被復(fù)制的寄存器前面加上(* noprune *)告知綜合器此寄存器不需要優(yōu)化,在vivado下需要在聲明被復(fù)制的寄存器前面加上(* keep =“true”*)告知綜合器此寄存器不需要優(yōu)化。
總結(jié):在編寫代碼時,需要注意信號驅(qū)動能力與扇出的問題,對扇出大的用戶邏輯信號進(jìn)行寄存器復(fù)制解決驅(qū)動能力不足的問題。對于全局復(fù)位信號,使用加BUFG解決驅(qū)動能力不足的問題。
數(shù)據(jù)選擇器過大
問題五:用戶需要使用大型的數(shù)據(jù)選擇器(8選1以上的選擇器)時,如果直接使用組合邏輯的case語句實(shí)現(xiàn)大型數(shù)據(jù)選擇器,可能會導(dǎo)致以下問題:綜合器綜合速度變慢,邏輯資源占用率變大,數(shù)據(jù)選擇器的相關(guān)信號時序違例會很大,數(shù)據(jù)選擇器的輸出結(jié)果也會極不穩(wěn)定。
有時序問題的部分代碼如下圖所示:用戶需要一個128選1的數(shù)據(jù)選擇器,圖中直接使用了一個組合邏輯的case語句實(shí)現(xiàn)了128選1。正確的處理方式應(yīng)該是將一個128選1的選擇器,分為3級,第一級使用了16個8選1,第二級使用了2個8選1,第三級使用了1個2選1,總共消耗3個時鐘,選出最終的輸出結(jié)果,注意每一級的輸出結(jié)果都需要打一拍后再輸入到下一級進(jìn)行選擇。
修改之后的部分代碼如下圖所示,
總結(jié):大型數(shù)據(jù)選擇器不能直接使用組合邏輯的case語句實(shí)現(xiàn),在對選擇器的延時要求不是很高的情況下,最好將大型數(shù)據(jù)選擇器進(jìn)行分級選擇的處理。
大位寬RAM數(shù)據(jù)總線約束
問題六:高速設(shè)計(jì)中,RAM的輸出或寄存器的位寬太寬時(64bit以上),可能會出現(xiàn)在某個時鐘的上升沿時,寄存器的某些bit由于布線導(dǎo)致路徑時延不一致,不能與其他bit的數(shù)據(jù)在同一個時鐘上升沿到達(dá),導(dǎo)致用戶采樣到數(shù)據(jù)出現(xiàn)錯誤或者采樣到亞穩(wěn)態(tài)。所以,用戶要使用某個數(shù)據(jù)位寬很寬的寄存器時,無論這個寄存器數(shù)據(jù)來源于FIFO還是來源于用戶邏輯,建議先將該寄存器打拍,然后使用打拍后的數(shù)據(jù),這樣更有利于時序收斂。? ? 但如果從RAM里面輸出的大位寬的數(shù)據(jù)總線經(jīng)過打拍后仍然不穩(wěn)定又該怎么辦呢?筆者在實(shí)際調(diào)試過程中發(fā)現(xiàn),采用對使用大位寬總線RAM的時鐘信號進(jìn)行約束的方法非常有效。具體實(shí)現(xiàn)跟FPGA外圍管腳時鐘信號約束的方法一樣,比如下圖中在vivado工具中可以對設(shè)計(jì)中內(nèi)部某個用到大位寬的RAM的時鐘進(jìn)行創(chuàng)建即可。
總結(jié):大位寬的數(shù)據(jù)總線需要保持?jǐn)?shù)據(jù)傳輸過程中時延的一致性,盡可能的多采用時序邏輯,同時對于大位寬RAM的時鐘要進(jìn)行約束。 ?
編輯:黃飛
?
評論
查看更多