GR-SAKURA で遊んでみる(5) 内蔵 Flash ROM の書き換え

RX63N の内蔵 Flash ROM の内容を書き換える方法が気になったので、調べてみた。

概要

GR-SAKURA の MCU の型名は R5F563NBDDFP で、1 MiB の ROM を内蔵している*1。この ROM の使い方は、ハードウェア・マニュアルの「47.フラッシュメモリ」に記載されている。この章には結構な頁数が割かれているが、これは FCU が持つ多くの機能について説明しているためであり、

  • E2 データフラッシュへのプログラム/イレーズ
  • BGO(Back Ground Operation)
  • ロックビットによるプロテクト機能
  • サスペンド/レジューム
  • 割り込み

の機能を使用しないと割り切ってドライバを書く場合は、それほど多くを実装する必要はなさそうだ。というわけで、今回は ROM の書き込み/消去機能のみを提供するドライバを実装する。

想像だが、全機能をサポートするドライバを書こうとすると結構な量のコーディングが必要になるのではないだろうか...

内蔵 ROM の特徴

RX63N では、ROM/E2 データフラッシュに対する操作は、専用シーケンサである FCU に対してコマンドを発行することで行う。コマンド発行は、対象領域の先頭アドレスへの書き込み操作により行う。

これ自体は、NOR Flash でお馴染みの形式であるが、

  1. FCU 制御用コマンドが CFI(Common Flash Memory Interface) で定義されているコマンドとは異なる。
  2. FCU 制御用のレジスタがあり、コマンド発行とは別に、これらのレジスタの設定も必要になる。
  3. Flash の内容を読み出すためのアドレスと、コマンド発行先のアドレス(ハードウェアマニュアルでは「P/E 用アドレス」と呼んでいる)が異なる。
  4. FCU(ROM/E2 データフラッシュの書き換えを行う専用のシーケンサ)の状態遷移に従う必要がある。
  5. 事前に FCU のファームウェアを所定の領域に転送する必要がある。

といった違いがある。

3. に関して少し補足すると、例えば ROM に割り当てられている 0xfff80000 番地に何らかの内容を書き込みたい場合は、イレーズおよびプログラムを行うコマンドを 0x00ff8000 番地に対して発行する、ということである。

つまり、同じ領域でも
ROM 内容の読み出し時のアドレス
-> [31:24] の bit は 0xff
ROM へのプログラム/イレーズのコマンド発行先アドレス
-> [31:24] の bit は 0x00
のように、用途によってアクセスするアドレスを使い分ける。

  読み出し用アドレス                            P/E 用アドレス
                    +--------------------------+
      0xFFE0 0000   |                          | 0x00E0 0000
                    |                          |
                    |  領域 3:  512 KiB ※     |
                    |                          |
      0xFFE7 FFFF   |                          | 0x00E7 FFFF
                    +--------------------------+
      0xFFE8 0000   |                          | 0x00E8 0000
                    |                          |
                    |  領域 2:  512 KiB ※     |
                    |                          |
      0xFFEF FFFF   |                          | 0x00EF FFFF
                    +--------------------------+
      0xFFF0 0000   |                          | 0x00F0 0000
                    |                          |
                    |  領域 1:  512 KiB        |
                    |                          |
      0xFFF7 FFFF   |                          | 0x00F7 FFFF
                    +--------------------------+
      0xFFF8 0000   |                          | 0x00F8 0000
                    |                          |
                    |  領域 0:  512 KiB        |
                    |                          |
      0xFFFF FFFF   |                          | 0x00FF FFFF
                    +--------------------------+

  ※ GR-SAKURA の場合は、領域 2 および 3 は存在しない

FCU の状態遷移

FCU は上図の領域 0-3 の各領域毎に状態を持っており、プログラム/イレーズ操作は FCU の状態を意識した実装にする必要がある。

