1. 程式人生 > >Vivado使用技巧(29):約束功能概述

Vivado使用技巧(29):約束功能概述

設計約束概述

設計約束就是定義編譯過程中必須滿足的需求,只有這樣才能保證在板子上工作時功能正確。但不是全部約束在所有過程中都會使用,比如物理約束只用在佈局和佈線過程中。Vivado工具的綜合和實現演算法時時序驅動型的,因此必須建立合適的時序約束。我們必須根據應用需求選擇合理的約束,過度約束或約束不足都會造成問題。

老版的ISE開發工具使用UCF(User Constraints File)檔案進行約束;新的Vivado開發工具使用XDC(Xilinx Design Constraints)進行約束。在描述設計約束方面,標準SDC(Synopsys Design Constraints)格式已經發展超過了20年,且應用最為廣泛。XDC約束正是基於SDC格式,再加入Xilinx的一些物理約束。

XDC約束可以用一個或多個XDC檔案,也可以用Tcl指令碼實現。XDC檔案或Tcl指令碼都要加入到工程的某個約束集(set)中。雖然一個約束集可以同時新增兩種型別約束,但是Tcl指令碼不受Vivado工具管理,因此無法修改其中的約束。

管理約束

Vivado支援使用一個或多個約束檔案。對於大型設計來說,僅使用一個約束檔案往往不便於維護。最好的做法是將時序約束和物理約束分別儲存到不同的檔案中。或者某些特定模組使用一個單獨的約束檔案。

約束檔案(XDC檔案或Tcl指令碼)需要新增到約束集中。一個工程可以包含多個約束集,一個檔案也可以新增到多個約束集中。如下圖所示:
在這裡插入圖片描述
另外注意,生成IP核時,IP核的約束檔案不會顯示在上圖列表中,只會顯示在IP Sources視窗中。

預設情況下,所有的XDC約束檔案會同時應用於綜合和實現過程中。在XDC檔案的屬性視窗中修改如下圖中選項,可以選擇XDC檔案的使用階段,對應的屬性為USED_IN_SYNTHESIS和USED_IN_IMPLEMENTATION:
在這裡插入圖片描述
但是DONT_TOUCH屬性不受上述設定的限制。比如在綜合XDC中使用了DONT_TOUCH屬性,即使Used In沒有選中Implementation,該屬性仍會傳遞到實現過程中。

排列約束的順序

XDC約束會遵循一套優先順序規則,按順序應用於設計中。當多個物理約束之間產生矛盾時,順序靠後的約束會覆蓋之前的約束。比如一個I/O埠前後綁定了兩個管腳位置,則順序上靠後的約束會起作用。推薦的約束順序如下:

  • 時序宣告部分:主時鐘、虛擬時鐘、生成的時鐘、時鐘組、匯流排斜率約束、輸入與輸出延遲約束。
  • 時序異常部分:虛假路徑、最大延遲/最小延遲、多時鐘路徑、個例分析、遮蔽的時序。
  • 物理約束部分:可以放在時序約束之前或之後,最好儲存在一個單獨的約束檔案中。

約束應該以時鐘定義開始,因為時鐘必須在被其它約束引用之前定義好。如果在定義之前便引用了時鐘,會導致錯誤發生,該約束將被忽略掉。約束檔案的順序相當重要,設計者應該確保每一個檔案中的約束不依賴於其它檔案中的約束。如果這種情況發生,應該考慮合併兩個檔案,或者按照更合理地方式重新組織約束檔案。

所有新的約束都會儲存到標記為target的XDC檔案的末尾。如果約束集中有多個XDC檔案,大多數情況下target檔案不是最後一個XDC檔案,這就導致儲存到磁碟上的約束順序和記憶體中的約束順序並不相同(記憶體中執行相當於在最後插入一個新約束,而儲存到磁碟中確是在中間插入了一個新約束),因此設計者需要驗證最終儲存的約束順序可以正確工作。

每個約束檔案都有PROCESSING_ORDER屬性,屬性值可以是:EARLY,必須首先被讀取;NORMAL,預設;LATE:必須最後被讀取。下面分兩類檔案討論一下約束的讀取順序:

  • 使用者XDC檔案:對於沒有IP核的工程而言,Vivado視窗中顯示的約束檔案順序便是其讀取順序,通過拖拽檔案便可以改變約束檔案的讀取順序。使用者XDC的PROCESSING_ORDER屬性如果相同,則按照其讀取順序執行。
  • IP核XDC檔案:如果工程中使用了IP核,許多IP核都會發佈一個或多個XDC檔案。生成IP核時,包含不依賴其它IP核時鐘和使用者時鐘約束的XDC檔案為EARLY,在使用者XDC檔案之前執行;包含依賴其它時鐘的XDC檔案為LATE,在使用者XDC檔案之後執行。IP核XDC的PROCESSING_ORDER屬性如果相同,則按照IP核匯入或建立的順序的執行。

