WhyCan Forum(哇酷开发者社区)

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn

您尚未登录。

#1 2020-04-24 12:41:37

ntmusic
会员
注册时间: 2020-02-24
累计积分: 43

关于F1C100s的DMA使用

这段时间断断续续在搞F1C100s的ADC音频采集,使用FIFO中断的方法采集数据没问题(这里有一个小坑,FIFO中断中读数据时,我用结构体地址指向的方式将Rx寄存器的值读出,发现不管读几次都只能读出FIFO最后一个值,按理说每读一次Rx应该就从FIFO中取下一个数据才对。后来实在没法子,用了Read32()函数,就可以了。。。。),但使用DMA传输就不行了,DMA缓冲区的数据一大部分压根不变化。啃了好几遍手册,各个Channel都试了,仍不行。一开始以为又进了读FIFO之类的坑,但最后排查来排查去,问题应该是Cache的问题。
    我是在RTT BSP中开发,在board.c对MMU的初始化函数中可以看到,32M的DDR是全部Cacheble的,RTT内存管理当然也是Cacheble,目标缓存是rt_malloc出来的,在DMA结束后,CPU操作这些缓存数据时使用的还是Cache中的数据,导致一致性问题。尝试了一下,在rt_hw_mmu_init中直接将mmu_enable_dcache()屏蔽掉后,编译运行一切正常。。
    当然关掉dcache是不实际的,但我不知道什么指令能使Cache和内存同步,mmu.c中的mmu_clean_invalidated_dcache()函数和mmu_clean_dcache()函数等好像也不起作用,于是换一种方式,在mmu_init函数的mem_desc表中,添加一部分DDR地址为“RW_NCNB”,把DMA传输的目标地址指向这个区域,就没问题了。

离线

#2 2020-04-24 16:10:12

armstrong
会员
注册时间: 2019-04-10
累计积分: 128

Re: 关于F1C100s的DMA使用

对cache的使用知识,是裸机开发ARM9必备啊。你可以从我的rtx+emwin包里,找到mmu_c.c和mmu_a.s,我把所有mmu操作的api都放在里边了。你这个情况,只要调用我的MMU_InvalidateDCacheArray函数,使dma访问过的ram对应的cache无效就行了。注意,“无效”和“清理”是两个截然不同的概念。

离线

#3 2020-04-24 16:12:39

armstrong
会员
注册时间: 2019-04-10
累计积分: 128

Re: 关于F1C100s的DMA使用

cache和mmu是arm9的法宝啊,要学会使用它们才行。这个经验才Cortex-M7上也用得着。

离线

#4 2020-04-24 16:18:35

ntmusic
会员
注册时间: 2020-02-24
累计积分: 43

Re: 关于F1C100s的DMA使用

armstrong 说:

对cache的使用知识,是裸机开发ARM9必备啊。你可以从我的rtx+emwin包里,找到mmu_c.c和mmu_a.s,我把所有mmu操作的api都放在里边了。你这个情况,只要调用我的MMU_InvalidateDCacheArray函数,使dma访问过的ram对应的cache无效就行了。注意,“无效”和“清理”是两个截然不同的概念。

谢谢指点,对arm9刚接触很多都还不熟悉,尤其是底层方面,还得向各位大牛学习!

离线

#5 2020-04-24 17:23:52

armstrong
会员
注册时间: 2019-04-10
累计积分: 128

Re: 关于F1C100s的DMA使用

cpu把数据写到内存,然后交给外设或dma,要用clean;
cpu读取由外设或dma放在ram的数据之前,要invalid;
dcacheinvalid和dcacheclean指令,要么是针对mva的,要么是针对cache-line的,要么就整个dcache
所以MMU_InvalidateDCacheArray函数封装了mva的遍历。
对ram变量也有cache-line对齐的要求,即变量或数组的大小和地址必须32字节对齐,否则会破坏变量。

离线

#6 2020-04-24 17:24:46

armstrong
会员
注册时间: 2019-04-10
累计积分: 128

Re: 关于F1C100s的DMA使用

如果直接把变量放在ncnb属性的区块,是方便很多;但会降低cpu访问这些数据性能。

最近编辑记录 armstrong (2020-04-24 17:25:15)

离线

#7 2020-04-24 19:38:15

sean68
会员
注册时间: 2020-04-24
累计积分: 7

Re: 关于F1C100s的DMA使用

c100s 有4组normal dma

离线

#8 2020-04-24 22:37:48

ntmusic
会员
注册时间: 2020-02-24
累计积分: 43

Re: 关于F1C100s的DMA使用

armstrong 说:

cpu把数据写到内存,然后交给外设或dma,要用clean;
cpu读取由外设或dma放在ram的数据之前,要invalid;
dcacheinvalid和dcacheclean指令,要么是针对mva的,要么是针对cache-line的,要么就整个dcache
所以MMU_InvalidateDCacheArray函数封装了mva的遍历。
对ram变量也有cache-line对齐的要求,即变量或数组的大小和地址必须32字节对齐,否则会破坏变量。

