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

32 KiB
Raw Blame History

B 階段研究KL630 / KL730 driver 擴展支援

對應 research index §40 範圍B 階段(在 A 階段 MVP 完成後)擴 driver 支援 KL630 + KL730並把它們納入 FW 偵測 + 升降版流程 撰寫日期2026-05-24 限制:純 plan、不出 code、不改既有檔案 路徑使用相對路徑(相對於 /Users/jimchen/visionA/local-tool/


0. TL;DR

  1. warrenchen 沒有對 KL630/KL730 做 FW 升降版——他們只 bundle 了 firmware tar 檔(為了未來雲端 /firmware/load endpointlegacy_plus121_runner.py 與相關升級流程完全只針對 KL520 + KL720 KDP1 → KDP2 場景。對 KL630/KL730、warrenchen 只實作 driver install + scan。
  2. 既有 visionA-local 對 KL630/KL730 偵測得到、但連不上
    • kneron_bridge.py:_KNOWN_PRODUCTS 已含 0x0630: "KL630" / 0x0730: "KL730"L657-664→ scan 階段會看到名字
    • handle_connect() 的 chip 判斷 fall-through 到 _device_chip = "KL520"L740-741→ 用 KL520 firmware 路徑載入會直接失敗
    • chipFromProductID()detector.go L97-111也是 default 回 KL520 → driver 會被建成 KL520 type
  3. 擴 driver 不大、但 .tar firmware 處理是 unknown 區
    • 認 product_id 並 route 到 _device_chip = "KL630"/"KL730" 是 5 行改動
    • 但 KneronPLUS Python API load_firmware_from_file() 接受單個檔案路徑scpu, ncpu.tar 是新格式KDP2 v2.x SDK 出現的打包形態)、SDK API 對 .tar 的接受方式必須查官方文件驗證
    • 建議B 階段先做「scan + 顯示 KL630/KL730」不能 inference、再做「FW load」、最後才做「FW 升降版」
  4. B 階段拆三層、不要一次做完
    • B0最簡driver 認 KL630/KL730、scan 看得到、不能連——使用者至少知道我們認得這晶片
    • B1bridge.py 能 load .tar firmware、能 connect、能 inference——FW 偵測完整
    • B2:完整 FW 升降版(手動降版面向一般使用者)——多版本管理 + UX

1. warrenchen 對 KL630 / KL730 怎麼處理(資料來源確認)

1.1 程式碼層面:只有 driver install + scan

/tmp/web_academy_prototype/local_service_win/LocalAPI/main.py grep KL630|KL730 只有 4 個位置:

位置 用途
L188 DriverInstallRequest.target 文件 API 參數 hintALL | KL520 | KL720 | KL630 | KL730 | KL830
L433-436 _target_product_ids() "KL630" / "KL730" 字串轉成 kp.ProductId.KP_DEVICE_KL630 / KP_DEVICE_KL730 enum
L443-445 同上 target == "ALL" 分支 ALL 把 KL630/KL730 也納入 driver install 對象
STRATEGY.md L196-203 /driver/ensure 註解 文件描述支援的 target 列表

完全沒有

  • load_firmware_from_file(KL630_files) 之類的 call
  • update_kdp_firmware_from_files(KL630_files) 之類的 call
  • /firmware/load 對 KL630/KL730 的專用處理
  • 任何 .tar 解壓 / 處理邏輯

結論warrenchen 對 KL630/KL730 的支援僅止於 Windows WinUSB driver 安裝。連線 / firmware load / inference 都沒實作過。

1.2 為什麼 warrenchen bundle 了 KL630/KL730 firmware但沒用

firmware/KL630/VERSIONSDK-v2.5.7firmware/KL730/VERSIONSDK-v1.3.0、跟 KL520/KL720 的 2.2.0 顯然命名規則不一樣KL630/KL730 用 SDK 版本、不是 firmware 版本)。

推測 warrenchen 的策略:

  1. 把 SDK 官方提供的 firmware 整套 vendored 進來(一次性 release 製作的副產物)
  2. 為未來 /firmware/load 端點預留——讓雲端業務邏輯決定要不要 load
  3. 但實際路徑、API 對應、.tar 解包處理 PoC 階段沒做

對我們的啟示:我們無法從 warrenchen 偷到 KL630/KL730 的「連線 / FW 升降版」流程實作、必須自己研究 KneronPLUS Python SDK 對這兩個晶片的 API

1.3 STRATEGY.md 對 KL630/KL730 的描述

