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

240 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.

# 研究摘要visionA-local Kneron Dongle FW 偵測 + 升降版
> 作者Architect Agent
> 日期2026-05-24
> 範圍:純研究 plan、不出 code、不改任何既有檔案
> 目標:在 visionA-localWails 桌面 app新增「自動偵測 Kneron Dongle FW 版本 + 升級 / 降版」功能
> 參考:同事 warrenchen 雲端版實作於 `/tmp/web_academy_prototype/`
---
## 1. Executive Summary
### 核心發現
1. **R5-Q9 砍 flash 的決策不阻擋本任務**——當時砍的是「使用者按按鈕去燒模型到 device flash」這條使用者旅程**FW 升降版是不同的事**。Q9 砍掉的並不是 firmware 燒錄的技術能力(我們既有的 `Flash()` driver method 跟 bridge.py 都還有完整的 `kp.core.load_firmware_from_file` 邏輯只是叫法叫「load_firmware」不叫「flash firmware」
2. **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 (pid=0x0200) → 強制 load KDP2 firmware 到 RAM 的路徑
- `DeviceInfo.FirmwareVer` 已在 driver interface 內、bridge 連線時也已回傳 `firmware` 欄位
- bundled firmware 已存在於 `server/scripts/firmware/{KL520,KL720}/fw_{scpu,ncpu}.bin`(合計約 ~360KB
3. **缺的核心是「持久化升降版」**
- 既有的 `load_firmware_from_file`**RAM-load**(裝置重新上電就消失)
- 真正的 FW 升降版需要呼叫 `kp_update_kdp_firmware_from_files`C API、會寫 flash
- warrenchen 雲端版用兩條路徑:(a) `kp.core.install_driver_for_windows` 風格的 Python 包裝;(b) `KneronDFUT.exe`Windows-only Qt GUI 工具的 CLI mode
4. **跨平台選 KneronPLUS C API、不選 DFUT.exe**
- DFUT.exe 是 Windows-only Qt5 binary、~30MB 依賴、不能用
- `libkplus.{dll,so,dylib}` 三平台都有、我們既有 bundle 已含、增加成本約 0KB已在 KneronPLUS wheel 內)
- 對應 Python API 是 `kp.core.update_kdp_firmware_from_files`、不需要走 ctypes除非舊 KDP1 → KDP2 那條超 legacy 路徑需要 `kp_connect_devices_with_magic_pass`、那段才必須 ctypes
### 推薦方案:兩階段
#### 階段 AMVP建議先做「FW 偵測 + 自動升級 KDP1 → KDP2」
**範圍**KL520 + KL720兩個我們已支援的晶片
**做什麼**
1. **被動偵測**scan / connect 時已知道 firmware 字串、在前端 UI 顯示 FW 版本(綠/黃/紅 badge
2. **主動升級**(單一動作):使用者按「升級到最新 KDP2」按鈕 → 走 warrenchen 的 legacy-upgrade flow → 進度回報 → 完成後 rescan
3. **不做手動降版**:階段 A 不暴露「給使用者選版本」UI、只有「升級到目前內建的最新版本」一個動作
**為什麼先做 A**
- 解決 90% 的真實痛點(拿到一根插上是 KDP1 legacy 的舊 dongle 沒辦法用)
- 工時 ~3-5 人天、安裝包 +0KBKL520/KL720 firmware 都已內建)
- 風險低失敗的話舊狀態還在、re-plug 重試
- 不踩 brick 風險KDP2 是 Kneron 官方 SDK 的標準版本、不是改寫過的 binary
#### 階段 B完整版視 A 驗證結果決定要不要做):「手動降版 + 多裝置支援」
**做什麼**
1. **手動降版 KDP2 → KDP1**(測試 / 復現舊 bug 用、僅內部)
2. **加入 KL630 / KL730 支援**(需要 KneronPLUS SDK 對應版本驗證)
3. **多版本 firmware 並存**(讓使用者選擇 firmware 版本)
**為什麼分開**
- 手動降版主要是「給開發者測試用」、不是真實使用者場景
- KL630 / KL730 我們目前 driver 沒驗證過、要先升級 driver code 才能加 FW 支援
- 安裝包 + 5-10MBKL630/KL730 firmware 是 .tar 約 2-4MB / 套)
### 工時 / 範圍對照
| 項目 | MVPA | 完整版A + B |
|------|---------|---------------|
| **裝置範圍** | KL520 + KL720 | + KL630 + KL730 |
| **操作範圍** | 自動升級 KDP1 → KDP2 | + 手動降版 + 多版本選擇 |
| **新增 API** | 3 個(`/api/devices/:id/firmware`, `/api/devices/:id/firmware/upgrade`, WebSocket progress room| + 2 個(`/firmware/downgrade`, `/firmware/versions` |
| **新增 UI** | 1 個 Devices 頁面卡片擴充FW badge + 升級按鈕 + progress modal | + Settings 內的「進階FW 版本管理」面板 |
| **bridge.py 新增 handler** | `firmware_upgrade` | + `firmware_downgrade` + `firmware_list_versions` |
| **driver 改動** | 加 `UpgradeFirmware()` method | + `DowngradeFirmware(version)` |
| **新文件** | TDD 補 §5.4 FW 升級子節 + 1 個新 ADR | + 1 個新 ADR |
| **預估工時** | **3-5 人天** | + 5-7 人天 = **8-12 人天合計** |
| **安裝包衝擊** | +0KB既有 KL520/KL720 已 bundle | +5-10MBKL630/KL730 firmware + 可能的舊版 KL520_kdp |
### R5-Q9 翻案分析
**Q9 原文**:「韌體燒錄 flash → B 砍掉」progress.md L776
**當時為何砍**
- progress.md 沒留下原因只有「B 砍掉」)
- 推測理由(需使用者確認):
1. visionA-local 是「local 推論工具」、不是「dongle 管理工具」、燒 flash 不屬核心使用旅程
2. 燒 flash 有 brick 風險、不想對使用者開放
3. 早期 MVP 範圍縮小、把非必要功能延後
4. 既有「load_firmware 到 RAM」夠用KL520 USB Boot 每次都重 load、KL720 已預燒 KDP2
**現在為何要翻案**
- 「拿到舊 dongle 完全不能用」是真實痛點progress.md L29 已有 Error 15 SEND_DATA_TOO_LARGE 經驗)
- 使用者明確要求做(聯合決策已收齊)
- 同事 warrenchen 雲端版已驗證可做、且安全
- 階段 A 範圍小、風險可控、工時可估
**翻案是否合理**:✅ 合理。
- Q9 的擔憂仍有效brick 風險、不是核心旅程),但**範圍縮小到「自動升級到內建 KDP2 標準版本」就能避開大部分風險**
- 不開放使用者「燒任意 binary」、只開放「升級到 Kneron 官方 KDP2 標準版本」
- 加 progress.md 留下決策痕跡(新 ADR 引用 Q9 + 標註翻案理由)
### 裝置範圍建議
**強烈建議 MVP 階段只做 KL520 + KL720**
| 晶片 | 現有 driver 支援 | warrenchen 提供 firmware | 建議 |
|------|---------------|-------------------------|------|
| KL520 | ✅ | ✅ KL520KDP2+ KL520_kdpKDP1 降版用) | **MVP 必做** |
| KL720 | ✅ | ✅ KL720KDP2 | **MVP 必做** |
| KL630 | ❌driver 沒處理 product_id 0x0630| ✅ kp_firmware.tar / kp_loader.tar | **延後**(要先擴 driver |
| KL730 | ❌driver 沒處理 product_id 0x0730| ✅ kp_firmware.tar / kp_loader.tar | **延後**(要先擴 driver |
KL630 / KL730 的 firmware 是 .tar 格式(不是 .bin代表 SDK 的 load_firmware API 對它們的處理流程也不同、需要先讓 Python bridge 能載這兩種、再做 FW 升降版。**強行加入 KL630 / KL730 會把 MVP 範圍翻倍**。
### 安裝包大小衝擊預估
**目前 macOS dmg163MB**progress.md 確認)
| 階段 | 新增內容 | 大小 |
|------|---------|------|
| MVPA | 0既有 KL520/KL720 已 bundle | +0KB |
| 完整版B| KL520_kdp~92KB+ KL630 .tar~2MB+ KL730 .tar~3MB| **+5MB** |
→ MVP 階段不衝擊安裝包大小、完整版約 +3%(接受範圍內)。
---
## 2. 與既有架構衝突點
### 2.1 與 TDD v2.1「watchServer Error state」機制的銜接
TDD v2.1 已決定 `watchServer` 失敗 → 切 `ServerStateError`、不再 `os.Exit`
**FW 升級失敗該怎麼處理**
- **建議**FW 升級是「device 層」失敗、不是「server 層」失敗、不應該讓 server 進 Error state
- 失敗時device 層回 Error state既有 `driver.StatusError`)、推 WebSocket 失敗訊息給 UI、server 繼續正常運行
- 這跟 watchServer 機制是平行的、不衝突
### 2.2 與 KL520 reset bug2026-04-21的銜接
bridge.py 已加 `fresh_firmware_loaded` flag、避免雙重 firmware load 浪費 60s。
**FW 升級流程要避免踩同樣坑**
- 升級成功後 device 會 re-enumerate裝置脫離 USB → 重新出現)
- 必須等 USB stable建議 5-8 秒)+ 主動 rescan、不要立刻 reconnect
- 升級流程內部需要 `kp.core.reset_device(KP_RESET_REBOOT)`、跟我們既有的 `restartBridge()` 流程重疊,要小心 race
### 2.3 與 R5-E 60s 啟動上限的銜接
FW 升級是**使用者主動觸發**、不在啟動 pipeline 內、跟 60s 上限無關。
**但**:升級可能需要 30-60 秒warrenchen 的 KL520 升級 timeout 設 30 秒、KL720 設 180 秒、UI 必須給 progress bar、不能 block。
### 2.4 既有 `Flash()` method 的角色重新定位
`flash/service.go:StartFlash()` 目前的語意是「load model 到 device RAM」、不是「燒 firmware」。
**建議**:保持 `flash/` 模組原意load model、**新建 `firmware/` 模組**(升降版)、不混用。
具體模組劃分:
- `server/internal/flash/` — 既有load model 到 device RAM語意保持
- `server/internal/firmware/`(新)— FW 偵測 + 升級 + 降版
### 2.5 文件命名包袱
`server/internal/driver/kneron/kl720_driver.go` 檔名其實是 KL520 + KL720 共用 driverprogress.md 已標註)、本任務不改檔名(範圍外)、但新文件不要再用 `kl720_*` 命名、用 `kneron_*``device_firmware_*`
---
## 3. 下一步建議
1. 給使用者 review 本份 plan這個檔 + 10/20/30 三個附檔)
2. 使用者確認後:
- **方案 A 通過** → 啟動 PM 補 PRD「FW 管理」章節(記錄 Q9 翻案 + MVP 範圍)→ Architect 補 TDD v2.1 §5.4 firmware 子節 + 新 ADR-009-firmware-management → Design 補 Devices 頁面 FW badge + 升級 modal 設計
- **方案 B 通過** → 同上 + 補 KL630/KL730 driver 擴展 plan多一份 research
- **要求修改 plan** → 我修
3. 文件完成後依 milestone 進入開發(建議拆 M9-1 ~ M9-5
### Milestone 建議拆法MVP 階段)
| # | Milestone | 預估 | 平行性 |
|---|-----------|------|-------|
| M9-1 | bridge.py 新增 `firmware_upgrade` handlerKneronPLUS C API | 1 人天 | 獨立 |
| M9-2 | Go driver + service`UpgradeFirmware()` + `firmware/service.go` | 1 人天 | 依賴 M9-1 |
| M9-3 | API handler + WebSocket progress room | 0.5 人天 | 依賴 M9-2 |
| M9-4 | 前端 Devices 頁 FW badge + 升級按鈕 + progress modal | 1.5 人天 | 依賴 M9-3 |
| M9-5 | 三平台實機驗證macOS / Windows / Linux | 1 人天 | 全部後 |
| **合計** | | **5 人天** | |
每個 milestone 都走 Reviewer 審查、最後 Testing E2E 驗收。
---
## 附錄B 階段研究索引2026-05-24 追加)
### 使用者最新決策2026-05-24
使用者拍板採方案 A + B 一次做完。新決策:
1. **手動降版面向一般使用者**(不只 dev mode、Settings 一般面板就要看得到)
2. **FW 全部內嵌進安裝包**、+5MB 接受、**不做線上更新通道**
### 本附錄涵蓋的新研究檔
| 檔案 | 主題 |
|------|------|
| `40-b-phase-kl630-kl730-extension.md` | KL630/KL730 driver 擴展研究主檔warrenchen 實作分析、既有 driver 擴點、SDK 落差、milestone M9-6 ~ M9-13|
| `41-tar-firmware-handling.md` | .tar firmware 處理細節(為什麼是 .tar、SDK API、解壓策略 X/Y/Z、大小估算|
| `42-manual-downgrade-for-end-users.md` | 手動降版面向一般使用者driver/bridge/API 細節、多版本儲存結構、Design Agent 需求清單)|
### 與主檔00 ~ 30的關係
| 主檔內容 | B 階段研究檔位置 |
|---------|---------------|
| §1 範圍對照表「階段 B 工時」5-7 人天 | **更新為 ~10.5 人天**B 階段拆三層 + 手動降版面向一般使用者 + SDK 驗證)詳見 40 §10 |
| §1 安裝包衝擊「+5-10MB」 | **更新為 +6-8MB**(保守 bundle 策略)詳見 42 §4 |
| §1 「KL630/KL730 延後」 | **變更A 階段做完後啟動 M9-6 SDK 驗證 → 決定 B 階段是否全做**、見 40 §11 |
| §3 「手動降版主要是給開發者測試用」 | **翻案**:使用者決策面向一般使用者、見 42 §1.1 |
### B 階段風險清單擴增
承前 R-FW-1 ~ R-FW-7、本研究新增
- **R-FW-8**KneronPLUS SDK 對 KL630/KL730 API 不可預測(高度)
- **R-FW-9**.tar 解包對安裝包大小衝擊(低度)
- **R-FW-10**KL630/KL730 沒有 Loader mode 概念(中度)
- **R-FW-11**:一般使用者誤觸降版 brick 風險高度B2
- **R-FW-12**:多版本管理 UX 複雜度中度B2
- **R-TAR-1 ~ R-TAR-4**.tar 解壓特定風險41 檔 §8
### B 階段啟動前必確認
1. KneronPLUS wheel 版本與升級可能性M9-6 第一件事)
2. KL630/KL730 是否有實機可測(沒有則 M9-6 降級為純文件研究)
3. Kneron 對 firmware re-distribution 的書面授權範圍是否含舊版 / KDP142 §7
4. 使用者對多版本 bundle 策略的選擇(保守 / 完整 / 極簡42 §4.3
5. 多版本目錄結構選項(建議 C、42 §3.2
### 下一步建議(更新版)
1. 給使用者 review 本份完整 plan00 + 10/20/30 + 40/41/42 七個檔)
2. 使用者最終 confirm 整體範圍後:
- **A 階段啟動**M9-1 ~ M9-55 人天)
- **B 階段預備**M9-6 SDK 驗證1 人天、可與 A 階段平行做 research
3. A 階段 release / 驗證後評估 M9-6 結論、決定 B 階段全做或調整 scope
4. B 階段執行M9-7 ~ M9-13合計 ~9.5 人天)
5. 整體合計:**~15.5 人天**A 5 + B 10.5