jim800121chen 46514d77d7 docs(local-tool): M9 — Kneron Dongle FW 偵測 + 升降版(A+B、翻案 R5-Q9)
L 級新功能、PRD/Design/TDD/ADR 三方協作 + 互審 + M9-6 SDK 雙驗證、總計 ~9000 行文件。

範圍:
- A 階段(MVP、5 人天):KL520 + KL720 自動升級 KDP1 → KDP2
- B 階段(10.5 人天):手動降版面向一般使用者 + KL630 / KL730 擴展
- 合計 15.5 人天、安裝包 +7MB(保守 bundle 策略)

關鍵決策:
- 翻案 R5-Q9(progress.md 第二輪使用者決策「韌體燒錄 flash → B 砍掉」)
- 跨平台用 KneronPLUS Python C API、不用 DFUT.exe
- 多版本目錄結構選 C metadata(firmware/<chip>/{version}/ + CURRENT_VERSION)
- Kneron firmware redistribution 授權與 R5-B4 預置模型同性質、發佈前評估

文件產出:
- PRD v2.2(PRD-v2.md 495 行 + features/feature-firmware-management.md 599 行)
- Design v2.2(firmware-management.md 948 行 + control-panel.md §6a graceful shutdown)
- TDD v2.2(v2/firmware-management.md 823 行 + ADR-001 218 行)
- 8 份 research(含 M9-6 弱驗證 + 強驗證、~3200 行)
- 3 份三方互審報告(PM/Design/Architect cross-review)

M9-6 強驗證重大發現(影響 B 階段):
- KL730 product_id 實際是 0x732(不是 0x0730)
- KL630/KL730 firmware 是 embedded Linux rootfs(不是 .bin、不同代設計)
- KneronPLUS Python 沒 update_kdp_firmware_from_files 公開 API、warrenchen 走 ctypes
- 不影響 A 階段、B 階段 M9-8 需 spike

下一步:派 backend M9-1 起跑(bridge.py handle_firmware_upgrade)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:40:56 +08:00

19 KiB
Raw Blame History

.tar Firmware 處理研究KL630 / KL730

對應 research index §41 範圍KL630 / KL730 firmware 是 .tar 格式(不是 .bin、SDK 處理路徑、解壓策略、安裝包衝擊 撰寫日期2026-05-24 限制:純 plan、不出 code、所有「需 SDK 文件驗證」段落都明標註 路徑使用相對路徑(相對於 /Users/jimchen/visionA/local-tool/


0. TL;DR

  1. KL520 / KL720 是 fw_scpu.bin + fw_ncpu.bin 兩個 raw binary、KL630 / KL730 是 kp_firmware.tar + kp_loader.tar 兩個 .tar 包
  2. .tar 裡面包什麼、SDK 怎麼吃,需要在 B 階段 M9-6 milestone 驗證——我們現在無法從 warrenchen 程式碼推斷(他們沒實作)
  3. 兩個候選策略:
    • 策略 Xruntime 解壓bridge.py 解 .tar 到 temp 目錄、餵解壓後的 .bin 給 SDK
    • 策略 Ybuild time 解壓build script 解壓進 firmware/<chip>/extracted/、ship 解壓後的 .bin
    • 策略 Z直接餵 .tar 給 SDK(如果 SDK 支援 load_firmware_from_tar 之類 API
  4. 推薦策略 Y + Z 二選一、視 SDK 驗證結果——不推薦策略 X每次 connect 解壓浪費時間 + temp file 管理麻煩)
  5. 安裝包大小衝擊估算:策略 Y 約 +5MB、策略 Z 約 +5MB一樣、因為 .tar 跟 .bin 解壓後大小接近、壓縮率不高)

1. 為什麼 KL630 / KL730 是 .tar、不是 .bin

1.1 從 warrenchen bundle 看到的事實

local_service_win/firmware/
├── KL520/
│   ├── fw_scpu.bin       ← raw binary
│   ├── fw_ncpu.bin       ← raw binary
│   ├── fw_loader.bin     ← raw binary
│   ├── dfw/minions.bin   ← raw binaryDFUT 用)
│   └── VERSION           ← "2.2.0"
├── KL720/
│   ├── fw_scpu.bin
│   ├── fw_ncpu.bin
│   └── VERSION           ← "2.2.0"
├── KL630/
│   ├── kp_firmware.tar   ← tar 打包
│   ├── kp_loader.tar     ← tar 打包
│   └── VERSION           ← "SDK-v2.5.7"(命名規則不同)
└── KL730/
    ├── kp_firmware.tar
    ├── kp_loader.tar
    └── VERSION           ← "SDK-v1.3.0"