僅有 1 處:/driver/ensure 註解列出支援 target。沒有「Legacy Firmware Story」式的 KL630/KL730 章節——對 KL520/KL720 寫了一大段STRATEGY.md L521-545、對 KL630/KL730 完全沒寫。


2. 既有 visionA-local 對 KL630 / KL730 的支援狀態

2.1 已認得 product_idscan 看得到)

server/scripts/kneron_bridge.py L657-664

_KNOWN_PRODUCTS = {
    0x0100: "KL520",
    0x0200: "KL720",
    0x0720: "KL720",
    0x0530: "KL530",
    0x0630: "KL630",
    0x0730: "KL730",
}

→ scan 階段(_scan_with_pyusb / handle_scan)會在 devices[].product_idchip 欄位顯示正確值。

2.2 chip 判斷邏輯漏判connect 會誤路由到 KL520

kneron_bridge.py L732-741

pid = target_dev.product_id
if "kl720" in device_type.lower():
    _device_chip = "KL720"
elif "kl520" in device_type.lower():
    _device_chip = "KL520"
elif pid in (0x0200, 0x0720):
    _device_chip = "KL720"
else:
    _device_chip = "KL520"   # ← KL630/KL730/KL530 全部掉這裡

→ KL6300x0630/ KL7300x0730會被當成 KL520 處理。

  • _resolve_firmware_paths("KL520") 會去找 firmware/KL520/fw_scpu.bin → 拿到的是 KL520 的 firmware
  • load_firmware_from_file(KL630_device, kl520_scpu) → 預期會炸(不確切錯誤碼、估計 KP_FW_INFO_ERR 或類似)

2.3 Go 端 detector 也漏判

server/internal/driver/kneron/detector.go L97-111

func chipFromProductID(productID string) (chip string, deviceType string) {
    pid := strings.ToLower(strings.TrimSpace(productID))
    switch pid {
    case "0x0100":
        return "KL520", "kneron_kl520"
    case "0x0200", "0x0720":
        return "KL720", "kneron_kl720"
    default:
        return "KL520", "kneron_kl520"   // ← KL630/KL730/KL530 全部掉這裡
    }
}

→ Go driver 也會把 KL630/KL730 當成 KL520。

2.4 沒 bundle KL630/KL730 firmware

server/scripts/firmware/ 目前只有 KL520/ + KL720/、沒有 KL630/ KL730/ KL530/


3. KneronPLUS Python SDK 對 KL630 / KL730 的 API 落差(需 SDK 官方文件驗證

3.1 已確認的事

從 warrenchen main.py L433-436 看到:

if target == "KL630":
    return [kp.ProductId.KP_DEVICE_KL630]
if target == "KL730":
    return [kp.ProductId.KP_DEVICE_KL730]

→ KneronPLUS Python wheel 內 kp.ProductId enum 有 KP_DEVICE_KL630 / KP_DEVICE_KL730

local_service_win/KneronPLUS-3.1.2-py3-none-any.whl 推測:

  • KneronPLUS SDK 3.x 系列就有 KL630/KL730 支援
  • 我們既有的 KneronPLUS wheel 版本需要查(待確認)——如果是 2.x、可能要升級

3.2 未確認、必須查 KneronPLUS 官方文件的事

問題 影響
kp.core.connect_devices([KL630_port_id]) 是否能跟 KL520/KL720 共用 API、不需要特殊參數 影響 connect 流程設計
load_firmware_from_file(dg, scpu, ncpu) 對 KL630/KL730 是否仍是兩個 .bin 檔(解壓 .tar 後)? 影響 .tar 處理策略runtime extract 還是 build time extract
是否有新的 load_firmware_from_tar() 之類的 API 如果有、我們直接餵 .tar 不用解壓
update_kdp_firmware_from_files() 對 KL630/KL730 是否適用?還是改用 update_kdp2_firmware_from_files() / update_kdp2_firmware_from_tar() 影響 FW 升降版實作
KL630/KL730 是否仍有「Loader / KDP / KDP2」三種 firmware state、還是只有兩種 影響 FW 偵測邏輯
KL630/KL730 是否仍像 KL520 一樣每次都要 load firmware 到 RAM、還是像 KL720 一樣 flash-based 影響 connect 流程設計(重大)
inference.generic_image_inference_send/receive 對 KL630/KL730 是否一致? 影響 inference 流程是否要分支
kp.core.scan_devices() 對 KL630/KL730 的 firmware 字串可能值("KDP2"? "KDP3"? 其他?) 影響 FW badge 顯示邏輯

3.3 取得這些資訊的方法

  1. KneronPLUS SDK 官方 PDF 文件——使用者應該有 SDK release 附的 documentationKneronPLUS_SDK_Reference_Manual.pdf 之類)、查 KL630/KL730 section
  2. wheel 內 Python 原始碼——unzip KneronPLUS-3.1.2-py3-none-any.whl 後看 kp/core.py 的 docstring + kp/__init__.py 看 enum 定義
  3. SDK example code——Kneron 通常會附 sample用 KL630/KL730 跑 inference 的範例)、看他們怎麼初始化裝置
  4. 在真實 KL630/KL730 device 上 trial-and-error——使用者手上若有 KL630/KL730 dongle、可以裝 KneronPLUS、Python REPL 跑 kp.core.scan_devices() 直接看回傳

