DeepDataFlow
Earlgrey in G2:用開源工具和 AI 助手跑百萬單元 SoC · 第 4 篇,共 6 篇

在獨立工具裡做簽核

用我們要交出去的檔案給完成的晶片做時序分析,揭露了兩個只在層次化流程裡才出現的 OpenROAD bug。我們修好兩個 bug、提交到上游,然後檢查了自己的工作。

發佈日期 2026-10-22 · 第 4 篇,共 6 篇 · DeepDataFlow

在第 3 篇的最後,晶片 v5 完成了繞線和 finish,15,056 個繞線違例都寫成已知的遺留問題。下一步是簽核時序。我們用業界的方式來做,而正是這個選擇找到了 bug。

簽核讀的是你要交出去的東西

佈局繞線工具可以從自己的資料庫報告時序,OpenROAD 也做得到。但是交給晶圓廠、或交給層次結構上一層的,是檔案:由佈局繞線工具寫出來的網表和寄生參數(SPEF)。簽核應該在獨立的工具裡讀取這些檔案。所以這個專案的規則是:簽核留在獨立的工具裡。OpenRCX 提取寄生參數,OpenROAD 寫出網表和 SPEF,再由獨立的 OpenSTA 讀取。

在佈局繞線工具裡分析時序,讀的是它的資料庫。簽核讀的是寫出來的檔案。
在佈局繞線工具裡分析時序,讀的是它的資料庫。簽核讀的是寫出來的檔案。

寄生提取需要每一層金屬的規則,而 sky130 只有五層。在我們 10 層的替代金屬堆疊上,OpenRCX 用原本的規則直接崩潰,而不是報錯。所以我們生成了規則,讓上面幾層複製下面幾層(metal 6–8 當作 metal 4,metal 9–10 當作 metal 5),並由此做出四個 corner,cworst 和 cbest 是 ±10%。這些規則是假的,凡是用到它們的數字,我們都會說明。

在 G2 裡,寄生提取是一個獨立的階段。它讀取完成的設計,每個 corner 寫一個 SPEF,並把檔案簽入設計資料管理器,放在網表旁邊。OpenSTA 的場景再從那裡取用兩者。

2:46

在 YouTube 觀看(英文) ↗在 Bilibili 觀看(中文) ↗

Bug 1:98,251 個驅動端沒有寄生參數

第一次獨立的 OpenSTA 執行警告有 98,251 個驅動端沒有寄生參數,還有 48,926 條「找不到實例」和 431,493 條「找不到 net」的訊息。這些驅動端裡大約 77,000 個根本沒有繞線,這是正常的。另外 21,000 個是真的有線,而且主要是整棵時脈樹。

可是 OpenROAD 用同一個 SPEF 讀自己的資料庫時,只有 490 個名字對不上。這個檔案對寫出它的工具沒問題,對任何其他工具卻是壞的。

最初錯誤的推論

Claude Code 的第一個答案是:時脈樹工具給它的 buffer 取錯了名字。這是一個說得通的說法,它還追蹤了一個 buffer 來佐證。工程師問了另一個問題:同一個檔案把那個模組裡合成出來的 net 都寫對了,帶著層次路徑。寫檔案的程式在那裡寫對了什麼,在這裡又寫錯了什麼?

這個問題把矛頭指向寫檔案的程式,而不是 CTS。工具在子模組裡建立的物件,存的是名字加上它所屬的模組。網表的寫出程式會讀這個模組。write_spef 卻沒有,所以 CTS 在層次結構裡建立的每一個 buffer,寫出來時都沒有路徑。

找到原因的那個問題:和同一個寫出程式寫對的地方比較。
找到原因的那個問題:和同一個寫出程式寫對的地方比較。

我們在 OpenROAD 裡修好了 write_spef,加上一個迴歸測試,並跑了原有的層次化測試:60 個全部沒有變化。在晶片上,「找不到實例」從 48,926 條變成 0,「找不到 net」從 431,493 條變成 164。整個調查都寫成了報告,包括最初錯誤的推論,以及修正前後的數字。

為什麼之前沒有出現?在 OpenROAD 裡面,資料庫是扁平而完整的,所以名字永遠對得上。只有讀取寫出來的檔案,才會揭露這個 bug。這就是簽核要獨立做的主要原因。由此還得到第二條規則:要修資料,而不是繞過去分析時序。

Bug 2:dummy load 放錯了模組

最後還剩一個缺口,在時脈樹的葉子 net 上。dummy load 是時脈樹工具為了平衡分支而加的小單元。在我們的晶片裡,每個時脈 buffer 都在 top_earlgrey 模組裡,但 CTS 卻把它的 14,348 個 dummy load 建立在晶片的最頂層模組,兩者之間沒有埠。

