KopherBit
診断通信

UDS リフラッシュシーケンス:0x10 → 0x27 → 0x31 → 0x34 / 0x36 / 0x37 完全ガイド

UDS (ISO 14229) リフラッシュの完全シーケンスを段階的に解説。session 切替、Security Access、erase routine、RequestDownload / TransferData / RequestTransferExit、ECU Reset、よくある NRC を網羅。

なぜリフラッシュにはこの完全シーケンスが必要か

ECU リフラッシュは「バイナリを flash に書く」だけでは済みません。bootloader は、相手が正規ツールであること、アプリが安全に停止していること、flash が消去済みであること、書き込みアドレス範囲とサイズが正しく合意されていること、各 payload チャンクが順序通り到着すること、そして最終イメージが CRC で検証されることを確認しなければなりません。UDS (ISO 14229) はこの一連を service ID として標準化し、ISO-TP (ISO 15765-2) の上で動作させることで、OEM やツール、ECU を超えた相互運用を可能にします。本稿では KITE Reflasher が実機上で送信する順序、すなわち 0x10 → 0x27 → 0x31 → 0x34 / 0x36 / 0x37 → 0x31 → 0x11 を一段ずつ分解します。

0x10 Diagnostic Session Control:default から programming へ

最初のステップはフラッシュ権限を持つ session への切替です。一般には extended diagnostic session (0x03) で前準備(非診断通信の停止など)を行い、続いて programming session (0x02) に移行して bootloader 側を起動させます。

# extended session に入る
Tx: 02 10 03 00 00 00 00 00
Rx: 06 50 03 00 32 01 F4 00      ; P2=50ms, P2*=5000ms

# programming session に入る
Tx: 02 10 02 00 00 00 00 00
Rx: 06 50 02 00 32 01 F4 00

切替後、KITE Reflasher は周期的に TesterPresent (0x3E) を送り P2 timeout を防ぎます。

0x27 Security Access:seed → DLL → key

programming session に入っても、Security Access が成功するまで bootloader は書込要求を一切受け付けません。手順は、ツールが seed を要求 (0x27 01) → 取得した seed を外部 DLL に渡して key を計算 → 0x27 02 で key を送信、です。

# seed 要求
Tx: 02 27 01 00 00 00 00 00
Rx: 06 67 01 11 22 33 44 00      ; seed = 11 22 33 44

# (ホスト) compute_key_from_dll(seed) → key
# key 送信
Tx: 06 27 02 AA BB CC DD 00
Rx: 02 67 02 00 00 00 00 00

KITE Reflasher は libloading で DLL をロードし、KopherBit の computeKeyFromSeed と Vector CANoe 互換の GenerateKeyEx の両方の export 規約に対応します。多くの OEM はどちらか一方を提供します。

0x31 Routine Control:pre-programming、erase、check、CRC

0x31 は 1 つの SID で多用途。bootloader は通常、4〜6 個の routine identifier (RID) を登録します:

  • Pre-programming — アプリに停止 / NV 保存の機会を与える。
  • Dependency check — ハードウェアと bootloader バージョンの整合性確認。
  • Erase memory — ツールが起点アドレスと長さを指定して消去。
  • Check programming dependencies — 書込み後のイメージ整合性チェック。
  • CRC check — ダウンロード済みセグメントの CRC 比較。

erase の例(subfunction 01 = startRoutine、RID FF 00、parameters address[4] | size[4]):

Tx: 0F 31 01 FF 00 00 02 00 00 00 00 80 00 00 00 00 00
Rx: 04 71 01 FF 00 00 00 00      ; routine 完了、結果データなし

erase は時間がかかるため、bootloader は 0x78 (RequestCorrectlyReceived-ResponsePending) で時間稼ぎをすることがあります。ツールは諦めず、最終 positive response を待ち続けなければなりません。

0x34 RequestDownload:アドレス、サイズ、format identifier

各セグメントのダウンロードは 0x34 で合意します。中身は:

  • dataFormatIdentifier — 上位 nibble = 圧縮、下位 nibble = 暗号化(通常 0x00)。
  • addressAndLengthFormatIdentifier — 上位 nibble = size の長さ、下位 nibble = address の長さ。0x44(4 + 4 bytes)が一般的。
  • memoryAddress(4 bytes)+ memorySize(4 bytes)。
# 0x00020000 から 0x00010000 bytes を書き込み
Tx: 0B 34 00 44 00 02 00 00 00 01 00 00
Rx: 04 74 20 04 02 00            ; lengthFormatId=0x20, maxBlockLen=0x0402

応答内の maxNumberOfBlockLength は、次フェーズ TransferData の 1 チャンク最大サイズ(SID と BSC を含む)を示します。KITE Reflasher はこれを上限として payload を分割します。

0x36 TransferData:block sequence counter とチャンキング