B 階段第一個 milestone 必須先把這些查清楚,否則所有後續設計都是猜的。


4. 既有 driver 怎麼擴:「新加 driver」vs「同一個 driver 加 case」

4.1 兩個選項對比

維度 A同一個 driver 加 case B拆出 KL630Driver / KL730Driver
程式碼複用 高(已支援 KL520/KL720 共用、邏輯相近處不重寫) 低(每個 chip 自己一份 driver、可能 80% 重複)
流程差異容納度 中(用 switch / if-else 處理差異、容易長成 spaghetti 高(每個 driver 清楚自己 chip 的特性)
維護負擔 中(單檔變大、但所有 chip-specific 邏輯集中) 4 個檔案要同步更新通用邏輯)
測試隔離 mock 整個 driver、無法只測 KL630 部分) 高(每個 driver 獨立測)
既有 bridge.py 是否共用 是(已是這個結構) bridge.py 必定要共用,重 spawn Python 太貴)
改動成本 低(擴 kl720_driver.go 既有結構) 高(拆 driver 還要改 detector / manager / DI

4.2 建議:選 A同一個 driver 擴 case

理由

  1. KL520/KL720 已是同一個 driver、檔名雖叫 kl720_driver.go 但已是「Kneron 通用 driver」再加 KL630/KL730 與這個方向一致
  2. bridge.py 強制共用Python subprocess spawn 成本高driver 拆但 bridge 不拆會造成 driver-bridge 對應錯位
  3. KL520 vs KL720 的差異USB Boot vs flash-based、是否每次都要 load firmware、reset 策略) KL520 vs KL630 的差異可能還大——KL630/KL730 是新世代、可能跟 KL720 行為更接近(都是 flash-based
  4. 拆 driver 不會省工作量、反而 5 個 driver 共用邏輯時更難改

接受的取捨

  • kl720_driver.go 檔名包袱繼續存在(已是現狀、本 B 階段不改名)
  • 內部 switch 變多(但每個 chip 的差異邏輯應 < 50 行、可控)

4.3 具體擴的位置

server/internal/driver/kneron/kl720_driver.go

改動 1NewKneronDriver()L48-59chip 判斷加 KL630/KL730

// 偽碼、不出 code
chip := "KL520"
typ := strings.ToLower(info.Type)
switch {
case strings.Contains(typ, "kl720"):
    chip = "KL720"
case strings.Contains(typ, "kl730"):
    chip = "KL730"
case strings.Contains(typ, "kl630"):
    chip = "KL630"
case strings.Contains(typ, "kl530"):
    chip = "KL530"   // 視 SDK 支援度決定要不要加warrenchen 沒提)
}

改動 2Connect() / Flash() / RunInference() 內所有 if chip == "KL720" 的分支點要加 KL630/KL730 行為(待 SDK 文件驗證後填

改動 3:新增 helper 把 chip 行為抽出來:

// 偽碼
type chipBehavior struct {
    RequireFirmwareLoadEverySession bool    // KL520=true, KL720/KL630/KL730=待驗
    SupportedFlashUpdate            bool    // FW 升降版是否支援
    TimeoutMS                       int     // KL720=60000, KL520=10000, KL630/KL730 待驗
    InferenceConfig                 map[string]interface{}  // 各 chip inference 細部設定
}

func behaviorFor(chip string) chipBehavior { ... }

→ 把現有散在各 method 裡的 if chip == "KL720" 集中到一個地方、之後加新 chip 只動這個表(Refactor、不算 B 階段的 mandatory work、但建議順手做

server/internal/driver/kneron/detector.go

改動 1chipFromProductID()L97-111switch 加 case

// 偽碼
case "0x0530":
    return "KL530", "kneron_kl530"   // 視 SDK 支援
case "0x0630":
    return "KL630", "kneron_kl630"
case "0x0730":
    return "KL730", "kneron_kl730"

改動 2DetectDevices() chipCount map 自動 work不用改


5. 連線流程跟 KL520 / KL720 的差異(待 SDK 文件驗證

5.1 KL520 vs KL720 既有差異對照visionA-local 已實作)

維度 KL520 KL720
Hardware 模式 USB Boot無 flash Flash-based
connect 階段 firmware 載入 必載(每次都要載 fw_scpu + fw_ncpu 到 RAM 不載firmware 已預燒在 flash
例外KDP1 legacypid=0x0200 n/a 必走 connect_without_check + 載 KDP2 firmware 到 RAM
Timeout 10s 60s大 NEF 傳輸)
reset 策略 restartBridge 8s 重啟 bridge kp.core.reset_device(REBOOT)
Model load 後狀態 只能 load 一個 model、要換 model 必須 restart bridge 可自由 reload

5.2 KL630 / KL730 推測(待驗證

從 warrenchen _target_product_ids 把 KL720_LEGACY 和 KL720 並列、但 KL630/KL730 只有單一 product_id、推測

  • KL630/KL730 沒有 legacy/new 雙版本(不像 KL720 有 KDP1 → KDP2 升級需求)
  • 都是 flash-based跟 KL720 而非 KL520 一族)
  • connect 階段不需要 load firmware除非在 Loader mode

但這只是推測。SDK 文件 / 實機驗證才能確認。

5.3 風險:如果 KL630/KL730 行為跟 KL520 更像(每次都要 load firmware

.tar firmware 的 runtime load 流程就變得頻繁(每次 connect 都跑一次解壓 + load、可能影響 connect 速度。

緩解

  • build time 預先解壓 .tarfirmware/KL630/extracted/{fw_scpu.bin, fw_ncpu.bin, ...}、connect 時直接餵解壓後檔案
  • 細節在「.tar firmware 處理」研究檔41-tar-firmware-handling.md展開

6. bridge.py 擴展工作

6.1 改動清單

改動 程式碼位置 大小
chip 判斷加 KL630/KL730 case handle_connect() L732-741 +10 行
_resolve_firmware_paths(chip) 支援 .tar 格式 _resolve_firmware_paths() L157-172 +30 行(解壓邏輯)/ 或 +5 行(直接餵 .tar 給 SDK
handle_connect() 對 KL630/KL730 的 firmware 流程分支 新增 if 分支或重構成 dispatcher +30-50 行(待 SDK 驗證)
handle_firmware_upgrade() 對 KL630/KL730 的支援 A 階段已加、B 階段擴) +20 行
handle_firmware_downgrade()(新) 新 handler +60-100 行
handle_firmware_list_versions()(新) 新 handler、列出 bundled 多版本 +30 行

6.2 chip 判斷擴展(具體偽碼)

# kneron_bridge.py handle_connect() 偽碼
pid = target_dev.product_id
device_type_lower = device_type.lower()

if "kl720" in device_type_lower or pid in (0x0200, 0x0720):
    _device_chip = "KL720"
elif "kl730" in device_type_lower or pid == 0x0730:
    _device_chip = "KL730"
elif "kl630" in device_type_lower or pid == 0x0630:
    _device_chip = "KL630"
elif "kl530" in device_type_lower or pid == 0x0530:
    _device_chip = "KL530"   # 視 SDK 支援度
elif "kl520" in device_type_lower or pid == 0x0100:
    _device_chip = "KL520"
else:
    _device_chip = "KL520"   # fallback、保留既有行為

6.3 firmware path resolution 擴展

# _resolve_firmware_paths(chip) 偽碼
def _resolve_firmware_paths(chip="KL520"):
    base = os.path.dirname(os.path.abspath(__file__))
    fw_dir = os.path.join(base, "firmware", chip)
    
    # KL520 / KL720 走原本 .bin 路徑
    if chip in ("KL520", "KL720"):
        scpu = os.path.join(fw_dir, "fw_scpu.bin")
        ncpu = os.path.join(fw_dir, "fw_ncpu.bin")
        if os.path.exists(scpu) and os.path.exists(ncpu):
            return scpu, ncpu
        return None, None
    
    # KL630 / KL730 走 .tar 路徑
    if chip in ("KL630", "KL730"):
        # 策略一:直接餵 .tar 給 SDK如果 SDK 接受)
        fw_tar = os.path.join(fw_dir, "kp_firmware.tar")
        if os.path.exists(fw_tar):
            return fw_tar, None  # second arg None or different semantics、待驗
        
        # 策略二build time 已解壓、走解壓後路徑
        extracted_scpu = os.path.join(fw_dir, "extracted", "fw_scpu.bin")
        extracted_ncpu = os.path.join(fw_dir, "extracted", "fw_ncpu.bin")
        if os.path.exists(extracted_scpu) and os.path.exists(extracted_ncpu):
            return extracted_scpu, extracted_ncpu
        return None, None
    
    return None, None

注意:以上是兩種候選策略、實際選哪個取決於 SDK 文件——詳見 41-tar-firmware-handling.md


7. inference 流程的差異(待 SDK 文件驗證

7.1 既有 visionA-local inference 流程

kneron_bridge.pygrep generic_image_inference_send),既有用 kp.inference.generic_image_inference_send/receive API。這 API 是 KneronPLUS v2.x+ 的通用 inference API、理論上支援所有晶片。

7.2 推測KL630/KL730 inference 流程可能不變

理由:

  • generic_image_inference_* 設計成 chip-agnostic
  • 模型編譯時已 target 特定 chipYOLOv5_KL520_.nef vs YOLOv5_KL630_.nefdriver 只負責跑、不在 driver 端區分 chip

7.3 但有可能需要調整的點

  • Model NEF target_chip 對應:我們既有 _detect_model_type() 根據 NEF 內容判斷 model—— _model_nef.target_chip 屬性對 KL630/KL730 NEF 會回什麼字串、需要驗證
  • Input image format / preprocessingKL630/KL730 NPU 規格不同、input shape / channel order 可能要從 NEF 拿、generic_image_inference_send 的 input 配置可能需要調整

B1 階段必驗:跑一支 KL630 NEF從 SDK 範例拿)、看 inference 是否能直接 work。


8. 多版本 firmware 並存B2 階段才做)

8.1 為什麼需要

「手動降版面向一般使用者」場景下、使用者可能需要選 firmware 版本:

  • 「我目前裝的是 KL630 SDK-v2.5.7、但我的 model 是用 SDK-v2.4 編的、想降版回 v2.4」
  • 「KL520 KDP2 v2.2.0 跟某個 third-party tool 不相容、想降版回 v2.1.0」

但這場景對「一般使用者」而言很罕見——更常見的是「我拿到舊 KDP1 dongle 想升級」A 階段已涵蓋)。

8.2 儲存結構(建議)

server/scripts/firmware/
├── KL520/
│   ├── current/                    ← MVP 既有 firmwaresymlink 或實檔)
│   │   ├── fw_scpu.bin
│   │   ├── fw_ncpu.bin
│   │   ├── fw_loader.bin
│   │   └── VERSION                ← "2.2.0"
│   ├── v2.2.0/                    ← 多版本目錄
│   │   ├── fw_scpu.bin
│   │   ├── fw_ncpu.bin
│   │   ├── fw_loader.bin
│   │   └── VERSION
│   ├── v2.1.0/                    ← 舊版(如果 bundle
│   │   ├── fw_scpu.bin
│   │   ├── fw_ncpu.bin
│   │   └── VERSION
│   └── kdp1/                      ← KDP1 降版用B 階段)
│       ├── fw_scpu.bin
│       ├── fw_ncpu.bin
│       └── VERSION                ← "1.x.x" 或 "KDP1"
├── KL720/
│   └── ...同結構...
├── KL630/
│   └── ...同結構,但 firmware 檔是 .tar...
└── KL730/
    └── ...同結構...

對 MVPA 階段)的影響

  • A 階段只用 KL520/current/ KL720/current/、現有結構不需動
  • B 階段加多版本時、把現有檔搬進 current/ + 加 v2.2.0/ 副本(保留 backward compat、_resolve_firmware_paths fallback 機制)

