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 上で、エンジニアは次の順にステップを配置します:
Diagnostic Session Control (0x03)Diagnostic Session Control (0x02)Security Access (0x27 level=01)— このステップがcompute_key_from_dllを自動呼出Routine Control (0x31 startRoutine, RID=Pre-programming)Routine Control (0x31 startRoutine, RID=EraseMemory, params=addr|size)Download Step— .hex/.vbf を読込、書込ブロックを選択、ツールが0x34 → 0x36×N → 0x37に展開Routine Control (0x31 startRoutine, RID=CheckProgrammingDependencies)Routine Control (0x31 startRoutine, RID=CRCCheck)ECU Reset (0x11 01)
各ステップは UI に idle / running / success / error を表示し、実行ループは中断可能です。シーケンス全体は .udss として保存でき、EOL 治具で同じ流れを再生できます。
よくある NRC
何かが失敗すると bootloader は 7F <SID> <NRC> を返します。よく出会うものは:
| NRC | 名称 | 主な発生場面 |
|---|---|---|
| 0x10 | generalReject | サービス組合せ不正 |
| 0x11 | serviceNotSupported | ECU 未実装の SID をツールが送った |
| 0x12 | subFunctionNotSupported | session や routine の subfunction が存在しない |
| 0x22 | conditionsNotCorrect | programming session に入らずに 0x34 を送った |
| 0x33 | securityAccessDenied | DLL の key 計算ミス、または level 不一致 |
| 0x35 | invalidKey | seed → key の計算結果が誤り |
| 0x37 | requiredTimeDelayNotExpired | 連続誤 key 後の遅延時間未達 |
| 0x71 | transferDataSuspended | 直前の 0x36 が完全受信されていない |
| 0x72 | generalProgrammingFailure | flash 書込ハードウェアエラー |
| 0x78 | requestCorrectlyReceivedResponsePending | 長処理(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 を共有できます。