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

warrenchen 雲端版 FW 偵測 + 升降版實作分析

對應 visionA-local 研究 plan §10 來源:/tmp/web_academy_prototype/ reposhallow clone, c9d56a6 HEAD 主要分析檔:

  • local_service_win/LocalAPI/main.pyFastAPI HTTP 服務、含 FW endpoints
  • local_service_win/LocalAPI/legacy_plus121_runner.py(用 ctypes 直接呼叫 libkplus.dll、最關鍵
  • local_service_win/STRATEGY.mdAPI spec + recovery flow
  • local_service_win/firmware/(裝置 firmware 二進位)
  • local_service_win/third_party/Kneron_DFUT/Windows-only Qt GUI 工具)

1. warrenchen 用了哪些技術做 FW 偵測 + 升降版

1.1 兩條獨立路徑(並存)

路徑 用途 技術 跨平台?
Path AKneronDFUT.exe KL520 升版 / 降版、KL720 升版 呼叫 Windows 第三方 KneronDFUT.exe CLIsubprocess Windows only
Path Blibkplus.dll via ctypes KL520 KDP1 → KDP2「載入到 RAM」升級不寫 flash 直接 ctypes 載 libkplus.dll、呼叫 C API 三平台都能用(換成 .so / .dylib

我們選 Path B——理由:

  • Path A 的 DFUT.exe 是 Windows-only Qt5 binary30+ DLLs、bin/ 目錄 ~40MB、無 macOS / Linux 版本、無法跨平台
  • Path B 用的 libkplus 三平台都有、我們既有 wheel 已含、額外成本 ~0KB
  • Path B 的 APIkp_update_kdp_firmware_from_files)也能寫 flash、不只 load to RAM

1.2 偵測 FW 版本

scan 時從 KneronPLUS SDK 拿到的 device_descriptor.firmware 字串、可能值:

firmware 字串 意義
KDP 舊版 KDP1legacy、需升級
KDP2 Comp/U KDP2、在 USB Boot 模式(剛 load 進 RAM
KDP2 Comp/F KDP2、在 Flash 模式(已永久燒入 flash
Loader USB Boot Loader 模式(等待 load firmware
Unknown / 空字串 偵測失敗

判斷邏輯(從 legacy_plus121_runner.py L228

if detected_firmware == "KDP":
    # 走 KDP1 → KDP2 升級路徑loader → load_firmware
elif detected_firmware == "Loader":
    # 在 USB Boot loader、直接 load_firmware
else:
    # 已是 KDP2 或其他、不需要升級

比對 product_id 也能輔助判斷visionA-local bridge.py L657 已有此表):

product_id 意義
0x0100 KL520
0x0200 KL720 KDP legacy舊韌體
0x0530 KL530
0x0630 KL630
0x0720 KL720 KDP2
0x0730 KL730

→ KL720 0x0200 vs 0x0720 直接區分新舊版本(不用看 firmware 字串)。

1.3 KDP1 → KDP2 升級流程(最核心)

legacy_plus121_runner.py L185-292 整理:

Step 1: kp_connect_devices_with_magic_pass(port_id, MAGIC=536173391)
        → 用 magic number 繞過 SDK 的 firmware version check
        → 拿到 device_group handle

Step 2: kp_set_timeout(device_group, 5000ms)
        → 給後續 API 設超時

Step 3: 偵測當前 firmware
        if firmware == "KDP":
            # KDP1 → 必須先切到 USB Boot loader
            Step 3a: kp_update_kdp_firmware_from_files(
                       device_group,
                       loader_path,    # fw_loader.bin
                       NULL,           # 不寫 NCPU
                       auto_reboot=True
                     )
                     → 寫 loader 到 flash、自動重啟
            Step 3b: kp_set_timeout(再設一次)
            Step 3c: kp_load_firmware_from_file(
                       device_group,
                       scpu_path,      # fw_scpu.bin
                       ncpu_path       # fw_ncpu.bin
                     )
                     → 載入 KDP2 firmware 到 RAM不寫 flash
        else:
            # 不是 KDP1、直接 load_firmware_from_file
            Step 3': kp_load_firmware_from_file(scpu_path, ncpu_path)

Step 4: kp_disconnect_devices(device_group)
        → 注意:升級後 disconnect 經常會回非零 codeUSB re-enumerate 中),但不算錯誤

注意 1:這條路徑的「升級」實際上是「載到 RAM」、不是「寫 flash」。對 KL520 USB Boot 裝置而言、每次斷電都要再 load 一次。這對我們夠用——KL520 反正每次 connect 都會 load_firmwarevisionA-local bridge.py L876 已實作)、加 legacy KDP → KDP2 升級邏輯只是擴充當前的 connect 流程。

注意 2:真正寫 flash 的是 kp_update_kdp_firmware_from_files、會留在 device 重啟仍存在。這個 API 是 warrenchen「legacy-upgrade」endpoint 用 DFUT.exe 跑的路徑Path A對應的 C API、跨平台可用。

1.4 警告與限制warrenchen 已踩過的坑)

