在我實際涉及的項目中,基本沒有遇到多周期路徑約束的情況,所以之前關注的不多,為了鞏固基本知識,借此梳理這個約束。
1. 目的
目的就是說什么時候需要用到多周期約束?
Vivado、TimeQuest等時序引擎默認是按照單周期關系分析數據關系的,即數據在發起沿發送,在捕獲被捕獲,發起沿和捕獲沿相差一個周期。
但是很多情況是,數據路徑邏輯較為復雜,導致延時較大,使得數據無法在一個時鍾周期內穩定下來, 或者數據可以在一個時鍾周期內穩定下來,但是在數據發送幾個周期之后才使用;在這些情況中,設計者的意圖都是使數據的有效期從發起沿為起始直至數個周期之后的捕獲沿,這樣的意圖無法被時序分析工具猜度出來,必須由設計者在時序約束中指明;否則時序分析工具會按照單周期路徑檢查的方式執行,往往會誤報出時序違規;
說白了,就是根據用於設計需求,改變原有的時序檢查機制,從而避免非真實的時序違例或者時序過緊導致的資源浪費。
2. 單周期時序檢查
如上圖所示,為時序引擎默認的單周期檢查機制。確定建立時間路徑和保持時間路徑規則如下:
- 建立時間路徑
以第一個發送沿為基准(current launch),再向后尋找距此發送沿最近的一個捕獲沿(current capture),並將兩者的setup定為1個周期。
- 保持時間路徑
每確定一個建立路徑,都會檢查兩個保持時間路徑:1)確保當前發送沿推出的數據不被上一個捕獲沿給捕獲,即hold1;2)確保下一個發送沿推出的數據不被當前捕獲沿給捕獲,即hold2。對這描述,為了便於分析記憶,簡而言之,1)previous capture與current launch 構成一組檢查;2)current capture與next launch 構成一組檢查。
對於兩條保持時間的檢查,時序報告最終只會給出裕量最小的一條。此外,還需要記住:一旦確定建立時間路徑,保持時間路徑會自動根據規則做調整。
3. 使用方法
對於多周期路徑的約束語法,就一條指令:set_multicycle_path
set_multicycle_path <path_multiplier> [-setup|-hold] [-start|-end]
[-from ] [-to
] [-through <pins|cells|nets>]
參數名稱 | 含義 |
---|---|
-setup | 表示建立時間所需要的時鍾周期個數 |
-hold | 表示分析保持時間時,相較於默認的捕獲沿,實際捕獲沿偏差的時鍾周期個數 |
-start | 表示以目的端時鍾作為時鍾周期計數基准 |
-end | 表示以源端時鍾作為時鍾周期的計數基准 |
在默認的情況下,set_multicycle_path
- 對建立時間的分析是設置目的時鍾為多周期,即-setup 與-end 搭配使用;
- 對保持時間的分析是設置源時鍾為多周期,即-hold與-start搭配使用;
注意,上述是默認情況,-end和-start可以省略。根據場景不同會有所不同,並且數周期個數是往前還是往后也會不同,下面會介紹。
知道了命令參數的含義,結合第2節中的路徑規則的文字描述,那么默認檢查機制就應該是下面的表達:
set_multicycle_path 1 -setup -end -from [get_clocks s_clk] -to [get_clocks d_clk]
set_multicycle_path 0 -hold -start -from [get_clocks s_clk] -to [get_clocks d_clk]
實際上這個約束的意義,就是使引擎按照設計者的邏輯來重新定義路徑分析的規則。如果沒有深刻理解多周期路徑,是不知道怎么使用的。為什么有的時候只需要setup,有的時候又需要setup和hold,怎么加-start和-end又不清楚。所以結合文檔給出的4中案例來加深對多周期路徑的理解,並且學會約束語句的使用。
4. 案例
4.1 同一時鍾域
在同一時鍾域下,有的時候,我們希望第一個發送沿推出數據,經過N各周期才被被第二個寄存器的捕獲沿給捕獲。最常見的就是,時鍾使能控制數據捕獲的情形。
如上圖所示,假設使能信號周期為驅動時鍾的2倍,顯然如果按照默認的規則進行時序檢查,就會發生錯誤。
首先,修改建立時間路徑,使用語句
set_multicycle_path 2 -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
表示以目的時鍾為基准,往前數,第2個沿作為捕獲沿。
根據默認規則,保持路徑會自動調整,如圖AFTER。顯然這個時候的捕獲沿的確定是不合適的,會導致保持時序過緊(實現工具會通過增加數據延遲的方式,來優化時序,從而導致資源浪費,功耗增加)。所以增加修改hold規則語句:
set_multicycle_path 1 -hold -end -from [get_pins data0_reg/C] \
-to [get_pins data1_reg/D]
表示以目的時鍾為基准,相較於默認的捕獲沿,往后數1個周期的沿作為實際的捕獲沿。
綜上,得到最終的路徑關系,如下圖所示:
同理,針對setup延遲5個周期的情況,修改setup規則:
set_multicycle_path 5 -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
隨着hold關系的自動變化,得到如下圖所示的情形。
修改hold規則,約束語句如下:
set_multicycle_path 4 -hold -end -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
表示以目的時鍾為基准,相較於默認的捕獲沿,往后數4個周期的沿作為實際的捕獲沿,如下圖所示
還有一種修改hold的方式,將-end改為-start,對應的關系如下圖所示。
那么上面兩種情況有什么區別?實際上,是一樣的,因為兩時鍾為同一時鍾域。所以針對同一時鍾域,可以不考慮基准,即約束語句可以忽略-end或-start。但是設計者一定要心里清楚。
針對同一時鍾域的多周期路徑,一般語句可表示為:
set_multicycle_path N -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
set_multicycle_path N-1 -hold -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
4.2 兩時鍾同一周期,但有相移
對於發送和捕獲的兩時鍾是同周期,但存在相移。有兩種情況:clk2較clk1,正相移;clk2較clk1,負相移。
4.2.1 正相移
如下圖所示,clk2較clk1偏移+0.3ns。根據默認的檢查規則,建立和保持路徑已經在圖中表示出來。
可見,數據要在0.3ns之內滿足建立時間,這太緊了。反之,數據給的保持時間卻是-3.7ns,對於時序檢查又太寬松。先對建立時間放寬一些,使捕獲沿往后沿一個周期,約束如下:
set_multicycle_path 2 -setup -from [get_clocks CLK1] -to [get_clocks CLK2]
隨着建立路徑的修改,自動判斷新的保持路徑,如上圖所示。此時不需要在對保持路徑進行調整了,所以這種情況只需要修改建立路徑規則即可。
4.2.2 負相移
如下圖所示,clk2較clk1偏移-0.3ns。可見,此時的建立路徑和保持路徑即不太緊又不太松,可不進行多周期約束。
綜上,對於同周期不同相位的兩時鍾分析,必須要考慮相移方向和相移量。對於負相移,也不是絕對的不用添加約束,如果負相移量太大,實際上就可視為正相移,同樣需要添加約束。所以視情況而定。
4.3 從慢時鍾到快時鍾
假設捕獲時鍾為發送時鍾頻率的3倍,默認的檢測機制如上圖所示。在實際設計中,捕獲寄存器肯定需要一個使能信號,才能合理捕獲數據,這個時候會用到多周期路徑約束。同樣先修改建立路徑規則:
set_multicycle_path 3 -setup -from [get_clocks CLK1] -to [get_clocks CLK2]
隨着建立路徑的修改,自動判斷新的保持路徑,如上圖所示。這個時候的保持路徑檢查,會導致消耗過多的資源,增加面積,增加功耗,所以對保持路徑進行修改:
set_multicycle_path 2 -hold -end -from [get_clocks CLK1] -to [get_clocks CLK2]
注意,這種情況和同周期時鍾的多周期路徑約束有相似的地方。但是此時-hold一定要增加-end,表示以目的時鍾為基准,較默認路徑,往回數2個周期作為捕獲沿。
綜上,對於慢時鍾到快時鍾的情況,一般約束如下:
set_multicycle_path N -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
set_multicycle_path N-1 -hold -end -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
4.4 從快時鍾到慢時鍾
如上圖所示,clk1是clk2頻率的3倍。原本默認檢查規則,滿足setup檢查的有3條路徑(有兩條是紅線補充),因為最終時序報告只會給出最苛刻的一條,所以只保留黑色這條setup路徑。
針對這種情況,需要同時修改建立路徑和保持路徑:
set_multicycle_path 3 -setup -start -from [get_clocks CLK1] -to [get_clocks CLK2]
set_multicycle_path 2 -hold -from [get_clocks CLK1] -to [get_clocks CLK2]
分析這兩條約束語句:首先,-setup必須搭配-start,因為必須要以源時鍾為基准,再往后數;
然后,根據默認規則,保持路徑自動調整為紅色的路徑;最后,約束hold,因為hold默認搭配-start,所以以源時鍾為基准,往前數了2個時鍾沿。
綜上,對於快時鍾到慢時鍾的情況,一般約束如下:
set_multicycle_path N -setup -start -from [get_clocks CLK1] -to [get_clocks CLK2]
set_multicycle_path N-1 -hold -from [get_clocks CLK1] -to [get_clocks CLK2]
綜上所有案例,可總結到下圖中:
5. 補充說明
- 關於setup 和hold 搭配start和end之后,是往前數還是往后數周期個數
一開始被高老師的《vivado從此開始》中有關多周期路徑約束講述的一點誤導了,在對-hold參數描述時,是這樣寫的
-hold 表示分析保持時間時,相對於默認的捕獲沿實際捕獲沿應回調的時鍾周期個數
“回調”二字我理解偏了,以為有關hold,不論-start還是-end都往回數周期,結果始終分析不出正確的結果。最后細讀UG903,才看到下面一圖。
所以,不論是setup還是hold,都可能往前數或者往后數,看具體搭配的是-start還是-end。
- 時序過松、過緊?資源又過渡消耗?
對於多周期路徑約束的使用,我們無非就是要解決由於默認的規則導致時序誤判。但是什么時候覺得過松或者過緊呢?實際上要深刻理解時序分析原理之后,靈活應用於多周期路徑分析。
如下圖所示,默認的檢查規則:
建立時間和保持時間裕量計算公式如下:
setup slack = (current latch edge - current launch edge ) + Tskew - (Tdelay + Tco + Tsu)
= T + Tskew - (Tdelay + Tco + Tsu)
hold slack = Tdelay + Tco - (Th + previous latch edge - current launch edge)
= Tdelay + Tco - Th
以4.1節兩時鍾是同一時鍾域的情況舉例,建立路徑延后1個周期,如下圖所示:
代入公式得到:
setup slack = (current latch edge - current launch edge ) + Tskew - (Tdelay + Tco + Tsu)
= 2*T + Tskew - (Tdelay + Tco + Tsu)
hold slack = Tdelay + Tco - (Th + previout latch edge - current launch edge)
= Tdelay+Tco - (T+Th)
可見,建立時間裕量增加了(多加了一個周期T),而保持時間裕量減少了(多減了一個周期T),這就導致保持時間過緊,實現的過程中需要盡可能增加Tdelay,這樣又會導致資源過度消耗,所以需要調整保持時間。
另一種情況,4.2.1 正相移那節中,如果hold路徑為負-3.7ns,根據公式,我的理解是保持裕量增加太多(減掉一個負數),導致時序太寬松。定性的理解就是,由於上一個捕獲沿在當前發送沿的左邊(正相移導致),所以當前推送的數據,不太可能被上一個捕獲沿所捕獲。
參考文獻
-
《Vivado從此開始》
-
《UG903》