ハードウェアマニュアルによれば、FCU の状態遷移は

   +-----------------------------------------------------------------+
   |                      ROM リードモード                           |
   | +-------------------------------------------------------------+ |
   | |                                                             | |
   | | +----------------------+  (B)  +--------------------------+ | |
   | | |                      | ----> |                          | | |
   | | |  P/E ノーマルモード  |       |  ステータスリードモード  | | |
   | | |                      | <---- |                          | | |
   | | +----------------------+  (A)  +--------------------------+ | |
   | |  ^  |  ^                                         |  ^       | |
   | |  |  |  | (A)  +--------------------------+   (C) |  |       | |
   | |  |  |  +----- |                          | <-----+  |       | |
   | |  |  |         | ロックビットリードモード |          |       | |
   | |  |  +-------> |                          | ---------+       | |
   | |  |       (C)  +--------------------------+   (B)            | |
   | |  |                                                          | |
   | |  |           E2 データフラッシュ P/E モード                 | |
   | +--+--------------------------------------------+-------------+ |
   |    |                                            |               |
   |    | FENTRYR=0080h                              | FENTRYR=0000h |
   |    |                                            V               |
   |  +-+---------------------------------------------------------+  |
   |  |           ROM/E2 データフラッシュリードモード             |  |
   |  +-+---------------------------------------------------------+  |
   |    |                                            ^               |
   +----+--------------------------------------------+---------------+
        | FENTRYR=0001h、0002h、0004h、00008h        |
        |                                            |
     +--+--------------------------------------------+-------------+
     |  |                 ROM P/E モード                           |
     |  V                                                          |
     | +----------------------+  (B)  +--------------------------+ |
     | |                      | ----> |                          | |
     | |  P/E ノーマルモード  |       |  ステータスリードモード  | |
     | |                      | <---- |                          | |
     | +----------------------+  (A)  +--------------------------+ |
     |     |  ^                                         |  ^       |
     |     |  | (A)  +--------------------------+   (C) |  |       |
     |     |  +----- |                          | <-----+  |       |
     |     |         | ロックビットリードモード |          |       |
     |     +-------> |                          | ---------+       |
     |          (C)  +--------------------------+   (B)            |
     +-------------------------------------------------------------+

      (A) : ノーマルモード移行コマンドを実行
      (B) : ノーマルモード移行、ロックビットリードモード移行以外のコマンドを実行
      (C) : ロックビットリードモード移行コマンドを実行

となっている。

今回は、E2 データフラッシュへのプログラム/イレーズは行わないので、E2 データフラッシュ P/E モードには遷移させない。従って、上記の状態遷移図は

      +-----------------------------------------------------------+
      |           ROM/E2 データフラッシュリードモード             |
      +-----------------------------------------------------------+
        |                                            ^
        | FENTRYR=0001h、0002h、0004h、00008h        |
        |                                            |
     +--+--------------------------------------------+-------------+
     |  |                 ROM P/E モード                           |
     |  V                                                          |
     | +----------------------+  (B)  +--------------------------+ |
     | |                      | ----> |                          | |
     | |  P/E ノーマルモード  |       |  ステータスリードモード  | |
     | |                      | <---- |                          | |
     | +----------------------+  (A)  +--------------------------+ |
     |     |  ^                                         |  ^       |
     |     |  | (A)  +--------------------------+   (C) |  |       |
     |     |  +----- |                          | <-----+  |       |
     |     |         | ロックビットリードモード |          |       |
     |     +-------> |                          | ---------+       |
     |          (C)  +--------------------------+   (B)            |
     +-------------------------------------------------------------+

のように簡略化できる。

さらに、ロックビットによるプロテクト機能も使わないので、もう少し簡略化できて

      +-----------------------------------------------------------+
      |           ROM/E2 データフラッシュリードモード             |
      +-----------------------------------------------------------+
        |                                            ^
        | FENTRYR=0001h、0002h、0004h、00008h        |
        |                                            |
     +--+--------------------------------------------+-------------+
     |  |                 ROM P/E モード                           |
     |  V                                                          |
     | +----------------------+  (B)  +--------------------------+ |
     | |                      | ----> |                          | |
     | |  P/E ノーマルモード  |       |  ステータスリードモード  | |
     | |                      | <---- |                          | |
     | +----------------------+  (A)  +--------------------------+ |
     +-------------------------------------------------------------+

となる。これだけの状態しか考えなくてよいなら、実装もきっと楽。

各状態の簡単な説明

ROM/E2 データフラッシュリードモード
ROM/E2 データフラッシュの読み出し用アドレスから、書き込まれている内容を読み出せる状態。

P/E ノーマルモード
ROM/E2 データフラッシュリードモードの状態で、FENTRYR レジスタの値を 0x0001、0x0002、0x0004、0x0008 のいずれかにすると、この状態に遷移する。プログラム/イレーズのコマンドは、(通常は)この状態に遷移してから発行する。

