This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

EDMA3只能传输四分之一数据(SYSBIOS)

Other Parts Discussed in Thread: SYSBIOS

疑问如题,已经阅读过e2echina.ti.com/question_answer/dsp_arm/c6000_multicore/f/53/t/108452?tisearch=e2e-quicksearch&keymatch=EDMA%20%E5%9B%9B%E5%88%86%E4%B9%8B%E4%B8%80这个帖子了,这个帖子的楼主也没回答到底解决没有。

还有一个帖子也读了,他是超过32767的限制才有问题,我这一共就船64个bytes,不会存在数量超过-32768~+32767的问题:

自己写的程序,按照C:\ti\edma3_lld_2_12_05_30E\examples\edma3_driver\src 这个例子里dma_test.c这里代码一点一点拷贝下来的,按理说应该是能正常运行的,可是看了下传输结果,只有4分之一传成功了,剩下的就报错了,配置如下(代码都是拷贝过来的,一个标点符号都没改...是不可能的,改了点,但是配置的东西还是拷贝的没变):

有个全局的变量声明:
#define MAX_ACOUNT                  (64u)  
#pragma DATA_ALIGN(Data_in_DDR3, 4);
#pragma DATA_SECTION ( Data_in_DDR3 , ".mysection_in_ddr3");//这个我放到了DDR3里
Uint32 Data_in_DDR3[MAX_ACOUNT];
#pragma DATA_ALIGN(Data_in_MSM, 4);
#pragma DATA_SECTION ( Data_in_MSM , ".mysection_in_msm");//这个我放到了MSMCSRAM里
Uint32 Data_in_MSM[MAX_ACOUNT];
Uint32 *s_addr,*d_addr;
  
函数内部代码,配置部分:
if (edmaResult == EDMA3_DRV_SOK)
    {
        s_addr = Data_in_DDR3 ;
        d_addr = Data_in_MSM  ;
        paramSet.srcAddr    = (uint32_t)(s_addr);
        paramSet.destAddr   = (uint32_t)(d_addr);
        aCnt = MAX_ACOUNT;//MAX_ACOUNT=64
        bCnt = 1;
        cCnt = 1;
        paramSet.aCnt       = aCnt;
        paramSet.bCnt       = bCnt;
        paramSet.cCnt       = cCnt;
        paramSet.srcBIdx    = MAX_ACOUNT;
        paramSet.destBIdx   = MAX_ACOUNT;
        paramSet.srcCIdx    = MAX_ACOUNT;
        paramSet.destCIdx   = MAX_ACOUNT;

        paramSet.bCntReload = bCnt;
        paramSet.linkAddr   = 0xFFFFu;
        paramSet.opt &= 0xFFFFFFFCu;//Source & Destination are in INCR modes
        paramSet.opt |= ((tcc << OPT_TCC_SHIFT) & OPT_TCC_MASK);//Program the TCC
        paramSet.opt |= (1 << OPT_ITCINTEN_SHIFT);
        paramSet.opt |= (1 << OPT_TCINTEN_SHIFT); 
        paramSet.opt &= 0xFFFFFFFBu;//syncType = EDMA3_DRV_SYNC_A
        edmaResult = EDMA3_DRV_setPaRAM(hEdma, chId, &paramSet);
    }

 

运行的时候看变量paramSet.opt的前4个bit都是0,那就代表着:

bit0:源Increment (INCR) mode,

bit1:目的Increment (INCR) mode,

bit2:A-synchronized,

bit3:0h = Set is not static,其他的没啥说的:最后打印的如下:edma3_test: Data write-read matchingFAILED at i = 16  ,这个16就是64的四分之一,刚开始用的256,到64就不对了,也是四分之一

改变最大数量,每次都只传四分之一,想了一上午没想通,有人遇到过类似的问题吗?附件是代码,想参考的可以拿去用。6318.Edma_No_Link.zip

