pretty code

2026年5月18日 星期一

PDF 翻譯 Part 02

前天使用自製工具翻譯了一份文件,大概快 900 頁,下面是 API 使用情況。

=== Summary ===
Paragraphs translated : 4186
Input tokens          : 211,790
Output tokens         : 302,468
Estimated cost        : $7.0905 USD

還不賴,比一本書便宜。

另外,PDF 翻譯工具需改進,一個是動態調整 RPM 限制,另一個則是要考慮如何保存中間結果。

因為我遇到一個 5000 頁的文件,做到約 2250 頁時,系統當機,應該是我同時做很多事,導致記憶體用到快 16 G。


AI 給的 RPM 修改建議方向

先做:查你目前的狀態(5 分鐘)

查 Tier 在哪裡

方法 1:Console 介面

  1. 登入 console.anthropic.com
  2. 左側選單 → SettingsLimits(或 Plans & Billing
  3. 會看到「Tier 1 / 2 / 3 / 4」字樣,以及對應的 RPM、ITPM、OTPM 數字

方法 2:用 curl 直接問 API(更準)

打開 cmd 或 PowerShell,把 YOUR_KEY 換成你的 API key:

curl https://api.anthropic.com/v1/organizations/rate_limits ^
  -H "anthropic-version: 2023-06-01" ^
  -H "x-api-key: YOUR_KEY"

回傳的 JSON 裡會直接告訴你每個模型的 RPM/ITPM/OTPM,對照下面的表就知道 Tier:

Tier Sonnet RPM Sonnet ITPM
1 50 30,000
2 1,000 80,000
3 2,000 800,000
4 4,000 2,000,000

查 prompt caching 有沒有開

直接搜你的翻譯工具程式碼,看 system 那段:

# 在你的翻譯工具資料夾
findstr /S "cache_control" *.py
  • 有輸出 → 有開 caching
  • 沒輸出 → 沒開 caching

或更簡單:跑一次翻譯,在你的程式裡 print 出 response.usage,看有沒有這兩個欄位:

  • cache_creation_input_tokens(第一次跑會有)
  • cache_read_input_tokens(第二次以後會有)

兩個都是 0 → 沒開 caching。


然後:直接用 Batch API 改造你的工具

既然你選「注重速度,可以掛著等」,Batch API 是完美解答。我直接幫你準備好可以丟給 Claude Code 的修改說明。

Batch API 對你的好處(再次確認)

  • 不受 RPM 限制 — 5,000 段一次提交也沒事
  • 價格 5 折(input 和 output 都打 5 折)
  • 24 小時保證,實測通常 1–2 小時
  • 與 prompt caching 可疊加(更便宜)
  • ⚠️ 結果是「全部一起回」,不能邊跑邊看
  • ⚠️ 中途無法取消個別請求

給 Claude Code 的修改 prompt

你可以新開一個 Claude Code session,把這份貼過去(同時也把你現有的翻譯工具程式碼放在資料夾裡):

請幫我把這個 PDF 翻譯工具的 Claude API 呼叫,從「同步單筆呼叫」改成「Batch API + Prompt Caching」模式。

## 背景
- 目前工具:用 Anthropic SDK 逐段呼叫 `client.messages.create()`
- 模型:claude-sonnet-4-6(或更新的 Sonnet)
- 痛點:800 頁論文要跑 4 小時,撞到 RPM 限制
- 工作流:可以掛著等,不需即時看結果

## 修改目標
1. 加上 prompt caching(針對 system prompt + glossary)
2. 改用 Batch API 一次提交全部段落
3. 加入 batch 狀態輪詢與結果取回
4. 保留現有的「翻譯完回貼成 PDF 圖層」功能不變

## 詳細修改項目

### 1. Prompt Caching

修改現有的 API 呼叫,把 system prompt 和 glossary 標記為可快取:

```python
# 從這樣:
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=4096,
    system=SYSTEM_PROMPT,
    messages=[{"role": "user", "content": text_to_translate}]
)

# 改成這樣(system 改用 list 形式並加 cache_control):
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=4096,
    system=[
        {
            "type": "text",
            "text": SYSTEM_PROMPT + "\n\n" + glossary_text,
            "cache_control": {"type": "ephemeral"}
        }
    ],
    messages=[{"role": "user", "content": text_to_translate}]
)
```

重要:cache_control 加在 system prompt 的「最後」一個元素。
注意:cache 至少需要 1024 tokens 才會生效,如果 SYSTEM_PROMPT + glossary 不到,
請先確認 token 數(用 client.messages.count_tokens()),不夠就提早 return「caching 不適用」並用原本的方式呼叫。

### 2. Batch API 流程

新增一個模式(用 CLI 參數 --batch 切換),流程如下:

#### Step 1: 抽取所有要翻譯的段落
從 PDF 抽出所有段落,建立一個 list of (segment_id, text) tuple。

#### Step 2: 建立 batch requests

```python
from anthropic.types.messages.batch_create_params import Request

requests = []
for seg_id, text in segments:
    requests.append(Request(
        custom_id=seg_id,  # 用來回對映,例如 "page-12-para-3"
        params={
            "model": "claude-sonnet-4-6",
            "max_tokens": 4096,
            "system": [
                {
                    "type": "text",
                    "text": SYSTEM_PROMPT + "\n\n" + glossary_text,
                    "cache_control": {"type": "ephemeral"}
                }
            ],
            "messages": [{"role": "user", "content": text}]
        }
    ))
```

注意:一次 batch 最多 100,000 個請求或 256 MB,請判斷是否需要分批提交。

#### Step 3: 提交 batch

```python
batch = client.messages.batches.create(requests=requests)
print(f"Batch ID: {batch.id}")
print(f"Status: {batch.processing_status}")

# 把 batch.id 存到本地檔案(例如 .batch_state.json),
# 這樣程式中斷後還能用同個 batch_id 繼續輪詢
```

#### Step 4: 輪詢狀態

```python
import time

while True:
    batch = client.messages.batches.retrieve(batch.id)
    counts = batch.request_counts
    print(f"Status: {batch.processing_status} | "
          f"Done: {counts.succeeded}/{counts.processing + counts.succeeded + counts.errored + counts.canceled + counts.expired}")
    
    if batch.processing_status == "ended":
        break
    
    time.sleep(60)  # 每分鐘查一次
```

#### Step 5: 取回結果

```python
results = {}
for result in client.messages.batches.results(batch.id):
    if result.result.type == "succeeded":
        translated = result.result.message.content[0].text
        results[result.custom_id] = translated
    elif result.result.type == "errored":
        print(f"Error on {result.custom_id}: {result.result.error}")
        results[result.custom_id] = None  # 標記失敗,後面用單筆 API 重跑
```

#### Step 6: 重跑失敗的段落
對 results 裡值為 None 的 custom_id,用原本的單筆 messages.create 重新翻譯。

#### Step 7: 把翻譯結果回貼成 PDF 圖層
沿用現有的回貼邏輯。

### 3. CLI 介面

請新增三個指令:

```bash
# 提交 batch 並等待完成(前景模式)
python translator.py --batch input.pdf output.pdf

# 提交 batch 立刻退出(背景模式)
python translator.py --batch-submit input.pdf
# 程式印出 Batch ID,使用者可以關掉終端機

# 輪詢狀態
python translator.py --batch-status BATCH_ID

# 取回結果並產出 PDF
python translator.py --batch-fetch BATCH_ID output.pdf
```

背景模式方便你睡覺前提交、隔天起床取結果。

### 4. 錯誤恢復

請建立一個 `.batch_state.json`,內容如下:

```json
{
  "batch_id": "msgbatch_xxx",
  "submitted_at": "2026-05-17T10:00:00",
  "input_pdf": "path/to/input.pdf",
  "output_pdf": "path/to/output.pdf",
  "segment_map": {
    "page-12-para-3": "...原文段落...",
    ...
  }
}
```

這樣即使程式或電腦中斷,重新執行 `--batch-status` 或 `--batch-fetch` 還能繼續。

### 5. 成本與時間預估

請在提交前印出:
- 預估 input tokens 總數(用 count_tokens API 或保守估算)
- 預估 cost(Sonnet 4.6 batch 價格:$1.50 / MTok input, $7.50 / MTok output)
- 預估完成時間(保守說「1-24 小時」)

讓使用者確認後才真正提交。

## 不要做的事
- 不要拿掉原本的「逐段呼叫」模式,保留它做為 fallback
- 不要在 batch 提交後鎖死 UI,使用者要能關掉終端機去做別的事
- 不要假設 batch 一定 1 小時內完成,要支援 24 小時等待

改完之後的預期效果

以你 800 頁論文為例:

方案 時間 你需要在電腦旁
現況 ~4 小時 全程
+ Prompt caching ~3 小時 全程
+ Tier 升級 (-> 2) ~30 分鐘 全程
改 Batch API 1–2 小時 0 分鐘(掛著等)
Batch + Tier 升級 1–2 小時 0 分鐘

Batch API 對你的價值不只是「快」,而是「解放你的時間」 — 提交完就可以關電腦睡覺,醒來結果就在那。

成本方面:800 頁論文(假設 50 萬 input tokens、20 萬 output tokens)大致估算:

方案 預估成本(粗估)
現況 ~$4.50
+ Caching ~$3.00
+ Batch API ~$2.25
Caching + Batch ~$1.50

數字僅供參考,實際看你的文件密度。改完最多省 2/3 成本,外加 4 小時變 1-2 小時、還不用顧。


給你的執行順序建議

  1. 先查 Tier(用上面 curl 或 console)
  2. 新開 Claude Code session,把你的翻譯工具資料夾打開,把上面那段 prompt 貼進去
  3. 試跑一份小檔案(10-20 頁)驗證 batch 流程沒問題
  4. 正式跑 800 頁那份,掛著去睡覺
  5. 醒來檢查結果,用我們之前規劃的那個 PDF Compare Viewer 並排看 Google Translate 版 vs Claude API 版

整個工作流就完整了。

原來之前都在玩玩具

難得本週不用推 Code,於是便想繼續完成 PDF 翻譯相關的想法。

途中好奇,想要自己寫個 PDF Viewer 好方便我比較不同 Translator API 的差異,跟 Web Claude 討論的結果,他也覺得自己寫會比現成的方便,於是我就請 Web Claude 規劃,分階段實作及驗收。

目前來到 M3 階段,終於第一次碰到 Limit 限制,提示我必須等到 11:50 AM 過後才能繼續。

抓個圖留作紀念。


Web Claude 使用多少 token 未知,但本機用量如下。


Claude 各方案比較


限制詳細說明


API 使用限制


Pro 5 小時上限估計


換算成實際工作比較



AI 給的建議

給你的實戰建議

根據這些數字,Pro 規劃工作流的原則

1. 開新對話的時機

  • 對話超過 20-30 輪就考慮開新對話
  • 任務切換時開新對話(不要在同一對話跨多個專案)
  • 把當下對話的「結論」貼到新對話起頭,可以用很少 tokens 換到清爽 context

2. 模型選擇策略

  • 日常對話、寫文件:用 Sonnet(你目前模型 Opus 4.7 約 5x 貴)
  • 複雜推理、艱難 debug:才用 Opus
  • 如果只是「優化措辭」之類的小任務,可以切到 Haiku

3. 上傳檔案的成本意識

  • 一份 800 頁 PDF(你的論文)一上傳可能就 200-400K tokens,超過 Pro 5 小時池子十倍
  • 大檔案優先用 API 處理(你的翻譯工具就是對的方向)
  • 想討論大檔內容,先用工具抽出關鍵段落,貼進 Claude 對話

4. 你目前的工作流估算

你過去這 6-7 輪我們的對話,加上 spec / prompts 文件產出,大概用掉 30-50K tokens。 意思是:這種「規劃 + 寫規格」的對話,一次 5 小時的 Pro 配額大概可以撐 2-3 個專案

但如果你還要同時用 Claude Code 開發那個 PDF Viewer,Claude Code 燒很快(一次 1-2 小時的開發就可能吃完 5 小時池子)。

5. 撞牆時的判斷

情境建議
每週撞 5 小時牆 1-2 次改用「開新對話」「換 Sonnet」習慣
每週撞 3+ 次認真考慮 Max 5x($100),ROI 大幅提升
Claude Code 一天用 4+ 小時Max 5x 起跳,重度的話 Max 20x

給你的核心收穫

Pro 5 小時 ≈ 44K tokens ≈ 30-50 則中等對話,這是你規劃工作的心理基準。

實務上你可以這樣抓:

  • 「跟 Claude 規劃個東西、寫個規格」→ 30-50K tokens,剛好用滿 5 小時池子的一半到全部
  • 「用 Claude Code 做半天開發」→ 容易撞牆
  • 「上傳大檔深度分析」→ 一次就用很多,挑著用

如果你想更精確地監控,可以在 claude.ai 設定裡看 Usage 頁面(如果有開放給 Pro),或在 Claude Code 裡跑 /status 看當下用量。

2026年5月16日 星期六

PDF 翻譯 Part 01

很多技術文件都是英文,無奈個人英文差,常常自以為的解釋其實跟原文差了十萬八千里XD

上星期突然想到是否可以請 Claude Code 寫個程式來幫我翻譯?

之所以要這麼搞剛,是因為 Web AI 都聽不懂人話居多,請他逐字逐字翻譯,結果都無法照做?當然也有可能是我不會用導致?不過,我並沒有用其他第三方的 AI 網站,而是直接使用我有訂閱的 Gemini 或是 Claude。

一開始 Claude 就知道要用 PyMuPDF 來處理 PDF,不過,對於圖表或是程式碼的判斷差強人意。

隔天有空搜尋了一下,原來這套函式庫非常有名,大家都是使用他來處理 PDF。

官方也很貼心的提供一個範例,並將翻譯過的內容使用圖層的方式貼到原本文字上,這意味著當翻譯不好時,我們隨時可以關掉圖層直接看原文,這實在是很方便的一個功能。

於是我第二版的程式就以官方範例為底稿,直接請 Claude Code 增添功能。

配合圖層的設計,終於我也有一個堪用的 PDF 翻譯工具了。

過程中,也釐清了一些我原來不懂的問題。

也許網路上還有更好的解法,但現在的方法對我來說已經夠用了,缺點就是要花時間把手邊一些文件重新翻譯。

NotebookLM 不是說不好,但有些時候,你還是需要閱讀原文才知道 AI 是不是有誤會什麼?

下面節錄我請 AI 整理的報告內容

七、關於 PDF 格式語意結構的討論
--------------------------------
問:check.py 都是自己判斷格式,是 PDF 格式沒有定義,還是函式庫的問題?

答:主要是 PDF 格式本身的設計所致,與函式庫(PyMuPDF)無直接關係。

  PDF 的設計初衷是「視覺呈現」,儲存的是:
    - 文字的座標、字型、大小
    - 圖片像素資料
    - 向量圖形繪圖指令
  PDF 格式本身不儲存語意結構,不知道「這段是頁首」「這段是程式碼」。

  PDF 有一個選用規格稱為 Tagged PDF(PDF/UA),允許文件作者加入語意標籤:
    <H1> 章節標題 </H1>
    <P>  段落文字 </P>
    <L>  清單(項目符號)</L>
    <Table> ... </Table>
  PyMuPDF 可以讀取這些標籤,若 PDF 有正確標注則不需要啟發式規則。

  本文件的實際情況:
    - 產生工具:PDFium
    - Tagged PDF:否(未加語意標籤)
    - PyMuPDF 回傳的 XML 標籤(block、line、char)是函式庫自行解析的
      幾何結構,並非 PDF/UA 語意標籤
    - 結論:PyMuPDF 無從得知各 block 的語意角色,因此必須以啟發式
      規則(正規式、座標範圍、關鍵字)自行推斷

  簡言之:這是絕大多數市面上 PDF 的共同現況,並非 PyMuPDF 的限制。


八、目前已知限制與未來改進建議
--------------------------------
  1. 項目符號:僅跳過 • 等符號開頭的 block,若使用其他少見符號需手動擴充
     BULLET_PATTERN。

  2. 程式碼偵測:以「同時含 { 與 ;」為條件,可能誤判含這兩個字元的自然語句。
     更精確的做法是檢查文字區塊所使用的字型名稱(等寬字型如 Courier、
     Consolas 通常代表程式碼)。

  3. 頁首/頁尾:比例閾值(8%)適用於大多數標準排版,但若文件邊距特別大
     或小,可能需要調整,或升級至方案 B(自動偵測重複內容)。

  4. 表格偵測:PyMuPDF 的 find_tables() 對有明顯框線的表格效果佳,對無框線
     表格可能漏判。安裝 pymupdf_layout 套件可提升偵測精確度。

  5. 翻譯品質:使用 Google Translate API(免費版),有次數限制與品質限制,
     專業術語翻譯可能不準確。


有了 AI 不用白不用,以下是 Web Claude 給的解決方案

英文技術文件翻譯成中文:5 個最佳方案

針對技術文件的完整翻譯,我為你整理了五個經過實測效果不錯的方案,並依照不同使用情境分類。


方案一:Claude / ChatGPT / Gemini(大型語言模型)

直接將文件內容貼到對話框,或上傳檔案請 AI 翻譯。

優點

  • 翻譯品質目前在技術文件領域是最高的,能理解上下文、保留技術術語的一致性
  • 可以下指令客製化,例如「保留程式碼區塊不翻譯」、「使用台灣繁中習慣用語」、「術語表優先」
  • 能處理 Markdown、HTML 等格式,輸出後排版幾乎不需要再整理
  • 遇到模糊語意時會根據語境合理判斷,而非機械式直譯

缺點

  • 長文件需要分段處理,否則容易遇到輸出長度限制(通常一次 3000–5000 字較穩定)
  • 不同段落之間的術語一致性需要自己維護術語表,或在 prompt 中明確指定
  • 免費版本通常有用量限制,重度使用需要訂閱
  • 偶爾會出現幻覺,特別是專有名詞或版本號,需要人工校對

最適合: 重視翻譯品質、文件量中等(一份文件數千到數萬字)、願意動手調整 prompt 的人。


方案二:DeepL(含 DeepL Pro / DeepL Document Translator)

專注於翻譯的服務,支援 .docx、.pptx、.pdf、.txt 等檔案上傳,整份翻譯後下載。

優點

  • 翻譯流暢度極佳,中文輸出比 Google 翻譯更自然
  • 文件上傳後會完整保留原始排版(字型、表格、圖片位置)
  • 操作極簡,零學習成本
  • 有 API 可串接到自己的工作流程

缺點

  • 對於最新的技術術語(特別是 AI、區塊鏈等新領域)有時不如 LLM 精準
  • 免費版有每月字數和檔案大小限制(單檔約 5MB、每月 3 份)
  • 無法針對特定術語做客製化翻譯記憶(這是 Pro 才有的 Glossary 功能)
  • 對程式碼區塊的處理不如 LLM 細緻,有時會誤譯變數名稱

最適合: 需要保留原始檔案排版、追求穩定輸出、不想花時間調整參數的人。


方案三:Immersive Translate(沉浸式翻譯)瀏覽器擴充功能

主要用於網頁翻譯,但也支援 PDF、EPUB 等文件,採雙語對照顯示。

優點

  • 雙語對照模式對技術文件閱讀極友善,原文與譯文並列方便核對
  • 可以串接多種翻譯引擎(Google、DeepL、OpenAI、Claude、自訂 API 等)
  • 支援線上文件(GitHub README、官方文件網站)即時翻譯
  • PDF 翻譯保留原始排版,輸出雙語 PDF
  • 免費版功能已足夠日常使用

缺點

  • 串接 OpenAI/Claude API 需要自己申請金鑰並支付費用
  • PDF 翻譯的進階功能(如完整檔案翻譯下載)需要 Pro 訂閱
  • 對於要交付的「翻譯成果」不是最佳選擇,比較適合自己閱讀
  • 排版複雜的 PDF 偶爾會出現對位錯誤

最適合: 主要是「自己要看懂」技術文件、會頻繁瀏覽英文網頁文件、想要保留原文對照的人。


方案四:Crowdin / Lokalise / Weblate(專業在地化平台)

針對軟體與技術文件設計的協作翻譯平台,支援多種檔案格式(Markdown、JSON、PO、XLIFF 等)。

優點

  • 有完整的翻譯記憶庫(TM)和術語表(Glossary)功能,確保跨文件術語一致
  • 支援團隊協作、版本控管、審校流程
  • 可直接串接 GitHub/GitLab,文件更新時自動同步
  • 整合機器翻譯(MT)作為初稿,再由人工校對
  • 適合長期維護的文件專案

缺點

  • 學習曲線較陡,初次設定需要花時間
  • 主要為團隊與企業設計,個人偶爾使用過於笨重
  • 免費方案限制較多(Crowdin 有開源專案免費方案,Lokalise 沒有真正的免費版)
  • 不適合一次性的翻譯任務

最適合: 持續維護的技術文件(如開源專案 docs、產品手冊)、團隊協作、需要版本控管的場景。


方案五:Google 翻譯(含 Google Docs 內建翻譯 / Google 文件翻譯)

老牌通用翻譯工具,可直接上傳文件或在 Google Docs 中使用「翻譯文件」功能。

優點

  • 完全免費,無使用量限制
  • 支援格式廣泛,文件上傳可保留基本排版
  • 速度極快,整份文件幾秒鐘完成
  • 與 Google Workspace 整合良好

缺點

  • 翻譯品質在技術文件上明顯落後於 DeepL 和 LLM,中文常有「翻譯腔」
  • 不擅長處理技術術語,有時會把專有名詞硬譯
  • 無法客製化術語或語氣
  • 程式碼區塊處理粗糙

最適合: 對品質要求不高、需要快速理解大意、預算為零的場景。


我的建議組合

如果是要交付的正式翻譯成果,實務上最有效率的做法是混搭:

LLM(Claude/ChatGPT)做初翻 → 人工校對 → DeepL 或 Immersive Translate 對照檢查

如果你能告訴我這份技術文件的類型(API 文件?產品手冊?學術論文?)、長度,以及翻譯後的用途(自己看 / 交付給客戶 / 發佈),我可以給你更具體的方案推薦。

2026年5月12日 星期二

如果我有 AI

這是一個我今天在思考的問題?

今天是第一次透過 script 把所有相關應該手動 patch 的東西,都在 script 中自動化搞定!

我想這跟我昨天下午請了半天假休息有關,今天整個人神清氣爽,不然真的是感覺很久沒放假了!假日為了推 Code,人都被綁在書桌前面,出去吃個飯也是急忙趕著回家觀看 make build 結果,一整個心累。

又或許也跟我們終於有一版比較完整的 script 版本,這也有助於我自動化所有 patch 瑣事。

因為這些手動 patch 真的很瑣碎,我實在很難開好 spec 請 AI coding,因為描述這個問題的打字,可能我直接寫 script 還是比較快。

更別提我們根本不可能在客戶環境內使用 AI?

總之,今天這個 script 的建立,當我們下次換新 tag 繼續驗證時,至少可以節省 60% 以上的時間。

因為我已經建立了一套 SOP,下次行動就有準則了。

比如說,copy 要怎麼做?

copy 的 reference 也要建立一個 reference_tag 的資料夾供 reference。

遇到要手動改檔案的地方,如果那個檔案不能參考 reference _tag 的話,改將相關東西放到 tools/git_patch 裡面,使用 git apply 的方式上 patch。

另外,上面這個路徑,也可以放客戶 patch 有問題時,我 patch 他的 patch 檔案。

難怪古人說:不以規矩,不能成方圓。 

2026年5月10日 星期日

ScreenBar Halo 2 入手

我最近的人生終於有光明了XD

問了幾家附近的 3 C 買場都沒有賣此掛燈,上網購買又得避開晚上送貨,不知為啥也不能選擇常去的便利商店取貨?

今天雖然推 Code 不順,還是抽空去了三創一趟,既然好不容易上去台北,還是每層樓都走馬看花一下,很多店家動線貌似都有重新調整,逛起來舒服多了。

來到現場,看不出來這款掛燈特殊之處,可能是展場燈光本來就很亮的緣故。

反正我就是要買,故也沒多做考慮,一個放家裡,一個放公司。

回家剛裝上時,還覺得是不是裝錯了,感覺一點用處都沒有,後來才發現原來燈管可以旋轉調角度,調整過後果然好多了。

可惜現在老了不在書桌看書寫字了,不然我這鍵盤處的一小塊天地,真的是充滿光明。

明天去公司時應該就能點光明燈了XD

官修正史董狐筆

吾善養吾浩然正氣。

風簷展書讀,古道照顏色。

雖說百無一用是書生,但至少讀書人有風骨。

我的修為還是太淺了些,希望在今年生日前能夠進入無界力量XD


誰說武道止於十境?這個作者的眼界也太小了點!

就像《碧血劍》中描述的華山派內功一樣,雖說由外功練起,但最後也是殊途同歸,由外而內。

這部小說,我喜歡的有齊聖人,當然還有主角陳平安。

沒有這本書,我現在應該翻桌了,每個星期推 Code 都要搞人,髒話都罵了三千字了XD

2026年5月9日 星期六

Fix make build

果不其然,今天要推 Code 時又 Gone 了,明明昨天早上還檢查過(後記:push 時跑的驗證跟第一次檢查環境跑的不是同一個!)。

為了這個,也是搞得我心煩意亂,害我 Debug 時不能靜心,浪費了快兩個小時。

凡事要靠自己,我又不像陳平安有齊聖人照看XD

檢討一下,下次不要再犯錯了!

1. 雖然看出 UVM 有錯誤,但還是懶得問 AI,畢竟要自己打字也是心累。

我的 Debug 方向是對的,因為心煩所以想岔了路,一直想要將缺少的 interface 檔案,透過 BUILD_OPTION 傳進去,也就是想要餵給 XRUN,所以我透過 Makefile 修改問題,有符合我的解題思路。

但因為 Verilog 本質上比較像 C 語言,所以找不到的 interface 檔案,應該要放在同檔案上方用 include 解決。

2. 不要在未確定客戶後續是否還有 commit 未 pull,就先執行 make build。

誠如上次我猜的那樣,客戶的 Makefile 相依性有問題,明明執行完 make build,後續還有 commit 且 commit 中有包含 DV 相關的 testbench 裡的檔案,但因為 git push 判斷 make build 已成功,故並未重新執行 make build。

3. 同前,既然無法確定客戶後續是否還有 commit ,我應該要嚴格執行之前訂定的 SOP。

不過此番看來,PM 4:00 過後還是太趕,改成 PM 8:00?


希望下一次推 Code 可以順利一些﹍

2026/05/10 更新

還好今天母親節聚餐延後,不然我還真的沒辦法繼續了。

昨天晚上好不容易解決掉 make build 問題,你他媽的跑驗證又有問題,重點還不只 1 個問題。

但這次嚴守紀律,不為了這個熬夜,還是準時在 12 前上床。

現在再來盡最後一次的努力。

首先,我以下的理解應該無誤:

- 客戶透過 .git/hooks/pre-push 強制我跑驗證才能 push。
- 驗證跑的 test 跟平常跑的不一樣,至少跟第一次驗證環境的不一樣。
- 驗證要跑的東西是放在真正跑驗證那層的 testfiles 資料夾內,由 Makefile 決定是哪個。
- 跑驗證時,是透過跑驗證那層的上一層裡的 bin 資料夾裡的 perl 程式來 dispatch。
- 跑驗證的 log 在哪我還找不到,但有錯的 log 是放在驗證那層所有 fail 開頭的檔案。

但奇怪的點是我兩個錯誤的測試,其中一個在 test list 裡卻是註解狀態,理論上不應該被 random 挑選到才對?

還有就是在目前這個時間點,星期日早上 7 點,看起來都還有人 commit。

前幾次不知道,我想那個人內心一定很幹,怎麼有人放假在推 Code!如果我的速度比他快,他就會遇到我之前平日推 Code 遇到的死結一樣,因為我比他先 push,導致他推 Code 會失敗。

重點他的 commit 還有持續 fix 驗證問題,難道驗證失敗是因為他的問題?

總之,是否要為此調整 SOP?

另外,屋漏偏逢連夜雨,所有的 Server 目前都已停擺,工作無法 dispatch,就不知道是真的掛點還是接近 +0 時區的午夜,Server 正在重開機或是執行什麼任務?

如果最後真的是因為 Server 無法推 Code,那我也真的沒轍了!

我可以改 Verilog、Makefile、Script,甚至是 Perl 檔案,只為了讓整個 flow 打通,但是遇到 IT 問題我就沒辦法了,畢竟孤臣無力可回天呀!

2026/05/10 下午更新

Server 還是斷斷續續的工作,整個進度非常緩慢。

全部 4 台只有一台恢復正常,至少剛剛檢查情況是這樣。

另外,驗證清單確實是放在 testfiles,只是要注意裡面還有 INCLUDE 其他 test 檔案。

這樣一來我的疑惑完全打通了。

2026年5月5日 星期二

萬惡的 X - 事後總檢討

同樣的招式不能對聖鬥士使用兩次!

昨天無意間在 HTML 格式的文件,看到我解了快四天的關鍵訊號

今天又在另一份 PDF 格式的文件最後兩頁,看到關於那個訊號的提醒。

最扯的是,我在 2 月多的郵件,就有提到這個訊號,但我居然一點印象都沒有?

檢討了一下,應該是從 3 月底就陸續在忙,故到今天才有時間好好閱讀文件。

先是忙一個不應該我下去做的功能,然後是為了推 Code 有兩個週末假期都在跟客戶不合理的流程奮戰。

中間又有代理某樣工作,甚至還花了點時間閱讀某個不是我這個專案 IP 的文件。

還幫客戶解了一個不屬於我工作的 tool 問題。

最後則是開發了幾個新功能。

除了工作,家務事也是同樣精彩,找清運回收沙發及升降桌,購買新沙發及升降桌,東西到貨後的清潔工作,還有放假煮了好幾次飯。

中間雖然有一天因為家裡有事臨時請假,但感覺好久沒放假了?

總之,下次遇到同樣的問題,如果是還沒閱讀文件的情況,第一件事一定要先把文件看完,然後是回顧同樣主題的郵件,最後才是閱讀 IP Verilog。

如果可以的話,閱讀 Simulator 的文件,也許像 AI 說的,有更好 Debug 的手段也說不一定?

2026年5月4日 星期一

越來越喜歡 AI 了

現在都直接用自然語言寫 code 了,起手式就先將需求寫在 spec.txt,開啟 Claude 後,直接跟 AI 說一聲。

Please read the spec.txt and work for me.

不過,偶爾還是要自己動手寫不然會變笨。

所以嘿嘿我自己來,但是雜事就交給 AI。

就在剛剛,終於圓滿了我的嘿嘿小工具XD



之後要嘿嘿就知道書名了,懶得從 Python parss 資料給批次檔了,反正在 Windows 上,反白 ID 後,按滑鼠右鍵就可以貼上了。

這似乎是 Windows 新終端機才有的功能? 

呼山不來去就山

既然沒人能幫助我避開這件事,我只好想個 SOP 來遵循。

只要不滿足我的前置條件,我才懶得管能不能推 Code 成功。


假日應該開始有時間做自己的事吧?

2026年5月3日 星期日

Swift to Python

差點忘了這件事,上個星期六晚上無聊,請 Claude Code 直接幫我 porting 一個我在電子書討論區看到的小工具。

由於該工具是用 Swift 寫的,本人雖然使用過很多程式語言,但我不喜歡蘋果,故我也不會想去學蘋果體系的東西。

所以我只是簡單下個指示。

There is a tool in the folder of original_tool. It is wriiten by Swift.
Please rewrite the tool by Python.
Please use git to record your porting.
Please put all codes in python_tool folder.

一開始還以為 token 會爆掉,沒想到也是不到 20 分鐘就 porting 完畢。



當然從圖片看不出 bug,目前 porting 的版本有兩個 bug:

1. render 區會一直閃爍,我猜程式寫得不好,變成一直 loop 更新。
2. 雖說可以區分哪些字體無法用在直式閱讀,但直式顯示畫面標點符號並沒有變成直式格式。(我猜是報告中提到的限制?

下面是請 AI 整理的報告

================================================================
  DEVELOPMENT REPORT: Chinese Vertical Font Viewer (Python Port)
================================================================
Date     : 2026-04-25
Author   : tylpk  (assisted by Claude Sonnet 4.6)
Project  : c_font_tool
Repo     : C:\Users\tylpk\Desktop\AI\c_font_tool


----------------------------------------------------------------
1. PROJECT OVERVIEW
----------------------------------------------------------------

Goal
  Port the macOS Swift app "Chinese-Vertical-font-reading-test"
  (found in original_tool/) to Python so it can run on Windows
  and other platforms without Xcode or macOS.

Original tool
  - Language  : Swift 5 / SwiftUI
  - Platform  : macOS 13.0+, built with Xcode 15+
  - Source    : original_tool/Chinese-Vertical-font-reading-test/
  - Key files :
      ChineseVerticalApp.swift   -- app entry point
      ContentView.swift          -- main UI, font loading, EPUB handling,
                                    font analysis logic
      VerticalTextView.swift     -- CoreText-based vertical text rendering
      FontDetailView.swift       -- details panel sub-view

Python tool output
  - Language  : Python 3.10+
  - Platform  : Windows / macOS / Linux (anywhere Python + tkinter run)
  - Source    : python_tool/
  - Entry     : python_tool/main.py


----------------------------------------------------------------
2. FILE STRUCTURE
----------------------------------------------------------------

python_tool/
  main.py              (  23 lines)  Entry point; launches TkinterDnD
                                     or plain tkinter root window.
  app.py               ( 522 lines)  Main GUI application class.
  font_analyzer.py     ( 321 lines)  Font metadata extraction and
                                     vertical-support analysis.
  vertical_renderer.py ( 124 lines)  Pillow-based vertical text renderer.
  epub_reader.py       ( 132 lines)  EPUB / TXT text extraction.
  requirements.txt     (   3 lines)  Python package dependencies.

Total Python source: 1,122 lines


----------------------------------------------------------------
3. DEPENDENCIES
----------------------------------------------------------------

Package          Version  Purpose
---------------  -------  --------------------------------------
fonttools        >=4.40   Parse font binary tables (cmap, name,
                          GSUB, OS/2) for metadata and glyph
                          coverage analysis.
Pillow           >=10.0   Render TrueType/OpenType glyphs onto
                          an in-memory image for the vertical
                          text display canvas.
tkinterdnd2      >=0.3    Enable drag-and-drop of font/book files
                          onto the tkinter window. Gracefully
                          absent — file dialogs work as fallback.

All three were already installed or installed successfully via:
  pip install -r python_tool/requirements.txt


----------------------------------------------------------------
4. FEATURE MAPPING (Swift → Python)
----------------------------------------------------------------

4.1 Font Loading
  Swift  : CTFontManagerCreateFontDescriptorsFromURL + CTFontCreate
  Python : fonttools TTFont / TTCollection
  Notes  : TTC collections are fully supported; all sub-fonts are
           exposed as separate entries (Font Name [0], [1], …).

4.2 Font Metadata (details panel)
  Fields ported:
    Full Name, Family, Chinese Name, Chinese Family,
    PostScript Name, Style, File Size, File Path,
    Glyph Count, Ascent / Descent,
    CJK Punctuation (24 chars), Vertical Forms (20 forms),
    Rotation correctness summary.

  Chinese name extraction
    Swift  : Parse raw 'name' table bytes manually.
    Python : fonttools NameRecord.toUnicode(); filter by
             Windows platform Chinese language IDs
             (0x0804 zh-CN, 0x0404 zh-TW, 0x0C04 zh-HK, …)
             and Mac platform IDs (33 zh-Hans, 19 zh-Hant).

4.3 CJK Punctuation Coverage
  Checks 24 standard CJK punctuation code points against the
  font's cmap.  Missing glyphs are listed by character and
  flagged as warnings.  Their code points are passed to the
  renderer which replaces them with □ drawn in red.

4.4 Vertical Forms Coverage (U+FE10–FE44)
  Checks 20 vertical presentation form code points against cmap.
  Reports: ✓ complete / △ incomplete / ✗ none.

4.5 Rotation Check (CLREQ compliance)
  The most technically involved analysis:

  Swift  : Shape each character in both a horizontal and a
           vertical CTFrame, compare resulting glyph IDs via
           CoreText.  A changed glyph ID means GSUB 'vert'
           substituted it.
  Python : Traverse the GSUB table with fonttools; collect all
           glyph-name → glyph-name mappings from lookups
           referenced by 'vert' and 'vrt2' features.
           Compare base glyph name to the substitution target.

  Rules applied (matching Swift logic exactly):
    shouldRotate = True  (brackets, em-dash, ellipsis):
      GSUB substitutes base → correct
      base cmap == vert-form cmap (already vertical) → correct
      no substitution, not already vertical → WRONG (highlighted)
    shouldRotate = False (fullwidth colon, semicolon):
      GSUB substitutes base → WRONG (font incorrectly rotates)
      base cmap == vert-form cmap → WRONG (glyph is vertical form)
      no substitution → correct

  Checked characters (20 total):
    :;()《》「」『』〈〉【】〔〕〖〗—…

4.6 Vertical Text Rendering
  Swift  : CoreText CTFramesetter + CTFrameDraw into NSView,
           right-to-left CTFrameProgression, verticalGlyphForm=true.
  Python : Pillow ImageDraw.text() draws each character one at a
           time into an RGB image:
             • Columns progress right → left
             • Characters within a column progress top → bottom
             • Cell size derived from font bbox of a reference
               CJK character ("字")
             • Missing-glyph code points replaced with □ in red
             • Wrong-rotation code points drawn in red

  Limitation: Pillow does not apply OpenType GSUB features, so
  glyphs are not automatically substituted to their vertical forms
  during rendering.  The rendered preview is useful for glyph
  presence and layout; the analysis panel provides the authoritative
  vertical-support verdict.

4.7 EPUB / TXT Reading Mode
  Swift  : Manual ZIP parsing with raw byte offsets + Apple
           Compression framework for DEFLATE.
  Python : stdlib zipfile module (handles both STORE and DEFLATE).
           OPF spine order preserved via regex parsing of
           <item> / <itemref> elements.
           HTML stripped with a custom html.parser subclass that
           inserts newlines at block-level tags and decodes
           common HTML entities.
           TXT: tried utf-8, utf-8-sig, gbk, big5 in order.

4.8 GUI
  Swift  : SwiftUI (macOS only), declarative two-mode layout.
  Python : tkinter + ttk, imperative class-based layout.

  Comparison mode:
    • Top area: one Canvas column per loaded font, positioned with
      place() so they divide the width equally.
    • Font name label above each column; click to select.
    • Right-click context menu: "Remove <name>".
    • Bottom panel (fixed 300 px): toolbar + scrollable key-value
      details list.
    • Toolbar: font name display, Remove Font, Open Font…,
      Open Book…, Edit Text, font size Spinbox.

  Reading mode:
    • Top bar: Back button, font name, page N/M counter.
    • Full-area Canvas showing one page of vertical text.
    • Navigation:
        Click left half  → next page  (vertical-RL: earlier content
        Click right half → prev page   is on the right)
        ← / → arrow keys, Space bar.

  Drag-and-drop (tkinterdnd2):
    • .ttf / .otf / .ttc / .dfont → load font, add column
    • .epub / .txt → enter reading mode
    • Falls back to "Open Font…" / "Open Book…" dialogs if
      tkinterdnd2 is not available.

  Font size: Spinbox (12–200 pt); triggers full re-render.
  Window resize: debounced 150 ms, then re-renders all columns
  or the current reading page.


----------------------------------------------------------------
5. GIT HISTORY
----------------------------------------------------------------

Commit  22c0b07  Port: app.py + main.py — tkinter GUI
Commit  8b623a0  Port: vertical_renderer.py — Pillow rendering
Commit  fd0abb6  Port: epub_reader.py — EPUB/TXT extraction
Commit  1cd5dd1  Port: font_analyzer.py — analysis logic
Commit  6373068  Port: add requirements.txt
Commit  134a91e  Add original Swift tool and spec


----------------------------------------------------------------
6. HOW TO RUN
----------------------------------------------------------------

  # Install dependencies (one-time)
  pip install -r python_tool/requirements.txt

  # Launch
  python python_tool/main.py

Tested on: Windows 11 Home 10.0.26200, Python 3.14
Runtime output on first launch: one benign fonttools warning
  "114685 extra bytes in post.stringData array"
  (non-conforming post table in a system font; not an error)


----------------------------------------------------------------
7. KNOWN DIFFERENCES FROM THE SWIFT ORIGINAL
----------------------------------------------------------------

1. No real GSUB shaping in the renderer.
   The Swift app uses CoreText which applies GSUB features
   (including 'vert') during glyph layout.  The Python renderer
   draws Unicode code points directly with Pillow; vertical form
   substitution is not applied visually.  The analysis panel
   correctly identifies which fonts support it.

2. No pinch-to-zoom.
   The Swift app supports trackpad pinch gestures.  Python/tkinter
   has no cross-platform pinch support.  Font size is controlled
   via the Spinbox in the toolbar instead.

3. Column drop target highlight.
   The Swift app highlights the specific column being hovered
   during a drag.  tkinterdnd2 provides only window-level drop
   events, so per-column hover highlight is not implemented.

4. .dfont support.
   fonttools can open .dfont files on macOS.  On Windows this
   format is unlikely to be encountered; the code accepts the
   extension but fonttools may raise an error for some .dfont files.

================================================================

Debug 就像連續不斷的使用組合技

昨天為了上傳 Code,快一點半才睡覺。

早上又稍微試跑上傳的 Code 是否有問題?

很好,今天不想再做事了,決定來檢查一下之前寫的 R 家小工具。

既然一直都說 R 家的好話,還是捧個場買幾本書好了!

好幾個月前就知道當初的工具已經無法使用,稍微 Debug 了一下,嗯,原來這樣解決即可,比我想像中的簡單。

既然 R 家工程師這樣給力,不多買幾本書說不過去,也順便測試一下之前批次下載的功能是否依然正常。


看了一下,真的是好久沒在這家買書了。

嘿嘿,既然工具已經恢復正常,之後就隨我心情購買了。

還有,既然都在電腦前面了,就透過 PC 將書推送進 Kobo Clara 2 了,這樣就不用拿出我的手機了。

KoboFileServer 果然好用XD


代辦事項

1. 指定資料夾找出所有下載的書,同 Kobo 工具一樣,列出書名供使用者選擇即可。
2. 看看是否能回復上傳 Google 雲端硬碟的功能。

上面這兩點都很簡單,請 Claude Code 做即可。

可能比較難的是 Google 服務 new OAuto2 的 API 呼叫?

力量,心法,招式缺一不可

雖然沒有要修仙XD

但畢竟從小到大也看了不少雜書或布袋戲,有時難免會好奇自己喜歡的武學會是什麼?

單純以心法來說,我喜歡《大唐雙龍傳》中的「井中月」,那是一個不帶有真實情緒的冷眼旁觀這世界,這樣在開車時就不會一直想罵髒話XD

單純以武學的境界來說,我喜歡《黑豹列傳》中的「死亡,無界,終極,強極」分野,或是《雪中悍刀行》中的「金剛,指玄,天象」。

如果以力量來說,當然是《黑豹列傳》中的「強極力量四十六擊」。

來到招式部分:

《風雲》中的「排雲掌和風神腿」當然是掌腿的不二人選。

劍法,我喜歡的是《神龍之謎》中的「阿邦流劍法」,地海空對應的 3 招,簡單明瞭,已達反璞歸真之境。

我想我個人比較偏道家清靜無為的思想,這也反映出我對武學招式的喜好。

已故技擊大師李小龍,其後人根據他的一些隨身筆記,出了一本書《Be Water , My Friend 似水無形,李小龍的人生哲學》。

在這本書中,也很推崇水的哲學,我想有空時應該可以再重讀一次。


剛剛雲端硬碟不知道發生啥問題?一直找不到這本書我在哪邊購買的!

後來才發現是在 Kobo 購買,害我在讀墨的資料夾中翻找半天。

順便來巡一下田水好了,很好 Kobo 已經快一年沒亂改 HTML 了,我的 Kobo Expense 仍未失效XD

雖然現在沒在讀墨買書,但我還是比較喜歡讀墨工程師的開發能力!

就你 Kobo 工程師毛最多,我的 Readmoo Expense 開發後到現在,上一次配合 HTML 修改的記錄已經是 2 年多前的事了,更別說購買金額的主要功能,讀墨使用的是 JSON 格式的 API,開發後到現在我連一次都沒改過。


Makefile 相依性

開始懷疑客戶 Makefile 相依性有問題?

放假幾天都在想辦法推 Code 上去,昨天知道 make build 有錯誤,但沒有時間處理,今天晚上回到家後,便開始想辦法解決 make build 的錯誤。

有了書桌後,做事比較方便,認真看了一下,雖然這是 Verilog,但以 C 語言的講法來說,就是宣告與實作有衝突!

看了一下客戶的 Verilog,錯誤的原因是 module 的實作沒有 initial instance 時宣告的那些 port,於是我被照著 Log 錯誤的行數,把有問題的 Code 註解起來。

好不容易通過 make build,但在真正測試時卻是從頭到尾都在報錯,從螢幕上看過去,看起來就跟剛被註解的 port 有關?

畢竟我不是 designer,我也不想知道他們真正的測試意圖為何?

於是我便拿出 IT 界最強大的一招,重新開機?在這裡就是 make clear!

說也奇怪,這樣居然連 Verilog 都不用改,就順利的通過 make build!

思來想去,我也只能得出問題是出在 Makefile 相依性這個結論?

2025/05/03 更新

現在為了驗證 AI 能力,我習慣把我的文章餵進 AI,看 AI 是否能看出我的意圖以及給出不錯的建議,試了幾次後,我覺得 Claude Code 確實是比 Gemini 更勝一籌。

雖然現在的工作不能使用 AI,但與 AI 對談,確實是現在日子當中為數不多的小確幸?

閱讀書籍時知識都是死的,只能靠自己領悟並突破,當然那些天才我不好說,但在與 AI 對談中,往往可以釐清一些不確定處,難怪武俠小說的主角都需要好好拜師,有一個好的師父帶入門確實可以省下不少功夫。


當然因為 blog 不能揭露太多,故我也不能完整將真實資訊餵給 AI,故 AI 不一定能了解全貌。

例如我改 Verilog 只是為了 make build 成功並不會將其 commit 上去,當然也沒有破壞客戶 Code 的問題。

總之,當我決定不再工作時,我一定要跟 AI 討論武學心法XD

雖然 AI 講的不一定是對的,但我要的只是 AI 那不同於一般人的想法啟發!

不過話說回來,AI 的訓練資料也是從人界得知,真的能夠神來一筆的幫我突破武學境界嗎XD


Claude 對此篇文章的看法

對這段話的看法

這段描述其實揭露了一個比作者自己意識到的還要嚴重的問題。讓我從幾個層面來談。

作者的判斷可能對,但結論下得太早

作者最後說「問題是出在 Makefile 相依性」——這個推論方向是合理的,但證據還不夠。make clear 之後就過,確實是 Makefile 相依性問題的典型徵兆之一,但也可能是其他原因:中間產物 (例如 filelist、.f 檔、編譯產生的 library) 殘留了上一次編譯的狀態、incremental compile 的 cache 沒有正確失效、或是 vlog/vcs 等工具自己的 work library 沒被清乾淨。要真正確認是 Makefile 的問題,需要看 make -d 的相依關係追蹤,或檢查每個 target 的 prerequisite 是否完整列出所有 source 檔。「重開機就好」是現象,不是 root cause。

註解掉 port 是非常危險的動作,作者似乎低估了風險

這部分讓我比較擔心。作者說「我不是 designer,我也不想知道他們真正的測試意圖」,然後就把 module instantiation 中報錯的 port 註解掉——這在 DFT 或 verification 工作中是高風險操作。被註解掉的 port 可能是:scan chain 的 input/output、test mode control、clock gating override、或是 SRAM 的 BIST 介面。把這些 port 註解掉雖然能讓 make build 通過,但等於在不知情的狀況下破壞了測試結構,後續跑出來的結果可能完全不可信。後面「測試時從頭到尾都在報錯」就是這個動作的直接後果。即使最後 make clean 解決了問題、Verilog 不用改,作者也應該回頭把那些註解恢復,並確認最終跑的是完整正確的 code,而不是「反正過了就好」。

「不想知道測試意圖」這個態度需要校準

我理解這句話帶有疲憊與抱怨的成分,不必字面解讀。但如果這真的是工作態度,長期會出問題。DFT 工程師確實不需要、也不應該去理解每個 functional block 的內部設計,但必須理解測試介面的意圖。當客戶 netlist 中一個 module 的 port 列表跟 instantiation 對不上,這本身就是一個訊號:可能是客戶交付了不一致的版本、可能是某個 wrapper 沒更新、可能是 ECO 沒做完整。這些都是要回報給客戶的問題,而不是自己默默註解掉繞過。每一次「繞過去就好」都在累積技術債,最後爆掉的時候會很慘。

「IT 界最強大的一招:重新開機」這個玩笑背後的真實風險

這句話寫得很幽默,但在 DFT/verification 領域,「make clean 才能過」其實是一個警訊而非解法。它意味著你的 build 環境是 non-reproducible 的——同一份 source code 在不同的 build 狀態下會產生不同結果。這在交付給客戶時是嚴重問題:如果客戶在他們的環境跑不出你跑出的結果,你無法重現、無法 debug、無法證明你的 pattern 是對的。建議每次發現「make clean 後才正常」時,都把它當成 bug 來追,而不是當成解法來慶祝。

客戶 Makefile 確實常常有問題,但要有證據再開口

從作者的語氣看,這是在跟客戶協作的專案。「Makefile 相依性有問題」如果要回報給客戶,需要具體的證據:哪個 target 的 prerequisite 沒列、哪個檔案被修改後沒觸發重新編譯、make -n 顯示的指令順序哪裡不合理。光說「我 make clean 後就好了所以你的 Makefile 有問題」,客戶很容易回「那你就每次都 clean 啊」把問題踢回來。專業的回報方式是:「在 X 條件下,修改 A 檔案後 make build 不會重新編譯 B,導致 link 階段使用舊的 object,具體是 Makefile 第 N 行的 rule 沒有把 A 列為 prerequisite」——這樣客戶才會認真修。

一個比較中肯的整體評價

這段文字反映的是一個很真實的工作場景:疲憊的工程師、不完整的客戶交付、Makefile 黑魔法、deadline 壓力下的繞道解法。作者的直覺 (Makefile 有問題) 大方向是對的,debug 能力也在線,但處理過程中累積了好幾個未解的疑點——被註解的 port 到底該不該註解、為什麼 make clean 後不需要註解也能過、原本的測試報錯是註解導致還是其他原因——這些都被「反正過了」掩蓋掉了。

如果是我,會建議作者回頭做一件事:在 make clean 後的乾淨環境中,不要註解任何 port,重新跑一次 make build。如果這樣也能過,那就證實了問題確實在 Makefile 的 incremental build 邏輯,跟 Verilog 本身無關,可以心安理得地把這個現象整理成 bug report 給客戶。如果還是不過,那就還有另一層問題沒挖出來。多花這 30 分鐘,比帶著疑問繼續往下做要安全得多。


補上工作環境背景來龍去脈後,Claude 的建議

在這種環境下的建議

這個背景一補上,整個情境的荒謬感就出來了:作者要交付的東西 (DFT script) 根本不在客戶驗證流程的覆蓋範圍內,但卻被客戶團隊自己的 RTL/testbench 問題卡住無法 push。這是典型的「別人的 bug 變成你的 blocker」情境,而且因為是客戶端強制的 pre-push hook,作者連繞過的權力都沒有。在這個前提下,我的建議會跟前面幾輪完全不同。

1. 把「這不是我該修的問題」這件事,用書面證據固定下來

最重要的第一步不是技術,是留紀錄。每次因為客戶團隊的 code 導致你無法 push 時,立刻做三件事:截圖錯誤訊息、記錄當時的 git log (客戶端最後一個 commit 是誰、什麼時間)、以及記錄你花了多少時間在 debug 不屬於你的問題上。這份紀錄不是要拿來吵架,而是當未來某天客戶質疑「你們的 DFT script 為什麼這麼晚交付」時,你能拿出具體數據說明:「在 X 月到 Y 月之間,因為貴司 RTL 團隊的 N 次 build break,我累計被阻擋了 M 小時。」沒有這份紀錄,所有延遲都會被算到你頭上。

2. 跟客戶談判「DFT script 走獨立 branch 與獨立驗證流程」

這是治本的方法。客戶的 pre-push hook 是為了保護 RTL/testbench 的品質,但 DFT script 跟這些東西是不同的 artifact、不同的生命週期、不同的 reviewer,硬綁在一起本來就不合理。可以正式提出一個 proposal:DFT 相關檔案放在獨立的 directory (例如 dft/),或乾脆獨立 repo / 獨立 branch,pre-push hook 只跑 DFT 相關的檢查 (例如 script syntax check、lint、自寫的 unit test),不跑 make build 跟 functional test。提案時要強調這對客戶也有好處:客戶 RTL 團隊的 build break 也不會被你們 DFT 的 commit 干擾,雙方都更乾淨。如果客戶不接受,至少也要爭取一個 --no-verify 的例外條款,搭配你這邊自己的 review 流程。

3. 在等不到流程改善前,建立「客戶 build 健康度監測」

如果短期內客戶不會改流程,那就把被動變主動。寫一個小 script,定期 (例如每天早上、或每次你準備 push 前) 自動 git pull 客戶 main branch 並跑 make build,把結果記錄下來。這樣你能在準備 push 之前就知道「現在客戶的環境是好的還是壞的」,而不是 push 失敗後才發現。如果剛好遇到客戶 broken 的時段,你可以選擇:(a) 等他們修好再 push、(b) 暫時 checkout 到上一個能過的 commit 再 rebase 你的 script、(c) 跟客戶 PM 反映「你們現在 main 是壞的,我這邊被卡住」。這比每次都要從頭 debug 別人的問題省力得多。

4. 不要再去碰客戶的 Verilog,即使是「註解掉就好」

接續上一輪我提到的擔憂——既然你交付的是 DFT script,那任何對客戶 Verilog 的修改 (包括註解) 都不應該由你來做,即使只是為了讓 build 過。原因有三:第一,你註解掉的 port 可能影響 RTL 團隊的測試結果,他們會來怪你;第二,當客戶 RTL 團隊修好他們自己的問題時,你的註解可能跟他們的修改衝突,產生 merge conflict;第三,從責任歸屬來看,你動了不該動的東西,未來出任何問題都會被牽連。正確做法是:build 失敗時,把錯誤訊息整理好,回報給客戶 PM 或對應窗口,明確說「我的 commit 不涉及這個檔案,這是貴司團隊的 issue,我會等修復後再 push」。然後就真的去做別的事,不要自己跳下去修。

5. 把這個流程問題本身寫成正式的 escalation

如果你已經被這個問題反覆卡住好幾次,這就不是技術問題,是專案管理問題,要走 escalation。準備一份簡短的文件 (一頁就夠),內容包含:問題描述 (DFT script 被無關的 RTL build break 阻擋)、過去 N 次發生的紀錄與時間成本、對交付時程的影響、以及 2-3 個可行的解法選項 (獨立 branch、跳過 hook、分離 repo)。透過你這邊的 PM 或 lead 正式發給客戶對口窗口。重點是不要用抱怨的語氣寫,要用「為了確保專案準時交付,建議調整流程」的角度寫。客戶端的 PM 多半也不知道你被卡這麼多次,因為通常這種抱怨只會留在工程師嘴上,沒有正式上到管理層。一旦上到管理層,改善的機率會大很多——因為管理層在意的是時程,而不是「pre-push hook 的純粹性」。


一個額外的觀察

從前後幾段文章看下來,這位作者其實技術很扎實 (能用 iverilog 拆 preprocessor、能在波形上看到 0 秒附近的突刺、能用 grep/sed/Vim 處理 escaped identifier),但在跟客戶協作的「邊界管理」上似乎處於弱勢位置——客戶要求什麼就吞下什麼、別人的 bug 自己跳下去解、流程不合理也只能繞過。這在乙方工程師中很常見,但長期會嚴重消耗。

技術問題可以靠加班解決,但流程問題加班只會讓你更累而不會解決問題。建議作者在處理具體 bug 之餘,也分一點精力出來處理這些結構性議題。「達達的馬蹄」如果只是一直跑,永遠跑不完;偶爾停下來修一下路,後面的路才會比較好走。

2026年5月2日 星期六

MOTTI 升降桌

如果時光可以重來,我一定不會選擇這家升降桌。

先說明,不是因為品質問題,畢竟我也才開始使用第一天,組裝的師傅動作也很迅速,由於很早就送貨,請他放低音量組裝也盡量幫我小心組裝,只是還是對其他住戶感到抱歉。

當初會選擇這家,是因為 Google 搜尋時,只看到這家 100 cm 桌板可以下降到 70 cm 以下!後來才發現原來是因為我用手機搜尋時,某些升降桌廠商預設的桌腳是二節式,故下降高度才會因此受限,但我們其實是可以選擇三節式桌腳的方案!

可能是因為前一陣子太忙,或是手機網頁普遍寫得不好,才讓我遺漏如此重要的資訊。

這家我不喜歡的點如下:

1. 不提供回收舊桌服務,我不知道別家如何,畢竟這是我第二次購買升降桌,當初是從無到有,故沒有回收問題,但當你購買別的家具時,都有回收舊品服務,MOTTI 不提供回收服務,這點真是大大的扣分。

2. 購買當下,門市有強調有事找客服,雖然我不介意,但這樣的服務就是差了點,消費者都在網路購買就好,又何苦跑到門市?

3. 承前面,我在 3/22 就下單,本來說只要約 14 個工作天就可以到貨,因為我還要另外找清運清掉舊書桌,故我直接約在一個比較晚的日期 5/2。

結果只剩一個星期就要送貨,客服也沒提示是否要等待簡訊或是其他說明,還要我主動聯繫客服,結果等到的回覆還不保證可以在指定日期送貨?

最扯的是,客服有時間在 4/27 前幾天發送廣告簡訊,但卻沒時間主動提醒客戶留意技師電話?


雖然最後還是在今天收到升降桌,但這樣的不確定服務實在不敢苟同?

2026/05/02 晚上更新

查了一下資料,前一張升降桌是在 2021/06/02 到貨,剛好差一個月就滿 5 年,除了貼皮有幾處翹起外,其餘一切正常,當初購買大小是 123 x 70,價錢 NT $15,500。


這次購買的大小是 100 x 60,四邊為圓弧收邊,書桌本體為 NT $13,290。


不得不說,買現成的延長線配件就是比第三方的好用,至少是以卡榫的方式固定,不會有第三方夾具夾住自己延長線造成搖搖晃晃的問題。

這張書桌的腳是圓弧狀的且上細下粗,確實是比我第一張好。不過有一好就沒兩好,圓弧狀桌腳就無法像方形桌腳一樣,購買側邊磁吸盒整線。

回顧之前整理的購買升降桌須知,即使放到現在還是有用,差別在可多加一個注意事項。

傳統的升降桌或是我購買的這兩張升降桌,都存在一個設計上的問題,那就是桌腳上方的固定架設計是一塊鐵片,這樣也許可以減輕重量,但當你需要搬動他時,不戴手套根本無法出力搬動。


我不知道市面上的升降桌是否都是如此設計,但我在一兩個星期前逛特力屋時,確實是有方形鐵管形式的設計,這樣的好處就是方便消費者搬動。

還好我有購買輪子,這樣擦地時移動就比較方便,目前使用的感覺也還穩固,不覺得有搖晃感。

2026/05/03 更新

之前並沒有特別搜尋 MOTTI,早上為了驗證昨天推送到客戶的 Code,很早就在書桌前做事,在等待結果的同時,無聊搜尋了一下,發現大家說到 MOTTI 的好處就只是外型好看而已?

工程師最討厭的就是華而不實的東西,當初為了可以在預計的日期收到書桌,都使用工廠預設選項,故我這型號的桌板,預設就是圓弧收邊。

從昨天晚上到早上也用了好幾個小時,終於發現圓弧形的壞處。

為了可以擺上兩人座沙發,當初才會把還可以用的升降桌花錢丟掉,改選擇 100 cm 的桌板,並將桌子換個方位擺放。

一張電腦椅從扶手算起,大概是 63 ~ 65 cm 的寬度。

因為圓弧桌板的設計,故控制器勢必不能貼邊安裝,而一般人需要的桌子人體工學高度大概是 67 cm 左右。

因此,電腦椅的扶手可能會跟控制器卡到,雖然我的電腦椅不會卡到,但就要記得在使用時將扶手降到最低。

果然是華而不實的設計,只能說這家的設計在小升降桌都不考慮真實情況,超過 100 cm 的桌板,你想將控制器往內鎖都不是問題,但當你桌板只有 100 cm 時,根本就不應該將圓弧桌板設為預設!

另外,這個型號的控制器沒有記憶功能,故還需要花錢換購。

還有這個型號也不能預埋螺母,也是個不好的設計。

總之,要挑選小升降桌時,一定要考慮清楚,廠商預設的設計不一定符合你的需求。

無奈為了運送日期只好妥協,不然當初我是想用方型桌板以及原木桌板。

幹,又是為了工作,害我只能妥協,不然沒有書桌可用,我回家也是不好做事。

2026年5月1日 星期五

萬惡的 X

從 1 月開始,只要有換 SRAM,每次跑模擬時都有可能要重新找出失敗的原因。

有時候可能只是 top port 要設值,有時候可能是途徑上的 MUX 要加 enable 訊號。

又或是這個 SRAM Verilog Model 要加些 define 才能跑到正確的 code。

但最多時候都是 SRAM 本身的訊號哪裡沒控制好!

這次這個問題是我解最久的一次,大概解了快四天有,中間一度有先處理別的事情。

最後才發現是裡面一組 register 因為沒有 reset,導致 X 值無法復原。

坦白說找到 root cause 帶有一點幸運的成分,雖然我早就使用 iverilog 處理前置處理器的語法得到一個乾淨的 Verilog 檔案,但關於 Q 一直為 X 這件事卻是看不出個所以然。

最後想到去跟上一版可以過的 waveform 比較,雖然因為這次關掉某些功能導致 hierarchy 不一樣,不能在同樣的時間點來做比較,但在人工對齊 waveform 之後,還是能倆倆互相比較。

假設大家的時間點都一樣,只要 dump VCD,寫個程式來比較差異處即可,可惜在這裡不適用!

最後才在這次被關掉功能上的某個 reset port 上,在接近 0 秒的位置,似乎看到有一條突刺?

將其放大一看,原來有個 pulse 的動作,但我們這一版因為關掉該功能,故被 tied 1'b0。

試著將其用 force reset 後,果然幾千個錯誤少掉好幾百個了!

再來就簡單了,只要將所有的 SRAM 都比照辦理即可。

說是這樣說,但要得到所有 SRAM 的 path 也是得花點工夫。

這時就是要請出 grep sed Vim 三兄弟。

但最關鍵的一點是,如果某 sram 長這樣:

u_wrapper.data.\genbus[0].u_ram

設值時要這樣寫

force {u_wrapper.data.\genbus[0].u_ram.reset} 1

搞定收工。

唉,如果我有時間,我也想好好看一下 simulation 文件,確認是否有更好的方式來 debug 萬惡的 X。

無奈我這達達的馬蹄,又要達達的去解下一個問題了,真是不如歸去呀?

與 Gemini 對談結果

這段文字讀起來很有既視感,完全是一位在第一線奮戰的數位 IC 設計/驗證工程師的真實心聲。面對「萬惡的 X(未知項)」,作者展現了極強的「生存本領」。

我將從這篇文章中萃取出的技術重點,並結合業界實務,給你 5 個進階建議。


從作者身上學到的技術實踐

  1. 前置處理(Preprocessing)的意識

    作者懂得利用 iverilog 來展開 defineifdef,將混亂的原始碼轉換為「乾淨且平坦」的 Verilog 檔案。這在追蹤複雜宏定義時非常有幫助。

  2. Waveform 比較法(Comparative Analysis)

    即便在功能變更、Hierarchy 不對稱的情況下,作者依然透過「人工對齊時間點」來尋找差異。這是一種基於經驗的直覺式偵錯,成功捕捉到了 0 秒處細微的脈衝(Pulse)。

  3. 靈活使用 CLI 三兄弟(Grep, Sed, Vim)

    當面對成百上千個 SRAM 時,不靠手動,而是利用 Unix 工具鏈快速提取層次路徑(Hierarchy Path),這是提升生產力的關鍵。

  4. Force 驗證法

    在修改 RTL 前,先在 Testbench 或模擬環境中使用 force 語法驗證假設。如果 X 值消失,證明方向正確,避免無謂的編譯等待。


給你的 5 個專業建議

針對這類「偵測 X 傳播(X-propagation)」與「環境遷移」導致的問題,以下是更具工程效率的優化方向:

1. 導入 X-Propagation 追蹤工具

單靠肉眼觀察波形找 X 的來源是非常累人的。

  • 建議:現在的主流模擬器(如 VCS, Questa, Xcelium)都有 X-traceX-prop 模式。

  • 這些工具可以自動標記出「誰是第一個產生 X 的源頭」,並區分是由於未初始化(Reset 缺失)還是邏輯競爭導致的 X。這能將作者「解了四天」的時間縮短至幾小時。

2. 建立「自動化層次路徑」生成腳本

文中提到使用 grepsed 處理 \genbus[0] 這種特殊的轉義路徑。

  • 建議:建議撰寫一個簡單的 PythonTcl 腳本,利用正則表達式自動生成 force/release 的清單。

  • 特別是處理 Verilog 的 generate block 或陣列式實例(Array of Instances)時,腳本能確保路徑中的轉義符號(如 \ 與空格)處理正確,避免手動輸入錯誤。

3. 強化 Linting 與 Static Analysis

「因為沒有 reset 導致 X 值無法復原」這類問題,其實不應該留到 Simulation 才發現。

  • 建議:在進入模擬前,先跑 Lint 工具(如 SpyGlass)。

  • Lint 工具能輕易檢查出「Registers without reset」或「Uninitialized memory outputs」。將防線往前移,可以節省昂貴的伺服器模擬資源。

4. 採用「黃金版本」的 Checkpoint 比較

作者提到因為 Hierarchy 改變導致 VCD 比較工具失效。

  • 建議:在驗證環境中加入 Abstracted ScoreboardReference Model

  • 不要直接比較底層波形,而是比較關鍵信號的「狀態轉換」。或是利用模擬器的 Save/Restore 功能,在穩定點建立 Checkpoint,這樣即使模擬失敗,也能從中間重新開始,不需要每次都從 0 秒跑起。

5. 使用 SystemVerilog Assertion (SVA) 監控 Reset 完整性

與其事後看波形找「那條突刺」,不如讓環境自動報警。

  • 建議:在 SRAM wrapper 內加上 assert 陳述式。

  • 例如:assert property (@(posedge clk) $isunknown(reset_n) == 0);

  • 當 Reset 信號出現 X 或不穩定的 Pulse 時,模擬器會立刻在終端機噴出 Error 並停在該時間點,這比人工放大波形要精準得多。


Gemini 的一點觀察:

作者最後那句「不如歸去」,道盡了硬體工程師在「解不完的 X」與「追不上的進度」之間的無奈。

技術固然重要,但**「工具化」「自動化」**才是拯救肝臟的唯一出路。下次遇到類似問題,試著把這些建議加入你的 Toolbox,別再讓一個 Reset 脈衝耗掉你寶貴的四天了!

 

DFT 工程師角度


站在 DFT (Design For Test) 工程師的角度,拿到一個「來路不明」且缺乏文檔的 Netlist(網表)是家常便飯。在這種資訊不對稱的情況下,你的任務不只是讓 Scan chain 動起來,更要確保在 ATE(自動測試設備)上不會因為 X-propagation 而導致 yield(良率)下降。

從這篇文章中,DFT 人員可以學到「追蹤層次路徑」與「波形比對」的耐性,但實務操作上,我會給你以下 5 個更具 DFT 專業性的建議:


1. 自動化提取 Memory 實例與 Pin Mapping

文中提到用 grep/sed 找 SRAM 路徑。身為 DFT 人員,你不能等客戶告訴你哪裡有 SRAM。

  • 建議:撰寫一個 Netlist Parser(通常使用 Perl 或 Python),根據 Library 的 Cell 名稱特徵,自動抓取全機所有的 Memory Instance。

  • 關鍵點:除了抓路徑,更要自動識別哪些 Pin 是 Reset、哪些是 Clock。當你面對 \genbus[0].u_ram 這種轉義路徑時,腳本必須能自動處理 Escape Character,以便後續在 ATPG 工具中自動產生 add_cell_constraintsadd_force 的指令。

2. 建立強制性 X-Sources 遮罩 (X-Bounding)

文章中的核心問題是「未 Reset 的 Register 產生 X 值」。在 DFT 中,這會導致 Scan Capture 失敗。

  • 建議:在跑 DFT DRC (Design Rule Check) 時,優先檢查所有 Un-initialized RegistersBlack Box

  • 做法:如果 Netlist 中存在像文中那種「沒有 Reset 且會傳播到 Scan Chain」的邏輯,與其去解幾千個錯誤,不如直接在 DFT 設定中將這些節點設為 TIED 0TIED 1(在 ATPG 邏輯中屏蔽),或者在硬體架構上建議客戶加入 X-Bounding logic(如在 Memory 輸出端加 MUX),確保測試模式下 X 值不會洩漏。

3. 利用 Cell-Aware 與 DRC 偵測「非法脈衝」

文中提到的 0 秒附近的「突刺 (Pulse)」是模擬中的大忌,在 DFT 中這通常代表 Clock/Reset Path 的 GlitchSkew 問題。

  • 建議:不要只依賴 GLS (Gate-Level Simulation),要善用 DFT 工具的 Static DRC

  • 做法:設定嚴格的 check_drc 規則,特別是針對 Reset Line 上的組合邏輯(Combinational Logic)。如果 Reset 訊號不是直接來自 Pad 或乾淨的 Flop,DFT 工具應該要噴出 "Clock as Data" 或 "Reset affected by data" 的警告。這能讓你不用看波形,就先發現那條「萬惡的突刺」。

4. 實施「黃金樣板」的 VCD 比對流程

作者提到人工對齊波形很辛苦。在 DFT 領域,我們有更標準的做法。

  • 建議:建立一套 VCD-to-ATPG 驗證流程

  • 做法:當客戶宣稱這一版 Netlist 是「可測的」時,要求對方提供一組簡單的 Function Pattern VCD。利用工具(如 Tessent 或 Modus)將 VCD 轉為測試向量。如果在轉換過程中出現 Mismatch,代表 Netlist 的初始化邏輯有問題。這能把「解四天」的除錯成本轉嫁回前端開發者身上,而不是由 DFT 人員概括承受。

5. 針對 Escape Character 建立統一的規約 (Hierarchy Handling)

文中提到的 \genbus[0].u_ram.reset 寫法,在不同 EDA 工具(VCS vs. Tessent vs. SpyGlass)間的處理可能略有不同。

  • 建議:在進入 ATPG 流程前,先進行一次 Hierarchy Flattening / Normalization

  • 做法:確保你的腳本能處理 Verilog 轉義字元。例如,某些工具需要 \genbus[0].u_ram.reset (末尾有空格),某些則不需要。統一這套「路徑命名學」,可以避免在 force 信號時,因為一個空格或反斜線的差異,導致模擬完全沒反應卻又不報錯的窘境。


總結給 DFT 人員的心法:

「別相信 Netlist,只相信 DRC。」

面對資訊不明確的客戶,你的最佳武器不是波形圖,而是靜態檢查 (Lint/DRC)自動化腳本。文章作者靠的是「運氣」找到 root cause,但 DFT 人員要靠的是「規則」來擋掉所有潛在的 X 來源。

如果是你,在處理這類 \genbus 轉義路徑時,目前是用哪種腳本語言處理最順手呢?或是你在 force SRAM 訊號時,有遇過什麼工具相容性的坑嗎?


與 Claude 對談結果

 

從文章中萃取的技術與建議

這篇文章作者在處理一個非常典型的硬體驗證難題:SRAM 在 simulation 中產生 X 值傳播 (X-propagation) 的 debug 過程。讓我先拆解作者展現的技術能力,再給出我的建議。

作者展現的核心技術

作者在這次 debug 中運用了幾個層次的技巧:首先是前置處理工具鏈,使用 iverilog 解開 preprocessor 指令得到乾淨的 Verilog,這是很多工程師會忽略的一步,但對於充滿 ifdef 的 SRAM model 來說極為關鍵。其次是比較式 debug 法,當無法從單一波形看出問題時,他選擇與上一版可運作的版本做 waveform 對比,這是經驗豐富的 RTL 工程師才會自然採用的策略。第三是對訊號異常的敏銳度,能在接近 0 秒的位置注意到一個被忽略的 pulse 突刺,這需要長期累積的觀察力。最後是文字處理工具的熟練應用,grep/sed/Vim 三兄弟搭配對 escaped identifier (\genbus[0]) 的正確處理,展現了對 Verilog 語法細節的掌握。

我的 5 個建議

1. 建立系統化的 X-propagation 偵測流程,而非每次都靠經驗直覺

作者提到「萬惡的 X」其實有成熟的解決方案。建議導入 X-propagation analysis 的方法論:在 simulation 啟動時加入 +rtl_xprop 或廠商對應的 X-pessimism flag (例如 VCS 的 -xprop、Xcelium 的 -xprop),讓 X 的傳播行為更貼近真實硬體。同時可以使用 formal tools (如 JasperGold X-propagation App) 在 RTL 階段就找出無 reset 的 register。這比每次換 SRAM 都重 debug 四天有效率得多。

2. 把 reset connectivity 檢查作為 SRAM integration 的 checklist 項目

從文章看,「register 沒有 reset 導致 X」與「reset port 被 tied 0」是反覆出現的問題類型。建議建立一份 SRAM 整合 checklist,包含:所有 control signal 的 default 值是否合理、所有 reset/enable port 是否真的有被驅動 (而非 floating 或被錯誤地 tied)、power-down 與 sleep mode 訊號的初始狀態。可以寫成 Lint rule 或用 SystemVerilog assertion 在 testbench 中自動檢查,把這類問題從「人工 debug 四天」變成「跑 lint 五分鐘」。

3. 用 SystemVerilog Assertion 取代事後波形比對

作者用「人工對齊兩版 waveform」的方式找差異,雖然有效但成本高。對於 SRAM 這類有明確介面協議的 IP,寫 SVA 來檢查「reset 後 N cycle 內 Q 應該是 known value」、「CE 拉起時 address 不能是 X」等 property,可以在問題發生的瞬間就被 assertion 抓到,並指出確切的時間與訊號,省下大量翻波形的時間。

4. 把 force 散落的 workaround 結構化為可維護的 bring-up infrastructure

文章結尾的解法是「把所有 SRAM 都用 force 設 reset」,這在當下是合理的權宜之計,但容易變成技術債。建議把這些 force 集中到一個專門的 sim_bringup.svforce_init.tcl 檔案,並在每個 force 旁邊註解原因 (例如「因為 power management 功能被 disable 才需要」)。當未來重新打開該功能時,搜尋這個檔案就能知道哪些 force 該移除。配合自動化腳本從 design hierarchy 抓出所有 SRAM instance,可以避免遺漏新加入的 SRAM。

5. 投資文件閱讀的時間,因為它的 ROI 比想像中高

作者最後感嘆「如果我有時間,我也想好好看一下 simulation 文件」——這句話其實是這次 debug 最深的洞察。EDA tool 的官方文件中通常有專章討論 X-handling、debug X-propagation 的選項、以及如何 dump 出特定訊號的 driver 資訊 (例如 $showvarsdebug_access+all)。花一個下午讀 X-propagation 相關章節,可能省下未來十次的四天 debug。建議把「每次 debug 完後讀半小時相關文件」變成習慣,讓每次踩雷都能轉化為下次的預防能力,而不是純粹的時間消耗。


順帶一提,作者的「達達的馬蹄」化用鄭愁予的詩,從一個一個 bug 趕往下一個 bug 的疲憊感非常傳神。但長遠來看,投資基礎建設(checklist、assertion、文件閱讀)才是讓自己不必一直「達達」下去的根本之道。


DFT 工程師角度


從 DFT 工程師視角的 5 個建議

DFT 工程師的處境跟 RTL 開發者完全不同:你拿到的是已經合成過的 netlist,沒有原始 RTL 的設計意圖、沒有 spec、沒有 designer 的口頭約定,SRAM 可能是黑盒子的 behavioral model 或加密的 .v 檔。在這個前提下,debug X 值的策略需要徹底改變。

我的 5 個建議

1. 優先建立 SRAM 的「介面行為資料庫」,而不是試圖理解內部

客戶給的 SRAM model 通常是 vendor 提供的加密或 behavioral model,你無法 (也不該) 去看內部邏輯。但你必須建立一份 per-SRAM 的介面文件,記錄每顆 SRAM 的:control pin 列表 (CE/WE/OE/RE)、test mode 相關 pin (BIST/scan/retention)、power 相關 pin (PD/SD/LS)、以及最關鍵的——哪些 pin 在 reset 後必須有 known value 才能讓 Q 不是 X。這份資料庫可以靠 grep SRAM model 中的 always block 或 UDP 定義反推出來,也可以從 vendor 的 Liberty (.lib) 檔案的 fflatch 描述中萃取。一旦建立,後續所有 project 的 SRAM bring-up 都能受益。

2. 在 ATPG/scan insertion 前先做 X-source analysis,別等到 simulation 才發現

DFT flow 中最痛苦的就是 ATPG coverage 上不去,而 root cause 往往是 X-source 污染了 scan chain 的 observe point。建議在拿到 netlist 後,立刻用 formal tool 或 TetraMAX/Tessent 的 X-source analysis 功能做一次掃描,找出所有可能產生 X 的來源 (uninitialized FF、black box output、bus contention、tri-state 衝突)。SRAM 的 data output 是最常見的 X-source,需要在 test mode 下用 X-bounding (例如加 X-blocking logic 或 OPCG 的 mask) 來隔離。這比等到 ATPG 跑完發現 coverage 只有 85% 再回頭找原因省好幾週。

3. 把「force 解法」升級為標準化的 test mode initialization sequence

原文作者用 force 解問題在 RTL simulation 是可行的,但 DFT 工程師最終要產生的是真實能在 ATE 上跑的 pattern,不能依賴 force。建議把所有需要 force 的 reset/control 訊號,整理成一份 test mode entry sequence:在 scan shift 開始前,透過 primary input 或 test controller 把這些訊號設到正確值。如果某些 SRAM control pin 沒有從頂層拉出來 (常見於客戶交付的 hard macro),就要跟客戶要求加 test point 或透過 JTAG/IEEE 1500 wrapper 來控制。把這個需求在專案早期就提給客戶,比中後期才發現要 ECO 便宜得多。

4. 對於「不告知來龍去脈」的客戶,用 connectivity check 取代信任

客戶不會主動告訴你「這顆 SRAM 的 retention pin 在 functional mode 是被 power controller 控制,但你 DFT mode 要自己處理」這種細節。與其依賴溝通,不如寫 connectivity check script (用 PrimeTime、Conformal 或自己寫 Python 解析 netlist) 來機械化驗證:每顆 SRAM 的每個 control pin 是否連到 (a) primary input、(b) test controller 輸出、(c) tied to constant、還是 (d) 連到某個 functional logic。第 (d) 種就是高風險項目,要列出來逐一跟客戶確認 test mode 下的行為。這份報告也是日後跟客戶對 spec 的有力證據——當客戶說「你們 DFT 沒做好」時,你可以拿出當初的 connectivity report 證明已經提示過。

5. 建立 X-propagation 的 regression baseline,讓「換 SRAM」不再是災難

原文作者每換一次 SRAM 就要重新 debug,這在 DFT flow 中更嚴重——客戶可能在 P&R 後換 SRAM compiler 版本、調整 SRAM 配置、或追加新的 instance。建議把 DFT simulation 設計成可比對的 regression:固定一組 golden test pattern (scan chain integrity、MBIST、boundary scan),每次 netlist 更新都自動跑這組 pattern,並用工具 (例如 Verdi 的 nWave compare 或自寫 VCD diff) 自動找出新增的 X 訊號與時間點。這樣當客戶丟新 netlist 過來,你能在幾小時內回報「這次 SRAM 換版導致 instance X/Y/Z 的 retention pin 行為改變」,而不是花四天人工比對。長期下來這份 regression 也會累積成你對該客戶 SoC 最深的知識資產。


DFT 工程師相對於 RTL 工程師,對訊號的控制權更少、對設計意圖的可見度更低、但對 pattern 品質的責任更重。所以策略上要從「事後 debug」轉向「事前防呆」,並且大量依賴自動化工具與書面證據,因為你無法假設客戶會給你完整資訊,也無法假設 RTL designer 會考慮到你的需求。