從平面規劃到單元的汪洋
上一篇指南裡你搭好了平面規劃——你定下晶片的外形輪廓,把大塊(記憶體、類比孤島)停放就位,並畫出了供電網的環與條帶。把它想像成一間空蕩蕩的開放式辦公室:牆立起來了,電梯和供電管道也鋪好了,可一張桌子都還沒擺。佈局這一步要做的,就是把桌子搬進來——而且數量*多得很*。
這些桌子就是標準單元:那些小巧、事先設計好的邏輯積木(一個 AND 閘、一個正反器、一個緩衝器),由你的合成工具產出。每個單元都做成同樣的高度,於是它們能像書架上的書一樣卡進一條條水平的列;但寬度會隨它承載的邏輯多寡而不同。一個實際的區塊可以容納幾十萬到幾百萬個這樣的單元。佈局要為它們之中的每一個決定 (x, y) 安身之處。
而且佈局並非憑空進行——它必須為後面繞線階段要走的線留出空間,繞開平面規劃預留的阻擋區,並尊重標準單元必須對齊的那些列。一旦做錯,無論流程的其餘部分多麼巧妙,晶片都會無法繞線或時序失敗。這正是佈局位於前段、且至關重要的原因。
全域佈局與詳細佈局
工具不會一口氣把一百萬個單元放到它們的最終位置——這個問題太龐大了,沒法精確求解。它轉而分兩遍來做,先粗後細,就像你會先決定每件家具放進*哪個房間*,然後再把每把椅子推到位。
全域佈局是粗略的那一遍。它幾乎把單元當成流體來處理:把它們鋪展到整片區域上,大致平衡密度、並把有連接關係的單元拉到一起,最佳化的是一個把總線長和擁擠程度揉在一起的成本函數。在這個階段,單元可以坐在*任何地方*——它們可以重疊,也還不必和列對齊。目標是一個整體上良好的排布,而不是一個可製造的排布。
詳細佈局是精細的那一遍。它接過那團粗略的雲,把每個單元輕推到一個真實、合法的安身處——卡進某一列、對齊到佈局網格、彼此不重疊——同時盡量少擾動全域的格局。它做的是局部清理:交換相鄰單元、讓單元沿列滑動、填補空隙。可以把全域佈局想成是在劃分街區,而詳細佈局則是把每輛車端端正正地停進車位線之間。
密度與壅塞
有兩個詞聽起來很像,含義卻大不相同,把它們搞混是新手的經典陷阱。密度(或使用率)講的是*地面空間*——可用面積裡有多少被單元填滿了。壅塞講的是*線*——有多少連接想穿過某個區域,對比那裡實際容得下多少繞線軌道。兩者完全可以一個高、一個不高。
使用率是個簡單而老實的數字:可佈局面積裡被單元佔用的那一部分。
utilization = (total cell area) / (placeable row area) # e.g. 0.72 -> 72% full, ~28% left as breathing room # typical placement target ~0.60-0.80 depending on the design
微妙之處在這裡:一個區域可能只是適中地滿,卻已經無可救藥地壅塞了,因為一團連接密集的單元正好落在那兒,想穿過那塊小格子的線,遠多於能承載它們的金屬軌道數。這就好比一個停了七成滿的停車場(沒問題),和一個所有車都想同時擠出去的唯一出口(徹底堵死)之間的差別。佈局兩者都盯著,因為後面的繞線只有在線真的塞得下時才能成功。
由時序驅動的佈局
到目前為止我們談的都是空間和線。但佈局還是懂時序的——這可以說是它最重要的本職。記住,訊號沿一條線傳播是需要時間的,線越長意味著延遲越大。於是工具會讀取時序約束,盡量把處在緊張路徑上的單元在物理上挨近放,縮短它們之間的連線。
最要緊的那條路徑就是關鍵路徑:兩個暫存器之間最慢的那條通路,也是富餘裕度最少的一條。如果佈局讓一條關鍵路徑上的單元漂得很遠,單單線延遲就能撐爆你的時序預算——而且後續再怎麼最佳化也無法完全挽回。於是工具會給這些單元一種磁吸般的吸引力,在仍然兼顧密度和壅塞的前提下,把它們拉緊。
下面就是工具在佈局時要對標的時序目標——一個時脈週期,它必須把每一條暫存器到暫存器的路徑都塞進這個週期之內:
# Clock the design is placed to honor (vendor-neutral SDC) create_clock -name CLK -period 1.25 [get_ports clk] ;# 800 MHz # A signal launched at one register must arrive at the next # WITHIN one period (minus setup) — placement keeps the cells # on each path close so wire delay does not eat that budget.
合法化
經過全域佈局和詳細佈局之後,單元*差不多*都各就各位了——但仍有少數可能重疊了一絲絲、偏離網格一星半點,或者跨在兩列的邊界上。合法化就是那道收尾整理的工序,把佈局弄得嚴格*合法*:每個單元都卡進某一列、對齊到製造網格、緊貼著鄰居放好,既不重疊,也沒有非法的空隙。
想像一堆胡亂塞上書架的書,有的歪著,有的探出了邊緣。合法化就是那位圖書館員,把每一本都推到端端正正地立在架子上、書脊對齊、彼此不疊。關鍵在於,它會盡量讓每個單元移動*最小*的距離來擺正它——因為它每挪動一個單元都會改變線長,可能擾亂前面幾遍辛辛苦苦才達成的時序。
- 跑全域佈局:把單元鋪展開以平衡密度、並把有連接關係的單元拉到一起,允許重疊和偏離網格的位置——這是粗稿。
- 跑詳細佈局:把單元輕推到各列上、形成乾淨的局部排布,在不擾動全域格局的前提下精修線長和壅塞。
- 做合法化:把每個單元卡到合法的列與網格位置上、互不重疊,每個都移動最小距離,讓時序和壅塞盡量貼近前幾遍得到的結果。
- 在如今已合法的佈局上重新核查時序和壅塞;若某個區域變差了,工具可以在交給時脈樹合成和繞線之前,對那裡做局部的重佈局和重合法化。
讀懂一張壅塞圖
在工具的圖形介面裡打開一份佈局、叫出一張壅塞圖,你會看到一幅活脫脫像是蓋在晶片上的氣象雷達圖。整片晶粒被切成一格格的小磚片,每片磚片按其繞線的擁擠程度被染上顏色——具體來說,是按需求與供給之比:有多少線想穿過那片磚片,對比有多少金屬軌道可供承載。
# Per-tile congestion (the number behind each color) overflow = wires_demanded - tracks_available # overflow <= 0 -> cool (blue/green): wires fit, healthy # overflow > 0 -> hot (yellow/red): more wires than tracks -> trouble
所以你就像看熱度一樣去讀它:大片的藍色和綠色是平靜、健康、可繞線的——線寬寬鬆鬆就放得下。黃色是開始緊張了。怒氣沖沖的紅色和橙色斑塊則是熱點,那裡繞線很可能會失敗,因為想穿過的線多過能容納它們的軌道。一份好的佈局看上去大體是冷色而均勻的;一片紅海,或是幾處兇狠的斑塊,就是你還沒開始繞線之前的一記警告。
是什麼造成了一團紅?往往是一團連接密集的邏輯被塞得太緊、是許多線不得不從一個硬核巨集單元旁擠著漏過去的地方,或是平面規劃在某個角落把繞線空間餓瘦了。解法呼應著前面幾節:讓工具把那片磚片裡的單元鋪開以降低局部密度、添加或調整佈局阻擋區把單元引開,或者當某個巨集單元卡住了一整條通道時,回頭重做平面規劃。