屬性值為LATE的IP核XDC檔名稱為<IP_NAME>_clocks.xdc。前面的文章也介紹過,在Tcl控制檯中使用report_compile_order -constraints命令可以報告所有約束檔案的狀態,其中就包括PROCESSING_ORDER屬性。該屬性可以在屬性視窗中修改:
在這裡插入圖片描述
如果PROCESSING_ORDER屬性相同,使用者XDC檔案可以通過拖拽移動來修改讀取順序,但IP核XDC檔案的讀取順序無法直接修改。當然也有特殊方法可以實現,但基本不會使用,因此不做介紹。

約束方法

完成約束有兩種方法:(1).直接編輯XDC檔案;(2).開啟某一階段的設計,Elaborated設計、綜合後設計或實現後設計,直接對某物件進行約束。採用第2種方法,在編輯約束時,Tcl控制檯中會顯示等價的XDC命令。該命令是儲存在記憶體中的,在綜合或實現前,必須點選Save Constraints儲存約束。如果是新的約束,則會新增到標記為target的約束檔案中;如果是對已存在的約束進行修改,則會修改XDC檔案中原來位置的命令。

上述兩種約束方法最好不要同時使用,否則容易混淆導致約束沒有起作用。如果需要在兩種約束方法之間切換,要確保儲存了當前約束,或者重新匯入一下設計。下圖給出了約束的流程圖:
在這裡插入圖片描述

管腳賦值與平面規劃

本節介紹兩種使用GUI完成約束的方法。第一種是建立與編輯頂層埠位置,即通常所說的管腳賦值(Pin Assignment)。開啟某一階段設計後,將檢視切換為“I/O Planning”,如下圖:
在這裡插入圖片描述
切換到該檢視後會自動開啟如下4個視窗:

  • Device:編輯埠在器件平面規劃圖中的位置。
  • Package:編輯埠在器件封裝中的位置。
  • I/O Ports:可以選擇一個埠,拖動到Package或Device視窗中的某個位置,也可以觀察每個埠的各個屬性。
  • Package Pins:觀察每個I/O Bank的資源利用率。

另一種是平面規劃(Floorplanning),主要是建立和編輯Pblock來限制某些物件的佈局範圍。開啟某一階段設計後,將檢視切換為“Floorplanning”,如上圖。切換到該檢視後會自動開啟如下3個視窗:

  • Netlist:選擇賦值到某個Pblock的單元物件。
  • Physical Constraints:觀察設計中的Pblock和各自的屬性。
  • Device:建立或編輯Pblock在器件中的形狀和位置。

在Netlist視窗中選擇某些單元,將其拖動到Device視窗的目標位置中,即可將單元位置約束到某一特定的BEL或SITE。這兩個部分都可以稱作“物理約束”,另外還有“時序約束”,需要藉助時序約束嚮導,比較複雜,單獨放在第30篇講述。

建立綜合約束

Vivado綜合引擎將設計的RTL描述轉換為一個工藝對映網表,在這個階段可以使用約束來指導綜合引擎解決設計需求。涉及到的約束包括4個方面:

  • RTL屬性:綜合屬性在本系列第24篇中有詳細介紹,這些約束通常與某些邏輯部分的對映方式直接相關,比如保留特定的暫存器和網路防止被優化、控制最終網表中的設計層次等等。
  • 時序約束:可以在該階段起作用的時序約束有create_clock、create_generated_clock、set_input_delay、set_output_delay、set_clock_groups、set_false_path、set_max_delay和set_multicyclye_path。
  • 物理與配置約束:這部分約束不會作用於該階段,因此會被綜合引擎忽略。
  • Elaborated設計約束:在綜合階段,網路延遲模型還不精確,因此主要目標是得到一個滿足時序或時序違背程度較小的綜合網表。可以對Elaborated設計分析RTL設計得到的物件進行約束。可以利用Tcl控制檯來測試想要執行的XDC命令是否有語法錯誤,再儲存到XDC檔案中。

注意上面之所以這樣描述,是因為一些RTL名稱再Elaborated設計中會被修改或刪除,因此不能直接使用RTL設計中的物件名稱。部分物件,如頂層埠、例項化原語再RTL和Elaborated設計中總是相同的,下面給出一些名稱會發生變化的例子,需要特別注意:

  • 單bit暫存器:RTL中的訊號名稱新增字尾_reg。如RTL中定義reg data,則對應的暫存器名稱為data_reg。
  • 多bit暫存器:與單bit暫存器相同,但約束時必須單獨約束每個bit或直接當作一組約束。如定義reg [3:0] data,可以對reg[0]或reg[*]約束,但不能對reg[1:0]約束。
  • 合併的暫存器和網路:儲存塊、DSP、移位暫存器等介面會將幾個設計物件合併到一個資源中,導致RTL原始檔中的一些暫存器或網路不會出現在Elaboratd設計中。對於這類物件,無法直接約束,應該尋找與其相連的其它暫存器或網路。
  • 層次名稱:預設情況下,綜合可能會將某些層次結構展開,融為一體(可以用-flatten_hierarchy設定),約束時要使用完整的層次名稱來指定物件,而不要使用萬用字元‘*’,如‘inst_A/inst_B/data_reg’。

