摘要: 1.1概述數字設計中,“時鐘”表示在寄存器間可靠地傳輸數據所需的參考時間。Vivado的時序引擎通過時鐘特徵來計算時序路徑需求,通過計算裕量(Slack)的方法報告設計時序空餘。時鐘必須有合適的定義,包含如下特性: ...
1.1概述 數字設計中,“時鐘”表示在寄存器間可靠地傳輸數據所需的參考時間。Vivado的時序引擎通過時鐘特徵來計算時序路徑需求,通過計算裕量(Slack)的方法報告設計時序空餘。時鐘必須有合適的定義,包含如下特性: 1)、定義時鐘樹的驅動管腳或端口,通常稱作根或源點。 2)、通過週期和波形屬性來描述時鐘邊沿。 3)、週期(period)以ns爲單位進行設定,與波形重複率相關。 4)、波形(waveform)以列表的形式給出,表中包含上升沿和下降沿在週期中的絕對時間,以ns爲單位。
如下圖給出了兩個時鐘 Clk0: period=10, waveform={0 5} Clk1: period=8, waveform = {2 8} 上述給出的只是時鐘的理想特徵,實際上當時鐘通過外部的晶振或者芯片時鐘,通過FPGA的管腳進入FPGA內部邏輯,時鐘會產生延遲、飄逸、抖動、失真等現象,所以在硬件設計和軟件設計上都必須考慮時鐘的影響。 1)、應該增加時鐘的穩定性,減少抖動和失真,在一些工業場合還要考慮溫度導致的時鐘偏移; 2)、高頻的時鐘應該走專用的FPGA時鐘管腳 3)、異步時鐘傳輸數據需要正確採取跨時鐘域的解決辦法 4)、使用VIVADO軟件進行正確的時序約束設計
1.2主時鐘Primary Clock 主時鐘通常由兩個來源: 1)、板級時鐘通過輸入端口進入設計; 2)、GT收發器的輸出管腳(如恢復時鐘)。主時鐘必須與一個網表對象相連,該對象代表了所有時鐘邊沿的開始點,並且在時鐘樹中向下傳遞。也可以說,主時鐘的源點定義了0時刻,Vivado靠此來計算時鐘延遲和不確定性。
主時鐘只能通過create_clock命令來定義,且必須放在約束的開始,這是因爲其它時序約束幾乎都要參考主時鐘。下面給出兩個主時鐘的例子。第一個例子如下圖所示,採用單端時鐘輸入: 如上圖,板級時鐘通過sysclk端口進入FPGA,通過一個輸入緩衝器和一個時鐘緩衝器後到達寄存器。下面是一些常用的時鐘約束定義方法:
1)、定義一個周圍爲10ns,佔空比50%,無相移的時鐘 create_clock –name sysclk -period 10 [get_ports sysclk] #10ns週期,, 2)、定義一個板級時鐘devclk,週期10ns,佔空比25%相移90°。 create_clock -name devclk -period 10 -wavefor {2.5 5} [get_ports sysclk] 3)、GTP/GTX恢復時鐘 高速通信GTP/GTX的時鐘是來自於恢復時鐘,對於初學者來說可能還不清楚什麼是恢復時鐘,在做光通信部分的例子就知道了。光通信1條數據通道,可以從1條數據通道中恢復出來傳輸的時鐘,然後用這個時鐘同步用戶數據就不會出錯了。在GT0的引腳上定義rxclk時,gt0 / RXOUTCLK就是由MMCM驅動的所有生成的時鐘的時鐘源點, create_clock -name rxclk -period 3.33 [get_pins gt0/RXOUTCLK] 4)、採用差分時鐘輸入只要約束P端就可以 create_clock -name sysclk -period 3.33 [get_ports SYS_CLK_clk_p] 上圖中差分時鐘驅動一個PLL,差分時鐘輸入通過IBUFDS轉爲單端進入CLKIN1。如果同時創建了P和N端輸入,將會導致錯誤的CDC路徑。 1.3虛擬時鐘Virtual Clock 虛擬時鐘通常用於設定輸入和輸出的延遲約束。之所以稱爲“虛擬”,是因爲這種時鐘在物理上沒有與設計中的任何網表對象相連。定義時使用create_clock命令,但無需指定源對象。使用虛擬時鐘在設置IO delay約束的時候可以簡化我們的代碼量,增加可讀性,這個在我們最後分析udp通信RGMII時序約束中會用到。 下列情況需要用到虛擬時鐘: 1)、所有設計時鐘都不是外部器件I/O的參考時鐘。 2)、FPGA的I/O路徑與一個內部生成的時鐘相關,但是該時鐘不能合適地通過對板級時鐘計時來生成(如兩個週期的比不是整數)。 3)、希望爲與I/O延遲約束相關的時鐘設定不同的抖動和延遲,但是不希望修改內部時鐘的特徵。 比如時鐘clk_virt的週期爲10ns,且不與任何網表對象相連,可以這樣定義: create_clock -name clk_virt –period 10 沒有指定objects參數。注意,虛擬時鐘必須在使用之前便定義好。
虛擬機時鐘的應用 1)、用於IO延遲案例1 create_clock -name clk –period 10 [get_ports clkin] create_clock –name vclk –period 5 set_input_delay 6 –clock clk [get_ports dina] set_input_delay 6 –clock vclk [get_ports dinb] 以上時序關於虛擬時鐘的約束作用和經過MMCM的輸出時鐘作用是一樣的。
2、用於IO延遲案例2 create_clock -name clk –period 10 [get_ports clkin] create_clock –name vclk –period 5 set_clock_latency –source 1[get_clocks vclk] set_input_delay –clock vclk –max 4 [get_ports dina] set_input_delay –clock vclk –min 2 [get_ports dina] 由於外部數據沒有匹配的輸入同步時鐘,所以可以指定一個虛擬時鐘設置dina的時序約束。 1.4生成時鐘Generated Clock 生成時鐘是指在設計內部由特殊單元(如MMCM、PLL)或用戶邏輯驅動的時鐘。生成時鐘與一個上級時鐘(注:官方稱作master clock,爲與primary clock作區分,這裏稱作上級時鐘)相關,其屬性也是直接由上級時鐘派生而來。上級時鐘可以是一個主時鐘,也可以是另一個生成時鐘。 生成時鐘使用create_generated_clock命令定義,該命令不是設定週期或波形,而是描述時鐘電路如何對上級時鐘進行轉換。這種轉換可以是下面的關係: 1)、簡單的頻率分頻 2)、簡單的頻率倍頻 3)、頻率倍頻與分頻的組合,獲得一個非整數的比例,通常由MMCM或PLL完成 4)、相移或波形反相 5)、佔空比改變 6)、上述所有關係的組合 注意:Vivado計算生成時鐘的延遲時,會追蹤生成時鐘的源管腳與上級時鐘的源管腳之間的所有組合和時序路徑。某些情況下可能只希望考慮組合邏輯路徑,在命令行後添加-combinational選項即可。 以下是一些生成時鐘的案例
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]
2)使用2分頻-edges命令 約束命令中使用**-source選項來設定上級時鐘,但如上所示,該選項只能設定爲一個端口或管腳類型的網表對象,不能直接設置爲時鐘類型對象。上面約束使用-divide_by選項設置分頻係數,此外還可以使用-edges**選項,如下所示: #該約束與上面等效 create_generated_clock -name clkdiv2 -source [get_pins REGA/C] -eedges {1 3 5} [get_pins REGA/Q] -edges的參數爲一個列表,該列表通過主時鐘的邊沿來描述生成時鐘的波形。列表中的值爲主時鐘邊沿的序號(注意觀察上圖),由時鐘上升沿開始,定義了生成時鐘邊沿的時間點。
3)、改變佔空比與相移 如果僅需要改變時鐘的相移,使用**-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] 4)、同時倍頻與分頻 這種情況通常用於定義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] 5)、僅通過組合路徑追蹤上級時鐘 我們想在多路複用器輸出上創建一個生成的時鐘,以反映從主時鐘到多路複用器的組合路徑的延遲。 這是通過使用-combinational命令行選項完成的: create_generated_clock -name clkout -source [get_pins mmcm0/CLKIN] –combinational [get_pins MUX/O] 下圖中,上級時鐘同時傳遞到寄存器和多路選擇器中,寄存器對時鐘進行2分頻。多路選擇器從寄存器的2分頻時鐘和上級時鐘中選擇一個作爲生成時鐘輸出。 顯而易見,從上級時鐘到生成時鐘有兩條路徑,一條爲時序路徑,一條爲組合路徑。如果我們只希望考慮組合路徑上的延遲時,定義生成時鐘時就需要使用-combinational選項。 5)、ODDR驅動的轉發時鐘 在此示例中,在由ODDR單元驅動的輸出端口上創建了轉發時鐘。 轉發的時鐘參考驅動ODDR / CLKDIV引腳的主時鐘,並具有與主時鐘相同的週期(-divide_by 1): create_generated_clock -name ck_vsf_clk_2 \ -source [get_pins ODDRE1_vsfclk2_inst/CLKDIV] -divide_by 1 [get_ports vsf_clk_2] 6)、自動生成時鐘 這種類型時鐘算是生成時鐘的一種特例,“自動”是指在已經定義了上級時鐘的情況下,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也支持對已經創建好的自動生成時鐘重新命名,但很少用到,這裏不做介紹。 1.5 時鐘組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.6 時鐘延遲、抖動與不確定性
1)、時鐘延遲latency 經過板子上和FPGA器件內部的傳輸,時鐘邊沿到達目的地後會有一個確定的延遲。這個延遲可以分爲兩個部分看待: 網絡延遲: 也稱作插入延遲,指再FPGA內部傳輸帶來的延遲。Vivado會自動分析計算該延遲,佈線過程前只是一個粗略的估計,佈線後便可以得到一個精確的值。對於生成時鐘,包含其本身的網絡延遲和上級時鐘的網絡延遲兩部分。 源端延遲: 通常指FPGA器件外,時鐘進入源點前的傳輸延遲,這部分延遲與PCB設計相關,需要用set_clock_latency命令進行約束。 VIVADO IDE 使用set_clock_latency 設置時鐘在外部的延遲 # 最小延遲 set_clock_latency -source -early 0.2 [get_clocks sysClk] # 最大延遲 set_clock_latency -source -late 0.5 [get_clocks sysClk] 2)抖動 #主時鐘傳輸過程中有±100ps的抖動 set_input_jitter [get_clocks -of_objects [get_clocks sysclk]] 0.1 不過,時鐘抖動對整個時鐘不確定性計算的影響不是太大。計算時鐘不確定性時對每條路徑都是獨立的,且主要依賴於時鐘拓撲結構、路徑上的時鐘對、時鐘樹上是否存在MMCM/PLL單元等其它因素。 2)、附加的時鐘不確定性 使用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之間的關係。
關於時序約束時鐘部分的介紹就到這裏,掌握了這些基本的概念和理論是我們後面實戰的基礎。 |