ステータスリードモード
ROM の状態を読み出せるモードで、ステータスリードモード移行コマンドの発行(P/E 対象アドレスへの 0x70 の書き込み)により、この状態に遷移する。
または、コマンド発行後のレディ中もこのモードに遷移する(というより、明示的なステータスリードモード移行コマンド発行を必要とするケースが思い付かない)。この場合は、FSTATR0 レジスタを読み出して FRDY ビットが 1 になる(か、タイムアウトになる)まで待機する。
その他、ROM P/E モード時に ROM 内容を読み出そうとしたり、逆に ROM/E2 データフラッシュリードモード時にイレーズコマンドを発行する等、FCU の状態遷移を無視した暴挙を働いたり、コマンドを発行したもののエラーになってしまった場合も、ステータスリードモードに遷移する。この場合、FCU はコマンドを受け付けなくなるので、エラーをクリアして元の状態に戻す必要がある。

FENTRYR レジスタによる状態遷移の制御

上の状態遷移図からも分かるとおり、ROM/E2 データフラッシュリードモード <-> ROM P/E モード間の遷移は、Flash P/E モードエントリレジスタ(FENTRYR) の書き換えによって行う。

但し、領域 0 から 3 の内、同時に二つ以上が ROM P/E モードになることはできない。つまり、どれか一つが ROM P/E モードの間、他の 3 つは ROM/E2 データフラッシュリードモードの状態である。

4つの領域の内、どれを ROM P/E モードとするかは、FENTRYR レジスタの下位 4 bit により制御する。以下に FENTRYR レジスタの構成を示す。

bit シンボル 機能 R/W
0 FENTRYR0 0: 領域 0 は ROM リードモード
1: 領域 0 は ROM P/E モード
R/W
1 FENTRYR1 0: 領域 1 は ROM リードモード
1: 領域 1 は ROM P/E モード
R/W
2 FENTRYR2 0: 領域 2 は ROM リードモード
1: 領域 2 は ROM P/E モード
R/W
3 FENTRYR3 0: 領域 3 は ROM リードモード
1: 領域 3 は ROM P/E モード
R/W
4-6 - 予約 R/W
7 FENTRYRD 0: E2 データフラッシュはリードモード
1: E2 データフラッシュは P/E モード
R/W
8-15 FEKEY[7:0] FENTRYR レジスタの書き換える場合、
このフィールドには 0xaa を書く
R/W

GR-SAKURA の場合は、ROM が 1MiB、つまり領域 0 と 1 しかないので、実際には FENTRYR2 および FENTRYR3 に 1 を書くことはない。

プログラムの手順

ROM へのプログラムは、対象領域を P/E ノーマルモードに遷移後、以下の手順でコマンドを発行することにより行う。

  1. プログラム対象アドレスに 0xE8 を書き込む。
  2. プログラム対象アドレスに 0x40 を書き込む。
  3. プログラム対象アドレスに書き込みデータを順に 64 回 2bytes 単位で書き込む。
  4. プログラム/イレーズ用アドレスに 0xD0 を書き込む。
  5. FSTATR0.FRDY が 1 になるのを待機する。待機時間は、最大
    (FCLK = 4MHz の場合) tP128 × 1.1 = 28 × 1.1 = 30.8 ms
    (20MHz <= FCLK <= 50MHz の場合) tP128 × 1.1 = 10 × 1.1 = 11 ms
  6. エラー確認

上記の「プログラム対象アドレス」は、128 bytes 境界にアラインしたアドレスである(つまり、書き込みは 128 bytes 単位で区切られた小領域に対して行う)。書き込み対象外の箇所には 0xffff を書き込む。例えば、128 bytes 境界の箇所から 4 bytes 後に "Hello World!" の文字列だけを書き込みたければ、手順 3. にて書き込みデータを
0xffff -> 0xffff -> 0x6548 -> 0x6c6c -> 0x206f -> 0x6f57 -> 0x6c72 -> 0x2164 -> 0xffff -> ... -> 0xffff
の順にする。

なお、内蔵 ROM 上の各 128 bytes 領域は、一回の消去につき一回しか書けない(上書き不可)。既に書き込まれている内容に対して、同じ内容を上書きしようとした場合であろうが、0xffff を上書きしようとした場合であろうが、プログラム手順を実行した場合はプログラムエラーが発生し、コマンドロック状態に陥る。この場合、エラーは手順 6. の段階で検出される。