總而言之,就是要明確約束的物件,否則很容易造成約束沒有按設計者意圖進行。比如不要對層次介面的管腳做約束,因為這些管腳僅僅起到了連線各個層次的作用;也不要對與組合邏輯運算子相連的網路做約束,因為組合邏輯運算會採用查詢表方式實現,導致該網路並不會出現在綜合網表中。

建立實現約束

綜合過後,將綜合網表和XDC檔案(或Tcl指令碼)一同匯入到記憶體中,用於實現過程。匯入時必須觀察Vivado報告的訊息,據此來驗證和修改那些沒有應用成功的約束。正如綜合約束使用的Elaborated設計物件名稱會和RTL中名稱不同,實現約束使用的綜合網表物件名稱也可能會和Elaborated設計中的名稱不同。如果發生上述情況,則必須重新建立某些約束,並僅作用於實現階段。

前文也說過物理和配置約束僅會在實現階段起作用,因此也最好儲存到一個單獨的XDC檔案中,設定為僅作用於實現階段。綜合過程中可能會複製某些暫存器,以提高設計效能,必須使用get_cells/get_pins -include_replicated_objects命令獲取物件,才能確保XDC約束也作用於複製出來的暫存器。我們當然很難直接感覺到哪個物件需要像上述這樣做,幸好在Vivado中執行Methodology檢查時,相關資訊會報告在XDCV-1和XDCV-2檢查資訊中,供設計者參考。

約束作用域

一個特定的XDC檔案中的約束可以選擇僅作用於一個特定的模組,或設計中的特定單元。這種約束方式可稱作塊級約束,實現機制稱作約束作用域機制。預設情況下,IP Catalog中匯出的所有IP核都採用這種約束方式。該機制通過設定XDC檔案的兩個屬性實現:
在這裡插入圖片描述

  • SCOPED_TO_REF:設定模組名稱,約束僅應用於設定模組中的所有例項。
  • SCOPED_TO_CELLS:給出應用約束的層次單元名稱列表。

匯出IP核時,輸出的XDC檔案會自動完成上述兩個屬性的設定。如果設計中需要為某個子模組進行單獨約束,也可以通過手動設定上述兩個屬性實現。

約束效率

編寫時序約束時,首要目標是讓約束變得簡單,僅為相關的網表物件設定約束,即為約束提供儘可能少的作用物件,以便精確並安全地覆蓋到預期的時序路徑。沒有效率地約束會導致更長的執行時間、更大的記憶體佔用率,最壞的情況是覆蓋到比預期更多的路徑從而與其它約束產生衝突,導致設計出現時序異常

Vivado中Methodology檢查的XDCB-1會報告涉及到超過1000個物件的時序約束,以防止出現時序異常情況。此外,還可以開啟某一階段設計後,使用如下命令檢視相關報告:

  • report_exceptions -coverage:給出每個時序異常的邏輯路徑範圍,將該時序異常作用的物件數量,與起點到端點間以有效的方式覆蓋的物件數目作比較。
  • report_exceptions -ignored:給出被其它時序約束覆蓋掉的時序約束,如set_false_path會被set_clock_group覆蓋而不起作用。應考慮修改約束或刪掉不起作用的約束。
  • report_exceptions -ignored_objects:給出被忽略的起點與斷點列表,如起點和斷點之間不存在設定的路徑,就會導致被忽略。

時序異常的具體內容在本系列第32篇中介紹。下面給出幾種改善約束執行時間的方法:

1.優化管腳查詢方式

使用get_pins代替get_cells會對執行時間有明顯的影響。如果需要從設計的所有管腳中查詢一個管腳列表,不要直接根據管腳名字查詢,最好是先用get_cells定位管腳所在的單元,再從該單元中查詢管腳。示例如下:

get_pins –hier * -filter {NAME=~xx*/yy*} //不推薦的方式
get_pins –filter {REF_PIN_NAME=~yy*} –of [get_cells –hier xx*]  //最佳方式

尤其對於大型設計,採用推薦方式進行約束可以顯著改善查詢時間。

2.不要使用all_registers查詢

儘可能地將對all_registers的查詢代替為對cells、pins的查詢,因為使用all_registers會在大量物件中進行搜尋。示例如下:

set_multicycle_path –from [all_inputs] –to [all_registers –clock clk1]
set_multicycle_path –from [all_inputs] –to [get_clocks clk1]

這兩條約束是等價的,但第二種方式的效率比第一種要高很多。