1.2 推測原因(待 SDK 文件驗證

從命名規則、版號規則、SDK 版本看:

觀察 推論
KL520/KL720 VERSION 是 firmware 版本2.2.0、KL630/KL730 是 SDK 版本SDK-v2.5.7 KL630/KL730 是新世代 SDKv2.x SDK release、firmware 與 SDK 綁定打包
KL520/KL720 用 fw_scpu.bin / fw_ncpu.bin 命名、KL630/KL730 用 kp_firmware.tar 命名 KneronPLUS v2/v3 改用統一 packaging 格式、把 scpu + ncpu + metadata 整包到一個 .tar
KL520/KL720 有 fw_loader.bin、KL630/KL730 有 kp_loader.tar Loader 也用同樣的打包策略、可能裡面除了 binary 還含 manifest
KL730 SDK-v1.3.0 比 KL630 SDK-v2.5.7 低、跟 chip 推出時間有關 KL730 可能還在較舊 SDK releasewarrenchen 取樣時的版本快照)

1.3 .tar 內容可能性(未驗證、僅猜測

.tar 通常會包:

  • fw_scpu.bin
  • fw_ncpu.bin
  • manifest.jsonmetadata.txt含版本、checksum、target chip 等)
  • 可能含其他 firmware sub-moduleFPP_FW 等視 chip 而異)

驗證方式M9-6 必做):

mkdir -p /tmp/kl630_inspect
cd /tmp/kl630_inspect
tar -tvf /tmp/web_academy_prototype/local_service_win/firmware/KL630/kp_firmware.tar
tar -tvf /tmp/web_academy_prototype/local_service_win/firmware/KL630/kp_loader.tar
tar -tvf /tmp/web_academy_prototype/local_service_win/firmware/KL730/kp_firmware.tar
tar -tvf /tmp/web_academy_prototype/local_service_win/firmware/KL730/kp_loader.tar
# 紀錄每個 .tar 內含檔案清單、大小、結構

本份研究無法跑這個 inspectArchitect Agent 不執行 shell 變動類指令、且這應該在實機驗證階段做——M9-6 milestone 必做、結果回填到本檔 §3。


2. KneronPLUS API 對 .tar 的處理(全部待 SDK 文件驗證

2.1 可能的 API 設計

從 KneronPLUS Python SDK 既有 API pattern 推測:

API 候選 簽名 可能性
kp.core.load_firmware_from_file(dg, scpu_path, ncpu_path) 既有 API、接受兩個 .bin KL630/KL730 是否能接受 .tar 內容路徑?需要先解壓?
kp.core.load_firmware_from_tar(dg, tar_path) 假設新 API 未確認存在——可能 SDK v3.x 才有
kp.core.update_kdp2_firmware_from_tar(dg, tar_path, auto_reboot) 假設新 APIKDP2 寫 flash 用) 未確認、推測對應 KL630/KL730 升級
kp.core.update_kdp_firmware_from_files(dg, scpu, ncpu, auto_reboot) 既有 APIA 階段用) 對 KL630/KL730 可能不適用KDP 是 KL520/KL720 老世代術語)

2.2 從 warrenchen wheel 推斷

local_service_win/KneronPLUS-3.1.2-py3-none-any.whl 是 KneronPLUS 3.1.2、推測 API 命名應該已統一v3.x 主版本)。

驗證方式M9-6 必做):

cd /tmp
unzip /tmp/web_academy_prototype/local_service_win/KneronPLUS-3.1.2-py3-none-any.whl -d kneron_plus_inspect
# 找 kp/core.py 或對應的 wrapper
grep -rn "tar\|load_firmware\|update_kdp\|update_firmware" /tmp/kneron_plus_inspect/kp/
# 找所有 firmware-related 函數
grep -rn "^def " /tmp/kneron_plus_inspect/kp/core.py | grep -i "fw\|firmware"

本份研究無法跑這個——M9-6 必做。

2.3 從 visionA-local 既有 KneronPLUS wheel 推斷

需要知道我們既有 wheel 版本(待查

# 假設安裝在 server/scripts/venv/ 或類似位置
ls server/scripts/*.whl 2>/dev/null  # 如果是直接 ship wheel
pip show kneronplus 2>/dev/null      # 如果是 installed package

重要:如果我們既有 wheel 比 warrenchen 的 3.1.2 還舊(例如 2.x可能

  • 既有 wheel 不認 KP_DEVICE_KL630 / KP_DEVICE_KL730 enum → driver 升級必須先升 wheel
  • 既有 wheel 沒有 .tar 處理 API → 必須升 wheel 才能支援 KL630/KL730

→ M9-6 評估「是否升級 KneronPLUS wheel」是 B 階段風險最高的決策點。


3. 大小估算warrenchen 提供的 .tar 各多大)

3.1 .tar 檔案大小(從 warrenchen bundle 推斷)

# 在 M9-6 驗證階段執行(本份研究不執行 shell write 動作)
ls -lh /tmp/web_academy_prototype/local_service_win/firmware/KL630/
ls -lh /tmp/web_academy_prototype/local_service_win/firmware/KL730/
ls -lh /tmp/web_academy_prototype/local_service_win/firmware/KL520/
ls -lh /tmp/web_academy_prototype/local_service_win/firmware/KL520_kdp/
ls -lh /tmp/web_academy_prototype/local_service_win/firmware/KL720/

預估值(基於檔案類型常識):

路徑 預估大小
KL520/fw_scpu.bin ~52KB TDD L3219 記錄
KL520/fw_ncpu.bin ~40KB TDD L3219 記錄
KL520/fw_loader.bin ~10KB KDP1→KDP2 升級用
KL520/dfw/minions.bin ~50KB DFUT 用、未來可能不 bundle
KL520_kdp/fw_scpu.bin ~50KB KDP1 降版用
KL520_kdp/fw_ncpu.bin ~40KB KDP1 降版用
KL720/fw_scpu.bin ~150KB KDP2、更複雜
KL720/fw_ncpu.bin ~100KB
KL630/kp_firmware.tar ~2-3MB tar 含多檔、新世代 firmware 較大
KL630/kp_loader.tar ~500KB loader
KL730/kp_firmware.tar ~3-4MB
KL730/kp_loader.tar ~500KB

B 階段所有 firmware 合計增量

  • KL520_kdp降版用~90KB
  • KL630升降版用~2.5-3.5MB
  • KL730升降版用~3.5-4.5MB
  • 合計:~6-8MB

multi-version bundleB2 階段)

  • 每 chip 額外 bundle 1 個舊版v2.1.0 之類):~+1-2MB
  • 合計:~7-10MB

3.2 與 macOS dmg 既有 163MB 比

階段 累計新增 累計安裝包 衝擊
既有(含 KL520/KL720 firmware 0 163MB baseline
A 階段(+ KL520/fw_loader.bin +10KB ~163MB <0.01%
B 階段(含 KL520_kdp + KL630 + KL730 firmware +6-8MB ~170-171MB +4-5%
B2 階段(多版本,每 chip + 1 個舊版) +7-10MB ~170-173MB +4-6%

使用者「+5MB 接受」的決策完全成立——B 階段全做完約 +7-10MB、不超過 +6%。

3.3 解壓後大小(估算)

.tar 通常不會做壓縮tar 預設無 compression、除非 .tar.gz、解壓後大小 ≈ 原 .tar 大小:

檔案 原始 .tar 解壓後
KL630/kp_firmware.tar (~3MB) 3MB ~3MB內含多個 .bin、metadata、加總接近 .tar 大小)
KL730/kp_firmware.tar (~4MB) 4MB ~4MB

→ 採策略 Ybuild time 解壓vs 策略 Zship .tar大小差異 < 100KB、可忽略。


4. 解壓策略對比

4.1 策略 Xruntime 解壓

做法

  • bridge.py 每次 connect KL630/KL730 時、解壓 .tartempfile.mkdtemp() 暫存目錄
  • 餵解壓後的 .bin 路徑給 kp.core.load_firmware_from_file()
  • 連線結束清理 temp 目錄

優點

  • 安裝包不增加解壓後檔案、只 ship 一份 .tar
  • 解壓邏輯集中在 bridge.py、單一資料夾管理

缺點

  • 每次 connect 多花 50-200ms 解壓時間
  • temp 目錄管理clean-up、permissions、tempfile 衝突
  • macOS hardened runtime 對 temp 目錄寫入權限可能有限制
  • Windows 上 tempfile 預設位置 %TEMP% 可能被防毒軟體掃描,慢
  • KL630/KL730 如果類似 KL520每次 connect 都要 load firmware解壓會發生在 hot path

不推薦——成本 vs 收益不划算。

4.2 策略 Ybuild time 解壓

做法

  • installer/ build script 在 wails build 之前、先解壓 .tarserver/scripts/firmware/<chip>/extracted/
  • ship 解壓後的 .bin不 ship 原始 .tar
  • bridge.py 直接餵解壓後路徑、_resolve_firmware_paths(chip) 解析 extracted/ 子目錄

優點

  • runtime 零成本、connect 速度不受影響
  • bridge.py 邏輯與 KL520/KL720 一致(都餵 .bin path
  • 安裝包大小不變(.tar 跟解壓後 .bin 大小接近)

缺點

  • 需要修改 installer build script單純做
  • 開發環境第一次 clone repo 後、必須跑解壓 script 一次(或加進 make setup / npm postinstall
  • 解壓後檔案進不進 git建議不進(避免 binary diff、靠 build script 即時產生)

推薦條件:如果 SDK 不支援 .tar 直接餵入、選此策略。

4.3 策略 Z直接餵 .tar 給 SDK

做法

  • SDK 提供 kp.core.load_firmware_from_tar() 或類似 API、bridge.py 直接傳 .tar 路徑

優點

  • 最簡(不需解壓邏輯)
  • runtime 零成本
  • 安裝包 ship 原始 .tar、與 warrenchen 一致

缺點

  • 依賴 SDK 支援——M9-6 必須驗證 API 存在
  • 如果 SDK 接受的是「.tar 內 fw_scpu.bin 解出來的路徑」、那就退回策略 Y
  • bridge.py 內 KL520/KL720 vs KL630/KL730 firmware 路徑解析邏輯不一致(一邊兩個 .bin、一邊一個 .tar_resolve_firmware_paths 必須回 union type 或分支處理

推薦條件:如果 SDK 確認支援、選此策略(最乾淨)。

4.4 決策樹

M9-6 SDK 驗證結果
├── SDK 支援 load_firmware_from_tar() / update_*_from_tar()
│   └── 選策略 Z
│       └── 改動_resolve_firmware_paths 回 (tar_path, None) 或 union
│
└── SDK 不支援、必須先解壓
    └── 選策略 Y
        └── 改動:
            - installer build script 加 tar -xf 步驟
            - .gitignore 加 firmware/<chip>/extracted/
            - 開發環境 setup 加解壓 step
            - _resolve_firmware_paths 找 extracted/fw_scpu.bin

絕對不要選策略 Xruntime 解壓)。


5. SDK API 可用性驗證計畫M9-6 必做)

5.1 驗證項目清單

# 驗證項目 方法 影響
1 既有 KneronPLUS wheel 版本 pip show kneronplus / 找 wheel 檔案 決定要不要升級 wheel
2 wheel 內 kp.ProductId.KP_DEVICE_KL630 / KL730 enum 存在 python -c "import kp; print(kp.ProductId.KP_DEVICE_KL630)" 不存在 → 升 wheel
3 kp.core.load_firmware_from_file 對 .tar 路徑的行為 實機跑、傳 .tar 看回傳 決定策略 Y or Z
4 kp.core.update_kdp_firmware_from_files 對 KL630/KL730 是否適用 實機跑(如果有 KL630/KL730 FW 升降版實作策略
5 是否有 load_firmware_from_tar() / update_*_from_tar() 新 API grep wheel source / 查 SDK 文件 策略 Z 是否可行
6 KL630/KL730 firmware 字串可能值 實機 kp.core.scan_devices() 看 firmware 欄位 FW badge 顯示邏輯
7 KL630/KL730 是否每次 connect 都要 load firmware 實機 connect 兩次、看第二次需不需要 load connect 流程設計
8 kp.inference.generic_image_inference_send/receive 對 KL630/KL730 NEF 是否能直接 work 實機跑 sample inference inference 流程是否要分支
9 .tar 內容(解壓看裡面有什麼) tar -tvf kp_firmware.tar bridge.py 解析策略
10 KneronPLUS wheel 升級對 KL520/KL720 的 regression 風險 升級後跑 既有 E2E 是否值得升 wheel

5.2 驗證所需資源

  • 使用者手上要有:
    • 至少 1 個 KL630 dongle理想
    • 至少 1 個 KL730 dongle理想
    • 如果沒有、只能查 SDK 文件 + wheel source、無法做動態驗證、風險變高
  • KneronPLUS SDK 官方文件PDF / online docs
  • Python REPL + 既有 venv

5.3 驗證結果回填位置

  • 本檔 §1.3、§2、§3.3 的「未驗證」段落填實際結果
  • 40-b-phase 檔 §3.2、§5.2、§7.1 對應段落
  • 30-integration-plan 檔 §6 階段 B 評估提示更新

6. .tar 解壓技術細節(如選策略 Y

6.1 解壓工具選擇

跨平台選 Python tarfile 模組(標準庫、無外部依賴):

# 偽碼、給 installer build script 用
import tarfile
import os
import shutil

def extract_firmware_tars(firmware_dir):
    """Walk firmware/<chip>/ dirs、解壓所有 .tar 到 extracted/ 子目錄"""
    for chip_dir in os.listdir(firmware_dir):
        chip_path = os.path.join(firmware_dir, chip_dir)
        if not os.path.isdir(chip_path):
            continue
        for fname in os.listdir(chip_path):
            if not fname.endswith(".tar"):
                continue
            tar_path = os.path.join(chip_path, fname)
            extract_to = os.path.join(chip_path, "extracted")
            os.makedirs(extract_to, exist_ok=True)
            with tarfile.open(tar_path, "r") as tar:
                tar.extractall(extract_to)

不選 shell tar -xf——Windows 上 tar 命令不一定存在Win10+ 才有、Python 標準庫穩定。

6.2 何時跑

  • CI/CD build pipeline:每次 build 安裝包前跑(保證 ship 出去的安裝包有解壓檔)
  • 開發環境 setupscripts/setup-dev.sh / make setup 加一步、或 npm postinstall
  • 首次 clone 後README 提醒「跑 python3 scripts/extract-firmware.py

6.3 .gitignore 規則

# .gitignore 追加B 階段 M9-8 milestone
server/scripts/firmware/*/extracted/

.tar 進 git、解壓後 .bin 不進 git避免雙份 binary 進版控、減少 repo 大小)

