この解説は、Skyworks (Silicon Labs) Si47XX PROGRAMMING GUIDE AN332 を基に、Google AI (Gemini) の協力を得て作成しています。
1 パッチ構造概要
Si4735/32などのカスタムファームウェア(主にSSBパッチ)で使われるパッチファイル(一般的に.csg形式や、
それをC言語の配列に落とし込んだ形式)における、0x15(PATCH_ARGS)と0x16(PATCH_DATA)の領域構成について解説します。
結論から言うと、パッチファイルは「最初に数行の0x15(初期化・領域確保)が走り、その後に膨大な0x16(データ本体)の塊が続き、 セクションの節目や最後に再び0x15(インデックス更新や確定)が挟まる」という構成(サンドイッチ構造)を採っています。
以下に、標準的なSSBパッチファイル(約1,000〜1,000数行)の具体的な領域構成のパターンを解説します。
結論から言うと、パッチファイルは「最初に数行の0x15(初期化・領域確保)が走り、その後に膨大な0x16(データ本体)の塊が続き、 セクションの節目や最後に再び0x15(インデックス更新や確定)が挟まる」という構成(サンドイッチ構造)を採っています。
以下に、標準的なSSBパッチファイル(約1,000〜1,000数行)の具体的な領域構成のパターンを解説します。
2 パッチファイルの全体構造(タイムライン)
1行=8バイト(コマンド1バイト + 引数7バイト)のデータが並ぶ中、領域は大きく分けて4つのフェーズに分かれています。
[ファイル先頭]
│
├── ① 【導入・初期化領域】 (0x15 がメイン、数行〜十数行)
│ └─ デバイスへのパッチ適用宣言、書き込み開始アドレスの指定など
│
├── ② 【データ転送メイン領域】 (0x16 が連続、数百〜1000行以上)
│ └─ ファームウェアバイナリの本体。ひたすら0x16が続く
│
├── ③ 【セクション切り替え領域】 (0x15 が1〜2行 挟まる)
│ └─ 次のメモリブロックへのポインタ移動(ページ切り替え)
│
├── ④ 【データ転送再開領域】 (再び 0x16 が連続、数百行)
│
├── ⑤ 【確定・実行領域】 (最後に 0x15 が数行)
│ └─ 転送完了通知、チェックサム検証のトリガー、パッチの有効化
│
[ファイル末尾]
│
├── ① 【導入・初期化領域】 (0x15 がメイン、数行〜十数行)
│ └─ デバイスへのパッチ適用宣言、書き込み開始アドレスの指定など
│
├── ② 【データ転送メイン領域】 (0x16 が連続、数百〜1000行以上)
│ └─ ファームウェアバイナリの本体。ひたすら0x16が続く
│
├── ③ 【セクション切り替え領域】 (0x15 が1〜2行 挟まる)
│ └─ 次のメモリブロックへのポインタ移動(ページ切り替え)
│
├── ④ 【データ転送再開領域】 (再び 0x16 が連続、数百行)
│
├── ⑤ 【確定・実行領域】 (最後に 0x15 が数行)
│ └─ 転送完了通知、チェックサム検証のトリガー、パッチの有効化
│
[ファイル末尾]
3 各領域の具体的なコード実例(イメージ)
C言語の2次元配列(const uint8_t patch[][8])として表現した場合、ファイル内は以下のようにブレイクダウンされます。
① 導入・初期化領域(0x15 領域)
ファイルの冒頭は、デバイスに「これからパッチを書き込むぞ」と教えるための制御コマンドから始まります。
{ 0x15, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, // 0x15: 開始宣言と初期引数
{ 0x15, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00 }, // 0x15: アドレスポインタの設定
// ※ここまでは 0x16 は一切登場しません
{ 0x15, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00 }, // 0x15: アドレスポインタの設定
// ※ここまでは 0x16 は一切登場しません
② データ転送メイン領域(0x16 領域)
初期化が終わると、ここから地平線の彼方まで 0x16 の大群が続きます。全体の9割以上がこの領域です。
{ 0x16, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78 }, // 0x16: バイナリデータ1行目
{ 0x16, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33 }, // 0x16: バイナリデータ2行目
...
... (数千バイト分、ひたすら0x16の行が連続する)
...
{ 0x16, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }, // 0x16: このセクションの末尾
{ 0x16, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33 }, // 0x16: バイナリデータ2行目
...
... (数千バイト分、ひたすら0x16の行が連続する)
...
{ 0x16, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }, // 0x16: このセクションの末尾
③ セクション切り替え領域(0x15 がスポットで挟まる)
デバイス内部のRAM容量やページの境界に達すると、途中にぽつんと 0x15 が出現し、内部ポインタを更新します。
{ 0x15, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0x15: 「次のページへジャンプ」等の制御
④ データ転送再開領域(0x16 領域)
ポインタが切り替わったら、再び 0x16 の大群に戻ります。
{ 0x16, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32 }, // 0x16: 新しいセクションのデータ開始
...
...
⑤ 確定・実行領域(最終 0x15 領域)
パッチデータの末尾(最後の数行)は、必ず 0x15 で締めくくられます。
{ 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0x15: 転送終了フラグ
{ 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0x15: パッチの検証&内部展開トリガー
// これで全パッチ送信が完了!
{ 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0x15: パッチの検証&内部展開トリガー
// これで全パッチ送信が完了!
4【応用】メモリ圧縮における「領域の切り出し方」
マイコンの容量節約(圧縮パッチ)を検討されている場合、このファイルの構成を利用して以下のようにデータを分解(セグメント化)します。
メイン配列(データのみ):
先頭の 0x15 や 0x16 をすべて削り、後ろの7バイトの純粋なデータだけを並べた巨大な1次元(または2次元)配列を作ります。
インデックス配列(番地のみ)
「何行目が 0x15 だったか」という数値(行番号)だけをまとめた、非常に小さな配列をもう一つ作ります。
【送信時のロジック】
マイコンから送信する際、ループのカウンタ(行数)が「インデックス配列」に登録されている番号と一致したら先頭に 0x15 を、一致しなければ 0x16 を動的にくっつけて8バイトにして送信します。
これにより、パッチ配列全体のサイズを 12.5%(1/8)丸ごと削減 できます。
これにより、パッチ配列全体のサイズを 12.5%(1/8)丸ごと削減 できます。