STRATEGY.md 「Legacy Firmware Story」段L521-545整理

  1. KP_ERROR_INVALID_FIRMWARE_24:很多出貨的 dongle 還在舊 KDP firmware、即使 connect_devices_without_check 也會回此錯誤。解法:用 kp_connect_devices_with_magic_passMAGIC=536173391繞過 version check。
  2. DFUT 對某些舊裝置 timeoutwarrenchen 加了一條後備邏輯main.py L1039-1052——如果 DFUT timeout 但 rescan 看到裝置已切到 KDP 狀態、就視為成功。
  3. disconnect 失敗不一定是錯FW load 後 USB 重 enumerate、原 device handle 失效、disconnect 回非零是預期行為。
  4. timeout 設定:升級 KL520 用 30 秒、升級 KL720 用 180 秒(後者要寫 flash、慢很多
  5. 驅動程式問題Windows升級前必須確認 WinUSB driver 已綁定、否則 kp_connect_devices 直接失敗。warrenchen 用 kp.core.install_driver_for_windows 自動處理、我們有自己的 kneron_winusb.inf + driver 安裝邏輯M1+ TODO

2. warrenchen 的 firmware 二進位內容

裝置 路徑 檔案 用途
KL520KDP2 主版) firmware/KL520/ fw_scpu.bin / fw_ncpu.bin 升級到 KDP2 / 連線時 load to RAM
KL520KDP2 主版) firmware/KL520/ fw_loader.bin USB Boot Loader升降版必要
KL520KDP2 主版) firmware/KL520/dfw/ minions.bin DFUT 用的中介 firmware不確定我們是否需要
KL520KDP1 降版) firmware/KL520_kdp/ fw_scpu.bin / fw_ncpu.bin 降版回 KDP1 用(測試用)
KL720 firmware/KL720/ fw_scpu.bin / fw_ncpu.bin 升級到 KDP2 / 連線時 load to RAM
KL630 firmware/KL630/ kp_firmware.tar / kp_loader.tar 整套打包格式(新 SDK 用)
KL730 firmware/KL730/ kp_firmware.tar / kp_loader.tar 整套打包格式

比對 visionA-local 既有server/scripts/firmware/

裝置 我們有 warrenchen 多了什麼
KL520 fw_scpu.bin + fw_ncpu.bin fw_loader.bin升降版必要、要拿、minions.bin可能要、KL520_kdp 整組(降版要)
KL720 fw_scpu.bin + fw_ncpu.bin 無新增
KL630 / KL730 全套(但我們延後做)

從 VERSION 檔看版本

  • KL520 / KL720 都是 firmware v2.2.0
  • KL630 是 SDK-v2.5.7(命名規則不同)

大小估算(合計)

裝置 檔案 估計大小
KL520KDP2 主版) fw_scpu + fw_ncpu ~92KB既有 TDD L3219 標 52+40KB
KL520 + fw_loader.bin + ~10KB估計
KL520_kdpKDP1 fw_scpu + fw_ncpu ~80KBKDP1 較小)
KL720 fw_scpu + fw_ncpu ~150KB估計、KDP2 較複雜)
KL630 kp_firmware.tar + kp_loader.tar ~2-3MB壓縮包
KL730 kp_firmware.tar + kp_loader.tar ~3-4MB壓縮包

→ MVPKL520 + KL720+ 補 fw_loader.bin + KL520_kdp 合計 < 300KB、可忽略。 → 完整版加 KL630/KL730 約 +5-7MB。

注意warrenchen 把 firmware/ 跟程式碼一起打包進 installer payloadSTRATEGY.md L513-518、跟我們做法一致。我們已經把 firmware 進 gitserver/scripts/firmware/)、新增的 firmwarefw_loader.bin、KL520_kdp/)也應該 commit 進來、不走 vendor-sync。


3. warrenchen 暴露的 FW APIHTTP REST

從 STRATEGY.md + main.py 整理。我們不會照搬全部、只取其中 3 個 endpoint 給 MVP

3.1 對我們有用的(要做)

