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 でお馴染みの形式であるが、
- FCU 制御用コマンドが CFI(Common Flash Memory Interface) で定義されているコマンドとは異なる。
- FCU 制御用のレジスタがあり、コマンド発行とは別に、これらのレジスタの設定も必要になる。
- Flash の内容を読み出すためのアドレスと、コマンド発行先のアドレス(ハードウェアマニュアルでは「P/E 用アドレス」と呼んでいる)が異なる。
- FCU(ROM/E2 データフラッシュの書き換えを行う専用のシーケンサ)の状態遷移に従う必要がある。
- 事前に 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 ノーマルモードに遷移後、以下の手順でコマンドを発行することにより行う。
- プログラム対象アドレスに 0xE8 を書き込む。
- プログラム対象アドレスに 0x40 を書き込む。
- プログラム対象アドレスに書き込みデータを順に 64 回 2bytes 単位で書き込む。
- プログラム/イレーズ用アドレスに 0xD0 を書き込む。
- 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 - エラー確認
上記の「プログラム対象アドレス」は、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 ノーマルモードに遷移後、以下の手順でコマンドを発行することにより行う。
- イレーズ対象アドレスに 0x20 を書き込む。
- イレーズ対象アドレスに 0xD0 を書き込む。
- 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 - エラー確認
上記のイレーズ対象アドレスは、128 bytes 境界にアラインしたものである必要はない。
ドライバの実装
内蔵 Flash ROM ドライバの実装例を以下に示す。
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 回まで保証されている)。