各チャンクは 0x36 で送出し、SID 直後の 1 byte が block sequence counter (BSC) です。01 から始まり、チャンクごとに +1、オーバーフロー時 00 に戻ります。

Tx: ... 36 01  <payload bytes>
Rx: 02 76 01 00

Tx: ... 36 02  <payload bytes>
Rx: 02 76 02 00
...

ECU が予期しない BSC を返した場合、チャンクが落ちた合図 — 元の BSC で再送します。1 チャンクは ISO-TP の CF (Consecutive Frame) に自然分割され、ツール側 ISO-TP 層が flow control(stmin / BS)を自動処理します。

0x37 RequestTransferExit:本セグメント終了(CRC 付き可)

セグメントの payload を全て送り終えたら、0x37 で終了します。0x37 には任意の transferRequestParameter を付けられ、ツール側で計算した CRC-32 を渡して bootloader 側で整合性検証させるのが定石です。

# パラメータなし
Tx: 01 37 00 00 00 00 00 00
Rx: 01 77 00 00 00 00 00 00

# CRC-32 付き
Tx: 05 37 12 34 56 78 00 00 00
Rx: 01 77 00 00 00 00 00 00

複数 sector を書く場合は 0x34 に戻り再合意します。

0x11 ECU Reset:新ファームウェアを適用

全セグメント書込みと最終 0x31 CRC + dependency check を通過後、0x11 01 で hard reset を発行します。bootloader はアプリへ制御を返し、リフラッシュ完了です。

Tx: 02 11 01 00 00 00 00 00
Rx: 02 51 01 00 00 00 00 00

このシーケンスにおける HEX / S-record / VBF の役割

ツール側が手にする HEX、S-record、VBF は「線形バイナリ 1 本」ではなく、アドレス付きブロックのリスト(records)です。0x34 フェーズに入る前、KITE Reflasher は memory-aware merge を行います:

  • HEX の ULBA + データレコード、S-record の S1/S2/S3、VBF の erase regions + data blocks を解析。
  • ユーザが MemoryConfigPanel で設定した sector layout に従い、連続するレコードを sector 単位に統合。
  • 隙間は 0xFF でパディング(Pad フラグ)し、ダウンロード段階での過剰分割を回避。
  • 統合後のバイナリは Intel HEX としても書き出され、audit artifact になる。

結果として、各 0x34 は統合済みの 1 sector に対応し、0x36 はその sector の bytes を maxBlockLen で切り出して送るだけです。

KITE Reflasher のシーケンサーでの組み立て方

UdsSequencerPanel 上で、エンジニアは次の順にステップを配置します:

  1. Diagnostic Session Control (0x03)
  2. Diagnostic Session Control (0x02)
  3. Security Access (0x27 level=01) — このステップが compute_key_from_dll を自動呼出
  4. Routine Control (0x31 startRoutine, RID=Pre-programming)
  5. Routine Control (0x31 startRoutine, RID=EraseMemory, params=addr|size)
  6. Download Step — .hex/.vbf を読込、書込ブロックを選択、ツールが 0x34 → 0x36×N → 0x37 に展開
  7. Routine Control (0x31 startRoutine, RID=CheckProgrammingDependencies)
  8. Routine Control (0x31 startRoutine, RID=CRCCheck)
  9. ECU Reset (0x11 01)

各ステップは UI に idle / running / success / error を表示し、実行ループは中断可能です。シーケンス全体は .udss として保存でき、EOL 治具で同じ流れを再生できます。

よくある NRC

何かが失敗すると bootloader は 7F <SID> <NRC> を返します。よく出会うものは:

NRC名称主な発生場面
0x10generalRejectサービス組合せ不正
0x11serviceNotSupportedECU 未実装の SID をツールが送った
0x12subFunctionNotSupportedsession や routine の subfunction が存在しない
0x22conditionsNotCorrectprogramming session に入らずに 0x34 を送った
0x33securityAccessDeniedDLL の key 計算ミス、または level 不一致
0x35invalidKeyseed → key の計算結果が誤り
0x37requiredTimeDelayNotExpired連続誤 key 後の遅延時間未達
0x71transferDataSuspended直前の 0x36 が完全受信されていない
0x72generalProgrammingFailureflash 書込ハードウェアエラー
0x78requestCorrectlyReceivedResponsePending長処理(erase / CRC)中、ツールは待機継続

0x78 はエラーではなく「待ってください」の意。ツールは P2 タイマをリセットし、最終 positive response を待ち続けなければなりません。KITE Reflasher の ISO-TP 層がこれを自動処理し、シーケンス全体は中断されません。

まとめ

押さえるべきは個々の SID ではなく、ステップ間の依存関係です。0x10 なしで 0x27 へ進めない、0x27 なしで 0x31 へ進めない、erase routine なしで 0x34 を送ると NRC 0x22 で弾かれる。シーケンスを .udss と ASC として残せば、bring-up と量産検証が同じ ground truth を共有できます。