Endpoint Method Body 我們的對應
/devices GET 我們已有 /api/devices、回傳已含 firmwareVersion 欄位
/firmware/legacy-plus121/load POST {port_id, loader_path, scpu_path, ncpu_path} 建議新增 /api/devices/:id/firmware/upgrade(自動推導 path、不暴露給前端

3.2 不需要照搬的(雲端版才需要)

Endpoint 為什麼不要
/firmware/legacy-upgrade/kl520 (DFUT.exe) DFUT 是 Windows only、跨平台不能用
/firmware/legacy-downgrade/kl520 (DFUT.exe) 同上、且降版主要是內部測試用、MVP 不做
/firmware/legacy-upgrade/kl720 (DFUT.exe) 同上
/firmware/load(暴露 scpu_path/ncpu_path 沒必要讓前端決定 firmware 路徑、後端內部處理
/devices/connect_force 我們 bridge.py handle_connect() 已有 connect_devices_without_check 自動 fallback、不暴露給 API
/driver/check / /driver/install / /driver/ensure 我們已有 ensureDriverInstalled()M1+ TODO、不暴露

4. 我們可以直接重用的 warrenchen 邏輯

來源 邏輯 我們怎麼用
legacy_plus121_runner.py:_load_libkplus ctypes 直接載 libkplus 的 argtypes/restype 宣告 不重用 ctypes 寫法——我們用 Python kp modulekp.core.update_kdp_firmware_from_files)即可,比 ctypes 乾淨
legacy_plus121_runner.py:_connect_with_magic 用 MAGIC 繞 firmware check 重用概念、用 Python API——kp.core.connect_devices_with_magic_pass(port_ids, magic=536173391)
legacy_plus121_runner.py:main() 的升級流程 KDP → loader → load_firmware 兩階段 直接重用流程(翻譯成 visionA-local bridge.py 的 handle_firmware_upgrade
legacy_plus121_runner.py:_reboot_and_reconnect reset → sleep → retry connect 重用visionA-local 已有類似的 restartBridge
main.py:_kl520_kdp_observed_after_timeout timeout 後 rescan 確認狀態 重用升級不確定有沒有真的成功時、rescan 來驗證)
main.py:_run_dfut_command 的 timeout / retry / streaming output 處理 子進程通訊管理 不重用(我們不用 DFUT

5. 我們不該照搬的(雲端版才需要的)

從 README + STRATEGY 可見 warrenchen 雲端版的特殊需求:

  1. 跨進程 RPC:雲端版有「網頁 → 雲端 → 本地服務」的 RPC 鏈、需要自訂 URL scheme + 引導下載安裝代理。我們單進程 Wails、不需要這套。
  2. DFUT.exe 整包打包:因為要支援所有舊裝置 / Windows 維運場景。我們只走 KneronPLUS Python API、跨平台、不打包 DFUT。
  3. FastAPI / Uvicorn:他們選 Python FastAPI 寫本地服務、我們是 Go server。我們不重用 service 層、只 reverse-engineer FW 升級邏輯。
  4. /tools/video-inference 視覺驗證頁:他們用 HTML 內嵌 page 做 PoC 視覺化、我們的 Next.js 推論頁已完整、不需要。
  5. /firmware/load 直接暴露 path 給前端:安全 / 易用考量、我們不暴露、後端自己解析。

6. 對接 visionA-local 既有 bridge.py 的具體改動建議(純 plan

server/scripts/kneron_bridge.py 新增一個 handler

def handle_firmware_upgrade(params):
    """
    升級 KL520 / KL720 dongle 到內建 KDP2 firmware跨晶片自動偵測。

    參數:
      port: 裝置 USB port id必填
      chip: "KL520" or "KL720"(必填)

    回傳(成功):
      {
        "status": "upgraded",
        "before_firmware": "KDP",
        "after_firmware": "KDP2",
        "method": "kp_update_kdp_firmware_from_files",
        "duration_ms": 28500
      }

    回傳(失敗):
      {"error": "...", "stage": "connect" | "loader" | "load_firmware" | "verify"}
    """
    # 1. scan + 找到 target_dev by port_id
    # 2. kp.core.connect_devices_with_magic_pass(port_ids, magic=536173391)
    # 3. kp.core.set_timeout(60000)
    # 4. 偵測 detected_firmware
    # 5. if detected_firmware == "KDP":
    #        a. kp.core.update_kdp_firmware_from_files(dg, loader_path, None, auto_reboot=True)
    #        b. wait_for_reenumerate(8s)
    #        c. reconnect (with magic if needed)
    #        d. kp.core.load_firmware_from_file(scpu, ncpu)  # 已是 visionA-local bridge 既有邏輯
    #    else:
    #        a. kp.core.load_firmware_from_file(scpu, ncpu)  # 直接 load 即可
    # 6. 重新 scan、驗證 firmware 已變
    # 7. disconnect容忍非零回傳
    # 8. 回傳結果

注意bridge.py 的 main loop 已是 JSON-RPC 模式L1159-1182、加新 cmd 只需在 switch 表加 elif action == "firmware_upgrade": result = handle_firmware_upgrade(cmd)、改動量極小。


7. 結論

warrenchen 的雲端版核心 FW 升級邏輯Path B100% 可移植到我們 local 版、且:

  • 不需要打包 DFUT.exe跨平台 win
  • 不需要 ctypes用 KneronPLUS Python API 即可)
  • 既有 bridge.py + firmware bundle 已 70% 就緒、只差升級 handler + 1 個 endpoint + 1 個 UI 卡片
  • 風險低、工時可估MVP 5 人天)

下一份檔(20-our-current-state.md)盤點我們確切有什麼、缺什麼。