8.3 版本選擇邏輯

  • 預設用 current/
  • 使用者主動選版本 → driver 傳 version 參數給 bridge.py
  • bridge.py 用 firmware/<chip>/<version>/... 解析路徑

→ 詳細 API + UI 設計在「手動降版」研究檔(42-manual-downgrade-for-end-users.md)。


9. 風險分析B 階段新增)

承前一份 R-FW-1 ~ R-FW-7、本份補

R-FW-8KneronPLUS SDK 對 KL630/KL730 API 不可預測(高度風險)

情境:我們現在對 KL630/KL730 的 SDK 行為都是推測、實際開發時可能:

  • load_firmware_from_file(.tar) 不接受、要先解壓
  • update_kdp_firmware_from_files() 對 KL630 沒效、要用別的 API
  • kp.ProductId.KP_DEVICE_KL730 在我們既有 wheel 版本不存在(要升級 wheel

緩解

  1. B0 milestone 第一件事:實機 + Python REPL 驗證所有 API、記錄到 41-tar-firmware-handling.md 後續更新
  2. 必要時升級 KneronPLUS wheel 版本(既有可能是 2.x、warrenchen 用 3.1.2
  3. wheel 版本升級會影響 KL520/KL720 既有行為regression 風險)、必須三平台回歸驗

R-FW-9.tar 解包對安裝包大小衝擊(低度風險)

情境:如果採 build time extract、安裝包同時包 .tar + 解壓後的 .bin、大小翻倍。

緩解

  • build script 解壓後刪原始 .tar只 ship .bin、安裝包多塞 .bin 但少塞 .tar、net 差約等於壓縮率
  • 估算KL630 .tar 預估 ~2-3MB、解壓後 .bin 約 ~3-4MB、淨增 +1MBacceptable

R-FW-10KL630/KL730 沒有 Loader mode 概念(中度風險)

情境:如果 KL630/KL730 是純 flash-based、永遠沒有 USB Boot Loader、那 kp_loader.tar 用不到、FW 升級流程也沒有「先 load loader 再 load firmware」這一段。

影響bridge.py handler 設計要分支、不能把 KL520/KL720 的「Loader 路徑」硬套到 KL630/KL730。

緩解B0 milestone 驗證時、用 kp.core.scan_devices() 看 KL630 firmware 字串可能值(是否會出現 "Loader" / "KDP" / "KDP2" / "KDP3" / 其他),決定 chip-specific 分支邏輯。

R-FW-11一般使用者誤觸降版 brick 風險高度風險B2

情境:手動降版面向一般使用者後、誤操作可能把 dongle 變磚。

緩解(你 architect 標記、實作由 design + frontend

  • UI 警告 + 二次確認design 領域)
  • driver 端做安全 guard
    • 拒絕降版到比 current 還新的版本(這是升版、不是降版、走升版 API
    • 拒絕跨晶片誤匹配KL520 firmware 拿來降 KL630
    • 降版前強制備份當前 firmware 版本資訊到 .autoflow/firmware-history.json(可選)

R-FW-12多版本管理 UX 複雜度中度風險B2

情境dropdown 多版本選擇對一般使用者過於 cluttered、容易誤選舊版。

緩解(標記給 design

  • 預設只顯示「升級到最新」+「降版」兩個按鈕
  • 「降版」展開後才顯示版本 dropdown
  • 不熟悉版號的使用者預設選最新降版目標("v2.1.0 latest before 2.2.0"

10. B 階段 milestone 拆法

承 A 階段 M9-1 ~ M9-5、B 階段拆 M9-6 ~ M9-12

整體依賴圖

M9-5 (A 階段三平台驗證) ✅
   ↓
M9-6 (research validation): SDK 文件 + 實機驗證 KL630/KL730 API 行為
   ↓
M9-7 (B0: 認 chip): driver + bridge.py 認 KL630/KL730、scan 顯示完整、connect 暫不支援
   ↓
M9-8 (B1.1: tar handling): bridge.py 處理 .tar firmware解壓策略選定 + 實作)
   ↓
M9-9 (B1.2: connect): bridge.py + driver 完整支援 KL630/KL730 connect + inference
   ↓
M9-10 (B1.3: FW 升版): firmware_upgrade handler 擴 KL630/KL730 支援
   ↓ ─────────────┐
M9-11 (B2.1: 多版本) M9-12 (B2.2: 降版 UI + UX)
   ↓                    ↓
M9-13 (三平台實機驗證所有 B 階段功能)

各 milestone 細節

M9-6 — SDK 驗證 + KneronPLUS wheel 升級評估1 人天)

負責Architect Agent純研究、不寫產品 code

任務

  1. 取得 KneronPLUS SDK 官方文件PDF 或網頁),查 KL630/KL730 章節
  2. unzip KneronPLUS-3.1.2-py3-none-any.whl、讀 kp/core.py / kp/__init__.py
  3. 在實機(使用者有 KL630/KL730 dongle 的話)跑 Python REPL
    import kp
    descs = kp.core.scan_devices()
    for i in range(descs.device_descriptor_number):
        dev = descs.device_descriptor_list[i]
        print(dev.product_id, dev.firmware, dev.is_connectable, dev.usb_port_id)
    # 試 connect、load .tar firmware、看回傳
    
  4. 評估是否要升級 KneronPLUS wheel從目前版本 → 3.1.2+ 回歸風險
  5. 產出 41-tar-firmware-handling.md 「實測結果」段落填空

驗收:研究文件補齊、確認所有 §3.2 未確認項目都有答案

M9-7 — B0 認 chip0.5 人天)