感谢您的热心指导,受益匪浅!实测使用mmu_invalidate_dcache是正确的,之前测试不起作用是粗心了没有设置对DMA的目的地址,犯了低级错误

离线

#9 2020-04-25 13:30:16

Quotation
会员
注册时间: 2018-10-04
累计积分: 239

Re: 关于F1C100s的DMA使用

armstrong 说:

cpu把数据写到内存,然后交给外设或dma,要用clean;
cpu读取由外设或dma放在ram的数据之前,要invalid;
dcacheinvalid和dcacheclean指令,要么是针对mva的,要么是针对cache-line的,要么就整个dcache
所以MMU_InvalidateDCacheArray函数封装了mva的遍历。
对ram变量也有cache-line对齐的要求,即变量或数组的大小和地址必须32字节对齐,否则会破坏变量。

看了这几个cache相关函数的实现,有个疑问。函数里需要遍历整块内存,以32字节为单位去调用 MCR p15……。这样岂不是很低效?
如果我有几百K的数据从外设读回来,实际上CPU cache只有一点点,并不需要遍历几百K去invalidate cache。
那怎样能够仅仅invalidate需要的cache呢?当然可以invalidate整个cache,还有没有更精简的方法呢?

void MMU_InvalidateDCacheArray(unsigned long mva, unsigned long num)
{
  signed long size = num;
  while (size > 0) {
    MMU_InvalidateDCacheMVA(mva);
    mva += CACHE_ALIGN;
    size -= CACHE_ALIGN;
  }
}

离线

#10 2020-04-25 13:40:02

armstrong
会员
注册时间: 2019-04-10
累计积分: 128

Re: 关于F1C100s的DMA使用

Quotation 说:

看了这几个cache相关函数的实现,有个疑问。函数里需要遍历整块内存,以32字节为单位去调用 MCR p15……。这样岂不是很低效?
如果我有几百K的数据从外设读回来,实际上CPU cache只有一点点,并不需要遍历几百K去invalidate cache。
那怎样能够仅仅invalidate需要的cache呢?当然可以invalidate整个cache,还有没有更精简的方法呢?

void MMU_InvalidateDCacheArray(unsigned long mva, unsigned long num)
{
  signed long size = num;
  while (size > 0) {
    MMU_InvalidateDCacheMVA(mva);
    mva += CACHE_ALIGN;
    size -= CACHE_ALIGN;
  }
}

还有一个指定index的操作,不过这个操作无法针对虚拟地址,几乎无用。除此之外应该是没办法了。
Cortex-M7也有cache需要管理,它的库里也是只能遍历来操作指定内存区域:

__STATIC_FORCEINLINE void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize)
{
  #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
    if ( dsize > 0 ) { 
       int32_t op_size = dsize + (((uint32_t)addr) & (__SCB_DCACHE_LINE_SIZE - 1U));
      uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_DCACHE_LINE_SIZE - 1U) */;
    
      __DSB();

      do {
        SCB->DCCMVAC = op_addr;             /* register accepts only 32byte aligned values, only bits 31..5 are valid */
        op_addr += __SCB_DCACHE_LINE_SIZE;
        op_size -= __SCB_DCACHE_LINE_SIZE;
      } while ( op_size > 0 );

      __DSB();
      __ISB();
    }
  #endif
}

离线

#11 2020-04-25 13:43:34

armstrong
会员
注册时间: 2019-04-10
累计积分: 128

Re: 关于F1C100s的DMA使用

armstrong 说:

还有一个指定index的操作,不过这个操作无法针对虚拟地址,几乎无用。除此之外应该是没办法了。
Cortex-M7也有cache需要管理,它的库里也是只能遍历来操作指定内存区域:

void MMU_InvalidateDCacheArray(unsigned long mva, unsigned long num)
{
  signed long size = num;
  while (size > 0) {
    MMU_InvalidateDCacheMVA(mva);
    mva += CACHE_ALIGN;
    size -= CACHE_ALIGN;
  }
}

另外一种惯用的方法就是,自己定义一个heap,这个heap用于管理non-cacheable的内存块;
就学学linux,用kmalloc申请的内存可以指定GFP_DMA类型,它就是从关闭cache的内存页分配。

最近编辑记录 armstrong (2020-04-25 13:47:36)

离线

#12 2020-06-23 09:19:49

逗倪豌儿
会员
注册时间: 2020-06-09
累计积分: 23

Re: 关于F1C100s的DMA使用

你好,能否加个好友交流下多谢:qq ______

sean68 说:

c100s 有4组normal dma

离线

#13 2020-06-23 09:28:57

哇酷小二
管理员
注册时间: 2020-04-22
累计积分: 1,852

Re: 关于F1C100s的DMA使用

逗倪豌儿 说:

你好,能否加个好友交流下多谢:qq ______

新朋友你好,技术问题直接在后面跟帖就可以了,没必要加QQ私聊。

如果大家都私聊,网站也失去意义。

离线

#14 2020-11-22 06:13:19

nianyan99
会员
注册时间: 2020-10-20
累计积分: 12

Re: 关于F1C100s的DMA使用

感謝,dma終於可以動了. 不過還是不太了解 cache 的相關知識. 有空再看書.

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB