ROMを書き換えたい

初めまして。

RX621でRX用シンプルフラッシュAPI を使ってROMのプログラムを書き換える勉強中です。

最終的には外部と通信して更新データを取得して更新したいんですが、通信が難しそうなのでとりあえず実行中のプログラムAを0xFFFF0000~0xFFFFBFFFに、更新用のプログラムBを0xFFFE0000~0xFFFEBFFFにいれてAを実行中にAを削除してBを0xFFFF0000~0xFFFFBFFFにコピーしてWDTでリセットするとBで動作しました。

しかし、この時にAとBでプログラムの大きさが違うとリセット後PCが0x00000000に飛んでしまいます。

恐らく大きさが変わる過程で関数の開始アドレスがずれるから正常に動作できなくなっていると思いますが、解決方法がわかりません。

上記考察は実際に関数内のa = a+1をa = a+2にするなど大きさが変わらないような変化だと正常に動作し、__nop()を追加しただけで動作しなくなったので間違いないと思っています。

書き換え前後のプログラムは上記の通りROMにありますが、フラッシュAPI等書き換えに関するプログラムはRAMで動作して書き換えはしません。

ヒントが少なくて恐縮ですが、何か思い当たる点や試してみるべきことなどあれば教えてください。

  • まあ普通に考えて割り込み関数のメモリ上配置アドレスが変わっているのに割り込みベクタを書き換えていないんでしょ。一番ありそうなのは FFFFFFFC 番地にあるリセットエントリの関数アドレスが変わったのに FFFFFFFC 付近の例外ベクタがそのままであるパターン。両者のマップファイルを見て確認してみましょう。

  • cacao99さんありがとうございます。

    確認したところ、FFFFFFFCのリセットは同じですが、それ以外の例外ベクタの値がすべて違いました。

    これを解決するには固定ベクタテーブルを含む領域も書き換えたら良いんでしょうか?

  • まあそうなるのですが、ということは全ROMを書き換えるのが妥当か否かという判断が必要になります。なんの工夫もせずに全領域消去+全領域再書き換えを行ってしまうと、客先で書き換え中に電源が落とされちゃうと二度とセルフ書き換えできなくなります。ウチではプログラムっつかROMを2分割して、「普通には消去も書き換えもしない領域」と「機器の制御プログラム本体部分」になるように運用しています。前者=リセットベクタを含む領域を選び、単体で動作する通信+書き換え機能を実装して、開発初期段階でしっかりデバッグし、通常の更新ではここの内容が1バイトたりとも変化しないようにします。当然、普通の書き換え時にはここを消去しません。単体で動作するように作ってあるので、再度の書き換え動作ができるわけです。そのためには、後者=CRCなどで書き込み結果の妥当性をチェックできるようにしておかなければなりません。

  • ファーム更新がしたいんですよね?

    ルネサスが用意したドキュメントは読まれましたか?
    www.renesas.com/.../renesas-mcu-firmware-update-design-policy-rev100

  • > 最終的には外部と通信して更新データを取得して更新したいんですが、
    データだけをスゲ替えるならそこだけを書き換えることを考えた方が良いのでは?
    データフラッシュのあるデバイスならデータフラッシュを使っても良いでしょうし、
    main関数以降のアプリケーション部も一緒に書き換えるなら開始番地だけ固定にして呼んでもらっても良いし。

    CC-RXであれば *.fsy ファイルを介して分割したプログラム間で外部参照をさせることができます。
    (参考) ブート領域、フラッシュ領域の分割方法
    https://www.renesas.com/jp/ja/document/mat/rx-family-cc-compiler-package-cc-rx-how-divide-boot-and-flash-areas-rev100
    ※ 記述や設定内容でなく、何をやっているかに着目して読んでみてください。この例ではブート部とアプリケーション部は完全に別のプログラムですが、ドライバもブート側に持たせて最上位層だけを分離するなど、応用は可能です。
    ※ ダイナミックリンクではないのでリンクする相手のアドレスが変わったら変わった後の *.fsy ファイルで再リンクが必要です。

    他にもこのフォーラムで "RX フラッシュ ブートローダ" とか検索すると色々出て来ます。

    まずは自分がやりたい事と実現手段が一致しているかから考える必要があるかと思います。

  • Shoji Yamamotoさんありがとうございます。

    >ファーム更新がしたいんですよね?

    はい。実行中にプログラムを書き換えて違う動作をしたいです。

    >ルネサスが用意したドキュメントは読まれましたか?

    この資料は読みました。ただ、この資料は途中に問題が発生した場合なども考慮したものですよね。

    将来外部との通信を実装する頃にはそういった点も考慮したものを作りたいですが、それ以前に違うプログラムを書き込むと正常に動作しない問題が発生しています。

    この資料では更新前のプログラムを残したまま更新後のプログラムを別領域に用意し、更新前後のプログラムを入れ替えていますが、ひとまず書き換え途中の障害は考慮せずやってみようと思っています。

  • ほやさんありがとうございます。

    データだけをスゲ替えるならそこだけを書き換えることを考えた方が良いのでは?

    更新データというより更新プログラムといった方が良かったですね。最終的にはアップデートするプログラムを外部から取得したいですが、今はそれ以前のところで止まってます。

    >まずは自分がやりたい事と実現手段が一致しているかから考える必要があるかと思います。

    やりたいことは実行中にプログラムを書き換えたいです。将来的にはそのプログラムを通信で取得したいんですが難しそうなのであらかじめROMの別領域に書き込んでおいてそれに書き換えたいです。

    その手段として書き換えるプログラムのセクションを指定して、そのセクションだけを上書きしようとしていますが、書き換えたセクション以外の領域(例外ベクタ等)も変わっている所があり、正常に動作しない問題が発生しています。

    そもそもの話ですが、書き換えるデータってどうやって用意するんでしょうか?

    今の以下の方法でやってます。

    1. 更新前の動作をするプロジェクトの一部を変更したプロジェクトをビルド&デバッグ・ツールへダウンロードしてメモリパネルから0xFFFF0000~0xFFFFBFFF番地のデータをコピーする

    2. コピーしたデータをconst char data[]={0x00,0x00…0x00}という要素数49152個の配列にして#pragma section Cを書いたcファイルにする

    0xFFFF0000~0xFFFFBFFF番地を書き換えるから0xFFFF0000~0xFFFFBFFF番地をコピペしたわけですがこれだと当然他の範囲は対応できませんよね。

    書き換える方法は検索して色々情報はありますが、書き換えるデータを用意する方法はよくわかってません。

  • > 書き換えるデータってどうやって用意するんでしょうか?
    ブートローダサンプルプログラムの中でXモデムがどうのと言っている辺りがそのイメージに近いかも?

    https://www.google.com/search?q=site:renesas.com+%22%EF%BC%B8%E3%83%A2%E3%83%87%E3%83%A0%22+%22RX%22

    通信で拾って来るブロック毎にフラッシュに書き込むような動作にするのが一般的だと思います。