Vivado使用技巧(31):時鐘的約束方法

FPGADesigner發表於2018-09-27

時鐘的基礎知識

數字設計中,“時鐘”表示在暫存器間可靠地傳輸資料所需的參考時間。Vivado的時序引擎通過時鐘特徵來計算時序路徑需求,通過計算裕量(Slack)的方法報告設計時序空餘。時鐘必須有合適的定義,包含如下特性:

  • 定義時鐘樹的驅動管腳或埠,通常稱作源點
  • 通過週期和波形屬性來描述時鐘邊沿。
  • 週期(period)以ns為單位進行設定,與波形重複率相關。
  • 波形(waveform)以列表的形式給出,表中包含上升沿和下降沿在週期中的絕對時間,以ns為單位。

如下圖給出了兩個時鐘Clk0: period=10, waveform={0 5}、Clk1: period=8, waveform = {2 8}。
在這裡插入圖片描述
上述給出的只是時鐘的理想特徵。當時鍾進入了FPGA器件,通過時鐘樹傳遞時,時鐘邊沿會有延時,通常稱作時鐘網路延遲;噪聲或硬體表現會導致時鐘隨時可能發生變化,通常稱作時鐘不確定性,包括時鐘抖動、相位錯位等等。Vivado在時序分析時會考慮這些非理想因素以得到精確的時序裕量。

Xilinx FPGA器件內部有專用的硬體資源,支援大量設計時鐘的使用。通常板子上有一個外部元件(如有源晶振)產生時鐘訊號,通過輸入埠進入器件內部。外部時鐘可以通過MMCM、PLL、BUFR等特殊原語生成其它時鐘,也可以由LUT、暫存器等常規單元進行轉換(通常稱作門控時鐘)。本文將講述如何根據應用情況定義時鐘。


主時鐘Primary Clock

主時鐘通常由兩個來源:(1).板級時鐘通過輸入埠進入設計;(2).GT收發器的輸出管腳(如恢復時鐘)。主時鐘必須與一個網表物件相連,該物件代表了所有時鐘邊沿的開始點,並且在時鐘樹中向下傳遞。也可以說,主時鐘的源點定義了0時刻,Vivado靠此來計算時鐘延遲和不確定性。

主時鐘只能通過create_clock命令來定義,且必須放在約束的開始,這是因為其它時序約束幾乎都要參考主時鐘。下面給出兩個主時鐘的例子。第一個例子如下圖所示,採用單端時鐘輸入:
在這裡插入圖片描述
板級時鐘通過sysclk埠進入FPGA,通過一個輸入緩衝器和一個時鐘緩衝器後到達暫存器。使用如下命令定義:

create_clock -period 10 [get_ports sysclk]  #10ns週期,50%佔空比,無相移
create_clock -name devclk -period 10 -wavefor {2.5 5} [get_ports sysclk]  #板級時鐘名稱devclk,10ns週期,25%佔空比,90°相移

第二個例子如下圖所示,採用差分時鐘輸入,這也是高速時鐘的輸入方式:
在這裡插入圖片描述
上圖中差分時鐘驅動一個PLL,定義主時鐘時必須只建立差分緩衝器的正極輸入。如果同時建立了正極、負極輸入,將會導致錯誤的CDC路徑。如“create_clock -name sysclk -period 3.33 [get_ports SYS_CLK_clk_p]”。


虛擬時鐘Virtual Clock

這種型別的時鐘對於初學者來說用的可能很少,虛擬時鐘通常用於設定輸入和輸出的延遲約束。之所以稱為“虛擬”,是因為這種時鐘在物理上沒有與設計中的任何網表物件相連。定義時使用create_clock命令,但無需指定源物件。在下列情況需要用到虛擬時鐘:

  • 所有設計時鐘都不是外部器件I/O的參考時鐘。
  • FPGA的I/O路徑與一個內部生成的時鐘相關,但是該時鐘不能合適地通過對板級時鐘計時來生成(如兩個週期的比不是整數)。
  • 希望為與I/O延遲約束相關的時鐘設定不同的抖動和延遲,但是不希望修改內部時鐘的特徵。

比如時鐘clk_virt的週期為10ns,且不與任何網表物件相連,可以這樣定義“create_clock -name clk_virt –period 10”,沒有指定objects引數。注意,虛擬時鐘必須在使用之前便定義好。


生成時鐘Generated Clock

生成時鐘是指在設計內部由特殊單元(如MMCM、PLL)或使用者邏輯驅動的時鐘。生成時鐘與一個上級時鐘(注:官方稱作master clock,為與primary clock作區分,這裡稱作上級時鐘)相關,其屬性也是直接由上級時鐘派生而來。上級時鐘可以是一個主時鐘,也可以是另一個生成時鐘。