继续修改程序,按照EDMA3 user's guide 的Figure 3-2. Block Move Example PaRAM Configuration的设置,依然只能传输四分之一数据,附带一个小提示,该文档有一个错误,如下图,红圈部分应该为0:

  • 看配置暂时没有看出什么问题。
    请问edma3_test: Data write-read matching FAILED at i = 16是查看目的地址没有收到数据还是只是与源地址不匹配?
  • 这个打印内容是 example里的内容搬运过来的,源代码如下:C:\ti\edma3_lld_2_12_05_30E\examples\edma3_driver\src的dma_test.c的325行

           for (i = 0; i < (acnt*bcnt*ccnt); i++)
            {
                if (srcBuff1[i] != dstBuff1[i])
                {
                    Istestpassed = 0u;
                    printf("edma3_test: Data write-read matching" \
                        "FAILED at i = %d\r\n", i);
                    break;
                }
            }

    我修改如下:

                for (i = 0; i < (MAX_ACOUNT*1*1); i++)
                {
                    if (s_addr[i] != d_addr[i])
                    {
                        Istestpassed = 0u;
                        platform_write("edma3_test: Data write-read matching" \
                            "FAILED at i = %d\r\n", i);
                        break;
                    }
                }

    所以我推断是用指针指向 数组里的内容,判断 收没收到数据的。目前就是没有头绪哪里导致到只有四分之一数据正确,paraSet.opt的bit3 STATIC 用0和1都试过,没有效果。

  • 直接查看一下d_addr的数据可以看到吗?看看是没有收到数据还是是错误的数据。
  • 查看过了,没有收到数据,原地址我写的是0-63,目的地址初始化的时候写的是63-0,DMA之后,0-15是传过去了,15位以后没变,就是数据没传过去。
    修改了数据类型,用signed char可以传送56个,其他int和float ,uint32_t只能传递16个,这一定是错觉,这应该和数据类型没有关系。
    将DMA修改为QDMA出现相同问题。
    还在尝试修改其他变量来查找原因。

  • 可否将完整的工程上传以及使用的SDK版本。
  • 文件我放在附件,2个附近,一个是用CCS10.1版本做的,一个是用CCS9.3做的,

    是用的模块:XDCtools 3.61.2.27  ,SYSBIOS 6.76.3.01 ,EDMA3 Low Level Driver edma3_lld_2_12_05_30E,MCSDK PDK TMS3320C6678 ,截图如下:

    用的PLATFORM截图如下:

    程序附件如下:10.11803.Edma3_Nolink_Test_V10dot1.zip

    9.3:7888.Edma_No_Link_CCS9dot3.zip

  • Acount是字节数,64字节就是16个32bit word啊。EDMA没有错。是理解上有点出入。

    你定义的数组是32bit的: Uint32 Data_in_DDR3[MAX_ACOUNT];

    改成8bit就对了。

  • 解释通,原来不是配置的问题,我还在用C++的思想理解硬件,C++一个memory copy不管数据类型 直接copy,EDMA3里边也是不管数据类型,只不过需要用户自己计算类型Acount的关系。
  • 有进步,根据Tony的说法,修改了数据类型和Acount的比例关系,现在每次都能传递7/8的数据了,最后1/8的数据跑哪里去了还在找。
  • 问题已解决:

    首先,是Tony说的问题,Acount对标的是字节数,而我用的int或者float类型,这个类型占用4个字节,需要调整Acount的数值。

    其次,就是DATA_SECTION的应用问题,这个问题还没搞明白原理,一会再去找找文档看下,在我这个代码里:

    //#pragma DATA_ALIGN(Data_in_DDR3, 4);
    //#pragma DATA_SECTION ( Data_in_DDR3 , ".mysection_in_ddr3");
    uint32_t Data_in_DDR3[16];
    
    #pragma DATA_ALIGN(Data_in_MSM, 4);
    #pragma DATA_SECTION ( Data_in_MSM , ".mysection_in_msm");
    uint32_t Data_in_MSM[16];

    如果注释掉#pragma DATA_ALIGN(Data_in_DDR3, 4);和#pragma DATA_SECTION ( Data_in_DDR3 , ".mysection_in_ddr3"); 就能够正常跑完64个数据,如果打开注释的话,传递的数据就少于64个,具体少几个不清楚,我尝试这修改了几次结果如下:

    float类型58/64 ,int 类型 8/16,14/16等。

    推断:我的platform中将程序,数据的内从都设置成了DDR3,如下图:

    本来数据就在DDR3中,再定义DATA_SECTION 的话会有问题?

    感谢Nancy的长期跟踪和提示,感谢Tony的提醒和指正。

    附件放上能正常使用的程序,上面的回帖内附件的程序是有问题的,请用这个:6545.Edma_No_Link.zip

  • Lucius Green 说:
    #pragma DATA_SECTION ( Data_in_DDR3 , ".mysection_in_ddr3");

    自定义了data section,需要另加一个.cmd文件将.mysection_in_ddr3分配到想放到的memory。这个cmd文件不需要memory定义部分,只要加上section部分。

    否则链接时应该会有warning,具体会分配到哪不一定,需要看产生的map文件。

  • Tony:

            嗯,我没有用CMD,因为我用的是SYSBIOS,在.CFG文件里有如下定义,是照搬一个点亮LED例子的书写方式:

    /* Example 3 Create our memory map - i.e. this is equivalent to linker.cmd */
    Program.sectMap[".mysection_in_msm"] = "MSMCSRAM";
    Program.sectMap[".mysection_in_ddr3"] = "DDR3";
    //Program.sectMap[".mysection_in_L2"] = "L2SRAM";
    Program.sectMap[".const"] = "DDR3";
    Program.sectMap[".text"] = "DDR3";
    Program.sectMap[".code"] = "DDR3";
    Program.sectMap[".data"] = "DDR3";
    Program.sectMap[".sysmem"] = "DDR3";
    Program.sectMap[".sharedVar"] = "DDR3";
    Program.sectMap["platform_lib"] = "DDR3";

    这样我推断应该和.CMD里分配内存是一样的。

    事情有了新的翻转,我上述附件里的文件还是不能用,昨天关电脑前想测试下单个block传递数据的极限,文档上写的是Acount最大65535个数据,理论上传递16383个uint32_t是没问题,而MSMCSRAM有4096KB,也足够大了,于是将如下数量的数据从DDR3传递到MSM里得到了有趣的测试结果:

        Source发送数据数量   Destinatio接收数据数量
    源在DDR3 目的在MSM 512   512
    750   750
    900   900
    950   950
    1000   1000
    1018   1018
    1020   1020
    1022   1022
    1023   1023
    1024   1018 ×
    2048   2042 ×
    2500   2490 ×

    而如果源地址和目的地址都放在DDR3中,传输完全失败。今天继续做改动和尝试,看看是否有新的发现和进展。

  • 如果是数据内容不对,注意一下Cache, CPU看的是Cache的内容,而EDMA传输的是物理内存。在EDMA之前,将目的地址做一下Cache invalid.

  • cache部分的代码是有的:

        unsigned int i;
        s_addr = Data_in_DDR3 ;
        d_addr = Data_in_MSM  ;
        for(i=0;i<DataNum;i++)
        {
            Data_in_MSM[i]=(DataNum-i);
            Data_in_DDR3[i]=i;
            s_addr[i]=i;
        }

       //#ifdef EDMA3_ENABLE_DCACHE
            //Note: These functions are required if the buffer is in DDR.
            //For other cases, where buffer is NOT in DDR, user
            //may or may not require the below functions.
            // Flush the Source Buffer
            if (edmaResult == EDMA3_DRV_SOK)
            {
                edmaResult = Edma3_CacheFlush((uint32_t)s_addr, MAX_ACOUNT);//这里定义的是字节数
            }
    
            //Invalidate the Destination Buffer
            if (edmaResult == EDMA3_DRV_SOK)
            {
                edmaResult = Edma3_CacheInvalidate((uint32_t)d_addr, MAX_ACOUNT);//这里定义的是字节数
            }
        //#endif  //EDMA3_ENABLE_DCACHE

    是在C:\ti\edma3_lld_2_12_05_30E\examples\edma3_driver\src的dma_test.c上修改的,因为发现EDMA3_ENABLE_DCACHE 右键是连接不到定义的,所以就把它注释掉了。

  • uint32_t *s_addr,*d_addr;
    这样定义一个指针,然后自增地址并对其赋值,这不安全吧。如果地址一直增加上去,会发生什么后果?当然如果这个地址一开始分配在代码的末尾,问题不大,但并不能保证如此吧。

    下面我标出的部分是不是有点混乱?Cache操作的是s_add, d_add,之后又将这两个指针指到了另外的数组Data_in_MSM和Data_in_DDR3,而这两个数据并没有做过Cache一致性操作。

  • 对,关于指针的用法同意你的说法,确实是很有危险,如果在我的上位机程序里我是不会这么用的。

    我学习EDMA3是为了弄清楚VLFFT的程序结构,然后自己跑下VLFFT,所以我自己的 EDMA3的程序是结合了C:\ti\edma3_lld_2_12_05_30E\examples\edma3_driver\src文件夹里的源代码和C:\ti\workspace_v9_3\vlfft_evmc6678l的源代码来做的,这个定义的s_addr和d_addr就是等价替换srcBuff1和dstBuff1的定义,而在C:\ti\edma3_lld_2_12_05_30E\examples\edma3_driver\src的 dma_test.c 的 第44行,第45行:

    extern signed char *srcBuff1;
    extern signed char *dstBuff1;

    他们定义的是extern->定义在其他文件里 ,再翻找他们的具体定义是在C:\ti\edma3_lld_2_12_05_30E\examples\edma3_driver\src的common.c的第97行和98行:

    signed char *srcBuff1;
    signed char *dstBuff1;

    翻找这里也没翻到他们具体的BUF大小,仅仅是给了一个指针,相同的情况也出现在VLFFT里,VLFFT的vlfftApps.c文件件里的Void vlfft_master(UArg arg0, UArg arg1)的函数内部有如下定义:

        Uint32           fftLoop;
    
        VLFFTparams_t    VLFFTparams;
        DMAparams_t      DMAparams;
        VLFFTbuffers_t   VLFFTbuffers;
        float            *ptrIn, *ptrOut;

    prtIn和ptrOut,没有给出大小仅仅给了一个指针,这指针就是用来存放第一次迭代时数据的,代码如下(vlfftApps.c第334行):

        /***************************/
        /* 1st iter of FFT         */
        /***************************/
    #if ENABLE_VLFFT_PROCESSING
        vlfftEdmaConfig_1stIter( &DMAparams);
    
        ptrIn  = inData;
        ptrOut = workBufExternal;
        VLFFT_1stIter( ptrIn, ptrOut, &VLFFTparams, &VLFFTbuffers, &DMAparams, 0);
    #endif // #if ENABLE_VLFFT_PROCESSIN

    如上2个例所示,所以才这么用,如果不是DSP内部有优化的话,这么用确实存在风险,这2个例子都存在错误的示范。但是正确的示范是什么?上位机的C++程序由于没有程序员可以定义DMA操作,所以数据从一个矩阵到另一个矩阵都是memory copy的,不会加中间一个BUFFER(也许CPU内部会执行某些操作,但是这些操作对编程人员是看不见的),DSP程序里加的中间这buf其实我是一直稀里糊涂用下来的,真不知道他的作用到底是啥,既然DMA了,为啥不能DDR3定义的变量名Data_in_DDR3里的数据 直接取到缓存,然后缓存数据直接存入MSM的变量名Data_in_MSM中去,加一个中间的buf不就是耗用的双倍的资源吗?像FPGA一样用资源换速度?运行的时候我查看了s_addr和d_addr的地址,完全和Data_in_DDR3 Data_in_MSM地址是一样的!他们就在DDR3和MSM里,他们不再缓存里啊?

    另一个问题也说的对

    s_addr = Data_in_DDR3 ; 这个在Tony老师您回帖之前已经放到函数最前端了,你说了句Cache问题提醒到了我,所以在您回复这个问题之前的我的那个回复里 顺序是调整过的:

    1.先将数据地址给中间buf:

    s_addr = Data_in_DDR3 ;
    d_addr = Data_in_MSM ;

    2.然后初始化数据:

    for(i=0;i<DataNum;i++)
    {
          Data_in_MSM[i]=(DataNum-i);
          Data_in_DDR3[i]=i;
          s_addr[i]=i;//这个感觉给Data_in_DDR3 数据进行了2遍的初始化
    }

    3.然后对缓存进行操作:

    if (edmaResult == EDMA3_DRV_SOK)
    {
          edmaResult = Edma3_CacheFlush((uint32_t)s_addr, MAX_ACOUNT);//这里定义的是字节数
    }

    //Invalidate the Destination Buffer
    if (edmaResult == EDMA3_DRV_SOK)
    {
          edmaResult = Edma3_CacheInvalidate((uint32_t)d_addr, MAX_ACOUNT);//这里定义的是字节数
    }

  • Lucius Green 说:
    上位机的C++程序由于没有程序员可以定义DMA操作,所以数据从一个矩阵到另一个矩阵都是memory copy的,不会加中间一个BUFFER(也许CPU内部会执行某些操作,但是这些操作对编程人员是看不见的),DSP程序里加的中间这buf其实我是一直稀里糊涂用下来的,真不知道他的作用到底是啥,既然DMA了,为啥不能DDR3定义的变量名Data_in_DDR3里的数据 直接取到缓存,然后缓存数据直接存入MSM的变量名Data_in_MSM中去,加一个中间的buf不就是耗用的双倍的资源吗?像FPGA一样用资源换速度?运行的时候我查看了s_addr和d_addr的地址,完全和Data_in_DDR3 Data_in_MSM地址是一样的!他们就在DDR3和MSM里,他们不再缓存里啊?

    将数据从一块地址搬到另一块,memory copy的实现是for 循环,速度慢,用DMA就是为快。for循环的C代码转换为汇编,你会看到是先将一个地址的数据读到寄存器,再从寄存器写到另一个地址,因为CPU是load/store架构,先load进来,再store回去;同时还会有地址修改,判断是否完全等,效率很低,而DMA就是在两个地址之间直接传输,效率极高。目的都是为了搬数据。

    并且在DSP上经常这么用,将数据从慢的DDR上通过EDMA搬到快的片上内存上,再运算处理,而不是直接在DDR上处理。也是为了快。

    定义指针没问题,就像你改过的一样,要对指针赋确定的值。

  • 在变量Data_in_DDR3和Data_in_MSM定义前加static后从DDR3到DDR3 传输成功,最高测试到16383*4个bytes,为什么必须加static 不清楚,还在探索原因。

    从DDR3到MSMCSRAM加static,最高传输1024*4个bytes成功,在多一个都传输失败,继续尝试。