2020年09月12日

STM32H7とNAND Flashメモリ

以前に紹介したNAND Flashメモリ を搭載したNucleo144 Memory Shieldを用いて、STM32H743と接続した際にH7シリーズ固有の問題に直面したたため、対処方法を少し紹介します。

STM32H7シリーズは非常に高速な上にIキャッシュとDキャッシュを搭載しているため、さらに効率的に処理をすることが可能です。一方でDキャッシュはDMA転送時等にデータ化け(キャッシュの一貫性が崩れる)が生じるため、なかなか厄介です。データ化けが生じる可能性を意識して実装する必要があります。データ化け、キャッシュの一貫性が崩れるとは、例えば、DMA転送の際にメモリからDMAコントローラに書き込んだはずが、キャッシュに溜まっているだけで、DMAコントローラに書き込まれていない、その逆も然りでDMAコントローラのデータを読み出そうとメモリを見てもキャッシュに溜まっているだけでメモリは更新されてないといったことが生じます。

DMA転送時のデータ化けの対処方法についてはこちらのサイトに紹介されています。今回はDMA転送だけでなく、FMCメモリコントローラでも同様のデータ化けが発生することが分かりました。考えてみれば、メモリコマンド指令も同じデータメモリのため、当たり前といえば当たり前ですが、FMCコントローラの場合、データが化けるという現象ではなく、NAND Flashメモリの初期化や書き込みでエラーが発生するという現象でした。データ化けによってメモリコマンドの書き込み、読み込みのデータが化けてエラーとなったようです。HAL_BUSY等のエラーレスポンスからデータ化けと気づくまでに時間を要してしまいました。

NAND Flashメモリの場合、初期化のMPU_Config内に下記のようにEthernetDMAの領域に加えて、NAND Flashメモリで使用する0x80000000以降の領域に対してライトスルーモードに設定します。EthernetDMAの領域の設定についての詳細はこちらに説明があります。

設定の注意点としては領域はMPU_REGION_NUMBER*で順番に設定することです。*が同じだと設定が上書きされてしまいます。

/* Configure the MPU attributes as Device not cacheable for ETH DMA descriptors */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x30040000;
MPU_InitStruct.Size = MPU_REGION_SIZE_256B;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* Configure the MPU attributes as Normal Non Cacheable for LwIP RAM heap which contains the Tx buffers */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x30044000;
MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* Configure the MPU attributes  for NAND Flash */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x80000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_256MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);


今回はNAND Flashメモリについて紹介しましたが、NOR Flash、QSPI等でも同様だと思います。0x80000000以降の領域に対してライトスルーモードに設定することでエラーが発生せずに読み書きできるようになりました。FMCコントローラの場合、データが化けるという現象ではなく、NAND Flashメモリの初期化や書き込みでエラーが発生しました。当初、タイミング信号のパラメータ設定ミスや各サイズの設定ミスを疑いましたが、FMC単体では動作して、様々な他の機能を入れると動作しないため、機能を削って確認すると最終的にDキャッシュと判明しました。Dキャッシュを完全に無効にすると正常に動作しますが、パフォーマンスを考えると必要な領域のみを個別に設定した方が良さそうです。
FMCの設定についてはこちらのPDFに分かりやすく説明されています。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする