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>
19 KiB
.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
- KL520 / KL720 是
fw_scpu.bin+fw_ncpu.bin兩個 raw binary、KL630 / KL730 是kp_firmware.tar+kp_loader.tar兩個 .tar 包 - .tar 裡面包什麼、SDK 怎麼吃,需要在 B 階段 M9-6 milestone 驗證——我們現在無法從 warrenchen 程式碼推斷(他們沒實作)
- 兩個候選策略:
- 策略 X:runtime 解壓(bridge.py 解 .tar 到 temp 目錄、餵解壓後的 .bin 給 SDK)
- 策略 Y:build time 解壓(build script 解壓進
firmware/<chip>/extracted/、ship 解壓後的 .bin) - 策略 Z:直接餵 .tar 給 SDK(如果 SDK 支援
load_firmware_from_tar之類 API)
- 推薦策略 Y + Z 二選一、視 SDK 驗證結果——不推薦策略 X(每次 connect 解壓浪費時間 + temp file 管理麻煩)
- 安裝包大小衝擊估算:策略 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 binary(DFUT 用)
│ └── 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 是新世代 SDK(v2.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 release(warrenchen 取樣時的版本快照) |
1.3 .tar 內容可能性(未驗證、僅猜測)
.tar 通常會包:
fw_scpu.binfw_ncpu.binmanifest.json或metadata.txt(含版本、checksum、target chip 等)- 可能含其他 firmware sub-module(FPP_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 內含檔案清單、大小、結構
本份研究無法跑這個 inspect(Architect 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) |
假設新 API(KDP2 寫 flash 用) | 未確認、推測對應 KL630/KL730 升級 |
kp.core.update_kdp_firmware_from_files(dg, scpu, ncpu, auto_reboot) |
既有 API(A 階段用) | 對 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_KL730enum → 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 bundle(B2 階段):
- 每 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 |
→ 採策略 Y(build time 解壓)vs 策略 Z(ship .tar)大小差異 < 100KB、可忽略。
4. 解壓策略對比
4.1 策略 X:runtime 解壓
做法:
- bridge.py 每次 connect KL630/KL730 時、解壓
.tar到tempfile.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 策略 Y:build time 解壓
做法:
installer/build script 在 wails build 之前、先解壓.tar到server/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
絕對不要選策略 X(runtime 解壓)。
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 出去的安裝包有解壓檔)
- 開發環境 setup:
scripts/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-1:SDK 不接受 .tar 直接餵(中度,B 階段 M9-6 驗)
情境:策略 Z 不可行、被迫退回策略 Y。
緩解:M9-6 預先驗證、選對策略才做 M9-8。
8.2 R-TAR-2:build time 解壓步驟漏跑(中度)
情境:CI/CD 流程沒加解壓 step、ship 出去的安裝包沒有解壓 .bin、KL630/KL730 連不上。
緩解:
- installer build script 加 mandatory
extract-firmware.pystep - 加 build-time check:「
firmware/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 的決策點
- A 階段 MVP 不涉 .tar——不影響本檔
- B 階段啟動前 M9-6 必跑——SDK 驗證、決定策略 Y/Z
- 策略選定後本檔 §4.4 「決策樹」結果回填到 progress.md「重要決策紀錄」
- 如果使用者沒有 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 規則延伸) |