負責Backend Agent

任務

  1. kneron_bridge.py chip 判斷加 KL630/KL730 case§6.2 偽碼)
  2. detector.go:chipFromProductID() 加 case§4.3 偽碼)
  3. kl720_driver.go:NewKneronDriver() 加 chip 判斷§4.3 偽碼)
  4. handle_connect() 對 KL630/KL730 暫時回 {"error": "KL630/KL730 not yet fully supported, only scan info available"}、不假裝能連
  5. Frontend Devices 頁面、未支援 chip 顯示 disabled 狀態 + tooltip「即將支援」

驗收

  • scan 看得到 KL630/KL730 dongle 名字、不會誤標成 KL520
  • 按 connect 會看到友善訊息(非靜默失敗)

M9-8 — B1.1 .tar firmware handling1.5 人天)

負責Backend Agent

任務

  1. 從 warrenchen 複製 firmware/KL630/{kp_firmware.tar, kp_loader.tar} firmware/KL730/{kp_firmware.tar, kp_loader.tar}server/scripts/firmware/
  2. firmware/KL630/VERSION firmware/KL730/VERSION
  3. _resolve_firmware_paths() 擴展支援 .tar§6.3 偽碼)
  4. 決定解壓策略runtime / build time根據 M9-6 SDK 驗證結果決定
    • 如果 SDK 接受 .tar → 直接餵
    • 如果不接受 → build time 解壓進 extracted/、安裝包 ship 解壓後 .bin
  5. 如果走 build time 解壓:擴 installer/ 的 build script、確保解壓步驟跑在 wails build 之前
  6. 加 unit testmock_resolve_firmware_paths("KL630") 回正確路徑

