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

13 KiB
Raw Blame History

visionA-local 既有架構盤點FW 偵測 + 升降版相關)

對應研究 plan §20 目的:列出我們已經有什麼、什麼、改動成本估計


1. 既有 driver 怎麼連 dongle

1.1 Driver 概覽

檔案server/internal/driver/kneron/kl720_driver.go(檔名歷史包袱、實際同時管 KL520 + KL720

架構

Go driver (KneronDriver)
  ↓ exec.Command(python3, kneron_bridge.py)
  ↓ JSON-RPC over stdin/stdout
Python bridge (kneron_bridge.py)
  ↓ kp module (KneronPLUS Python wheel)
  ↓ ctypes → libkplus.{dll,so,dylib}
USB device (Kneron Dongle)

已實作 driver methodsdriver.DeviceDriver interface

  • Connect() / Disconnect() / IsConnected()
  • Flash(modelPath, progressCh)語意load model 到 device RAM(不是燒 firmware
  • RunInference(imageData) / ReadInference() / StartInference() / StopInference()
  • GetModelInfo()
  • Info() — 回傳 DeviceInfoFirmwareVer

1.2 connect 流程(既有)

KneronDriver.Connect() (L250-323) 已做:

  1. 啟動 Python bridge subprocess
  2. send connect cmd 到 bridge
  3. bridge handle_connect() 內:
    • kp.core.scan_devices() 找 target by port
    • 判斷 chip type從 device_type 或 product_id
    • 如果是 KL720 KDP legacypid=0x0200:自動走 connect_devices_without_check + 載 KDP2 firmware 到 RAML749-804
    • 如果是 USB Boot Loader mode:自動載 firmware 到 RAML872-914
    • 回傳 firmware 字串 + fresh_firmware_loaded flag
  4. driver 根據 fresh_firmware_loaded 決定是否做 restartBridge() resetL310-321

1.3 已實作的 FW 偵測能力

bridge.py 已能偵測

  • USB vendor / product idkp.core.scan_devices()、含 KL520 / KL630 / KL720_legacy / KL720_KDP2 / KL730、L657-664 已列表)
  • firmware 字串(從 device_descriptor.firmware
  • kn_number、is_connectable、usb_port_id

Go driver 已記錄

  • DeviceInfo.FirmwareVerinterface.go L26
  • DeviceInfo.ProductID / VendorID

1.4 已實作的 FW load 能力

這部分是關鍵——我們其實已經會 load_firmware_to_RAM

kneron_bridge.py 內:

  • _resolve_firmware_paths(chip) — 從 server/scripts/firmware/<chip>/fw_{scpu,ncpu}.bin 解析路徑
  • handle_connect() L749-764KL720 legacy 路徑、kp.core.load_firmware_from_file(dg, scpu, ncpu)
  • handle_connect() L876-914USB Boot Loader 路徑、同 API

載完之後也會做 reconnect + verifyL887-912

缺什麼

  • 沒呼叫 kp.core.update_kdp_firmware_from_files(這個才寫 flash、持久升級
  • 沒呼叫 kp.core.connect_devices_with_magic_pass(升級舊 KDP1 dongle 必要)
  • 沒「使用者主動升級」的入口、目前都是 connect 自動觸發

2. 既有 flash 模組(仍存在、語意 = model load

2.1 程式碼狀態

R5-Q9 砍 flash 後、程式碼還在

檔案 大小 用途
server/internal/flash/service.go 158 行 StartFlash(deviceID, modelID) — load model
server/internal/flash/progress.go 未讀(推測 progress tracker progress 追蹤
server/internal/driver/kneron/kl720_driver.goFlash() method L425-604 實際呼叫 load_model cmd

Flash() 的實際行為(看 L425-604

  • 不是燒 firmware
  • load .nef model 到 deviceKL520 RAM / KL720 也是 load model
  • 含 KL520 USB Boot mode 的 retry + restartBridge fallback

R5-Q9 砍掉的是 UI 入口、不是技術能力

  • device_handler.go:FlashDevice() (L128-160) 仍存在、route 也仍註冊progress.md M1-2 跳過 cluster/tunnel/flash/update 是指 ut/update 這套 cluster 機制,不是這支 flash
  • 前端 UI 是否還顯示「Flash」按鈕沒查證、但即使有也是「load model」、跟 firmware 升降版無關

2.2 對本任務的啟示

flash 模組保留原語意load model、不要把 firmware 升降版邏輯塞進去。 → 新建 server/internal/firmware/ 模組plan 30 會展開)、跟 flash 並列、職責清楚。


3. Bridge.py 是否有支援升降版能力KneronPLUS Python SDK 文件對照)

3.1 KneronPLUS Python API 可用清單

kneron_bridge.py 內已 import 的 kp module從 imports 推測 + warrenchen 程式碼驗證):

API 用途 bridge.py 已用?
kp.core.scan_devices() scan USB 已用
kp.core.connect_devices(port_ids) 標準 connect 已用
kp.core.connect_devices_without_check(port_ids) 繞 firmware check connect 已用
kp.core.connect_devices_with_magic_pass(port_ids, magic) 用 magic 繞檢查 缺、升級舊 KDP1 必需
kp.core.set_timeout(dg, ms) 設超時 已用
kp.core.reset_device(dg, mode) 重啟 device 已用
kp.core.load_firmware_from_file(dg, scpu, ncpu) load to RAM 已用
kp.core.update_kdp_firmware_from_files(dg, scpu, ncpu, auto_reboot) 寫 flash 持久升級 缺、本任務核心
kp.core.load_model_from_file(dg, path) load model 已用
kp.core.disconnect_devices(dg) disconnect 已用
kp.core.install_driver_for_windows(target) 裝 Windows driver 我們有自己的 kneron_winusb.inf 機制、可能不需要

核心缺 2 個 API call、其他都有

3.2 既有 firmware bundle

路徑 內容 對 MVP 是否足夠
server/scripts/firmware/KL520/fw_scpu.bin KL520 SCPU firmwareKDP2、~52KB
server/scripts/firmware/KL520/fw_ncpu.bin KL520 NCPU firmwareKDP2、~40KB
server/scripts/firmware/KL720/fw_scpu.bin KL720 SCPU firmwareKDP2
server/scripts/firmware/KL720/fw_ncpu.bin KL720 NCPU firmwareKDP2
server/scripts/firmware/KL520/fw_loader.bin KL520 USB Boot Loader binary 缺、升級舊 KDP1 必需
server/scripts/firmware/KL520/dfw/minions.bin 跟 DFUT 配套(可能不需要) 可能要研究

MVP 階段要從 warrenchen repo 補一個 KL520/fw_loader.bin~10KB

3.3 為什麼缺的 API call 不太可能踩坑

kp.core.update_kdp_firmware_from_files 是 KneronPLUS SDK 標準 API、warrenchen 的 ctypes 版本(legacy_plus121_runner.py)也是用同一個 C symbollib.kp_update_kdp_firmware_from_files)、不是冷僻 API。

但要注意:

  • 必須先 connect_devices_with_magic_pass——否則舊 KDP1 device 連 connect_devices_without_check 都會被拒KP_ERROR_INVALID_FIRMWARE_24
  • auto_reboot=True 後 disconnect 會回非零——預期行為、不能視為 error
  • timeout 要拉長warrenchen KL720 用 180s

4. 現有 /api/devices endpoint 結構

4.1 既有 routesdevice_handler.go

GET    /api/devices            ListDevices       回 [DeviceInfo]
GET    /api/devices/scan       ScanDevices       rescan + 回 [DeviceInfo]
GET    /api/devices/:id        GetDevice         回單一 DeviceInfo
POST   /api/devices/:id/connect       ConnectDevice
POST   /api/devices/:id/disconnect    DisconnectDevice
POST   /api/devices/:id/flash         FlashDevice  load model、保留
POST   /api/devices/:id/inference     StartInference
DELETE /api/devices/:id/inference     StopInference

WebSocket rooms推測、需 grep 確認):

  • flash:<deviceId> — flash progress
  • inference:<deviceId> — inference results

4.2 DeviceInfo 已含的欄位

type DeviceInfo struct {
    ID           string
    Name         string
    Type         string         // "kl520" / "kl720"
    Port         string
    VendorID     uint16
    ProductID    uint16
    Status       DeviceStatus
    FirmwareVer  string         // 已有!
    FlashedModel string
}

4.3 為了 FW 管理需要新增的欄位(建議)

type DeviceInfo struct {
    // ... 既有欄位 ...
    
    // === FW 管理新增 ===
    FirmwareIsLegacy   bool   `json:"firmwareIsLegacy,omitempty"`    // true=需升級到 KDP2
    FirmwareCanUpgrade bool   `json:"firmwareCanUpgrade,omitempty"`  // true=有 bundled firmware 可升
    BundledFirmwareVer string `json:"bundledFirmwareVersion,omitempty"` // "v2.2.0"(從 VERSION 檔)
}

→ 這些都是 bridge.py 回傳完就能計算的衍生欄位、不額外查 USB。

4.4 為了 FW 管理需要新增的 endpoints

Endpoint Method 用途
POST /api/devices/:id/firmware/upgrade POST 觸發升級到內建 KDP2
WebSocket room firmware:<deviceId> 升級 progress 廣播

MVP 不做

  • POST /firmware/downgrade(手動降版、階段 B
  • GET /firmware/versions(列出可選版本、階段 B

5. 既有架構幾條重要限制FW 升級必須避開)

5.1 KL520 reset bug 教訓2026-04-21

bridge.py L867-927 已有 fresh_firmware_loaded flag、避免雙重 load 浪費 60s。

升級流程要小心

  • 升級成功後 device re-enumerate、driver 端的 _device_group handle 失效
  • 必須清掉 _device_group、回到 Go 端、讓使用者重新 scan / connect
  • 不要在升級 handler 內試圖「升級完繼續用同一個 connection」

5.2 Connect timeout = 120sWindows worst-case

device_handler.go L90 已設 120s。

升級不該共用這個 timeout

  • KL520 升級 ~30s、KL720 升級 ~180s
  • 升級 endpoint 用獨立的 context.WithTimeout(300s) 或乾脆走 background goroutine + WebSocket 推進度
  • 推薦做法HTTP API 立刻回 202 Accepted + taskID、實際進度走 WebSocket跟 flash 一樣的 pattern

5.3 Python bridge 是 per-driver-instance

每個 KneronDriver 啟動自己的 Python bridge subprocess。升級時的 bridge 必須是當前 connected 的那一個。

升級流程

  • 升級前 bridge 已在 connected 狀態
  • 升級期間 bridge 內部會 reset device、disconnect、reconnect
  • 升級後 bridge 仍存活、但 _device_group 已換新

5.4 三平台差異

OS 特殊處理
macOS 已用 _preload_kneron_dylibs_macos() 預載 libusb / libkplus避開 hardened runtime 砍 DYLD
Windows 需要 WinUSB driver 綁定 + libusb-1.0.dll 在 PATH
Linux wheel 自帶 libusb.so.1.0.0 + LD_LIBRARY_PATH

→ 升級流程本身不需要額外的平台特化邏輯(用既有的 kp import 流程)。

5.5 既有 restartBridge() 不能拿來做 FW 升級

restartBridge() (L369-415) 是用來在 KL520 換 model 時清掉 USB Boot sessionkill bridge → sleep 8s → 重啟 bridge、跟 firmware update 完全不同

→ FW 升級走獨立的 handler、不重用 restartBridge。但升級完成後、driver 應該 mark needsReset=true、下次 connect 走完整 reset flow既有邏輯保證 clean state。


6. 既有 UI 概況(推測、未實際讀前端 code

從 progress.md + TDD 推測 visionA-local Web UI 結構:

  • /devices 頁面Next.js顯示掃描到的 dongle 清單
  • 每張 device card 已顯示 firmwareVersion 欄位(既有 DeviceInfo 有此欄位)
  • 但沒有「升級」按鈕、沒有 FW 健康度視覺化(綠/黃/紅)

MVP UI 新增plan 30 會展開):

  • FW badgeKDP2 最新、黃KDP2 但版本低、紅KDP1 needs upgrade
  • 「升級韌體」按鈕(紅 badge 時 primary、其他 secondary
  • 升級 modalprogress bar + 階段提示 + 取消按鈕)

7. 缺項摘要

類別 缺什麼 取得方式
Firmware binary KL520/fw_loader.bin 從 warrenchen local_service_win/firmware/KL520/fw_loader.bin 複製進 server/scripts/firmware/KL520/
Firmware binaryB 階段) KL520_kdp/fw_{scpu,ncpu}.bin(降版用) 從 warrenchen 複製
Firmware binaryB 階段) KL630/ KL730/ 從 warrenchen 複製、但要先驗 driver 支援
Python API call kp.core.connect_devices_with_magic_pass 已內建於 KneronPLUS wheel、加 import 即可
Python API call kp.core.update_kdp_firmware_from_files 已內建於 KneronPLUS wheel
bridge.py handler handle_firmware_upgrade() 新寫 ~80-100 行plan 30 有 stub
bridge.py handlerB 階段) handle_firmware_downgrade() 新寫
Go driver method UpgradeFirmware() error 新寫 ~40-60 行plan 30
Go service server/internal/firmware/service.go 新建模組(仿 flash/service.go 結構)
Go API handler POST /api/devices/:id/firmware/upgrade 新增 ~50 行
WebSocket room firmware:<deviceId> 沿用既有 WS hub broadcast pattern
DeviceInfo 欄位 FirmwareIsLegacy / FirmwareCanUpgrade / BundledFirmwareVer 加在 interface.go
Frontend 元件 FW badge + 升級按鈕 + progress modal 1.5 人天
Frontend store update Devices store 加 fw progress 訂閱 已有 flash progress 訂閱 pattern 可參考
Frontend i18n 升級相關文案(中英雙語) ~20 個新 keys

下一份檔(30-integration-plan.md)展開完整 milestone + 受影響檔案 + 風險清單。