6.4 衝突處理

如果使用者手動修改了 extracted/ 內檔案、下次 build 會覆蓋。可接受、因為這個目錄不該手動改。


7. 如果選策略 Z直接餵 .tar的細節

7.1 bridge.py 簽名變化

# _resolve_firmware_paths 偽碼變化
def _resolve_firmware_paths(chip="KL520"):
    base = os.path.dirname(os.path.abspath(__file__))
    fw_dir = os.path.join(base, "firmware", chip)
    
    if chip in ("KL520", "KL720"):
        # 既有路徑 .bin
        scpu = os.path.join(fw_dir, "fw_scpu.bin")
        ncpu = os.path.join(fw_dir, "fw_ncpu.bin")
        if os.path.exists(scpu) and os.path.exists(ncpu):
            return {"format": "bin", "scpu": scpu, "ncpu": ncpu, "loader": ...}
        return None
    
    if chip in ("KL630", "KL730"):
        fw_tar = os.path.join(fw_dir, "kp_firmware.tar")
        loader_tar = os.path.join(fw_dir, "kp_loader.tar")
        if os.path.exists(fw_tar):
            return {"format": "tar", "firmware": fw_tar, "loader": loader_tar}
        return None
    
    return None

→ 回 dict / union type 而不是 tuple、給 caller 判斷 format。