驗收

  • python3 kneron_bridge.py_resolve_firmware_paths("KL630") 回正確路徑(不接 device 也能測)
  • 安裝包大小變化在預估範圍(+3-5MB

M9-9 — B1.2 connect + inference2 人天)

負責Backend Agent

任務

  1. handle_connect() 對 KL630/KL730 的完整流程(連線 + 必要時載入 firmware
  2. driver Flash() / RunInference() 對 KL630/KL730 的 chip-specific 邏輯(從 M9-6 研究結果得出)
  3. 建立 chipBehavior 抽象§4.3 改動 3、可選 refactor
  4. Frontend Devices 頁面、KL630/KL730 解除 disabled、能正常 connect + 跑 inference
  5. 三平台 smoke testKL630/KL730 各跑一支 sample NEF

驗收

  • KL630/KL730 dongle 能 connect + 跑 inference 成功
  • 既有 KL520/KL720 沒有 regression

M9-10 — B1.3 FW 升版擴 KL630/KL7301 人天)

負責Backend Agent

任務

  1. handle_firmware_upgrade() 加 KL630/KL730 路徑A 階段的 handler 擴展)
  2. 升版 stage 邏輯依照 M9-6 結論調整(如 KL630/KL730 沒有 "KDP" legacy state、就跳過 loader 那段)
  3. Frontend FW badge 對 KL630/KL730 顯示對應狀態
  4. 三平台實機驗證升版

