这段时间断断续续在搞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传输的目标地址指向这个区域,就没问题了。
离线
对cache的使用知识,是裸机开发ARM9必备啊。你可以从我的rtx+emwin包里,找到mmu_c.c和mmu_a.s,我把所有mmu操作的api都放在里边了。你这个情况,只要调用我的MMU_InvalidateDCacheArray函数,使dma访问过的ram对应的cache无效就行了。注意,“无效”和“清理”是两个截然不同的概念。
离线
cache和mmu是arm9的法宝啊,要学会使用它们才行。这个经验才Cortex-M7上也用得着。
离线
对cache的使用知识,是裸机开发ARM9必备啊。你可以从我的rtx+emwin包里,找到mmu_c.c和mmu_a.s,我把所有mmu操作的api都放在里边了。你这个情况,只要调用我的MMU_InvalidateDCacheArray函数,使dma访问过的ram对应的cache无效就行了。注意,“无效”和“清理”是两个截然不同的概念。
谢谢指点,对arm9刚接触很多都还不熟悉,尤其是底层方面,还得向各位大牛学习!
离线
cpu把数据写到内存,然后交给外设或dma,要用clean;
cpu读取由外设或dma放在ram的数据之前,要invalid;
dcacheinvalid和dcacheclean指令,要么是针对mva的,要么是针对cache-line的,要么就整个dcache
所以MMU_InvalidateDCacheArray函数封装了mva的遍历。
对ram变量也有cache-line对齐的要求,即变量或数组的大小和地址必须32字节对齐,否则会破坏变量。
离线
如果直接把变量放在ncnb属性的区块,是方便很多;但会降低cpu访问这些数据性能。
最近编辑记录 armstrong (2020-04-24 17:25:15)
离线
c100s 有4组normal dma
离线
cpu把数据写到内存,然后交给外设或dma,要用clean;
cpu读取由外设或dma放在ram的数据之前,要invalid;
dcacheinvalid和dcacheclean指令,要么是针对mva的,要么是针对cache-line的,要么就整个dcache
所以MMU_InvalidateDCacheArray函数封装了mva的遍历。
对ram变量也有cache-line对齐的要求,即变量或数组的大小和地址必须32字节对齐,否则会破坏变量。
感谢您的热心指导,受益匪浅!实测使用mmu_invalidate_dcache是正确的,之前测试不起作用是粗心了没有设置对DMA的目的地址,犯了低级错误
离线
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;
}
}
离线
看了这几个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
}
离线
还有一个指定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)
离线
你好,能否加个好友交流下多谢:qq ______
c100s 有4组normal dma
离线
感謝,dma終於可以動了. 不過還是不太了解 cache 的相關知識. 有空再看書.
离线
我的DMA也动起来
离线