visionA/local-tool/.autoflow/04-architecture/adr/ADR-001-firmware-management.md
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

219 lines
13 KiB
Markdown
Raw 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.

# ADR-001: Kneron Dongle FW 偵測 + 升降版(翻案 R5-Q9
## Status
**Accepted** — 使用者於 2026-05-24 拍板方案 A + B 一次做完、L 級正規流程。
**2026-05-25 update**M9-6 弱驗證執行(見 `research-kl520-fw-management/55-m9-6-weak-validation-result.md`)後追加兩個新事實:
1. **visionA-local KneronPLUS wheel 三平台版本不一致**macOS/Linux = 2.0.0、Windows = 3.1.2。對 A 階段KL520/KL720 升級)不阻塞(既有 API 在兩個版本都存在);對 B 階段KL630/KL730影響重大、B 階段啟動前必須統一升級到 3.1.2+ 並跑三平台 KL520/KL720 回歸M9-13。詳見 TDD `v2/firmware-management.md` §1.3。
2. **PRD AC-FW-3.5KL630/KL730 升降版)延後至 B 階段 M9-10**:弱驗證確認 warrenchen 沒做 KL630/KL730 升降版reference 實作為零)、且需要實機才能驗 `update_kdp_firmware_from_files` 對 KL630/KL730 是否適用。A 階段 scope 縮限為 KL520+KL720 自動升級。Decision §1 表格的 A 階段範圍不變(本來就只列 KL520+KL720、Consequences §5 風險敘述強化。
## Context
### 痛點背景
visionA-local 自 2026-04 上線以來、累積以下與 Kneron Dongle FW 相關的痛點:
1. **拿到舊 dongle 完全不能用**:使用者拿到的 KL520 dongle 若是 KDP1 legacypid=0x0200、插上 visionA-local 後 connect 流程在 `kp.core.load_firmware_from_file` 階段失敗、無法 inference。
2. **KL520 firmware 殘留導致 Error 15 SEND_DATA_TOO_LARGE**2026-04-21 已修、但 root cause 在 FW 層commit `ddf0eb8` 為了解 Windows 60s HTTP timeout 加「KL520 首次 connect 跳過 reset」優化、後來發現 KL520 若 session 間 firmware 殘留(`fw=KDP2 Comp/U`)、直接 load_model + inference 100% 炸 Error 15。已 revert、但這暴露既有 FW 偵測 + reset 邏輯的脆弱性。
3. **KL630 / KL730 偵測不到**scan 階段顯示得到名字、但 `handle_connect` fall-through 到 KL520 路徑、用錯誤的 firmware 檔載入 → 連不上。新世代 dongle 完全不可用。
4. **舊 model 與新 FW 相容性**:進階使用者反映「我的舊 NEF model 在升版 FW 後跑不出結果」、缺乏降版機制。
### R5-Q9 砍 flash 的歷史決策
`progress.md` 重要決策紀錄 §「第二輪使用者決策 Q9」
> 韌體燒錄 flash → B 砍掉
(行號為動態值、本 ADR 不寫具體 L 行號避免日後 progress.md 增刪後失準;以「第二輪使用者決策 Q9」描述位置為準
當時砍的理由(推測、原 progress.md 沒留下細節):
1. visionA-local 是「local 推論工具」、不是「dongle 管理工具」、燒 flash 不屬核心使用旅程
2. 燒 flash 有 brick 風險、不想對使用者開放
3. 早期 MVP 範圍縮小、把非必要功能延後
4. 既有「load_firmware 到 RAM」夠用KL520 USB Boot 每次都重 load、KL720 已預燒 KDP2
### 為何現在要翻案
1. **痛點 #1 是真實場景**:使用者已陸續遇到舊 KDP1 dongle 完全不能用Q9 原始假設「既有 load_firmware 到 RAM 夠用」不成立)
2. **同事 warrenchen 雲端版已驗證可做、且安全**`/tmp/web_academy_prototype/local_service_win/` 有完整實作可參考
3. **範圍可切割**Q9 砍的是「使用者按按鈕燒任意 model 到 device flash」、本期是「升級到 Kneron 官方 KDP2 標準版本」、避開大部分 brick 與責任風險
4. **跨平台技術可行**KneronPLUS Python API`kp.core.update_kdp_firmware_from_files`+ `libkplus.{dll,so,dylib}` 三平台都已在既有 wheel 內、無需引入 Windows-only DFUT.exe
### 技術現況盤點
visionA-local 既有 code 已有 70% 的 FW 偵測 + RAM-load 邏輯:
- `kneron_bridge.py:handle_connect()` 已會偵測 Loader mode、從 `firmware/<chip>/fw_scpu.bin + fw_ncpu.bin` load firmware 到 RAM
- `kneron_bridge.py:handle_connect()` 已處理 KL720 KDP legacy → 強制 load KDP2 firmware 到 RAM
- `DeviceInfo.FirmwareVer` 已在 driver interface、bridge 連線時也回傳 `firmware` 欄位
- bundled firmware 已存在 `server/scripts/firmware/{KL520,KL720}/`(合計 ~360KB
缺的是「持久化升降版」:
- 既有的 `load_firmware_from_file` 是 RAM-load裝置重新上電就消失
- 真正的 FW 升降版需要呼叫 `kp_update_kdp_firmware_from_files`(會寫 flash
## Decision
採以下五項決策:
### 1. 採方案 A + B、不分階段交付
| 階段 | 範圍 | 工時 |
|-----|------|------|
| A | KL520 + KL720 自動升級 KDP1 → KDP2、安裝包 +0KB | 5 人天 |
| B | + KL630 + KL730 driver 擴展 + 手動降版(面向一般使用者)+ 多版本管理、安裝包 +7MB | 10.5 人天 |
| **合計** | — | **15.5 人天** |
不採「先做 A、之後再評估 B」的分階段策略——使用者明確要求一次做完。
### 2. 翻案 R5-Q9範圍切割如下
| 「燒 flash」場景 | 本期決策 |
|-----------------|---------|
| 使用者按按鈕燒任意 modelNEF到 device flash | **繼續砍**R5-Q9 原始決策維持)|
| 升級到 Kneron 官方 KDP2 標準版本 | **本期做**(範圍切割後翻案)|
| 手動降版到 bundled 舊版(含 KDP1| **本期做**B2 階段、面向一般使用者)|
| 使用者燒入自編 firmware binary | **不做**brick 風險過高、責任風險過高)|
切割後翻案的核心邏輯Q9 擔憂的 brick 風險來自「使用者燒任意 binary」、而非「升級到官方標準版本」。本期只暴露「升級到 Kneron 官方 KDP2」+ 「降版到 bundled 已知舊版」、binary 來源已嚴格限制。
### 3. 跨平台用 KneronPLUS Python C API
| 替代方案 | 否決原因 |
|---------|---------|
| DFUT.exewarrenchen 雲端版用)| Windows-only、~30MB Qt5 binary、跨平台不通 |
| ctypes 包 KneronPLUS C API | 既有 Python wheel 已包好、不需重做 |
| 自寫 USB 協議實作 | 範圍過大、需逆向工程 Kneron protocol、Kneron 不認可 |
採 KneronPLUS Python API`kp.core.update_kdp_firmware_from_files`)路徑:
- 三平台都有 wheel既有 bundle 已含、增加成本 ~0KB
- 對應 C 函式經 SDK 驗證、不踩 brick 風險
- 與既有 `kp.core.load_firmware_from_file` 路徑共用、bridge.py 改動可控
### 4. 多版本目錄結構採選項 C — CURRENT_VERSION metadata
```
firmware/<chip>/
├── CURRENT_VERSION ← 單行檔:"v2.2.0"
├── v2.2.0/{...} + VERSION
├── v2.1.0/{...} + VERSION
└── kdp1/{...} + VERSION
```
| 替代方案 | 否決原因 |
|---------|---------|
| 選項 A — symbolic link`current/` 指向版本目錄)| Windows symlink 需 admin、`tar` 處理跨 OS 不一致 |
| 選項 B — 實體副本(`current/``v2.2.0/` 的 file copy| 每 chip 多佔一份(合計 +7-8MB 額外)、跨平台簡單但浪費空間 |
| 選項 C — `CURRENT_VERSION` 單行檔 + 版本目錄並列 | **選此**:架構乾淨、空間最省、跨平台無 symlink 風險、bridge.py 多一次 file readtrivial 成本)|
### 5. 保守 bundle 策略 +7MB
| Chip | bundled 版本 | 估算 |
|------|------------|------|
| KL520 | current + kdp1 + v2.1.0 | ~290KB |
| KL720 | current + v2.1.0(如有)| ~500KB |
| KL630 | current + 1 個舊版(如有)+ extracted | ~9MB |
| KL730 | current + extracted | ~8MB |
合計 ~18MB極大值。實際採保守策略 = 不一定每個 chip 都 bundle 舊版KL630/KL730 舊版可能 SDK release 取不到)、估算 +7MB。
macOS dmg 從 163MB → ~170MB+4-5%)、使用者「+5MB 接受」決策對齊。
## Consequences
### 正面影響
1. **解決真實痛點**:拿到舊 KDP1 dongle 完全不能用的情境消除A 階段)、新世代 dongle KL630/KL730 變可用B 階段、進階使用者可手動切版本B2 階段)
2. **架構乾淨**:新 `firmware/` 模組與既有 `flash/` 分離、職責清楚、未來擴 KL830 / KL530 有明確 hook 點
3. **跨平台一致**:三平台同走 KneronPLUS Python API、無 Windows-only 依賴、維護成本均勻
4. **回溯可追**:本 ADR + 研究檔research-kl520-fw-management/+ TDD v2/firmware-management.md 留下完整決策痕跡、未來新人接手不會問「為何 Q9 砍了又做」
### 負面影響(接受的取捨)
1. **+7MB 安裝包**B 階段、A 階段 +0KBmacOS dmg 從 163MB → ~170MB、使用者已接受
2. **Brick 風險未完全消除**KL720 升級會寫 flash、升級中拔 USB 仍有 brick 可能緩解UI 警告、不打包 DFUT 但提供內部 SOP
3. **法律 / 簽章授權待釐清**Kneron firmware redistribution含 KDP1 / 舊版)需發佈前評估、與 R5-B4 同性質、不阻塞開發但是 PRD P0 懸念
4. **15.5 人天工時**:比原估 11-12 多 ~30%、主因 B2 階段「面向一般使用者」UX safety net + 多版本管理 + M9-6 SDK 驗證
5. **B 階段對 KL630/KL730 SDK 行為仍有未知**M9-6 驗證後可能需要升級 KneronPLUS wheel、有 KL520/KL720 regression 風險
6. **CURRENT_VERSION 機制需要 A → B2 migration**A 階段 firmware 檔扁平放、B2 階段才搬進版本目錄、需小心不破壞 A 階段已 commit 路徑
### 風險
完整風險清單見 `v2/firmware-management.md` §10R-FW-1 ~ R-FW-12、R-TAR-1 ~ R-TAR-4 合計 16 條)。最關鍵:
| # | 風險 | 等級 |
|---|------|------|
| R-FW-8 | KneronPLUS SDK 對 KL630/KL730 API 不可預測 | 高 |
| R-FW-11 | 一般使用者誤觸降版 brick 風險 | 高 |
| R-FW-1 | 升級中拔除 deviceKL720 寫 flash 階段)| 中 |
## Alternatives Considered
### 替代方案 1不做 FW 管理
| 維度 | 評估 |
|------|------|
| 工時 | 0 |
| 痛點解決 | 不解 |
| 否決原因 | 痛點 #1KDP1 dongle 不能用)是真實且高頻場景、使用者明確要求做 |
### 替代方案 2用 DFUT.exewarrenchen 雲端版方案)
| 維度 | 評估 |
|------|------|
| 工時 | 較少(直接呼叫 .exe|
| 跨平台 | **Windows-only**、macOS / Linux 完全不通 |
| 安裝包衝擊 | +30MB Qt5 binary |
| 否決原因 | visionA-local 是三平台 desktop app、Windows-only 工具不接受 |
### 替代方案 3只做內部 dev mode 降版(非面向一般使用者)
| 維度 | 評估 |
|------|------|
| 工時 | 少(不用做 UX safety net、~12 人天)|
| 痛點解決 | 痛點 #4(舊 model 相容性)不解、仍需技術支援介入 |
| 否決原因 | 使用者明確決策2026-05-24手動降版面向一般使用者、不只 dev mode |
### 替代方案 4多版本目錄結構選 symlink / 副本
| 維度 | 選 A symlink | 選 B 副本 | 選 C metadata本期選此|
|------|-------------|----------|---------------------|
| 跨平台 | Windows symlink 需 admin、Windows zip/tar 處理 symlink 不一致 | 完美跨平台 | 完美跨平台 |
| 空間 | 最省 | 每 chip 多一份(+7-8MB| 最省(只多一個單行檔)|
| 複雜度 | 高symlink fall-back 邏輯)| 低 | 低(讀 metadata 一行)|
### 替代方案 5等 A 階段驗證後再評估 B
| 維度 | 評估 |
|------|------|
| 工時 | A 階段 5 人天先交付 |
| 風險 | A → B 整合時的 .tar handling / chip 判斷 / multi-version migration 必須重整 A 階段已 commit 程式碼 |
| 否決原因 | 使用者明確要求一次做完、且 ADR / TDD 一次定稿能避免 A 階段做出不利 B 階段擴展的設計 |
## Related
| 編號 | 關聯 |
|------|------|
| R5-Q9 | 第二輪「韌體燒錄 flash → B 砍掉」、本 ADR 翻案 |
| R5-B4 | 「Kneron 預置模型 re-distribution 授權」未解決問題、firmware redistribution 同性質、發佈前統一處理 |
| R5-E | 60s 啟動 hard timeout、FW 升降版不在啟動 pipeline、無關 |
| 2026-04-21 commit | KL520 reset bug fix、本 ADR 必須維持「升級成功後 needsReset=true」確保不踩 Error 15 |
| watchServer Error state | `v2/server-lifecycle.md`、FW 升降版失敗不觸發 server Error state |
| TDD `v2/firmware-management.md` | 本 ADR 對應的 TDD 章節、含完整 API / 流程 / 工時 / 風險 |
| PRD `02-prd/features/feature-firmware-management.md` v2.2 | 對應 user storyUS-FW-1 ~ US-FW-3與成功指標 |
| research-kl520-fw-management/ | 本 ADR 的研究依據(含 warrenchen 實作分析、SDK API 落差、.tar handling、降版 UX 需求、M9-6 弱驗證結果)|
## Compliance
- [x] Architect 與 PM Agent 確認本 ADR 影響業務指標FW 升級流程是 PRD v2.2 新功能)
- [x] 成本影響已評估:開發 15.5 人天 + 基礎設施 +7MB 安裝包 + 維運 0無 OTA 通道、無監控 SaaS 需求)
- [ ] 與 Kneron 取得 firmware redistribution 授權(發佈前必須完成、不阻塞開發)
- [ ] Design Agent 確認 UX 多層 safety net 落地(二次確認 modal + DOWNGRADE 輸入 + persistent banner
## 變更記錄
| 日期 | 版本 | 變更 | 作者 |
|------|------|------|------|
| 2026-05-24 | 1.0 | 初版產出、Status: Accepted | Architect Agent |
| 2026-05-25 | 1.1 | 三方互審後修:(1) Status 區塊加 2026-05-25 update 反映 M9-6 弱驗證新事實wheel 三平台版本不一致 + AC-FW-3.5 延後 B 階段);(2) Context R5-Q9 行號改為描述式引用(不寫具體 L 行號);(3) Related 表補 PRD v2.2 條目;(4) 不改 Decision/Alternatives/Compliance 主體(決策邏輯沒變、仍 Accepted | Architect Agent |