驗收

  • KL630/KL730 dongle如果有比 bundled 還舊的 firmware能升級成功

M9-11 — B2.1 多版本 firmware 並存1.5 人天)

負責Backend Agent

任務

  1. 重整 firmware/ 目錄結構為 §8.2 設計(每 chip 多版本 + current/ symlink/副本)
  2. bridge.py handle_firmware_list_versions(chip) 新 handler、列出可選版本
  3. bridge.py handle_firmware_upgrade / handle_firmware_downgrade 接受 version 參數
  4. driver 與 service 層擴展傳 version 參數
  5. API endpoint GET /api/devices/:id/firmware/versions
  6. Bundle 多版本(每 chip 至少 1 個非 current 的版本可選、實際版本由使用者決定 ship 哪些)

驗收

  • API GET /firmware/versions?chip=KL520 回傳 ["v2.2.0", "v2.1.0", "kdp1"]
  • 升版 / 降版 API 接受 version 參數能正確 route

M9-12 — B2.2 降版 UI面向一般使用者2 人天)

負責Frontend Agent + Design Agent設計部分

任務

  1. Design Agent 補 Settings > 韌體面板的 wireframe + 警告語 + 二次確認 UX不是 architect 範圍)
  2. Frontend 實作 Settings 韌體面板:
    • 顯示當前 FW 版本
    • 「升級到最新」按鈕
    • 「降版」按鈕 → 展開版本 dropdown + 警告語 + 二次確認 modal
  3. 警告語內容(給 design 補強):
    • 「降版可能導致現有 model 無法運作」
    • 「降版過程不可中斷、否則裝置可能損壞」
    • 「請確認版本相容性」
  4. 二次確認 modal
    • 需要使用者輸入「DOWNGRADE」字串防誤觸
    • 顯示降版預估時間 + before/after 版本對照
  5. i18n 新增 keys中英雙語

