1. 今回のやりたいこと
マイコンに2つの外付けのFLASHを接続し、動作させたいプログラムをそれらに書き込み、動作させるということをします。動作させるプログラムは、LEDをちかちかさせるプログラムになります。
2. 環境
-
使用ボード:nucleo l4r5zi
Cortex-M4搭載、2MBのFlash、640KのSRAM、USB OTG - 開発環境:Atollic TrueSTUDIO® for STM32, Built on Eclipse Neon.1a.(Version: 9.3.0 )
-
OS:KOZOS
12ステップで作る 組込みOS自作入門で紹介されているOSになります。ソースコードも展開されています。 - FLASH :W25Q64JV
3. 「2つのFLASH上でプログラムを動かす」とは?
私が使用しているボード(nucleo l4r5zi)には、外部シリアルFlashメモリとの通信インタフェースを提供してるOCTOSPIと呼ばれる周辺機能があります。このOCTOSPIという周辺機能を使用することで、2つのFLASH上でプログラムを動かすことが可能になります。順を追って詳しく説明していきます。
3.1 OCTOSPIとは
OCTOSPIはデータ線が8bit幅のSPI接続をサポートしており、以下3つのモードで動作します。
-
Indirect mode
通常のSPIと同じように動作するモードです。 -
Automatic status-polling mode
メモリのステータスレジスタをハードウェアで管理する自動ポーリングが可能なモードです。 -
Memory-mapped mode
内蔵メモリと同じように、外付けのメモリにアクセスできるモードです。
例えば、インタフェースがQUADSPI(4本のデータ線を使用するIF)のメモリとの接続図は以下のようになります。
dm00310109-stm32l4-series-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdfから抜粋
外付けのフラッシュ上のプログラムを動作させるためには、OCTOSPIをMemory-mapped modeにする必要があります。
3.2 DUAL-QUAD-SPI
タイトルにある「2つのFLASH上でプログラムを動かす」ということはどういうことなのか?2つのFLASH上でプログラムを動かすためには、OCTOSPIのDUAL-QUAD-SPIという機能を使用します。DUAL-QUAD-SPIは、OCTOSPIが持つ8本のデータ線のうち4本を1つのQUADSPIのメモリ、残りの4本をもう1つのQUADSPIメモリに接続することで、2つのメモリに同時にアクセスすることを可能にします。接続の例は以下の通りです。
dm00310109-stm32l4-series-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdfから抜粋
DUAL-QUAD-SPIを使用してFlashのデータを読み込んだときのシーケンスは以下になります。
an4760-quadspi-interface-on-stm32-microcontrollers-and-microprocessors--stmicroelectronics.pdfから抜粋
フラッシュのデータを読み込む際は、読み込み命令(上記図のInstruction)、アドレス(上記図のAddress)をフラッシュへ送信しますが、DUAL-QUAD-SPIでは、同じコマンド、同じアドレスがそれぞれのFLASHに送信されます。読み込み命令、アドレス0x00000000から2byteを読む際は、最初の1byte目はFlash1の0x00000000に格納されているデータ、次の2byte目にFlash2の0x00000000に格納されているデータがマイコン側へ送信されます。つまり、マイコン側からは、偶数のアドレスにはFlash1のデータ、奇数のアドレスにはFlash2のデータが格納されているように見えます。
2つのFLASH上でプログラムを動かすためには、そのプログラムをFLASH1、FLASH2にそれぞれ、交互に書き込む必要があります。また、同じコマンド、同じアドレスが送信されるため、2つのFLashはそれぞれ同じ種類であることも必要になります。
FLASHを接続しMemory-mapped modeに設定したときは、0x90000000~0x9FFFFFFFにマッピングされるため、FLASH上で動作させたいプログラムは0x90000000~0x9FFFFFFFに配置されるようにビルドする必要があります。
3.3 ボードとFLASHの接続図
今回はFlashデバイスとして、WinbondのW25Q64JVを使用します。ボードとW25Q64JVの接続図を以下に示します。
4. 実装
FLASH上でプログラムを動かすということで、まずはそのプログラムをFLASHに書く必要があります。プログラムはBlueTooth経由で受信することとします。
今回のソフトウェア構成図を以下に示します。
今回で初めて作成したソフトウェアを黄色く塗っています。
各ソフトウェアの説明を以下に示します。
-
プログラム実行アプリ
BlueTooth経由で受信したプログラムを交互に2つのFlashに書き込み、Flash上のプログラムを実行します。 -
FLASHマネージャ
FLASHをアクセスするためのソフトになります。いろいろなアプリからFlashへのアクセスがあることを考慮し排他等の制御を行っています。 -
W25Q64JVデバイスドライバ
W25Q64JVを制御するソフトになります。
プログラム実行アプリの一部を紹介したいと思います。
- FLASHにプログラムを書き込むコード
// プログラムをフラッシュに書く
// (*)2つの外部フラッシュでxipを行う
static void loading_app_msg_write_dual_program(uint32_t par)
{
LOADING_APP_CTL *this = &loading_app_ctl;
int32_t ret;
uint32_t i;
uint8_t data;
uint8_t verify_ret = 1;
char snd_str[256];
console_str_send("dual write program\n");
// 書き込み
for (i = 0; i < (this->buf_idx/2); i++) {
// DeviceAに書き込む
ret = flash_mng_write(FLASH_MNG_KIND_W25Q20EW1, i, &(this->load_program_buf[i*2]), 1);
if (ret != E_OK) {
console_str_send("DeviceA write error\n");
}
// DeviceBに書き込む
ret = flash_mng_write(FLASH_MNG_KIND_W25Q20EW2, i, &(this->load_program_buf[i*2+1]), 1);
if (ret != E_OK) {
console_str_send("DeviceB write error\n");
}
}
// ベリファイ
for (i = 0; i < (this->buf_idx/2); i++) {
// 読み込み
ret = flash_mng_read(FLASH_MNG_KIND_W25Q20EW1, i, &data, 1);
// 判定
if (this->load_program_buf[i*2] != data) {
sprintf(snd_str, "addr:%x expexted:%x read:%x\n", (unsigned int)(i*2), this->load_program_buf[i*2], data);
console_str_send(snd_str);
verify_ret = 0;
}
// 読み込み
ret = flash_mng_read(FLASH_MNG_KIND_W25Q20EW2, i, &data, 1);
// 判定
if (this->load_program_buf[i*2+1] != data) {
sprintf(snd_str, "addr:%x expexted:%x read:%x\n", (unsigned int)(i*2+1), this->load_program_buf[i*2+1], data);
console_str_send(snd_str);
verify_ret = 0;
}
}
// 結果表示
if (verify_ret) {
console_str_send("write success\n");
}
}
-
FLASHのプログラムを実行するコード
LEDをちかちかさせるプログラムのmain関数が0x90000581に配置されているため、そこへジャンプします。
・・・
#define FLASH_PROGRAM_ADDR (0x90000581) // フラッシュプログラム
・・・
// Execute in place
static void loading_app_msg_xip(uint32_t par)
{
volatile int (*f)(void);
// jump to main function in exterbal Flash
f = (int(*)(void))(FLASH_PROGRAM_ADDR);
f();
}
具体的な実装については、Nucleo-L4R5ZI_Systemを参照お願いいたします。
5. 動作確認
同じLEDをちかちかするプログラムを1つのFLASHで動作させたときと2つのFLASHで動作させたときを比べてたいと思います。期待としては、2つのFLASHで動作させたときのほうが、1つのFLASHで動作させたときに比べて、LEDの点滅周期が2倍になることです。
同じプログラムでも2つのFLASHで動作させたほうが早いことがわかるかと思います。
6. 最後に
初めて外付けのFLASH上でプログラムを動かすということをやってみました。OCTOSPIがどういうものかを知るいいきっかけになりました。業務でも外付けのFLASHでプログラムを動作させるというのはよくあるかと思うので、今回の経験を活かしたいと思います。
何か不明点、ご意見等ありましたらコメントいただけると助かります。