生成時鐘使用create_generated_clock命令定義,該命令不是設定週期或波形,而是描述時鐘電路如何對上級時鐘進行轉換。這種轉換可以是下面的關係:

  • 簡單的頻率分頻
  • 簡單的頻率倍頻
  • 頻率倍頻與分頻的組合,獲得一個非整數的比例,通常由MMCM或PLL完成
  • 相移或波形反相
  • 佔空比改變
  • 上述所有關係的組合

Vivado計算生成時鐘的延遲時,會追蹤生成時鐘的源管腳與上級時鐘的源管腳之間的所有組合和時序路徑。某些情況下可能只希望考慮組合邏輯路徑,在命令列後新增-combinational選項即可。

這裡先解釋一下本文甚至本系列大量使用的兩個詞,埠(Port)管腳(Pin)。埠通常用get_ports命令獲取,管腳使用get_pins命令獲取。二者的含義是不同的,但管腳的範圍更廣泛,比如設計中用到的一個暫存器都有3個管腳:clk、D和Q。下面給出幾個定義生成時鐘的例子:

1.簡單的2分頻

下圖中,主時鐘clkin通過埠進入FPGA,使用一個暫存器REGA對其2分頻,得到的生成時鐘clkdiv2驅動其它的暫存器管腳。
在這裡插入圖片描述
可以採用如下兩種方法對生成時鐘進行約束:

#定義主時鐘,週期10ns,50%佔空比
create_clock -name clkin -period 10 [get_ports clkin]
#約束方法1,主時鐘作為源點
create_generated_clock -name clkdiv2 -source [get_ports clkin] -divide_by 2 [get_pins REGA/Q] 
#約束方法2,REGA的始終管腳作為源點
create_generated_clock -name clkdiv2 -source [get_pins REGA/C] -divide_by 2 [get_pins REGA/Q]  

約束命令中使用**-source選項來設定上級時鐘,但如上所示,該選項只能設定為一個埠或管腳型別的網表物件,不能直接設定為時鐘型別物件。上面約束使用-divide_by選項設定分頻係數,此外還可以使用-edges**選項,如下所示:

#該約束與上面等效
create_generated_clock -name clkdiv2 -source [get_pins REGA/C] -eedges {1 3 5} [get_pins REGA/Q]

-edges的引數為一個列表,該列表通過主時鐘的邊沿來描述生成時鐘的波形。列表中的值為主時鐘邊沿的序號(注意觀察上圖),由時鐘上升沿開始,定義了生成時鐘邊沿的時間點。

2.改變佔空比與相移

如果僅需要改變時鐘的相移,使用**-edge_shift**選項可以正向或反向設定每一個生成時鐘波形的相移量。注意,-edge_shift選項不能與-devide_by、-multiply_by、-invert選項同時使用。下圖中上級時鐘為clkin,進入mmcm0單元,產生一個25%佔空比、相移90°的時鐘:
在這裡插入圖片描述
可以採用如下方法對生成時鐘進行約束。使用上級時鐘的1、2、3標號邊沿(即0ns、5ns、10ns)定義生成時鐘,為了得到預期波形,1和3標號邊沿要分別移動2.5ns,得到2.5ns、5ns、12.5ns的波形。

#定義主時鐘,週期10ns,50%佔空比
create_clock -name clkin -period 10 [get_ports clkin]
#定義生成時鐘,週期10ns,25%佔空比,90°相移
create_generated_clock -name clkshifit -source [get_pins mmcm0/CLKIN] -edges {1 2 3} -edge_shift {2.5 0 2.5} [get_pins mmcm0/CLKOUT] 

3.同時倍頻與分頻

這種情況通常用於定義MMCM或PLL的輸出,一般使用這些IP核時會自動建立相應約束。考慮上例中的圖,假設MMCM將上級時鐘倍頻到4/3倍,無法直接倍頻,需要同時使用-divede_by和-multiply_by選項來實現:

create_clock -name clkin -period 10 [get_ports clkin] #定義主時鐘
#定義生成時鐘,4/3倍頻 
create_generated_clock -name clk43 -source [get_pins mmcm0/CLKIN] -multiply_by 4 -divide_by 3 [get_pins mmcm0/CLKOUT] 

4.僅通過組合路徑追蹤上級時鐘

前面簡單介紹了-combinational選項的使用,為了更好理解,這裡舉一個具體例子。下圖中,上級時鐘同時傳遞到暫存器和多路選擇器中,暫存器對時鐘進行2分頻。多路選擇器從暫存器的2分頻時鐘和上級時鐘中選擇一個作為生成時鐘輸出。
在這裡插入圖片描述
顯而易見,從上級時鐘到生成時鐘有兩條路徑,一條為時序路徑,一條為組合路徑。如果我們只希望考慮組合路徑上的延遲時,定義生成時鐘時就需要使用-combinational選項。