驗收

  • 一般使用者打開 Settings 能看到韌體管理面板
  • 誤觸降版按鈕後、二次確認 modal 出現、不容易誤過
  • 降版完成後 UI 正確顯示新版本

M9-13 — B 階段完整三平台實機驗證1 人天)

負責Testing Agent

任務

  • 三平台 × {KL520, KL720, KL630, KL730} × {scan, connect, inference, firmware upgrade, firmware downgrade} = 60 個 smoke test cells
  • 重點異常路徑:降版中拔 device、降版 timeout、跨晶片 firmware mismatch、版本回滾

驗收

  • 所有 cells PASS或標註 N/A 且解釋原因)
  • 無 regression

工時合計

Milestone 工時 累積
M9-6 1 1
M9-7 0.5 1.5
M9-8 1.5 3
M9-9 2 5
M9-10 1 6
M9-11 1.5 7.5
M9-12 2 9.5
M9-13 1 10.5

B 階段合計 ~10.5 人天、加 A 階段 5 人天 = 完整版總計 ~15.5 人天(比原預估 11-12 略多、主因是「面向一般使用者」這個範圍升級加上 SDK 驗證工時)

Reviewer 切點

  • M9-6 純研究、Architect 自身產出、不過 Reviewer
  • M9-7 ~ M9-12 每個 milestone 結束過 Reviewerprogram code review + 設計合規)
  • M9-13 testing report 過 Reviewer

平行性

  • M9-6 必須先做(其他所有 milestone 都依賴研究結論)
  • M9-7 ~ M9-10 序列(每個依賴前一個)
  • M9-11 ~ M9-12 可平行(多版本後端 + 降版 UI 設計可同時做)
  • M9-13 在所有 milestone 之後

11. 給 Orchestrator 的下一步建議

  1. A 階段 MVP 先做(不變)——M9-1 ~ M9-5、5 人天
  2. A 階段驗證後(或同時平行)啟動 M9-6——SDK 驗證、純研究、Architect 自己跑
  3. M9-6 結論回填41-tar-firmware-handling.md + 更新本檔 §5.2 / §7.1 / R-FW-8
  4. B 階段是否要全做、依 M9-6 結果決定——如果 SDK 不支援、可能要先升級 wheel、回歸成本變高、可重新 scope
  5. B2 降版 UX 由 Design Agent 主導——本研究只標需求、不做 UI 設計

12. 與 A 階段研究的相依關係

項目 A 階段(既定) B 階段(本研究)
範圍 KL520 + KL720、自動升級 KDP1→KDP2 + KL630 + KL730、+ 手動降版面向一般使用者、+ 多版本
bridge.py handler handle_firmware_upgrade + handle_firmware_downgrade + handle_firmware_list_versions + chip 判斷擴展 + .tar 處理
Go driver method UpgradeFirmware() + DowngradeFirmware(version) + ListFirmwareVersions()
API endpoint POST /firmware/upgrade + POST /firmware/downgrade + GET /firmware/versions
Frontend UI Devices 頁 FW badge + 升級 modal + Settings 韌體面板(含降版 + 多版本選擇)
安裝包大小衝擊 +0KB +5MB詳細估算見 42-manual-downgrade-for-end-users.md
工時 5 人天 10.5 人天
Reviewer 切點 M9-1 ~ M9-5 各一輪 M9-7 ~ M9-12 各一輪 + M9-13 testing
風險新增 R-FW-1 ~ R-FW-7 + R-FW-8 ~ R-FW-12