7.2 firmware load 分支

# handle_connect / handle_firmware_upgrade 內偽碼
fw_paths = _resolve_firmware_paths(chip)
if fw_paths is None:
    return {"error": f"firmware not found for {chip}"}

if fw_paths["format"] == "bin":
    kp.core.load_firmware_from_file(dg, fw_paths["scpu"], fw_paths["ncpu"])
elif fw_paths["format"] == "tar":
    # 假設 SDK 提供這個 API、待驗證
    kp.core.load_firmware_from_tar(dg, fw_paths["firmware"])

8. 風險(針對 .tar 處理)

8.1 R-TAR-1SDK 不接受 .tar 直接餵中度B 階段 M9-6 驗)

情境:策略 Z 不可行、被迫退回策略 Y。

緩解M9-6 預先驗證、選對策略才做 M9-8。

8.2 R-TAR-2build time 解壓步驟漏跑(中度)

情境CI/CD 流程沒加解壓 step、ship 出去的安裝包沒有解壓 .bin、KL630/KL730 連不上。

緩解

  • installer build script 加 mandatory extract-firmware.py step
  • 加 build-time checkfirmware/KL630/extracted/fw_scpu.bin 不存在 → build fail」
  • 安裝包 smoke test解壓安裝包、grep extracted/fw_scpu.bin 必須存在