自動生成時鐘

這種型別時鐘算是生成時鐘的一種特例,“自動”是指在已經定義了上級時鐘的情況下,Vivado會自動為時鐘管理單元CMBs(Clock Modifying Blocks)的輸出管腳建立約束。官方稱作Automatically Derived Clocks或Auto-generated Clock。

7系列FPGA的CMB單元包括MMCM、PLL、BUFR、PHASER;UltraScale系列FPGA的CMB單元種類與數量更多,這裡不陳列。如果約束中已經存在使用者在某一網表物件上定義的時鐘,則不會建立相同物件上的自動生成時鐘。

下面給出一個具體例子。下圖中上級時鐘clkin驅動clkip/mmcm0單元的CLKIN輸入,該單元是一個MMCME2資源的例項。則自動生成時鐘的定義源點為clkip/mmcm0/CLKOUT,頂層與此源點連線的網路名為clkip/cpuClk,自動生成時鐘的名字便是cpuClk。
在這裡插入圖片描述
如上所述,Vivado會自動建立自動生成時鐘的名稱(Name),如果兩個名稱發生衝突也會自動新增字尾,如usrclk、usrclk_1等等。Vivado也支援對已經建立好的自動生成時鐘重新命名,但很少用到,這裡不做介紹。


時鐘組Clock Group

很多初學者應該也沒有接觸過時鐘組這個概念。預設情況下,Vivado會測量設計中所有時鐘之間的路徑時序。新增如下兩種約束可以控制該功能:

  • set_clock_groups:建立時鐘組,Vivado不會對不同時鐘組的時鐘之間進行時序分析。
  • set_false_path:將兩個時鐘之間的路徑設定為false path後,不會對該路徑進行任何時序分析。

劃分時鐘組通常有兩個依據:(1).原理圖或時鐘網路報告中的時鐘樹拓撲圖,判斷哪些時鐘不應該放在一起做時序分析;(2).時鐘互動報告檢視兩個時鐘間存在的約束,判斷它們是否有共享的主時鐘(代表是否有已知的相位關係)或者是否有公共週期。

但要明白,我們設定時鐘組的目的還是為了保證設計在硬體中能正常工作,因此我們必須確保這些忽略了時序分析的路徑有合適的再同步電路或非同步資料傳輸協議。根據時鐘間的關係,可以做如下分類:

  • 同步時鐘:即兩個時鐘間有可預知的相對相位,通常它們的時鐘樹源自網表中的同一個根,且有一個公共週期。
  • 非同步時鐘:兩個時鐘間有無法預知的相對相位。比如兩個獨立的晶振訊號通過兩個輸入埠進入FPGA中,生成兩個時鐘。由於兩個主時鐘沒有明確的相位關係,兩個生成時鐘間便是非同步的。
  • 不可擴充套件時鐘:官方稱作Unexpandable Clocks,是指時序引擎在1000個週期內無法判斷兩個時鐘是否有公共週期。這種情況通常發生在兩個時鐘週期比是一個特殊的分數,比如一個主時鐘通過MMCM生成一個週期為5.125ns的時鐘clk1和一個週期為6.666ns的時鐘clk2,儘管它們在時鐘樹的根上有一個確定的相位關係,但是在1000個週期內時鐘上升沿無法再次對齊。

1.非同步時鐘組

同步時鐘可以安全地進行時序分析。非同步時鐘和不可擴充套件時鐘雖然通過時序分析也會得到一個裕量值,但這個值不可作為可靠結果。從這個角度出發,不可擴充套件時鐘也可以視作一種特殊的非同步時鐘。這就需要通過設定時鐘組來忽略非同步時鐘的時序路徑上的時序分析。

這裡舉個例子,一個主時鐘clk0通過MMCM生成兩個時鐘usrclk和itfclk;另一個主時鐘clk1通過另一個MMCM生成兩個時鐘clkrx和clktx。用如下命令建立非同步時鐘組:

set_clock_groups -name async_clk0_clk1 -asynchronous -group {clk0 usrclk itfclk} -group {clk1 clkrx clktx}
#如果時鐘名稱事先不知道,可以用如下寫法
set_clock_groups -name async_clk0_clk1 -asynchronous -group [get_clocks -include_generated_clocks clk0] -group [get_clocks -include_generated_clocks clk1]

2.互斥時鐘組

