IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> [调试笔记] STM32 FileX库BUG 关于Cache、MPU与DMA -> 正文阅读

[嵌入式][调试笔记] STM32 FileX库BUG 关于Cache、MPU与DMA

????????最近在研究ThreadX和FileX,发现官方STM32H7 x-cube-azrtos包(目前最新版本1.1.0)有一个BUG,本文记录一下,供大家参考。


问题描述:

????????移植官方例程Fx_uSD_File_Edit后程序正常运行,但使能FX_ENABLE_EXFAT宏启用exfat文件系统支持和D-Cache数据缓存后,无法挂载exfat文件系统,返回FX_IO_ERROR,关闭D-Cache可以挂载使用exfat文件系统。


原因分析:

????????最开始调试的时候思路错了,在FileX协议库里面找了很久没有头绪,但后来一想FileX库具备各种安全认证应该不会出错,于是就从STM32官方包的移植接口中寻找BUG?。

????????首先介绍一下Cache,如果CPU要读取的SRAM内的数据在Cache 中已经加载好,这就叫读命中(Cache hit),如果Cache 里面没有怎么办,这就是读Cache Miss。如果CPU要写的SRAM区数据在Cache 中已经开辟了对应的区域,这就叫写命中,如果Cache 里面没有开辟对应的区域,这就是所谓的写Cache Miss。

????????STM32H7的Cache可以通过MPU配置成四种策略,

本文没有使用MPU所以Cache默认的策略是01即write-back,write and read allocate。

????????介绍一下什么是write-back和write allocate。如果CPU要写的SRAM区数据在Cache 中已经开辟了对应的区域,那么会写到Cache 里面,而不会立即更新SRAM;如果没有,就用到配置write allocate,CPU写到往SRAM里面的数据,会同步在Cache 里面开辟一个空间将SRAM中写入的数据加载进来,如果此时立即读此SRAM区,那么就会有很大的速度优势。

????????但如果Cache命中的情况下,此时仅Cache更新了,而SRAM没有更新,那么DMA直接从 SRAM里面读出来的就是错误的。因此如果同时使用Cache的write-back和DMA功能,需要额外的措施保持内存一致性。

????????FileX对SD卡读写接口全部位于fx_stm32_sd_driver.c中,由CubeMX自动生成,查看该文件可以发现在读写函数中已经考虑到使用Cache与DMA的情况,如下代码所示。

SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, DEFAULT_SECTOR_SIZE);
SCB_InvalidateDCache_by_Addr((uint32_t*)media_ptr->fx_media_driver_buffer, num_sectors * DEFAULT_SECTOR_SIZE);

????????SCB_InvalidateDCache_by_Addr()函数用于将该地址D-Cache无效化,无效化的意思是将Cache Line 标记为无效,等同于删除操作。这样下次读写该地址数据时,D-Cache中无此数据,CPU直接对SRAM读写数据,保证CPU读取到的数据是真实的。


源码分析:

以读取函数为例:

case FX_DRIVER_READ:
        {
            media_ptr->fx_media_driver_status = FX_IO_ERROR;
            unaligned_buffer = (UINT)(media_ptr->fx_media_driver_buffer) & 0x3;

            if (sd_read_data(media_ptr, media_ptr->fx_media_driver_logical_sector + media_ptr->fx_media_hidden_sectors,
                             media_ptr->fx_media_driver_sectors, unaligned_buffer) == FX_SUCCESS)
            {
                media_ptr->fx_media_driver_status = FX_SUCCESS;
            }

            break;
        }

????????第4行判断读取地址是否四字节对齐,因为使用MDMA读写SD卡需要地址四字节对齐,并作为参数传入sd_read_data()函数中。

static UINT sd_read_data(FX_MEDIA *media_ptr, ULONG start_sector, UINT num_sectors, UINT use_scratch_buffer)
{
    ...

    if (use_scratch_buffer)
    {
    	read_addr = media_ptr->fx_media_driver_buffer;

        for (i = 0; i < num_sectors; i++)
        {
            /* Start DMA read into the scratch buffer */
            status = BSP_SD_ReadBlocks_DMA(SD_INSTANCE, (uint32_t*)scratch, start_sector++, 1);

            if (status != BSP_ERROR_NONE)
            {
                /* DMA transfer failed, release semaphore and return immediately */
                tx_semaphore_put(&transfer_semaphore);
                return FX_IO_ERROR;
            }

            /* Block while trying to get the semaphore until DMA transfer is complete */
            if(tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) != TX_SUCCESS)
            {
                return FX_ACCESS_ERROR;
            }

#if (ENABLE_CACHE_MAINTENANCE == 1)
            SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, DEFAULT_SECTOR_SIZE);
#endif

            _fx_utility_memory_copy(scratch, read_addr, DEFAULT_SECTOR_SIZE);
            read_addr += DEFAULT_SECTOR_SIZE;
        }

        /* Check if all sectors were read */
        if (i == num_sectors)
        {
            status = FX_SUCCESS;
        }
        else
        {
            status = FX_BUFFER_ERROR;
        }
    }
    else
    {

        status = BSP_SD_ReadBlocks_DMA(SD_INSTANCE, (uint32_t*)media_ptr->fx_media_driver_buffer, start_sector, num_sectors);

        if (status != BSP_ERROR_NONE)
        {
            /* DMA transfer failed, release semaphore and return immediately */
            tx_semaphore_put(&transfer_semaphore);
            return FX_IO_ERROR;
        }

        /* Block while trying to get the semaphore until DMA transfer is complete */
        if(tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) != TX_SUCCESS)
        {
            return FX_ACCESS_ERROR;
        }

#if (ENABLE_CACHE_MAINTENANCE == 1)
        SCB_InvalidateDCache_by_Addr((uint32_t*)media_ptr->fx_media_driver_buffer, num_sectors * DEFAULT_SECTOR_SIZE);
#endif

        status = FX_SUCCESS;
    }

    /* Operation finished, release semaphore */
    tx_semaphore_put(&transfer_semaphore);

    return status;
}

? ? ? ? 第4行,若传输地址不是四字节对齐,则使用内部定义的一块32字节对齐的地址作为中转站,先用MDMA读取到数据,再将数据拷贝到用户提供地址。else分支若地址已经是四字节对齐,则直接使用用户提供的地址进行MDMA传输。

? ? ? ? 代码中tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) 这个信号量操作用于在MDMA传输过程中使该线程进入休眠,节省CPU资源,在传输完成中断中释放信号量,使该线程恢复运行。

????????然而else分支的代码存在一个bug,当用户传入的地址为四字节对齐时,进入else分支,在MDMA传输完成后,进行SCB_InvalidateDCache_by_Addr()操作,但是该函数要求传入的地址为32字节对齐,因为Cortex-M7内核的Cache line大小为八个字。

????????所以当传入的地址为4字节对齐但非32字节对齐时,SCB_InvalidateDCache_by_Addr()调用传入的参数不符合要求,D-Cache缓存清除错误,使CPU读取到缓存中错误的值。这就是BUG产生的原因。


解决方案:

????????问题找到了,修改起来就很容易了,在判断地址是否4字节对齐的地方改成判断地址是否32字节对齐,BUG圆满解决。

unaligned_buffer = (UINT)(media_ptr->fx_media_driver_buffer) & 0x1f;

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-10 13:34:55  更:2021-08-10 13:35:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/12 15:45:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码