硬汉嵌入式论坛

 找回密码
 立即注册
查看: 6163|回复: 12
收起左侧

[Cache] 关于H7使用串口+DMA接收的Cache问题求教

[复制链接]

1

主题

12

回帖

15

积分

新手上路

积分
15
发表于 2021-12-12 14:54:34 | 显示全部楼层 |阅读模式
本帖最后由 zhoudn 于 2021-12-12 15:00 编辑

程序描述:程序采用串口+超时中断+DMA接收不定长数据,串口变量使用AXI_SRAM存储空间,开启MPU并把AXI内存区域设置为回写模式(Write-Back,Read-Allocate,Write-Allocate),
当串口接收完一帧数据并进入超时中断后,由于DCache的存在,为防止DMA未及时写入到相应内存中,又为了防止后续读取串口数据存在和实际数据不一致的隐患,需要在读取前串口数据进行数据一致性处理。

问题1:  不明白为啥调用SCB_CleanInvalidateDCache函数没有效果,而只调用SCB_InvalidateDCache函数是可以的,难道不怕只写入到Dcache中,而未写入到实际内存中???
问题2:  假如在该MPU配置模式下使用DMA,我当前理解是:DMA写完后要进行写DClean操作,让数据从DCache更新到真实的物理内存中。如果使用DMA读,要在读之前进行无效化Invalidate读操作,让CPU从真正的地址去取数据,而不是在Cache命中情况下只从DCache内取数据,不知道我这样理解是 否有问题??求教!
问题3:  硬汉的SD+FATFS例程中AXI内存设置为透写模式(Write-Through,Read-Allocate,No-Write-Allocate),在DMA读取SD卡数据时,为何在读取后做DCache无效化读操作,不应该是读之前做吗?


PS:程序采用DTCM作为变量存储SRAM

附问题3硬汉的例程代码图:

问题3硬汉的例程代码

问题3硬汉的例程代码
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2021-12-13 00:38:21 | 显示全部楼层
1、不正常。
2和3:理解有误。

DMA发送前Clean操作,也就是将Cache里面的数据写入到实际的RAM空间,保证DMA前是最新的数据。
DMA发送后,做无效化操作,保证我们从Cache里面读取的是最新的数据
回复

使用道具 举报

22

主题

196

回帖

262

积分

高级会员

积分
262
发表于 2021-12-13 13:53:32 | 显示全部楼层
eric2013 发表于 2021-12-13 00:38
1、不正常。
2和3:理解有误。

硬汉哥,就1楼的这段程序,sd的读在系统中有加锁吗,我在程序里没看到相关的加锁,我看这还有个超时,那如果切换到别的任务导致超时,岂不会读错误
回复

使用道具 举报

1

主题

12

回帖

15

积分

新手上路

积分
15
 楼主| 发表于 2021-12-13 14:28:08 | 显示全部楼层
eric2013 发表于 2021-12-13 00:38
1、不正常。
2和3:理解有误。

是不是如果是Write-Back,Read-Allocate,Write-Allocate模式,DMA接收,DMA绕过CPU直接写了对应内存,读取前只做无效化操作就可以了
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2021-12-13 14:44:15 | 显示全部楼层
yuanzhongda 发表于 2021-12-13 13:53
硬汉哥,就1楼的这段程序,sd的读在系统中有加锁吗,我在程序里没看到相关的加锁,我看这还有个超时,那 ...

FatFS的底层驱动,我不加锁,仅一个任务使用。那个任务要操作都给这个任务发消息。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2021-12-13 14:46:55 | 显示全部楼层
zhoudn 发表于 2021-12-13 14:28
是不是如果是Write-Back,Read-Allocate,Write-Allocate模式,DMA接收,DMA绕过CPU直接写了对应内存,读 ...

DMA接收是绕过CPU的,DMA直接接收数据的。而你这里调用SCB_CleanInvalidateDCache不正常,而SCB_InvalidateDCache正常,我觉得不合理。程序应该其它的地方有点问题。
回复

使用道具 举报

1

主题

12

回帖

15

积分

新手上路

积分
15
 楼主| 发表于 2021-12-13 17:11:23 | 显示全部楼层
eric2013 发表于 2021-12-13 14:46
DMA接收是绕过CPU的,DMA直接接收数据的。而你这里调用SCB_CleanInvalidateDCache不正常,而SCB_Invalida ...

好的,谢谢硬汉哥,我在检查一下
回复

使用道具 举报

1

主题

12

回帖

15

积分

新手上路

积分
15
 楼主| 发表于 2021-12-13 23:15:53 | 显示全部楼层
eric2013 发表于 2021-12-13 14:46
DMA接收是绕过CPU的,DMA直接接收数据的。而你这里调用SCB_CleanInvalidateDCache不正常,而SCB_Invalida ...

硬汉大哥:有没有可能是串口接收缓存在Cache内被标记了(老数据),此时做Clear操作会把Cache内的数据回写到AXI串口缓存中,这样就刚好覆盖掉了DMA刚接收到的数据(新数据),说以使用SCB_CleanInvalidateDCache无效,也是不正确的?
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2021-12-14 09:33:55 | 显示全部楼层
zhoudn 发表于 2021-12-13 23:15
硬汉大哥:有没有可能是串口接收缓存在Cache内被标记了(老数据),此时做Clear操作会把Cache内的数据回 ...