下面再介紹另一種會用到時鐘組的情況。某些設計會有幾個操作模式,不同操作模式使用不同的時鐘。這些時鐘通常由專用的時鐘選擇器進行選擇,如BUFGMUX和BUFGCTRL,最好不要用LUT作時鐘選擇器。

這些單元都是組合邏輯單元,Vivado會將所有輸入傳遞到輸出。在Vivado IDE中,幾個時序時鐘可以同時存在時鐘樹上,方便地同時報告所有操作模式。但是在硬體中這是不可能的,它們之間是互斥的,這些時鐘便稱作互斥時鐘

舉個例子,一個MMCM例項生成的兩個時鐘clk0和clk1,與一BUFGMUX例項clkmux相連,clkmux的輸出驅動設計時鐘樹。預設情況下,雖然clk0和clk1共享同一時鐘樹,且不能同時存在,Vivado還是會分析clk0和clk1之間的路徑。這個問題要通過設定互斥時鐘組來解決,達到禁止分析這兩個時鐘間路徑 的目的。約束如下:

set_clock_groups -name exclusive_clk0_clk1 -physically_exclusive -group clk0 -group clk1

在ASIC工藝中使用-physically_exclusive和-logically_exclusive代表不同的訊號完整性分析模式,但對於Xilinx FPGA而言,二者是等價的,都可以使用。


時鐘延遲、抖動與不確定性

本文的上述約束可以說都是對時鐘的理想特徵進行約束,為了更精確地進行時序分析,設計者還必須設定一些與執行環境相關的可預測變數和隨機變數。這部分也稱作時鐘的不確定性特徵。

1.時鐘延遲latency

經過板子上和FPGA器件內部的傳輸,時鐘邊沿到達目的地後會有一個確定的延遲。這個延遲可以分為兩個部分看待:

  • 網路延遲:也稱作插入延遲,指再FPGA內部傳輸帶來的延遲。Vivado會自動分析計算該延遲,佈線過程前只是一個粗略的估計,佈線後便可以得到一個精確的值。對於生成時鐘,包含其本身的網路延遲和上級時鐘的網路延遲兩部分。
  • 源端延遲:通常指FPGA器件外,時鐘進入源點前的傳輸延遲,這部分延遲與PCB設計相關,需要用set_clock_latency命令進行約束。

下面給出一個約束源端時鐘延遲的例子:

#設定最小源端延遲值
set_clock_latency -source -early 0.2 [get_clocks sysclk]
#設定最大源端延遲值
set_clock_latency -source -late 0.5 [get_clocks sysclk]

2.時鐘抖動jitter

對於ASIC器件來說,時鐘抖動通常代表了時鐘不確定性特徵;但對於Xilinx FPGA而言,抖動屬性被當作可預測變數看待。抖動有的需要單獨設定,有的在時序分析過程中自動計算。抖動分為兩種:

  • 輸入抖動:指實際時鐘邊沿與理想時鐘邊沿到達時刻之間的差值,使用set_iput_jitter命令為每個主時鐘單獨設定輸入抖動。但是不能直接為生成時鐘設定輸入抖動,這部分由工具自動計算,如果(1).生成時鐘由一個組合或時序單元建立,生成時鐘的抖動與上級時鐘相同;(2).生成時鐘由 MMCM或PLL驅動,生成時鐘的抖動為一個自動計算的值。
  • 系統抖動:指電源噪聲、板級噪聲或其它原因引起的整體的抖動,對於整個設計,使用set_system_jitter命令設定一個值即可,會應用到所有時鐘。

下面給出一個約束輸入抖動的例子:

#主時鐘傳輸過程中有±100ps的抖動
set_input_jitter [get_clocks -of_objects [get_clocks sysclk]] 0.1

不過,時鐘抖動對整個時鐘不確定性計算的影響不是太大。計算時鐘不確定性時對每條路徑都是獨立的,且主要依賴於時鐘拓撲結構、路徑上的時鐘對、時鐘樹上是否存在MMCM/PLL單元等其它因素。

3.附加的時鐘不確定性

使用set_clock_uncertainty命令可以根據需要為特定的時鐘關係定義附加的時鐘不確定性,這樣在時序分析時,可以為設計中的某些部分增加額外裕量。

前面文章說過XDC約束帶有順序性,後面的約束會重寫前面的約束。但在這裡,時鐘間的不確定性總是優先於單個時鐘的不確定性,不管約束順序如何。看下面的例子:

set_clock_uncertainty 2.0 -from [get_clocks clk1] -to [get_clocks clk2] 
set_clock_uncertainty 1.0 [get_clocks clk1]

這裡首先約束從clk1到clk2有一個2ns的時鐘不確定性,接著又約束clk1有1ns的時鐘不確定性,但是後面這條約束不會改動從clk1到clk2之間的關係。

相關文章