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>
731 lines
29 KiB
Markdown
731 lines
29 KiB
Markdown
# M9-6 SDK 驗證計畫書
|
||
|
||
> 對應 research index §50
|
||
> 範圍:B 階段啟動前的 KneronPLUS SDK + KL630/KL730 SDK 行為實機驗證計畫
|
||
> 撰寫日期:2026-05-24
|
||
> 限制:純 plan、不出 code、不執行驗證(驗證由 testing/backend 工程師執行)
|
||
> 路徑使用相對路徑(相對於 `/Users/jimchen/visionA/local-tool/`)
|
||
|
||
---
|
||
|
||
## 0. TL;DR
|
||
|
||
1. 本檔是「**將來要做的驗證計畫**」、不是驗證執行報告。執行階段由 testing + backend Agent 接手、結果回填到本檔 §8 與相關研究檔。
|
||
2. 必須驗證的關鍵 unknown 共 **10 項**,分 3 大類:
|
||
- **SDK API 行為**(API 簽名 / .tar 支援 / chip enum 完整性)— 6 項
|
||
- **KL630/KL730 連線行為**(USB scan / connect / firmware load)— 3 項
|
||
- **KneronPLUS wheel 版本相容性**(既有 wheel 是否需升級 + 升級風險)— 1 項
|
||
3. 驗證有兩個強度等級:
|
||
- **強驗證**(有實機 KL630/KL730 dongle)— 涵蓋 10 項全部、結果可信
|
||
- **弱驗證**(無實機、只查 SDK 文件 + wheel source)— 涵蓋 6 項(API 層)、剩 4 項(連線行為)只能靠文件推斷、留風險到 M9-9 開發階段
|
||
4. 預估時間:強驗證 2 人天、弱驗證 1 人天
|
||
5. **關鍵風險**:拿不到 KL630/KL730 硬體 → 強驗證降級為弱、B 階段所有 milestone 都帶 SDK unknown 風險
|
||
|
||
---
|
||
|
||
## 1. 為什麼需要 SDK 驗證
|
||
|
||
### 1.1 前兩輪研究的 SDK unknown 清單
|
||
|
||
從 `40-b-phase-kl630-kl730-extension.md` §3.2 + `41-tar-firmware-handling.md` §2 + §5 整理:
|
||
|
||
| 來源 | unknown | 影響範圍 |
|
||
|------|---------|---------|
|
||
| `40-b-phase` §3.2 row 1 | KL630 `connect_devices()` 是否與 KL520/KL720 共用簽名 | connect 流程設計 |
|
||
| `40-b-phase` §3.2 row 2 | `load_firmware_from_file()` 對 KL630/KL730 是否仍接受兩個 .bin(解壓後) | .tar 處理策略選定(Y or Z)|
|
||
| `40-b-phase` §3.2 row 3 | 是否有 `load_firmware_from_tar()` 之類新 API | 策略 Z 是否可行 |
|
||
| `40-b-phase` §3.2 row 4 | `update_kdp_firmware_from_files()` 對 KL630/KL730 是否適用 | FW 升降版 API 選擇 |
|
||
| `40-b-phase` §3.2 row 5 | KL630/KL730 是否仍有 Loader / KDP / KDP2 三 state | FW 偵測邏輯 |
|
||
| `40-b-phase` §3.2 row 6 | KL630/KL730 是 flash-based 還是每次都要 load firmware | connect 速度設計 |
|
||
| `40-b-phase` §3.2 row 7 | `generic_image_inference_send/receive` 對 KL630/KL730 是否一致 | inference 流程是否分支 |
|
||
| `40-b-phase` §3.2 row 8 | KL630/KL730 firmware 字串可能值 | FW badge 顯示邏輯 |
|
||
| `41-tar` §1.3 | .tar 內容(解壓後有哪些檔案、metadata) | bridge.py 解析邏輯 |
|
||
| `41-tar` §2.3 | visionA-local 既有 KneronPLUS wheel 版本 vs 3.1.2 | wheel 升級決策 |
|
||
|
||
### 1.2 為什麼這些必驗、不能靠推測
|
||
|
||
每個 unknown 都有「假設錯了會炸」的後果:
|
||
|
||
- API 簽名假設錯 → bridge.py 寫出來直接 import error / type error / runtime crash
|
||
- .tar 處理策略選錯 → 安裝包大小 / build script 設計全錯、要從 M9-8 整段重做
|
||
- wheel 版本假設錯 → KL520/KL720 既有功能 regression、回頭重做 A 階段
|
||
|
||
→ M9-6 是 B 階段 critical path、所有後續 milestone 都依賴它。
|
||
|
||
---
|
||
|
||
## 2. 驗證目標清單(10 項)
|
||
|
||
每項 = 一個必須回答的具體問題。每項標:是否依賴實機、強度等級、後續決策影響。
|
||
|
||
### 類別 A:SDK API 行為(弱驗證即可、查 wheel source / SDK 文件)
|
||
|
||
#### A-1:既有 KneronPLUS wheel 版本是什麼
|
||
|
||
- **問題**:visionA-local repo 內目前用的 KneronPLUS wheel 是哪個版本?是否內含 `KP_DEVICE_KL630` / `KP_DEVICE_KL730` enum?
|
||
- **依賴實機**:否(只需 Python 環境)
|
||
- **影響**:決定要不要升級 wheel(升級會牽動 KL520/KL720 regression 風險)
|
||
|
||
#### A-2:wheel 內 `kp.ProductId.KP_DEVICE_KL630` / `KL730` enum 是否存在
|
||
|
||
- **問題**:既有 wheel `kp.ProductId.KP_DEVICE_KL630` / `KL730` 是否可 import、值是多少
|
||
- **依賴實機**:否
|
||
- **影響**:A-2 若失敗 → 必須升級 wheel(連 driver 認 chip 都做不到)
|
||
|
||
#### A-3:`kp.core.load_firmware_from_file()` 對 .tar 的行為
|
||
|
||
- **問題**:把 `.tar` 路徑當 `scpu_file` 參數餵進去、SDK 回什麼?接受、解析錯誤、還是 silent fail?
|
||
- **依賴實機**:否(傳路徑、看 exception type)
|
||
- **影響**:決定 .tar 處理策略(Y 解壓 or Z 直接餵)
|
||
|
||
#### A-4:是否有 `load_firmware_from_tar()` / `update_*_from_tar()` 新 API
|
||
|
||
- **問題**:wheel 內是否提供 .tar 專用 firmware load API?簽名為何?
|
||
- **依賴實機**:否(grep wheel source)
|
||
- **影響**:策略 Z 是否可行(最乾淨方案)
|
||
|
||
#### A-5:`update_kdp_firmware_from_files()` 對 KL630/KL730 是否適用
|
||
|
||
- **問題**:把 KL630 device + KL630 .tar / .bin 餵進去、SDK 回什麼?是否有 chip-aware 內部 dispatch、還是 KL520/KL720 專用?
|
||
- **依賴實機**:部分(無實機只能查 source code 的 chip 分支邏輯)
|
||
- **影響**:FW 升降版 API 選擇
|
||
|
||
#### A-6:KneronPLUS wheel 升級對 KL520/KL720 既有行為的 regression 範圍
|
||
|
||
- **問題**:從目前 wheel → 3.1.2(或 latest)有哪些 breaking change?
|
||
- **依賴實機**:強驗證(升級後跑既有 E2E test);弱驗證(看 release notes / changelog)
|
||
- **影響**:是否值得升 wheel、升級後 A 階段 KL520/KL720 是否需要回歸測試
|
||
|
||
### 類別 B:KL630 / KL730 連線行為(需實機強驗證)
|
||
|
||
#### B-1:KL630 / KL730 在 USB scan 時的 product_id
|
||
|
||
- **問題**:實機 `kp.core.scan_devices()` / pyusb scan 時,KL630 dongle 的 `product_id` 是 `0x0630` 嗎?KL730 是 `0x0730` 嗎?是否有 legacy/new 雙版本(像 KL720 的 `0x0200` vs `0x0720`)?
|
||
- **依賴實機**:是(必須有 dongle)
|
||
- **影響**:`_KNOWN_PRODUCTS` map 是否需擴展、`chipFromProductID()` switch 是否要加額外 case
|
||
|
||
#### B-2:KL630 / KL730 是否每次 connect 都要 load firmware
|
||
|
||
- **問題**:實機連續 connect 兩次、第二次是否需要重新 load firmware?(KL520 要、KL720 不要)
|
||
- **依賴實機**:是
|
||
- **影響**:connect 流程設計(速度 + 用量)、是否走 build time 解壓 vs runtime 解壓
|
||
|
||
#### B-3:KL630 / KL730 firmware 字串可能值
|
||
|
||
- **問題**:實機 `scan_devices()` 回傳的 `device.firmware` 欄位字串是什麼?"KDP2" / "KDP3" / "Loader" / 其他?是否能透過此值區分「需要升級」vs「已 ready」?
|
||
- **依賴實機**:是
|
||
- **影響**:FW badge UI 顯示邏輯、`detector.go` firmware state 判斷
|
||
|
||
### 類別 C:.tar 檔案內容(無實機也能驗、純 tar -xf)
|
||
|
||
#### C-1:.tar 內容實測
|
||
|
||
- **問題**:`tar -tvf kp_firmware.tar` / `tar -tvf kp_loader.tar` 內含哪些檔案?大小?是否有 manifest?
|
||
- **依賴實機**:否(只需 warrenchen bundle 的 .tar 檔)
|
||
- **影響**:bridge.py 解壓後檔案路徑解析邏輯、`_resolve_firmware_paths` 設計
|
||
|
||
---
|
||
|
||
## 3. 各項驗證方法(執行 step-by-step)
|
||
|
||
> **注意**:這節是給未來執行 M9-6 的 testing/backend 工程師看的、step 描述要可重複。
|
||
|
||
### A-1:既有 wheel 版本查詢
|
||
|
||
**前置**:在 visionA-local repo 根目錄、Python 環境已 setup。
|
||
|
||
**步驟**:
|
||
```bash
|
||
# Step 1: 找 KneronPLUS wheel 位置
|
||
find . -name "KneronPLUS*.whl" 2>/dev/null
|
||
find . -name "kneronplus*.whl" 2>/dev/null
|
||
# 若上述都沒結果、檢查是否 venv 內安裝
|
||
. server/scripts/venv/bin/activate 2>/dev/null && pip show kneronplus 2>/dev/null
|
||
# 也檢查專案有沒有 vendored Python source
|
||
find . -path "*/kp/__init__.py" -not -path "*/node_modules/*" 2>/dev/null
|
||
```
|
||
|
||
**預期結果三種**:
|
||
- **A**:找到 `KneronPLUS-X.Y.Z-py3-none-any.whl` → 記錄版號 X.Y.Z
|
||
- **B**:找到 installed package、`pip show` 回版本 → 記錄
|
||
- **C**:找不到 → wheel 是 lazy import 或 system-wide install、需查 `kneron_bridge.py` `_macos_preload_native_libs()` 內的 lib path 反推 wheel 位置
|
||
|
||
**回填位置**:本檔 §8.1、`41-tar-firmware-handling.md` §2.3
|
||
|
||
### A-2:wheel 內 KL630/KL730 enum 存在驗證
|
||
|
||
**前置**:A-1 已找到 wheel、Python 環境可 `import kp`。
|
||
|
||
**步驟**:
|
||
```bash
|
||
python3 -c "
|
||
import kp
|
||
print('KP_DEVICE_KL520:', getattr(kp.ProductId, 'KP_DEVICE_KL520', 'NOT FOUND'))
|
||
print('KP_DEVICE_KL720:', getattr(kp.ProductId, 'KP_DEVICE_KL720', 'NOT FOUND'))
|
||
print('KP_DEVICE_KL630:', getattr(kp.ProductId, 'KP_DEVICE_KL630', 'NOT FOUND'))
|
||
print('KP_DEVICE_KL730:', getattr(kp.ProductId, 'KP_DEVICE_KL730', 'NOT FOUND'))
|
||
print('KP_DEVICE_KL530:', getattr(kp.ProductId, 'KP_DEVICE_KL530', 'NOT FOUND'))
|
||
"
|
||
```
|
||
|
||
**預期結果三種**:
|
||
- **A**:四個都 print 出整數值 → 既有 wheel OK、不必升級(僅針對 enum 層)
|
||
- **B**:KL520/KL720 有、KL630/KL730 沒有 → 必須升級 wheel
|
||
- **C**:`import kp` 直接 import error → wheel 未安裝、A-1 結果有誤、回去查
|
||
|
||
**回填位置**:本檔 §8.1、影響 M9-7 決策
|
||
|
||
### A-3:load_firmware_from_file 對 .tar 行為
|
||
|
||
**前置**:A-1 + A-2 完成、warrenchen .tar 檔已取得(在 `/tmp/web_academy_prototype/local_service_win/firmware/KL630/kp_firmware.tar`)。
|
||
|
||
**步驟**:
|
||
```bash
|
||
python3 << 'EOF'
|
||
import kp
|
||
import sys
|
||
|
||
# 試 connect KL630 dongle、若無實機跳過、改成 mock test
|
||
tar_path = "/tmp/web_academy_prototype/local_service_win/firmware/KL630/kp_firmware.tar"
|
||
|
||
# 情境 1:用實機(若有)
|
||
try:
|
||
descs = kp.core.scan_devices()
|
||
kl630_port = None
|
||
for i in range(descs.device_descriptor_number):
|
||
dev = descs.device_descriptor_list[i]
|
||
# 假設 KL630 product_id = 0x0630、若 B-1 驗證後值不同要修
|
||
if hex(dev.product_id) == "0x630":
|
||
kl630_port = dev.usb_port_id
|
||
print(f"KL630 found at port {kl630_port}")
|
||
break
|
||
if kl630_port:
|
||
dg = kp.core.connect_devices([kl630_port])
|
||
# 試把 .tar 直接餵
|
||
try:
|
||
ret = kp.core.load_firmware_from_file(dg, tar_path, "")
|
||
print(f"load_firmware_from_file(.tar, '') = {ret}")
|
||
except Exception as e:
|
||
print(f"load_firmware_from_file(.tar, '') raised: {type(e).__name__}: {e}")
|
||
# 試把 .tar 當 scpu + ncpu 同檔
|
||
try:
|
||
ret = kp.core.load_firmware_from_file(dg, tar_path, tar_path)
|
||
print(f"load_firmware_from_file(.tar, .tar) = {ret}")
|
||
except Exception as e:
|
||
print(f"load_firmware_from_file(.tar, .tar) raised: {type(e).__name__}: {e}")
|
||
except Exception as e:
|
||
print(f"No device, skip runtime test: {e}")
|
||
|
||
# 情境 2:純靜態查 source(無實機也能跑)
|
||
import inspect
|
||
print("\nload_firmware_from_file source:")
|
||
try:
|
||
print(inspect.getsource(kp.core.load_firmware_from_file))
|
||
except Exception as e:
|
||
print(f"Cannot get source: {e}")
|
||
EOF
|
||
```
|
||
|
||
**預期結果三種**:
|
||
- **A**:SDK 接受 .tar、回 success → 策略 Z 可行
|
||
- **B**:SDK 報 `KP_FW_INFO_ERR` / 類似錯誤碼 → 策略 Y 必須走(build time 解壓)
|
||
- **C**:source 顯示函式內有 `.endswith(".tar")` 之類判斷 → 確認 SDK 內部分支邏輯
|
||
|
||
**回填位置**:本檔 §8.1、`41-tar-firmware-handling.md` §2.1 + §4.4
|
||
|
||
### A-4:load_firmware_from_tar / update_*_from_tar 是否存在
|
||
|
||
**步驟**:
|
||
```bash
|
||
python3 << 'EOF'
|
||
import kp
|
||
import kp.core
|
||
|
||
# 列出 kp.core 所有 firmware-related 函式
|
||
for name in dir(kp.core):
|
||
if any(k in name.lower() for k in ["firmware", "fw_", "load_fw", "update_kdp", "tar"]):
|
||
print(f"kp.core.{name}")
|
||
|
||
# 列出 kp 模組頂層 firmware-related
|
||
for name in dir(kp):
|
||
if any(k in name.lower() for k in ["firmware", "fw_", "tar"]):
|
||
print(f"kp.{name}")
|
||
EOF
|
||
```
|
||
|
||
**或解壓 wheel grep**(無 Python 環境也能跑):
|
||
```bash
|
||
cd /tmp && unzip -o KneronPLUS-X.Y.Z-py3-none-any.whl -d kneron_plus_inspect/
|
||
grep -rn "load_firmware_from_tar\|update_kdp.*tar\|extract_tar\|from_tar" kneron_plus_inspect/kp/
|
||
grep -rn "^def \|^ def " kneron_plus_inspect/kp/core.py | grep -iE "fw|firmware|tar"
|
||
```
|
||
|
||
**預期結果**:
|
||
- **A**:找到 `load_firmware_from_tar()` 或 `update_kdp2_firmware_from_tar()` → 策略 Z 確定可行
|
||
- **B**:找不到、只有既有 `load_firmware_from_file()` → 策略 Y(build time 解壓)
|
||
|
||
**回填位置**:本檔 §8.1、`41-tar-firmware-handling.md` §2.1 表格
|
||
|
||
### A-5:update_kdp_firmware_from_files 對 KL630/KL730 chip dispatch
|
||
|
||
**步驟**:
|
||
```bash
|
||
python3 << 'EOF'
|
||
import kp.core
|
||
import inspect
|
||
|
||
# 看 update_kdp_firmware_from_files source 是否有 chip 判斷
|
||
try:
|
||
src = inspect.getsource(kp.core.update_kdp_firmware_from_files)
|
||
print(src)
|
||
except Exception:
|
||
pass
|
||
|
||
# 列其他 update 系列函式
|
||
for name in dir(kp.core):
|
||
if "update" in name.lower() and "fw" in name.lower():
|
||
print(name)
|
||
EOF
|
||
```
|
||
|
||
**預期結果**:
|
||
- **A**:source 內有 `if chip == "KL630"` 之類分支 → 適用、可直接用
|
||
- **B**:source 只處理 KL520/KL720 → 需找 `update_kdp2_firmware_*` 之類新 API
|
||
- **C**:找不到對應 API → 須 fall back 用 `load_firmware_from_file` + 手動 reboot 流程
|
||
|
||
**回填位置**:本檔 §8.1、`40-b-phase` §3.2 row 4
|
||
|
||
### A-6:wheel 升級對 KL520/KL720 regression 範圍
|
||
|
||
**步驟**(弱驗證版本,無實機):
|
||
```bash
|
||
# Step 1: 取得 SDK changelog
|
||
# 通常在 Kneron developer portal 或 wheel METADATA
|
||
cd /tmp && unzip -o KneronPLUS-X.Y.Z-py3-none-any.whl -d wheel_old/
|
||
unzip -o /tmp/web_academy_prototype/local_service_win/KneronPLUS-3.1.2-py3-none-any.whl -d wheel_new/
|
||
diff -r wheel_old/kp/ wheel_new/kp/ | head -100
|
||
# 重點看 kp/core.py 和 kp/inference.py 的 API 簽名變化
|
||
```
|
||
|
||
**步驟**(強驗證版本,有實機):
|
||
1. 備份既有 wheel 安裝
|
||
2. pip install warrenchen wheel 3.1.2
|
||
3. 跑 visionA-local 既有 KL520 + KL720 完整 E2E:scan → connect → load firmware → inference
|
||
4. 比對 inference 結果(用同一張圖、同一個 NEF、檢查 detection box 是否一致)
|
||
5. 紀錄 break point
|
||
|
||
**預期結果**:
|
||
- **A**:API 簽名完全相容、inference 結果一致 → 升級安全
|
||
- **B**:API 簽名有 breaking change(部分函式 rename / 參數順序變) → 升級需配合 bridge.py 更新
|
||
- **C**:inference 結果不一致 → 升級風險高、需深查
|
||
|
||
**回填位置**:本檔 §8.1、新增風險紀錄到 R-FW-8
|
||
|
||
### B-1:KL630/KL730 USB scan product_id
|
||
|
||
**前置**:實機 KL630 + KL730 dongle 接上電腦、driver 已裝。
|
||
|
||
**步驟**:
|
||
```bash
|
||
python3 << 'EOF'
|
||
import kp
|
||
descs = kp.core.scan_devices()
|
||
print(f"Total devices: {descs.device_descriptor_number}")
|
||
for i in range(descs.device_descriptor_number):
|
||
dev = descs.device_descriptor_list[i]
|
||
print(f" device {i}:")
|
||
print(f" product_id = {hex(dev.product_id)}")
|
||
print(f" usb_port_id = {dev.usb_port_id}")
|
||
print(f" firmware = {dev.firmware}")
|
||
print(f" is_connectable = {dev.is_connectable}")
|
||
print(f" kn_number = {dev.kn_number}")
|
||
EOF
|
||
```
|
||
|
||
**同時用 pyusb 比對**(看 OS 層級看到什麼 product_id):
|
||
```bash
|
||
python3 << 'EOF'
|
||
import usb.core
|
||
devs = usb.core.find(find_all=True, idVendor=0x3231) # Kneron VID
|
||
for dev in devs:
|
||
print(f"VID:PID = {hex(dev.idVendor)}:{hex(dev.idProduct)}")
|
||
EOF
|
||
```
|
||
|
||
**預期結果**:
|
||
- KL630 → product_id = `0x0630`、KL730 → `0x0730`、跟前兩輪研究假設一致
|
||
- 若有 legacy/new 雙版本(像 KL720 的 0x0200 vs 0x0720)→ 紀錄並更新 `_KNOWN_PRODUCTS` map
|
||
|
||
**回填位置**:本檔 §8.2、`40-b-phase` §2.1
|
||
|
||
### B-2:KL630/KL730 每次 connect 都要 load firmware 嗎
|
||
|
||
**前置**:B-1 完成、確認能識別 KL630/KL730。
|
||
|
||
**步驟**:
|
||
```bash
|
||
python3 << 'EOF'
|
||
import kp
|
||
import time
|
||
|
||
descs = kp.core.scan_devices()
|
||
# 找 KL630(或 KL730)
|
||
port_id = None
|
||
for i in range(descs.device_descriptor_number):
|
||
dev = descs.device_descriptor_list[i]
|
||
if hex(dev.product_id) == "0x630": # KL630
|
||
port_id = dev.usb_port_id
|
||
firmware_before = dev.firmware
|
||
is_connectable_before = dev.is_connectable
|
||
break
|
||
if not port_id:
|
||
print("No KL630 found")
|
||
exit()
|
||
|
||
print(f"Before connect: firmware={firmware_before}, is_connectable={is_connectable_before}")
|
||
|
||
# 第一次 connect、不 load firmware
|
||
dg1 = kp.core.connect_devices([port_id])
|
||
print(f"First connect OK, dg={dg1}")
|
||
del dg1
|
||
time.sleep(2)
|
||
|
||
# 重新 scan、看 firmware 字串是否變化
|
||
descs2 = kp.core.scan_devices()
|
||
for i in range(descs2.device_descriptor_number):
|
||
dev = descs2.device_descriptor_list[i]
|
||
if dev.usb_port_id == port_id:
|
||
print(f"After first connect: firmware={dev.firmware}, is_connectable={dev.is_connectable}")
|
||
break
|
||
|
||
# 第二次 connect、看是否仍能 inference(不重 load)
|
||
dg2 = kp.core.connect_devices([port_id])
|
||
# 試跑 model load (用 KL630 sample NEF)
|
||
# kp.core.load_model_from_file(dg2, "...sample.nef")
|
||
# 觀察是否 OK
|
||
EOF
|
||
```
|
||
|
||
**預期結果**:
|
||
- **A**:第二次 connect 後直接能 inference、不用重 load firmware → flash-based、像 KL720
|
||
- **B**:第二次 connect 後 inference 失敗、需重新 `load_firmware_from_file()` → 像 KL520
|
||
|
||
**回填位置**:本檔 §8.2、`40-b-phase` §5.2 / §5.3 R-FW-10
|
||
|
||
### B-3:KL630/KL730 firmware 字串可能值
|
||
|
||
**步驟**:在 B-1 跑完已自然取得(`dev.firmware` 欄位)。額外重複測試不同 state:
|
||
|
||
1. KL630 剛從盒子拿出來(未灌任何 firmware)→ 紀錄 firmware 字串
|
||
2. KL630 跑過一次 inference 後(已 load firmware)→ 紀錄
|
||
3. KL630 reboot 後 → 紀錄
|
||
|
||
**預期結果**:
|
||
- 可能值:`""` / `"KDP"` / `"KDP2"` / `"KDP3"` / `"Loader"` / `"App"` / 其他
|
||
- 紀錄 chip × state 對照表
|
||
|
||
**回填位置**:本檔 §8.2、`40-b-phase` R-FW-10、`detector.go` firmware state map
|
||
|
||
### C-1:.tar 檔案內容實測
|
||
|
||
**前置**:warrenchen bundle 已 clone 到 `/tmp/web_academy_prototype/`。
|
||
|
||
**步驟**:
|
||
```bash
|
||
cd /tmp/web_academy_prototype/local_service_win/firmware
|
||
|
||
# KL630
|
||
echo "=== KL630 kp_firmware.tar ==="
|
||
ls -lh KL630/kp_firmware.tar KL630/kp_loader.tar
|
||
tar -tvf KL630/kp_firmware.tar
|
||
tar -tvf KL630/kp_loader.tar
|
||
|
||
# KL730
|
||
echo "=== KL730 kp_firmware.tar ==="
|
||
ls -lh KL730/kp_firmware.tar KL730/kp_loader.tar
|
||
tar -tvf KL730/kp_firmware.tar
|
||
tar -tvf KL730/kp_loader.tar
|
||
|
||
# 試解壓到 temp
|
||
mkdir -p /tmp/kl630_extracted
|
||
tar -xf KL630/kp_firmware.tar -C /tmp/kl630_extracted
|
||
ls -lhR /tmp/kl630_extracted/
|
||
file /tmp/kl630_extracted/*
|
||
|
||
# 看是否有 manifest / metadata
|
||
for f in /tmp/kl630_extracted/*.json /tmp/kl630_extracted/*.txt /tmp/kl630_extracted/*.yml /tmp/kl630_extracted/*.yaml; do
|
||
[ -f "$f" ] && echo "--- $f ---" && cat "$f"
|
||
done
|
||
```
|
||
|
||
**預期結果**:
|
||
- **A**:.tar 內含 `fw_scpu.bin` + `fw_ncpu.bin` + 可能 `manifest.json` → 策略 Y 解壓後拿 .bin 直餵
|
||
- **B**:.tar 內含其他結構(如 nested .tar / 加密檔) → 處理複雜度上升
|
||
|
||
**回填位置**:本檔 §8.3、`41-tar-firmware-handling.md` §1.3
|
||
|
||
---
|
||
|
||
## 4. 驗證環境需求
|
||
|
||
### 4.1 硬體
|
||
|
||
| 硬體 | 強驗證 | 弱驗證 | 取得來源 |
|
||
|------|--------|--------|---------|
|
||
| KL520 dongle | 可選(用於 wheel 升級回歸測試)| 不需 | 既有開發環境 |
|
||
| KL720 dongle | 可選(同上)| 不需 | 既有開發環境 |
|
||
| KL630 dongle | **必須 ≥1 顆** | 不需 | 問 Innovedus team / Kneron 借機 |
|
||
| KL730 dongle | **必須 ≥1 顆** | 不需 | 問 Innovedus team / Kneron 借機 |
|
||
| 跑驗證的主機 | macOS / Linux 至少 1 台、Windows 1 台(驗 driver install)| macOS 或 Linux 1 台即可 | 既有開發環境 |
|
||
|
||
### 4.2 軟體
|
||
|
||
- Python 3.10+(既有 venv 即可)
|
||
- KneronPLUS Python wheel(既有 + warrenchen 3.1.2 兩版各一份)
|
||
- KL630 / KL730 sample NEF 模型(從 Kneron sample 拿、用於 B-2 inference 測試)
|
||
- 解壓工具:Python `tarfile` 標準庫 + shell `tar`(macOS / Linux 內建、Windows 10+ 內建)
|
||
|
||
### 4.3 SDK 文件
|
||
|
||
- KneronPLUS SDK Reference Manual PDF(從 Kneron developer portal 下載)
|
||
- SDK changelog / release notes(決定升級的 breaking change 範圍)
|
||
|
||
### 4.4 平台覆蓋策略
|
||
|
||
**M9-6 階段**只驗 1 平台(推薦 macOS 或 Linux)、剩 2 平台留到 M9-13 整體三平台驗證跑。
|
||
|
||
理由:M9-6 主要驗 SDK API 行為(跨平台一致)、不是驗安裝 / driver / OS 整合(那是 M9-13)。
|
||
|
||
---
|
||
|
||
## 5. 預估時間
|
||
|
||
### 5.1 強驗證(有實機)
|
||
|
||
| 階段 | 工時 | 累積 |
|
||
|------|------|------|
|
||
| 環境準備(取得硬體、裝 driver、setup Python) | 0.5 天 | 0.5 |
|
||
| A 類驗證執行(A-1 ~ A-6)| 0.5 天 | 1.0 |
|
||
| B 類驗證執行(B-1 ~ B-3)| 0.5 天 | 1.5 |
|
||
| C 類驗證執行(C-1)| 0.1 天 | 1.6 |
|
||
| 結果回填到本檔 §8 + 其他研究檔 | 0.4 天 | 2.0 |
|
||
| **合計** | **2 人天** | |
|
||
|
||
### 5.2 弱驗證(無實機)
|
||
|
||
| 階段 | 工時 | 累積 |
|
||
|------|------|------|
|
||
| 環境準備(裝 wheel、解壓 wheel source)| 0.2 天 | 0.2 |
|
||
| A 類驗證執行(弱版本,無實機部分)| 0.3 天 | 0.5 |
|
||
| B 類驗證跳過(只靠 SDK 文件推斷)| 0.2 天 | 0.7 |
|
||
| C 類驗證執行 | 0.1 天 | 0.8 |
|
||
| 結果回填 + 標註「需 M9-9 補強驗」| 0.2 天 | 1.0 |
|
||
| **合計** | **1 人天** | |
|
||
|
||
### 5.3 預期 B 階段風險增量(採弱驗證的話)
|
||
|
||
如果 M9-6 走弱驗證、B-1 / B-2 / B-3 留到 M9-9 開發階段才實機驗:
|
||
- M9-9 工時可能從 2 天 → 3 天(多 1 天驗 SDK 行為 + 修 bridge.py)
|
||
- M9-13 三平台驗證可能多發現 1-2 個原本以為 work 的 case 實際不 work
|
||
|
||
→ **強烈建議走強驗證**、即使要等硬體到位。
|
||
|
||
---
|
||
|
||
## 6. 風險
|
||
|
||
### R-VAL-1:拿不到 KL630 / KL730 硬體(高度風險)
|
||
|
||
**情境**:Innovedus / Kneron 沒法提供 dongle、或要等好幾週。
|
||
|
||
**緩解選項**:
|
||
- **選項 A**:降級為弱驗證、B 階段啟動時 SDK unknown 帶到 M9-9
|
||
- **選項 B**:延後 B 階段啟動、等硬體到位(不影響 A 階段 MVP)
|
||
- **選項 C**:先做 mock 驗證(建 mock SDK、用 monkey patch 假裝 KL630 device)、等實機到再覆驗
|
||
|
||
**建議**:選項 B(延後 B 階段)+ 順手做選項 C(建 mock 為將來測試鋪路)
|
||
|
||
### R-VAL-2:KneronPLUS wheel 不支援 KL630/KL730(中度)
|
||
|
||
**情境**:A-2 驗證結果是 "B"(既有 wheel 沒有 KL630/KL730 enum)。
|
||
|
||
**緩解**:
|
||
- 升級 wheel 到 warrenchen 3.1.2 或更新版
|
||
- A-6 評估 regression 範圍、必要時跑完整 KL520/KL720 回歸測試
|
||
- 預留 0.5-1 人天到 M9-7 做 wheel 升級 + regression
|
||
|
||
### R-VAL-3:驗證結果跟前兩輪研究假設不同(中度)
|
||
|
||
**情境**:例如 B-1 發現 KL630 product_id 不是 `0x0630`、是 legacy + new 雙 ID。
|
||
|
||
**觸發重大調整 plan 的條件**:
|
||
1. KL630 / KL730 product_id 跟假設不同 → 更新 `40-b-phase` §2.1 + bridge.py 改動範圍
|
||
2. KL630 / KL730 是 USB Boot 而非 flash-based → 整段 §5.2 推測重寫、`.tar` 處理流程設計重來
|
||
3. SDK 完全沒有 .tar 支援 API → 策略 Z 永久排除、回到策略 Y、build script 必加
|
||
4. wheel 升級對 KL520 regression > 30% → 不升級、找其他方案(可能要找 Kneron 要 dev 版)
|
||
|
||
每個觸發條件對應的 plan 重做工時估算:
|
||
- 觸發 1 → 0.2 天重寫 + M9-7 多 0.3 天
|
||
- 觸發 2 → 1 天重寫研究檔 + M9-8/M9-9 各多 0.5 天
|
||
- 觸發 3 → 不影響(策略選定即可)
|
||
- 觸發 4 → 1-2 天找替代方案
|
||
|
||
### R-VAL-4:硬體環境不穩、間歇性失敗(低度)
|
||
|
||
**情境**:USB 接觸不良、dongle 過熱、driver 偶發崩潰。
|
||
|
||
**緩解**:
|
||
- 每個 B 類驗證重複 3 次、取一致結果
|
||
- 紀錄不一致 case、單獨追
|
||
|
||
---
|
||
|
||
## 7. 驗證完成的下游影響
|
||
|
||
### 7.1 既有研究檔需要更新
|
||
|
||
| 檔案 | 章節 | 更新內容 |
|
||
|------|------|---------|
|
||
| `40-b-phase-kl630-kl730-extension.md` | §3.2 表格 | 填入「實測值」column |
|
||
| `40-b-phase-kl630-kl730-extension.md` | §5.2 | KL630/KL730 推測段落改成「驗證結果」 |
|
||
| `40-b-phase-kl630-kl730-extension.md` | §7 | inference 流程驗證 |
|
||
| `40-b-phase-kl630-kl730-extension.md` | R-FW-8 / R-FW-10 | 風險紀錄狀態更新 |
|
||
| `41-tar-firmware-handling.md` | §1.3 | .tar 內容實測結果 |
|
||
| `41-tar-firmware-handling.md` | §2.1 / §2.2 / §2.3 | wheel 版本 + API 驗證結果 |
|
||
| `41-tar-firmware-handling.md` | §3.3 | .tar 大小實測 |
|
||
| `41-tar-firmware-handling.md` | §4.4 | 策略選定(Y or Z) |
|
||
| `41-tar-firmware-handling.md` | §5 | 驗證計畫狀態改「已完成」+ 結果 |
|
||
|
||
### 7.2 milestone 重排可能性
|
||
|
||
| milestone | 受 M9-6 結果影響的程度 |
|
||
|-----------|---------------------|
|
||
| M9-7(B0 認 chip) | 低:B-1 結果可能調整 chip 判斷邏輯、+0.1 天 |
|
||
| M9-8(.tar handling) | 高:策略 Y vs Z 完全不同實作、工時可能 ±0.5 天 |
|
||
| M9-9(connect + inference) | 中:B-2 / B-3 結果決定 connect 流程設計 |
|
||
| M9-10(FW 升版擴 KL630/KL730) | 中:A-5 結果決定用哪個 API |
|
||
| M9-11(多版本) | 低 |
|
||
| M9-12(降版 UI) | 低 |
|
||
| M9-13(三平台驗證) | 中:若驗證發現新問題、增加 case 數 |
|
||
|
||
### 7.3 TDD v2.2 firmware-management.md 影響
|
||
|
||
從 `30-integration-plan.md` 已標 firmware-management 章節範圍。M9-6 結果可能影響:
|
||
- §「SDK API 對照表」需填實測值
|
||
- §「KL630/KL730 連線流程」需確認 flash-based vs USB Boot
|
||
- §「.tar 處理策略」需選定 Y or Z
|
||
- §「KneronPLUS wheel 升級需求」需填升級決策
|
||
|
||
→ **TDD v2.2 firmware-management.md 應該等 M9-6 完成再 finalize**、或 M9-6 結果作為 v2.2 → v2.3 的 update trigger。
|
||
|
||
---
|
||
|
||
## 8. 驗證結果填空區(執行後填)
|
||
|
||
> 本節是執行 M9-6 的工程師填寫的、現階段空白。
|
||
|
||
### 8.1 A 類結果
|
||
|
||
| 項目 | 結果 | 證據 / 紀錄路徑 | 對下游決策影響 |
|
||
|------|------|---------------|--------------|
|
||
| A-1:既有 wheel 版本 | _未驗證_ | | |
|
||
| A-2:KL630/KL730 enum 存在 | _未驗證_ | | |
|
||
| A-3:load_firmware_from_file 對 .tar 行為 | _未驗證_ | | |
|
||
| A-4:load_firmware_from_tar 是否存在 | _未驗證_ | | |
|
||
| A-5:update_kdp_firmware_from_files 對 KL630/KL730 適用性 | _未驗證_ | | |
|
||
| A-6:wheel 升級 regression 範圍 | _未驗證_ | | |
|
||
|
||
### 8.2 B 類結果
|
||
|
||
| 項目 | 結果 | 證據 | 對下游決策影響 |
|
||
|------|------|------|--------------|
|
||
| B-1:KL630/KL730 product_id | _未驗證_ | | |
|
||
| B-2:KL630/KL730 是否每次 connect 重 load firmware | _未驗證_ | | |
|
||
| B-3:KL630/KL730 firmware 字串可能值 | _未驗證_ | | |
|
||
|
||
### 8.3 C 類結果
|
||
|
||
| 項目 | 結果 | 證據 | 對下游決策影響 |
|
||
|------|------|------|--------------|
|
||
| C-1:.tar 內容 | _未驗證_ | | |
|
||
|
||
### 8.4 整體結論(執行後填)
|
||
|
||
- 策略 Y vs Z 選定:_待驗證後填_
|
||
- wheel 升級決策:_待驗證後填_
|
||
- B 階段啟動 go/no-go:_待驗證後填_
|
||
|
||
---
|
||
|
||
## 9. 給 Orchestrator 的執行建議
|
||
|
||
### 9.1 何時派工
|
||
|
||
**選項 A:A 階段 MVP 同步起跑**(使用者目前決策)
|
||
- M9-6 與 M9-1 ~ M9-5 平行
|
||
- 派 testing/backend Agent 在 A 階段任何時點啟動 M9-6(推薦 A 階段 50% 進度時)
|
||
- M9-6 完成後 B 階段才能啟動、不卡 A 階段
|
||
- 風險:A 階段也在動 bridge.py、M9-6 可能跟 A 階段同檔修改衝突(協調好就好)
|
||
|
||
**選項 B:A 階段 MVP 完成後啟動**(原計畫)
|
||
- M9-6 + A 階段 5 人天 + B 階段 10.5 人天 = 序列
|
||
- 簡單、無衝突
|
||
- 風險:B 階段啟動延後 5 人天
|
||
|
||
**建議**:選 A、但安排 M9-6 在 A 階段 M9-3 或 M9-4 完成後啟動(bridge.py 大改動已完成)、避開檔案衝突。
|
||
|
||
### 9.2 派工 Agent 選擇
|
||
|
||
**強驗證階段**:
|
||
- 主要:testing Agent(驗證執行 + 結果紀錄是專業)
|
||
- 配合:backend Agent(讀 SDK source code + 寫驗證 script 偏 backend 領域)
|
||
- 推薦:testing 主派、backend 顧問
|
||
|
||
**弱驗證階段**(若無實機):
|
||
- 主要:backend Agent(純查 source + 文件)
|
||
- testing 可不參與
|
||
|
||
### 9.3 派工前必須先做的事
|
||
|
||
1. **跟使用者確認硬體狀態**:問「你或 Innovedus team 手上有 KL630 / KL730 dongle 嗎?多久能拿到?」
|
||
2. **根據答案決定強 / 弱驗證**:
|
||
- 有 → 強驗證、派 testing + backend
|
||
- 沒、3 週內能拿到 → 延後 M9-6、改先做選項 C mock(順手鋪路)
|
||
- 沒、無法取得 → 弱驗證、接受 M9-9 補強驗的風險
|
||
3. **取得 KneronPLUS SDK Reference Manual PDF**:問使用者 / 從 Kneron developer portal 下載、放到 `.autoflow/04-architecture/research-kl520-fw-management/refs/` 供工程師查
|
||
|
||
### 9.4 派工 prompt 模板(給 Orchestrator 用)
|
||
|
||
```
|
||
路徑資訊:
|
||
- 你的角色定義:(對應 agent CLAUDE.md 絕對路徑)
|
||
- 專案目錄:當前工作目錄
|
||
- 本任務 plan:`.autoflow/04-architecture/research-kl520-fw-management/50-m9-6-sdk-validation.md`
|
||
- 結果回填到本檔 §8.1 / §8.2 / §8.3
|
||
|
||
任務:執行 M9-6 SDK 驗證(強驗證版本)
|
||
範圍:本檔 §2 列出的 10 個驗證項目、按 §3 的 step 執行
|
||
產出:
|
||
1. 本檔 §8 填空區填實測結果
|
||
2. 更新 §7.1 列的下游研究檔(標明「本次更新」)
|
||
3. 結果摘要丟給 Orchestrator、附 go/no-go 建議
|
||
|
||
不要做:
|
||
- 改 bridge.py / driver code(驗證 only、不寫產品 code)
|
||
- 改 TDD v2.2 firmware-management.md(那是 architect 範圍、本任務只回填到研究檔)
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 與其他研究檔的關係
|
||
|
||
| 連結 | 引用內容 |
|
||
|------|---------|
|
||
| `40-b-phase-kl630-kl730-extension.md` §3.2 | 本檔 §1.1 / §2 類別 A 大部分項目來源 |
|
||
| `40-b-phase-kl630-kl730-extension.md` §5.2 / R-FW-10 | 本檔 §2 類別 B(連線行為驗證) |
|
||
| `40-b-phase-kl630-kl730-extension.md` M9-6 段落 | 本檔是 M9-6 的詳細展開 |
|
||
| `41-tar-firmware-handling.md` §1.3 | 本檔 §2 C-1 來源 |
|
||
| `41-tar-firmware-handling.md` §2 | 本檔 §2 類別 A row A-3 / A-4 來源 |
|
||
| `41-tar-firmware-handling.md` §5 | 本檔取代之前的 「待 M9-6 驗證」placeholder、§5 升級為 reference 本檔 |
|
||
| `30-integration-plan.md` 階段 B 評估 | M9-6 結果回填觸發 TDD v2.2 firmware-management.md 更新 |
|