8.3 R-TAR-3解壓對 macOS notarization 影響(低度)

情境macOS notarization 對 dmg 內 binary file 要求 codesign、解壓出來的 .bin 是否需要簽?

研究

  • firmware .bin 不是 executable是 NPU instruction binary、預估不需 codesign
  • 但 Apple Gatekeeper / XProtect 可能誤判某些 binary pattern
  • M9-13 milestone 三平台驗證時必須跑 notarized dmg、確認沒被砍

8.4 R-TAR-4.tar 解壓路徑跨平台問題(低度)

情境Python tarfile.extractall() 在 Windows 上對絕對路徑 / .. path entry 有警告Python 3.12+ 預設拒絕)。

緩解

  • 解壓前用 tarfile.data_filter 過濾Python 3.12+ 內建)
  • 或預先驗證 .tar 內容、refuse 含 .. / 絕對路徑的 .tar

9. 給 Orchestrator 的決策點

  1. A 階段 MVP 不涉 .tar——不影響本檔
  2. B 階段啟動前 M9-6 必跑——SDK 驗證、決定策略 Y/Z
  3. 策略選定後本檔 §4.4 「決策樹」結果回填到 progress.md「重要決策紀錄」
  4. 如果使用者沒有 KL630/KL730 dongle——降級驗證強度:只查 SDK 文件 + wheel source、不做動態驗證、風險 R-FW-8 提升

10. 與其他研究檔的關係

連結 引用內容
40-b-phase-kl630-kl730-extension.md §6.3 _resolve_firmware_paths 擴展偽碼(本檔詳化)
40-b-phase-kl630-kl730-extension.md R-FW-9 .tar 解壓對安裝包大小衝擊(本檔 §3 詳化)
40-b-phase-kl630-kl730-extension.md R-FW-10 KL630/KL730 是否有 Loader mode本檔 §5.1 驗證 #6/#7 確認)
42-manual-downgrade-for-end-users.md 多版本 .tar 儲存結構(本檔 §6.3 .gitignore 規則延伸)