イレーズの手順

ROM のイレーズは、対象領域を P/E ノーマルモードに遷移後、以下の手順でコマンドを発行することにより行う。

  1. イレーズ対象アドレスに 0x20 を書き込む。
  2. イレーズ対象アドレスに 0xD0 を書き込む。
  3. FSTATR0.FRDY が 1 になるのを待機する。待機時間は、最大
    (FCLK = 4MHz の場合) tP128 × 1.1 = 28 × 1.1 = 30.8 ms
    (20MHz <= FCLK <= 50MHz の場合) tP128 × 1.1 = 10 × 1.1 = 11 ms
  4. エラー確認

上記のイレーズ対象アドレスは、128 bytes 境界にアラインしたものである必要はない。

ドライバの実装

内蔵 Flash ROM ドライバの実装例を以下に示す。

flash.c:

    1 #include "string.h"
    2 #include "io.h"
    3 
    4 #define MIN(a, b)       ((a) < (b) ? (a) : (b))
    5 
    6 #define T_FCUR  (35)
    7 #define T_PCKA  (120)
    8 #define T_P128  (33600)
    9 #define T_E16K  (576000)
   10 
   11 static const struct {
   12         uint32_t start_addr;
   13         uint32_t end_addr;
   14         uint32_t area_no;
   15 } block_info[] = {
   16          /* 4 KiB * 8 blocks */
   17         {       0xfffff000,     0xffffffff,     0       },
   18         {       0xffffe000,     0xffffefff,     0       },
   19         {       0xffffd000,     0xffffdfff,     0       },
   20         {       0xffffc000,     0xffffcfff,     0       },
   21         {       0xffffb000,     0xffffbfff,     0       },
   22         {       0xffffa000,     0xffffafff,     0       },
   23         {       0xffff9000,     0xffff9fff,     0       },
   24         {       0xffff8000,     0xffff8fff,     0       },
   25 
   26         /* 16 KiB * 22 blocks */
   27         {       0xffff4000,     0xffff7fff,     0       },
   28         {       0xffff0000,     0xffff3fff,     0       },
   29         {       0xfffec000,     0xfffeffff,     0       },
   30         {       0xfffe8000,     0xfffebfff,     0       },
   31         {       0xfffe4000,     0xfffe7fff,     0       },
   32         {       0xfffe0000,     0xfffe3fff,     0       },
   33         {       0xfffdc000,     0xfffdffff,     0       },
   34         {       0xfffd8000,     0xfffdbfff,     0       },
   35         {       0xfffd4000,     0xfffd7fff,     0       },
   36         {       0xfffd0000,     0xfffd3fff,     0       },
   37         {       0xfffcc000,     0xfffcffff,     0       },
   38         {       0xfffc8000,     0xfffcbfff,     0       },
   39         {       0xfffc4000,     0xfffc7fff,     0       },
   40         {       0xfffc0000,     0xfffc3fff,     0       },
   41         {       0xfffbc000,     0xfffbffff,     0       },
   42         {       0xfffb8000,     0xfffbbfff,     0       },
   43         {       0xfffb4000,     0xfffb7fff,     0       },
   44         {       0xfffb0000,     0xfffb3fff,     0       },
   45         {       0xfffac000,     0xfffaffff,     0       },
   46         {       0xfffa8000,     0xfffabfff,     0       },
   47         {       0xfffa4000,     0xfffa7fff,     0       },
   48         {       0xfffa0000,     0xfffa3fff,     0       },
   49 
   50         /* 16 KiB * 8 blocks */
   51         {       0xfff9c000,     0xfff9ffff,     0       },
   52         {       0xfff98000,     0xfff9bfff,     0       },
   53         {       0xfff94000,     0xfff97fff,     0       },
   54         {       0xfff90000,     0xfff93fff,     0       },
   55         {       0xfff8c000,     0xfff8ffff,     0       },
   56         {       0xfff88000,     0xfff8bfff,     0       },
   57         {       0xfff84000,     0xfff87fff,     0       },
   58         {       0xfff80000,     0xfff83fff,     0       },
   59 
   60          /* 32 KiB * 8 blocks */
   61         {       0xfff78000,     0xfff7ffff,     1       },
   62         {       0xfff70000,     0xfff77fff,     1       },
   63         {       0xfff68000,     0xfff6ffff,     1       },
   64         {       0xfff60000,     0xfff67fff,     1       },
   65         {       0xfff58000,     0xfff5ffff,     1       },
   66         {       0xfff50000,     0xfff57fff,     1       },
   67         {       0xfff48000,     0xfff4ffff,     1       },
   68         {       0xfff40000,     0xfff47fff,     1       },
   69 
  70          /* 32 KiB * 8 blocks */
   71         {       0xfff38000,     0xfff3ffff,     1       },
   72         {       0xfff30000,     0xfff37fff,     1       },
   73         {       0xfff28000,     0xfff2ffff,     1       },
   74         {       0xfff20000,     0xfff27fff,     1       },
   75         {       0xfff18000,     0xfff1ffff,     1       },
   76         {       0xfff10000,     0xfff17fff,     1       },
   77         {       0xfff08000,     0xfff0ffff,     1       },
   78         {       0xfff00000,     0xfff07fff,     1       },
   79 
   80 #ifdef  NEVER   /* R5F563NBDDFP does NOT have following blocks */
   81          /* 64 KiB * 8 blocks */
   82         {       0xffef0000,     0xffefffff,     2       },
   83         {       0xffee0000,     0xffeeffff,     2       },
   84         {       0xffed0000,     0xffedffff,     2       },
   85         {       0xffec0000,     0xffecffff,     2       },
   86         {       0xffeb0000,     0xffebffff,     2       },
   87         {       0xffea0000,     0xffeaffff,     2       },
   88         {       0xffe90000,     0xffe9ffff,     2       },
   89         {       0xffe80000,     0xffe8ffff,     2       },
   90 
   91          /* 64 KiB * 8 blocks */
   92         {       0xffe70000,     0xffe7ffff,     3       },
   93         {       0xffe60000,     0xffe6ffff,     3       },
   94         {       0xffe50000,     0xffe5ffff,     3       },
   95         {       0xffe40000,     0xffe4ffff,     3       },
   96         {       0xffe30000,     0xffe3ffff,     3       },
   97         {       0xffe20000,     0xffe2ffff,     3       },
   98         {       0xffe10000,     0xffe1ffff,     3       },
   99         {       0xffe00000,     0xffe0ffff,     3       }
  100 #endif  /* NEVER */
  101 };
  102 
  103 int
  104 flash_get_block_info(uint32_t addr, uint32_t *start, uint32_t *end,
  105                 size_t *area_no, size_t *block_no)
  106 {
  107         size_t i;
  108 
  109         for (i = 0; i < sizeof(block_info) / sizeof(block_info[0]); i++)
  110                 if (addr >= block_info[i].start_addr
  111                         && addr <= block_info[i].end_addr) {
  112                         if (start != NULL)
  113                                 *start = block_info[i].start_addr;
  114                         if (end != NULL)
  115                                 *end = block_info[i].end_addr;
  116                         if (area_no != NULL)
  117                                 *area_no = block_info[i].area_no;
  118                         if (block_no != NULL)
  119                                 *block_no = i;
  120 
  121                         return 0;
  122                 }
  123 
  124         return -1;      /* out of ROM area */
  125 }
  126 
  127 static void
  128 reset_fcu(void)
  129 {
  130         /* forcibly terminate programming/erasure operations */
  131         writew(FRESETR_ADDR, 0xcc01);
  132 
  133         /* start CMT1 */
  134         writew(CMT1_CMCR_ADDR, 0x0040);
  135         writew(CMT1_CMCOR_ADDR, T_FCUR * (PCLK / 8) / 1000000);
  136         writew(CMT1_CMCNT_ADDR, 0x00000);
  137         writew(CMSTR0_ADDR, readw(CMSTR0_ADDR) | 0x0002);
  138 
  139         while (!readb(IR29_ADDR)) ;
  140 
  141         /* stop CMT1 */
  142         writew(CMSTR0_ADDR, readw(CMSTR0_ADDR) & ~0x0002);
  143         writeb(IR29_ADDR, 0x00);
  144 
  145         /* release reset state */
  146         writew(FRESETR_ADDR, 0xcc00);
  147 
  148 #ifdef  DEBUG
  149         /* turn on user LED(D1) */
  150         writeb(PORTA_PODR_ADDR, readb(PORTA_PODR_ADDR) | 0x01);
  151         writeb(PORTA_PDR_ADDR, readb(PORTA_PDR_ADDR) | 0x01);
  152 #endif  /* DEBUG */
  153 }
  154 
  155 static int
  156 check_fstatr(uint32_t peaddr)
  157 {
  158         if (readb(FSTATR1_ADDR) & 0x80) {
  159                 /* FCU error occured: reset FCU */
  160                 reset_fcu();
  161                 return -1;
  162         }
  163 
  164         if (readb(FSTATR0_ADDR) & 0x40) {
  165                 /* illegal command error occured */
  166                 if (readb(FASTAT_ADDR) & 0x10) {
  167                         /* command lock state:
  168                            clear FASTAT.ROMAE, DLFAE, DFLRPE and DFLWPE */
  169                         writeb(FASTAT_ADDR, 0x10);
  170 #ifdef  DEBUG
  171                         /* turn on user LED(D2) */
  172                         writeb(PORTA_PODR_ADDR, readb(PORTA_PODR_ADDR) | 0x02);
  173                         writeb(PORTA_PDR_ADDR, readb(PORTA_PDR_ADDR) | 0x02);
  174 #endif  /* DEBUG */
  175                 }
  176 
  177                 /* issue a status register clear command */
  178                 writel(peaddr, 0x50);
  179 
  180                 return -1;
  181         }
  182 
  183         if (readb(FSTATR0_ADDR) & 0x30) {
  184                 /* erasure error and/or programmming error occured */
  185                 /* issue a status register clear command */
  186                 writel(peaddr, 0x50);
  187 #ifdef  DEBUG
  188                 /* turn on user LED(D3) */
  189                 writeb(PORTA_PODR_ADDR, readb(PORTA_PODR_ADDR) | 0x04);
  190                 writeb(PORTA_PDR_ADDR, readb(PORTA_PDR_ADDR) | 0x04);
  191 #endif  /* DEBUG */
  192                 return -1;
  193         }
  194 
  195         return 0;
  196 }
  197 
  198 static int
  199 wait_frdy(uint32_t timeout)
  200 {
  201         int frdy;
  202 
  203         timeout = (timeout + 99) / 100;         /* round up to a multiple of 100 usec */
  204         while (timeout-- > 0) {
  205                 /* start CMT1 */
  206                 writew(CMT1_CMCR_ADDR, 0x00c0);
  207                 writew(CMT1_CMCOR_ADDR, (PCLK / 8) / 10000);
  208                 writew(CMT1_CMCNT_ADDR, 0x0000);
  209                 writew(CMSTR0_ADDR, readw(CMSTR0_ADDR) | 0x0002);
  210 
  211                 frdy = 0;
  212                 while (!readb(IR29_ADDR))
  213                         if (readb(FSTATR0_ADDR) & 0x80) {
  214                                 frdy = 1;
  215                                 break;
  216                         }
  217 
  218                 /* stop CMT1 */
  219                 writew(CMSTR0_ADDR, readw(CMSTR0_ADDR) & ~0x0002);
  220                 writew(IR29_ADDR, 0x00);
  221         }
  222 
  223         return frdy ? 0 : -1;
  224 }
  225 
  226 static int
  227 switch_to_read(uint32_t peaddr)
  228 {
  229         int ret;
  230 
  231         /* check FRDY bit(tWAIT = tE16K) */
  232         if (wait_frdy((uint32_t)(T_E16K)) == -1) {
  233                 reset_fcu();
  234                 return -1;
  235         }
  236 
  237         /* error check */
  238         ret = check_fstatr(peaddr);
  239 
  240         /* clear FENTRYR.FENTRYn(n = 0 to 3) bits to 0 */
  241         writew(FENTRYR_ADDR, 0xaa00);
  242         while (readw(FENTRYR_ADDR) != 0x0000) ;
  243 
  244         /* disable programming and erasure of the ROM/E2 DataFlash,
  245            programming and erasure of lock bits, reading of lock bits,
  246            and blank checking */
  247         writeb(FWEPROR_ADDR, 0x02);
  248 
  249         return ret;
  250 }
  251 
  252 static int
  253 notify_fclk(uint32_t peaddr)
  254 {
  255         /* set frequency of Flash IF clock(FCLK) in PCKAR  */
  256         writew(PCKAR_ADDR, (FCLK + 500000) / 1000000);
  257 
  258         /* write 0xE9 to the destination address
  259            for peripheral clock notification */
  260         writeb(peaddr, 0xe9);
  261 
  262         /* write 0x03 to the destination address
  263            for peripheral clock notification */
  264         writeb(peaddr, 0x03);
  265 
  266         /* write 0x0f0f to the destination address
  267            for peripheral clock notification three times(as a word) */
  268         writew(peaddr, 0x0f0f);
  269         writew(peaddr, 0x0f0f);
  270         writew(peaddr, 0x0f0f);
  271 
  272         /* write 0xd0 to the destination address 
  273            for peripheral clock notification */
  274         writeb(peaddr, 0xd0);
  275 
  276         /* FRDY bit check(tWAIT = tPCKA) */
  277         if (wait_frdy(T_PCKA) == -1) {
  278                 reset_fcu();
  279                 return -1;
  280         }
  281 
  282         /* error check */
  283         return check_fstatr(peaddr);
  284 }
  285 
  286 static int
  287 switch_to_pe(size_t peaddr)
  288 {
  289         static int fclk_notified = 0;
  290         size_t area_no;
  291 
  292         /* confirm whether destination address is within the range of ROM area */
  293         if (flash_get_block_info(peaddr | 0xff000000,
  294                                 NULL, NULL, &area_no, NULL) == -1)
  295                 return -1;
  296 
  297         /* enable programming and erasure */
  298         writeb(FWEPROR_ADDR,  0x01);
  299 
  300         /* force FENTRYR to be cleared */
  301         writew(FENTRYR_ADDR, 0xaa00);
  302         while (readw(FENTRYR_ADDR) != 0x0000) ;
  303 
  304         /* set FENTRYR.FENTRYn bit to 1 */
  305         writew(FENTRYR_ADDR, 0xaa00 | (0x0001 << area_no));
  306 
  307         /* error check */
  308         if (check_fstatr(peaddr))
  309                 return -1;
  310 
  311         if (!fclk_notified) {
  312                 /* issue a peripheral clock notification command */
  313                 if (notify_fclk(peaddr))
  314                         return -1;
  315 
  316                 fclk_notified = 1;
  317         }
  318 
  319         return 0;
  320 }
  321 
  322 void
  323 flash_init(void)
  324 {
  325         volatile uint16_t prcr_bak;
  326 
  327         /* cancel module-stop state of CMT1 */
  328         prcr_bak = readw(PRCR_ADDR) & 0x00ff;
  329         writew(PRCR_ADDR, 0xa503);
  330         writel(MSTPCRA_ADDR, readl(MSTPCRA_ADDR) & ~0x00004000);
  331         writew(PRCR_ADDR, 0xa500 | prcr_bak);
  332 
  333         /* transit to lock bit mode if 0x71 command is issued */
  334         writeb(FMODR_ADDR, 0x00);
  335 
  336         /* disable flash ready interrupt */
  337         writeb(FRDYIE_ADDR, 0x00);
  338 
  339         /* disable flash access error interrupt */
  340         writeb(FAEINT_ADDR, 0x00);
  341 
  342         /* clear FENTRYR */
  343         writew(FENTRYR_ADDR, 0xaa00);
  344 
  345         /* enable write protection */
  346         writeb(FWEPROR_ADDR, 0x00);
  347 
  348         /* we'll never access to E2 DataFlash area */
  349         writew(DFLRE0_ADDR, 0x2d00);
  350         writew(DFLRE1_ADDR, 0xd200);
  351         writew(DFLWE0_ADDR, 0x1e00);
  352         writew(DFLWE1_ADDR, 0xe100);
  353 
  354         /* enable access to the FCU RAM */
  355         writew(FCURAME_ADDR, 0xc401);
  356 
  357         /* transfer the FCU firmware to FCU RAM area */
  358         memcpy((void *)FCU_RAM_ADDR, (void *)FCU_FRM_ADDR, FCU_FRM_SIZE);
  359 }
  360 
  361 void
  362 flash_deinit(void)
  363 {
  364         /* nothing to do */
  365 }
  366 
  367 int
  368 flash_write_rom(uint32_t dest, const uint8_t *src, uint32_t n)
  369 {
  370         uint32_t ra, wa;
  371         size_t npad_lead, npad_trail;
  372         size_t l, m;
  373         uint16_t *p = (uint16_t *)src;
  374         int odd = 0;
  375 
  376         if ((dest & 0xff000000) != 0xff000000)
  377                 return -1;      /* given address is not for reading */
  378 
  379         while (n > 0) {
  380                 /* get address for programming/erasure */
  381                 ra = dest & 0x00ffffff;
  382                 wa = ra & ~((uint32_t)(128 - 1));
  383 
  384                 l = m = MIN(128 - (ra - wa), n);
  385                 npad_lead = ra - wa;
  386                 npad_trail = 128 - (npad_lead + m);
  387 
  388                 /* switch to P/E normal mode */
  389                 if (switch_to_pe(wa) == -1)
  390                         return -1;
  391 
  392                 /* disable lock bit protection */
  393                 writew(FPROTR_ADDR, 0x5501);
  394 
  395                 writeb(wa, 0xe8);
  396                 writeb(wa, 0x40);
  397 
  398                 /* program leading 0xff bytes */
  399                 while (npad_lead > 1) {
  400                         writew(wa, 0xffff);
  401                         npad_lead -= 2;
  402                 }
  403                 if (npad_lead == 1 && m >= 1) {
  404                         writew(wa, (*p << 8) | 0x00ff);
  405                         m--;
  406                         odd = 1;
  407                 }
  408 
  409                 /* program words specified in the seconds argument */
  410                 if (odd) {
  411                         while (m > 1) {
  412                                 writew(wa, (*p >> 8) | (*(p + 1) << 8));
  413                                 p++;
  414                                 m -= 2;
  415                         }
  416                 } else
  417                         while (m > 1) {
  418                                 writew(wa, *p++);
  419                                 m -= 2;
  420                         }
  421 
  422                 /* program trailing 0xff bytes */
  423                 if (m == 1) {
  424                         writew(wa, *p++ | 0xff00);
  425                         npad_trail--;
  426                 }
  427                 while (npad_trail > 0) {
  428                         writew(wa, 0xffff);
  429                         npad_trail -= 2;
  430                 }
  431 
  432                 writeb(wa, 0xd0);
  433 
  434                 /* check FRDY bit(tWAIT = tP128 * 1.1) */
  435                 if (wait_frdy(T_P128 * 11 / 10) == -1) {
  436                         reset_fcu();
  437                         return -1;
  438                 }
  439 
  440                 /* switch to ROM/E2 DataFlash read mode */
  441                 if (switch_to_read(wa) == -1)
  442                         return -1;
  443 
  444                 dest += l;
  445                 n -= l;
  446         }
  447 
  448         return 0;
  449 }
  450 
  451 int
  452 flash_erase_rom(uint32_t dest)
  453 {
  454         uint32_t ba;
  455 
  456         if ((dest & 0xff000000) != 0xff000000)
  457                 return -1;      /* given address is not for reading */
  458 
  459         /* get address for programming/erasure */
  460         ba = dest & 0x00ffffff;
  461 
  462         /* switch to P/E normal mode */
  463         if (switch_to_pe(ba) == -1)
  464                 return -1;
  465 
  466         /* disable lock bit protection */
  467         writew(FPROTR_ADDR, 0x5501);
  468 
  469         writeb(ba, 0x20);
  470         writeb(ba, 0xd0);
  471 
  472         /* check FRDY bit(tWAIT = tE16K * 1.1) */
  473         if (wait_frdy((uint32_t)(T_E16K * 11 / 10)) == -1) {
  474                 reset_fcu();
  475                 return -1;
  476         }
  477 
  478         /* switch to ROM/E2 DataFlash read mode */
  479         return switch_to_read(ba);
  480 }

補遺

実はドライバを書き上げてから気付いたのだが、内蔵 ROM は僅か 1,000 回までしか書き換えられないらしい*2
# SPANSION の FM3 あたりは 100,000 回なのに?

そうとは知らず、GR-SAKURA を入手したての頃は、HEW で ROM 上のコードに S/W ブレークを張りまくったり、FDT で頻繁に ROM 内容を書き換えていたような...

せっかく実装したけど、このドライバを使うことはなさそうだ (--;

※2018/JUN/15 追記
下記にコメント頂いているとおり、ROM(コード格納用フラッシュメモリ)は 1000 回までしか保証されていないが、E2 データフラッシュはより多くの書き換えに耐えられる(RX63N の場合は 100,000 回まで保証されている)。

*1:配布されている回路図では型名が R5F563NEDDFP になっているが、これは誤記と思われる

*2:正確には、ハードウェアマニュアルの「電気的特性」の章で最低 1,000 回は書き換えられる旨が記述されている