在 OpenROAD 裡資料庫是扁平的,所以每一條都還是一條 net。但是網表寫出程式會寫出兩條同名的 net:一條有驅動、沒有線,另一條有線、沒有驅動。13,566 條時脈 net 就這樣被拆開,619 pF 的時脈線電容裡,有 206.7 pF(33%)落在錯誤的那一半。

1:59

在 YouTube 觀看(英文) ↗在 Bilibili 觀看(中文) ↗

放錯模組的 dummy load,會在寫出來的網表裡把一條時脈 net 拆成兩條。
放錯模組的 dummy load,會在寫出來的網表裡把一條時脈 net 拆成兩條。

CTS 的修正:把每個 dummy load 建立在它所負載的 buffer 所在的模組裡。新的測試還發現了另一件事:OpenROAD 一個現有的參考輸出(gated_clock4)早就有同樣的拆分,有 18 個 dummy load 在最頂層模組裡。參考檔案裡也可能藏著 bug。其他 53 個 CTS 測試都沒有變化。

從 CTS 開始重跑晶片,要超過一天。我們改用一個五分鐘的腳本,在完成的 v5 資料庫裡把 13,553 個 dummy load 移到正確的模組,然後重新做寄生提取和時序分析。

24,531 → 13,531 → 8沒有寄生參數的時脈 buffer:修正前、SPEF 修正後、CTS 修正後
142,733 → 112OpenSTA 裡「未連接」的警告
98,251 → 73,824沒有寄生參數的驅動端,大部分根本沒有線

提交到上游

兩個 bug 都會影響每一個用 OpenROAD 跑層次化流程的人,所以我們把它們提交到上游:

  • 每個 bug 都縮小到幾個單元、用 OpenROAD 自己的測試風格重現,並先在最新的上游版本上確認。
  • 完整的測試套件都和參考輸出比對過:rcx、cts 和 dbSta 通過 142 個中的 142 個,rsz 通過 295 個中的 294 個,和修改之前一樣。
  • 每個修正一個分支。
  • 在工程師同意之前,什麼都沒有發布。

結果是兩個 issue 和兩個 pull request:issue #11839 和 #11840,以及 pull request #11841 和 #11842。截至 2026-10-22,它們都還開著,正在等待審查。

然後,我們檢查了自己的工作

G2 工具包裡的 OpenROAD 升級之後,較小的沙盒晶片顯示了兩個問題:pad 環上的地線缺口,以及沒有寄生參數的時脈 net。Earlgrey 也有嗎?這些檢查放進一個測試版本 v5a:兩個階段,不重新佈局繞線,和其他執行一樣記錄在版本表裡。

1:54

在 YouTube 觀看(英文) ↗在 Bilibili 觀看(中文) ↗

電源網路。 在 floorplan 上的檢查發現 VDD 斷開:416 個電源網路圖形和一千多個 pad 電源 pin 沒有連上。我們的電源網路腳本連到 pad 時,沒有指定 pad 的金屬層。這是我們自己的錯誤,從晶片 v2 就藏著,而下一個版本的修法已經知道了。

檢查本身也出了問題。斷開檢查只花了大約一分鐘;接著 OpenROAD 的短路檢查在一個核上跑了 9 小時 20 分鐘,沒有任何輸出。我們把它停掉,保留它已經寫下的結果,並把它記成一個 bug。VSS 沒有檢查,這也列在清單上。

寄生參數。 在同樣的網表和 SPEF 上跑了一個探測。73,824 個沒有寄生參數的驅動端聽起來很糟,但按原因分類之後,幾乎全部都是沒有線(71,633 個)或沒有負載(1,939 個),或者是埠。只有 120 個真的驅動著東西:這才是真正的缺口,而且很小。

G2 裡的 v5a 測試版本:README 裡寫著目的、結果和下一步,結果也顯示在版本表裡。
G2 裡的 v5a 測試版本:README 裡寫著目的、結果和下一步,結果也顯示在版本表裡。

一個發現是壞消息,一個是好消息,兩個都寫在下一個人會去看的地方:版本表裡的一行筆記、README、bug 清單,以及接續頁面。

簽核的數字,以及附帶的說明

兩個修正都做了之後,慢速 corner(ss、cworst)頂層 reg2reg 的 setup slack 在 10 MHz 下是 −7.87 ns,而且沒有任何暫存器的時脈 pin 缺少時脈。輸入輸出路徑還沒有時序例外,所以它們的數字還沒有意義,也還沒有 hold 報告。這些以及 VDD 的修正,都在晶片 v6 的計畫裡。

下一篇:工程師和 Claude Code 怎麼分工,包括工程師睡著的那個晚上。