不会,对于这个接收的缓冲器,我们没有写操作。
回复

使用道具 举报

270

主题

605

回帖

1415

积分

至尊会员

积分
1415
发表于 2022-3-29 10:42:04 | 显示全部楼层
为什么我用这个 SCB_InvalidateDCache() 这个函数之后,把 usart3_rx_size 这个变量都清0了呢。
进来的时候是9,执行下,就变成0了。

image.png 而且 SCB_InvalidateDCache() 这个函数放在USART3的接收中断处理时,还进入了hardfault了。
是因为把这个 usart3_buf_tx 这个地址弄非法了吗?

image.png

回复

使用道具 举报

0

主题

7

回帖

7

积分

新手上路

积分
7
发表于 2022-8-20 21:08:45 | 显示全部楼层
jplzl10000 发表于 2022-3-29 10:42
为什么我用这个 SCB_InvalidateDCache() 这个函数之后,把 usart3_rx_size 这个变量都清0了呢。
进来的时 ...

我今天也遇到类似的问题,但是我把配置弄成较低配置就没有这么多问题了,反而使用的更加舒服
我试了高配置的,隐藏问题比较多,一下子吸收不了得慢慢来
回复

使用道具 举报

0

主题

7

回帖

7

积分

新手上路

积分
7
发表于 2024-5-31 08:42:41 | 显示全部楼层
jplzl10000 发表于 2022-3-29 10:42
为什么我用这个 SCB_InvalidateDCache() 这个函数之后,把 usart3_rx_size 这个变量都清0了呢。
进来的时 ...

我的理解是这样的:如果使用了write back策略,调用SCB_InvalidateDCache() 就要慎重。write back机制是要把写入内存的数据先缓存在cache中,并做标记,cache替换的时候才写入主存,如果你没有Clean就直接invalidate就会丢数据。
回复

使用道具 举报

15

主题

65

回帖

110

积分

初级会员

积分
110
发表于 2025-5-11 10:06:41 | 显示全部楼层
本帖最后由 Superusrss 于 2025-5-11 10:10 编辑

刚查了下发现类似的问题,不知道对不对,Cache同步是按行同步的,如果同步的内容的地址不是与Cache行的地址对齐,将会在无效化Cache操作时,丢失与同步内容地址相邻的但存放于Cache中的数据,出处: https://blog.csdn.net/Fairchild_1947/article/details/122268377
但是我不同意他贴出的代码,其中第一个& 0x3 是确保idma地址是4字节对齐,第二个& ~0x1F 确保地址是32byte对齐。我开启所有cache后fatfs还是报错FR_INT_ERR,确实是SCB操作把dcache内容改了,关闭cache后正常,头大。已开启ENABLE_SCRATCH_BUFFER 和 ENABLE_SD_DMA_CACHE_MAINTENANCE
[C] 纯文本查看 复制代码
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
  DRESULT res = RES_ERROR;
  uint32_t timeout;
#if defined(ENABLE_SCRATCH_BUFFER)
  uint8_t ret;
#endif
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
  uint32_t alignedAddr;
#endif

  /*
  * ensure the SDCard is ready for a new operation
  */

  if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0)
  {
    return res;
  }

#if defined(ENABLE_SCRATCH_BUFFER)
  if (!((uint32_t)buff & 0x3))
  {
#endif
    if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff,
                             (uint32_t) (sector),
                             count) == MSD_OK)
    {
      ReadStatus = 0;
      /* Wait that the reading process is completed or a timeout occurs */
      timeout = HAL_GetTick();
      while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
      {
      }
      /* in case of a timeout return error */
      if (ReadStatus == 0)
      {
        res = RES_ERROR;
      }
      else
      {
        ReadStatus = 0;
        timeout = HAL_GetTick();

        while((HAL_GetTick() - timeout) < SD_TIMEOUT)
        {
          if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
          {
            res = RES_OK;
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
            /*
            the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address,
            adjust the address and the D-Cache size to invalidate accordingly.
            */
            alignedAddr = (uint32_t)buff & ~0x1F;
            SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
#endif
            break;
          }
        }
      }
    }
#if defined(ENABLE_SCRATCH_BUFFER)
  }
    else
    {
      /* Slow path, fetch each sector a part and memcpy to destination buffer */
      int i;

      for (i = 0; i < count; i++) {
        ret = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1);
        if (ret == MSD_OK) {
          /* wait until the read is successful or a timeout occurs */

          timeout = HAL_GetTick();
          while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
          {
          }
          if (ReadStatus == 0)
          {
            res = RES_ERROR;
            break;
          }
          ReadStatus = 0;

#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
          /*
          *
          * invalidate the scratch buffer before the next read to get the actual data instead of the cached one
          */
          SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE);
#endif
          memcpy(buff, scratch, BLOCKSIZE);
          buff += BLOCKSIZE;
        }
        else
        {
          break;
        }
      }

      if ((i == count) && (ret == MSD_OK))
        res = RES_OK;
    }
#endif

  return res;
}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-12 07:03 , Processed in 0.051567 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表