《Xilinx約束學習筆記》為自己閱讀 Xilinx 官方 UG903 文檔后的學習筆記,大多數為翻譯得來,方便大家學習。
1 約束方法學
1.1 組織約束文件
Xilinx 建議將時序約束和物理約束分開保存為兩個不同的文件。甚至可以將針對某一個模塊的約束單獨保存在一個文件中。
1.1.1 綜合和實現可以使用不同的約束文件
可以使用 USED_IN_SYNTHESIS 和 USED_IN_IMPLEMENTATION 屬性指定約束文件是在綜合或實現過程中使用。
注意:特別是IP、DCP這類使用OOC模式的模塊,因為這些模塊在綜合過程中是一個黑盒,當頂層約束指定的約束路徑為黑盒中的內容時,可能在綜合過程中會報找不到目標的錯誤。因此,最好的方法是將約束會為兩個文件,一個綜合用,一個實現時用。
在工程模式中可以使用如下代碼指定約束文件使用時機。
set_property USED_IN_SYNTHESIS false [get_files wave_gen_pins.xdc]
set_property USED_IN_IMPLEMENTATION true [get_files wave_gen_pins.xdc]
非工程模式中,不需要如此設置。因為什么時候使用約束文件,只和讀取約束文件的時機相關。如下:其中 wave_gen_timing.xdc 會在綜合和實現過程中使用,而 wave_gen_pins.xdc 只會在實現過程中使用。
read_verilog [glob src/*.v]
read_xdc wave_gen_timing.xdc
synth_design -top wave_gen -part xc7k325tffg900-2
read_xdc wave_gen_pins.xdc
opt_design
place_design
route_design
1.2 約束的順序
推薦約束順序如下:
## Timing Assertions Section
# Primary clocks
# Virtual clocks
# Generated clocks
# Clock Groups
# Bus Skew constraints
# Input and output delay constraints
## Timing Exceptions Section
# False Paths
# Max Delay / Min Delay
# Multicycle Paths
# Case Analysis
# Disable Timing
## Physical Constraints Section
# located anywhere in the file, preferably before or after the timing constraints
# or stored in a separate constraint file
1.3 創建綜合約束
Vivado 綜合將設計中的 RTL 描述轉換為工藝映射網表。此過程分為多個步驟進行,包括一些時序導向的優化。FPGA 包含許多邏輯特性,可以有許多不同的應用方式。需要通過約束來引導綜合引擎工作,以滿足實現時所需的要求。
綜合約束可以分為4類:
- RTL Attributes
- Timing Constraints
- Physical and Configuration Constraints
- Elaborated Design Constraints
1.3.1 RTL Attributes
RTL Attributes 必須寫入 RTL 文件中。他們通常用來選擇邏輯的某些部分的映射樣式,以及保留某些寄存器和網線,或控制最終網表中的設計層次結構。
IMPORTANT: The DONT_TOUCH attribute does not obey the properties of USED_IN_SYNTHESIS and USED_IN_IMPLEMENTATION . If you use DONT_TOUCH properties in the synthesis XDC, it is propagated to implementation regardless of the value of USED_IN_IMPLEMENTATION .
注意:DONT_TOUCH 屬性不受 USED_IN_SYNTHESIS 和 USED_IN_IMPLEMENTATION 的控制,當在綜合約束中使用 DONT_TOUCH 時,他會傳遞到實現過程中,無論 USED_IN_IMPLEMENTATION 的屬性值是什么。
set_property DONT_TOUCH true [get_cells fsm_reg]
1.3.2 Timing Constraints
Timing Constraints 必須通過一個或多個 XDC 文件傳遞給綜合引擎。只有以下與建立時間分析相關的約束對綜合結果有實際影響:
- create_clock
- create_generated_clock
- set_input_delay
- set_output_delay
- set_clock_groups
- set_false_path
- set_max_delay
- set_multicycle_path
1.3.3 Physical and Configuration Constraints
Physical and Configuration Constraints 會被綜合算法忽略。(既然會被忽略為什么又提到?)
1.3.4 Elaborated Design Constraints
創建綜合 XDC 的第一個版本時,只需要使用簡單的時序約束來描述高級設計要求即可。因為此階段的路徑延遲並不准確,目的只是為了在實現開始前,盡量讓綜合的結果滿足時序。並且在此階段可能需要反復迭代 RTL 代碼和 XDC 文件,以便查找各種網名和排除各種語法錯誤。
在 Elaborated Design 的創建過程中,某些 RTL 名稱被修改或丟失。需要注意如下幾點:
- 網表中寄存器的名稱會比在 RTL 代碼中的名稱多一個
_reg
的后綴。 - 有些寄存器或網線會被吸收掉,例如二維寄存器會被綜合成 Memory Block,乘除法會綜合成 DSP。
- 在使用
get_*
查詢路徑時,最好不要使用-hierarchical
參數,路徑中使用/
顯示描述路徑的層級。 - 不要對組合邏輯的網線附加約束。它們很可能會被合並到一個 LUT 中並從網表中消失。
1.4 創建實現約束
在某些情況下,綜合網表中的對象名稱與 Elaborated Design 中的名稱不同。如果是這種情況,就必須使用正確的名稱重新創建一些約束,並將它們保存在僅用於實現的 XDC 文件中。當工具可以正確加載所有 XDC 文件后,就可以運行時序分析以便:
- 添加缺少的約束,例如輸入和輸出延遲。
- 添加時序例外,例如偽路徑、多周期路徑和最小/最大延遲約束。
- 識別由於設計中的長路徑而導致的嚴重違規,修改 RTL 代碼。
在綜合期間,為了提高設計性能,有些寄存器可能被復制。但綜合器不會把復制的單元添加到用戶 XDC 約束中。如要將時序約束附加到 Vivado Synthesis 復制的對象上,根據約束的寫入方式不同,復制的單元有可能不會被 XDC 約束覆蓋到,這可能會影響結果的實現質量。可以使用 -include_replicated_objects
參數來避免此問題。
例如 set_false_path –from [get_cells –hierarchical *rx_reg]
可以改為 set_false_path -from [get_cells -hierarchical *rx_reg -include_replicated_objects]
。
下面幾個例子都可以獲取到被復制的單元。ORIG_CELL_NAME
表示原單元名稱。另外注意 -filter
和 -include_replicated_objects
不能一起使用,因為被復制的名稱為 *reg_replica*
,會被 -filter
過濾掉。
get_cells -include_replicated_objects *rx_reg
get_cells -include_replicated_objects [get_cells -hier -filter {NAME =~ *rx_reg}]
get_cells -hierarchical -filter {NAME =~ *rx_reg || ORIG_CELL_NAME =~ *rx_reg}
1.4.1 對黑盒的約束
對於使用OOC模式的黑盒模塊,頂層約束只能夠訪問到黑盒的輸入輸出腳。
- 在 OOC 模塊中自動推衍出的時鍾,不能對其進行重命名。
- 不能引用 OOC 模塊內部定義的時鍾名稱。傳播到 OOC 模塊輸出的時鍾是根據連接到模塊端口的網線命名的,而不是根據它在模塊內部的名稱,即使時鍾在模塊 XDC 內部被重命名。
- 如果頂級約束需要引用來自 OOC 模塊的時鍾,則應使用諸如
get_clocks -of_objects [get_pins <MODULE_OOC_OUTPUT_CLOCK_PORT>]
之類的查詢語句。
1.5 約束范圍
如果有需要,可以選擇將來自某個 XDC 文件的約束范圍限定到特定的模塊或單元。這對於在沒有任何頂層信息的情況下創建和應用約束到子級模塊很方便。模塊級約束必須獨立於頂級約束開發,並且必須盡可能通用,以便它們可以在各種環境中使用。它們也不得影響超出塊范圍的任何邏輯。默認情況下,Vivado 中生成的所有 IP 核都使用此機制將其約束加載到工程中。
1.5.1 XDC 范圍屬性
-
SCOPED_TO_REF:此屬性采用模塊名稱。約束僅應用於指定模塊的所有實例。
-
SCOPED_TO_CELLS:此屬性采用分層單元名稱列表。約束范圍被單獨應用於每個分層單元。
-
SCOPED_TO_REF + SCOPED_TO_CELLS:如果指定了這兩個屬性,則約束將應用於 SCOPED_TO_CELLS 列表的每個單元格,位於 SCOPED_TO_REF 指定的模塊內。
# SCOPED_TO_REF 指定模塊名:
set_property SCOPED_TO_REF uart_tx_ctl [get_files uart_tx_ctl.xdc]
# SCOPED_TO_CELLS 指定實例名:
set_property SCOPED_TO_CELLS uart_tx_i0/uart_tx_ctl_i0 [get_files uart_tx_ctl.xdc]
# SCOPED_TO_CELLS 指定實例名,SCOPED_TO_REF 指定上層模塊名:
set_property SCOPED_TO_REF uart_tx [get_files uart_tx_ctl.xdc]
set_property SCOPED_TO_CELLS uart_tx_ctl_i0 [get_files uart_tx_ctl.xdc]
當一個模塊被例化多次時,可使用如下約束。ORIG_REF_NAME
指定模塊名。
set_property SCOPED_TO_REF [get_cells -hierarchical -filter {ORIG_REF_NAME == uart_tx_ctl}] [get_files uart_tx_ctl.xdc]
1.5.2 XDC 范圍機制
除了端口,約束范圍依賴於 current_instance 機制,它是 (SDC) 標准的一部分。當使用 current_instance 命令將范圍設置為某個設計層次結構時,對象查詢命令只能返回包含在該級別或以下級別的對象。
唯一的例外是時序時鍾對象和網表端口:
- 時序時鍾對象由 create_clock 或 create_generated_clock 定義。無論當前的實例設置如何,它們在整個設計中都是可見的。 get_clocks 命令可以查詢當前實例中不存在或傳播到當前實例之外的時鍾。 Xilinx 不建議在構建范圍約束時在時鍾上定義時序例外,除非時鍾完全包含在當前實例中。要在 XDC 中引用時鍾時,必須已經先定義了該時鍾。當引用在定義之前時,可能需要更改項目中 XDC 文件的順序。
- 即使使用 current_instance 命令將范圍設置為較低級別的實例時,get_ports 命令依然會返回頂級端口。但是,當使用 read_xdc -ref/-cells 命令讀取范圍為較低級別實例的 XDC 文件時,或者在設置 SCOPED_TO_REF/SCOPED_TO_CELLS 文件屬性后加載設計時,get_ports 命令的行為是不同的:
- 與 get_ports 一起使用的端口名稱是作用域實例接口的端口名稱,而不是頂級端口名稱。
- 如果作用域實例端口直接連接到頂級端口,則 get_ports 命令返回頂級端口,並將約束應用於頂級端口。
- 如果在作用域實例端口和頂級端口之間存在任何單元,包括 IO 和時鍾緩沖器,則 get_ports 命令變為 get_pins 命令並返回作用域實例引腳。
1.5.3 范圍查詢指南
- get_cells/get_nets/get_pins 對象查詢僅限於作用域實例及其子級別。其中對象的 NAME 屬性顯示了對象相對於頂層的完整層次結構路徑,而不僅僅是范圍實例。
如果在 NAME 屬性上使用 get_* 命令的 -filter 選項,則必須使用 glob 字符串匹配運算符,並提供以 * 開頭的模板。例如get_nets -hierarchical -filter {NAME =~ *clk}
。 - 如果 Block/IP 的端口直接連接到頂級端口,則 get_ports 返回頂級端口。否則,get_ports 返回一個分層引腳。
- 網表輔助命令也有范圍: all_ffs 、all_latches 、all_rams 、all_registers 、all_dsps 、all_hsios 僅返回當前實例中包含的實例。
- IO 輔助命令不能在范圍約束 XDC 中使用: all_inputs 、all_outputs
- 時鍾命令沒有作用域,將返回您設計的所有時序時鍾。get_clocks、all_clocks
- 可以通過使用
get_clocks -of_objects
探測網表,查詢頂級和本地時鍾對象。- 使用
get_clocks -of_objects [get_ports <interfacePinName>]
檢索進入當前實例的時鍾 - 使用
get_clocks -of_objects [get_pins <instName/outPin>]
檢索當前實例內部自動生成的時鍾,其中 instName 是時鍾生成器實例。
- 使用
- 使用
-of_objects
選項可以查詢設計中的任何對象:例如:get_pins -leaf -of_objects [get_nets local_net]
- 支持查詢連接到當前實例接口網絡的頂級端口:get_ports -of_objects [get_nets <scoped_instance_net>]
- 不允許查詢IP/子模塊接口引腳。例如
get_pins clk
返回錯誤。 - 路徑跟蹤命令也有作用域:all_fanin/all_fanout 遍歷作用域設計並在其邊界處停止。
- 使用 get_cells/get_pins/get_nets 命令並指定詳細的路徑,而不是使用帶有 -clock 選項的 all_registers 命令來查詢連接到特定時鍾的所有單元。因為 all_registers 返回的列表可能非常大,而只有少數對象需要約束。這會對運行時產生負面影響。
1.5.4 范圍時序約束指南
為避免對頂層設計產生負面影響,重要的是確保給 IP 或模塊編寫的時序約束不會傳播到其范圍之外。例如,進入到 IP 或模塊的兩個時鍾,在其范圍約束 XDC 中定義不相關路徑,但這種不相關性可能不適用於整個設計的其余部分。如果這兩個時鍾在設計的其余部分相關時,這個約束則會引起問題。再如,范圍約束 XDC 文件中定義的時序例外可能比頂級約束具有更高的優先級,並且可以覆蓋它們,這同樣是不希望的。為避免這種情況,建議將約束應用於 IP 本地的網表對象。在兩個全局時鍾之間存在不相關路徑的情況下,時序例外路徑也必須從 IP 內的一組起點單元應用到 IP 內的另一組終點單元。這種技術被稱為點對點時序例外而不是全局時序例外。
1.5.5 IP/子模塊XDC的推薦約束規則
塊級約束必須符合以下規則:
-
如果期望頂層設計中定義時鍾,則不要在塊級約束中創建時鍾。可以使用
get_clocks -of_objects
的方式在塊內查詢時鍾。示例:set blockClock [get_clocks -of_objects [get_ports clkIn]]
如果確實需要在模塊內部定義時鍾,則它必須位於輸入或雙向端口上,並且此端口必須直接驅動輸入/雙向緩沖器的。再或者位於創建/轉換時鍾的單元的輸出上。例如:帶有輸入緩沖器的輸入時鍾、時鍾分頻器和 GT 恢復時鍾。
-
僅當端口直接連接到頂級端口,並且 I/O 緩沖區在 IP 內例化時,才指定輸入和輸出延遲。
-
不要定義兩個未綁定到 IP 的時鍾之間的時序例外。
-
不要按名稱引用時鍾,因為名稱可能會因頂級時鍾名稱或模塊被多次例化而有所不同。
-
如果模塊可以在同一個頂層設計中多次實例化,則不要添加布局約束。
1.6 約束效率
1.6.1 檢閱約束覆蓋率
在編寫時序約束時,應當保持約束簡單,並僅在相關網表對象上指定約束。低效約束會導致更長的運行時間和更大的內存消耗。低效約束還可能產生不當約束,因為時序例外覆蓋的路徑可能會比預期更多,並與其他約束發生沖突。
Vivado 提供了多種獲取時序例外反饋的方法:
- 方法學檢查 XDCB-1 ( report_methodology ) 報告引用大型對象集合(超過 1000)的時間約束。
- 報告例外命令 (report_exceptions) 提供有關已定義的時序例外的覆蓋和沖突信息。
Xilinx 建議仔細分析以下報告:
-
report_exceptions -scope_override
該報告會列出頂層時序約束和范圍時序約束部分或全部重疊的部分。但是,它不會報告一個范圍時序約束和另一個范圍時序約束間的重疊部分。此選項可用於驗證IP 約束沒有被用戶的頂級約束未覆蓋 。 -
report_exceptions –coverage
該報告為每個時序例外提供了一個邏輯路徑的覆蓋范圍。將傳遞給時序例外的對象數量與有效覆蓋的起點和終點數量進行比較。我們應該注意查看在對象數量和起點/終點數量之間具有顯着差異的約束。 -
report_exceptions –ignored
此報告提供了被其他時序約束覆蓋的時序約束列表(例如,被 set_clock_group 覆蓋的 set_false_path)。我們應該檢查有覆蓋的約束的正確性或刪除無用的約束。 -
report_exceptions –ignored_objects
此報告提供了被忽略的起點和終點的列表。例如,不存在從這些起點出發的路徑或到不存在到達這些終點的路徑。
1.6.2 提升約束運行效率
由於設計中的管腳數是單元數的數倍,因此使用 get_pins 比 get_cells 會對運行時間產生重大影響。在處理 XDC 約束(例如 open_checkpoint 運行時)或執行 Tcl 腳本時,可能會遇到運行效率下降。 Xilinx 建議利用引腳和單元對象之間的關系來加快大量引腳查詢的運行時間。與其在設計中的所有引腳中直接根據引腳名稱查找特定的引腳列表,不如先找到所需引腳的單元,然后再對這一次查詢結果進行過濾,以此對所需引腳進行查詢。
# 直接對整個設計中的所有PIN進行查詢,效率低
get_pins –hier * -filter {NAME=~xx*/yy*}
# 可以替換成如下兩種方法,以提升運行效率
get_pins –filter {REF_PIN_NAME=~yy*} –of [get_cells –hier xx*]
get_pins –filter {REF_PIN_NAME=~yy*} –of [get_cells –hier * -filter {NAME=~xx*}]
# 如下示例,直接查詢PIN
set_max_delay 15 -from [get_pins -hier -filter {NAME=~*/aclk_dpram_reg*/*/CLK}] \
-to [get_cells -hier -filter {NAME=~*/bclk_dout_reg*}] -datapath_only
# 可替換如下寫法
set_max_delay 15 -from [get_pins -of [get_cells -hier –filter
{NAME =~ *aclk_dpram_reg*/*}] -filter {REF_PIN_NAME == CLK}] \
-to [get_cells -hier bclk_dout_reg*] -datapath_only