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

459 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# .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.json``metadata.txt`含版本、checksum、target chip 等)
- 可能含其他 firmware sub-moduleFPP_FW 等視 chip 而異)
**驗證方式**M9-6 必做):
```bash
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)` | 假設新 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 必做):
```bash
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 版本(**待查**
```bash
# 假設安裝在 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 推斷)
```bash
# 在 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內含多個 .binmetadata加總接近 .tar 大小 |
| `KL730/kp_firmware.tar` (~4MB) | 4MB | ~4MB |
採策略 Ybuild time 解壓vs 策略 Zship .tar大小差異 **< 100KB**、可忽略
---
## 4. 解壓策略對比
### 4.1 策略 Xruntime 解壓
**做法**
- 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-uppermissionstempfile 衝突
- 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 之前先解壓 `.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()` 或類似 APIbridge.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` 模組**標準庫無外部依賴
```python
# 偽碼、給 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 簽名變化
```python
# _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 分支
```python
# 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 流程沒加解壓 stepship 出去的安裝包沒有解壓 .binKL630/KL730 連不上
**緩解**
- installer build script mandatory `extract-firmware.py` step
- 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 的決策點
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 規則延伸|