我酱爆,要爆呃! 2025-01-10 10:01 采纳率: 75%
浏览 170
已结题

SG-cyclic模式Axi-Dma,如何实时更新缓存区内数据

**
循环模式Axi-Dma进行MM2S通道传输数据,描述符链所表示的数据缓存区内数据由FPGA不断更新,更新完成后会产生一个中断信号,获取到中断信号后继续通过MM2S通道传输数据,在MM2S通道的中断回调函数中加入停止条件,满足停止条件后停止传输。以上流程可以通过普通SG模式完成,但是因为普通SG模式每次传输完成后都要重新配置描述符链,带宽太低,于是改用cyclic模式传输。
cyclic模式下,在通过官方给的函数将配置好的描述符链传给硬件后,传输开始,但是FPGA无法向缓存区内更新数据,DMA一直在循环传输固定的第一帧数据。
我的问题是在cyclic模式传输时,如何实时更新缓存区内的数据?
以下是用官方例程sgcyclic_intr修改的cyclic模式MM2S通道传输图像数据代码,将传输通道设置和描述符设置合并到了一起,其它例如中断系统设置和S2MM通道设置都与官方例程一样。请有软核开发SG模式AxiDma经验的大老赐教。
**


//循环传输数据
static int SendPacket(XAxiDma * AxiDmaInstPtr)
{
    XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(AxiDmaInstPtr);
    XAxiDma_Bd *BdPtr, *BdCurPtr, BdTemplate;
    int Status;
    int Index, Pkts;
    UINTPTR BufferAddr;

#define TOTAL_BD_COUNT   NUMBER_OF_PKTS_TO_TRANSFER   //传输数量

    XAxiDma_BdRingIntDisable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);

    u32 BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
            (UINTPTR)TX_BD_SPACE_HIGH - (UINTPTR)TX_BD_SPACE_BASE + 1);
    if(BdCount >= TOTAL_BD_COUNT){
        BdCount = TOTAL_BD_COUNT;
    }

    //连接指定数量的Bd,形成Bd环
    Status = XAxiDma_BdRingCreate(TxRingPtr, TX_BD_SPACE_BASE,
            TX_BD_SPACE_BASE, XAXIDMA_BD_MINIMUM_ALIGNMENT, TOTAL_BD_COUNT);
    if (Status != XST_SUCCESS) {
        xil_printf("Failed create BD ring, error code %d\r\n", Status);
        return XST_FAILURE;
    }

    XAxiDma_BdClear(&BdTemplate);
    Status = XAxiDma_BdRingClone(TxRingPtr, &BdTemplate);
    if (Status != XST_SUCCESS) {
        xil_printf("Failed clone BDs\r\n");
        return XST_FAILURE;
    }

    //检查BdRing是否连接
    Status = XAxiDma_BdRingCheck(TxRingPtr);
    if(Status != XST_SUCCESS){
        xil_printf("Failed check BdRings %d\r\n", Status);
        return XST_FAILURE;
    }

    //指定传输次数合并为一次中断
    Status = XAxiDma_BdRingSetCoalesce(TxRingPtr, 1, 0);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    XAxiDma_BdRingEnableCyclicDMA(TxRingPtr);
    XAxiDma_SelectCyclicMode(AxiDmaInstPtr, XAXIDMA_DMA_TO_DEVICE, 1);

    XAxiDma_BdRingIntEnable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);

    //开启传输通道
    Status = XAxiDma_BdRingStart(TxRingPtr);
    if (Status != XST_SUCCESS) {
        xil_printf("Failed bd start\r\n");
        return XST_FAILURE;
    }

    img_stop = 0;  //Rx通道中断完成标志

    while(!img_stop){

        Xil_DCacheFlushRange((UINTPTR)TX_BUFFER_BASE, NUMBER_OF_PKTS_TO_TRANSFER * MAX_PKT_LEN);

        Status = XAxiDma_BdRingAlloc(TxRingPtr, NUMBER_OF_BDS_TO_TRANSFER, &BdPtr);
        if (Status != XST_SUCCESS) {
            xil_printf("Failed bd alloc error code %d\r\n", Status);
            return XST_FAILURE;
        }

        BufferAddr = (UINTPTR)Packet;
        BdCurPtr = BdPtr;

        Img_Intr = 0;   //图像中断标志
        //等待图像数据
        internal_trigger_get_img_data();
        while(!Img_Intr);

        for(Index = 0; Index < NUMBER_OF_PKTS_TO_TRANSFER; Index++) {

            for(Pkts = 0; Pkts < NUMBER_OF_BDS_PER_PKT; Pkts++) {

                Status = XAxiDma_BdSetBufAddr(BdCurPtr, BufferAddr);
                if (Status != XST_SUCCESS) {
                    xil_printf("Tx set buffer addr %x on BD %x failed %d\r\n",
                    (unsigned int)BufferAddr,
                    (UINTPTR)BdCurPtr, Status);

                    return XST_FAILURE;
                }

                Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN,
                            TxRingPtr->MaxTransferLen);
                if (Status != XST_SUCCESS) {
                    xil_printf("Tx set length %d on BD %x failed %d\r\n",
                    MAX_PKT_LEN, (UINTPTR)BdCurPtr, Status);

                    return XST_FAILURE;
                }

                u32 CrBits = 0;
                if (Pkts == 0) {
                    CrBits |= XAXIDMA_BD_CTRL_TXSOF_MASK;
                }
                if(Pkts == (NUMBER_OF_BDS_PER_PKT - 1)) {
                    CrBits |= XAXIDMA_BD_CTRL_TXEOF_MASK;
                }
                XAxiDma_BdSetCtrl(BdCurPtr, CrBits);
                XAxiDma_BdSetId(BdCurPtr, BufferAddr);
            }
            BufferAddr += MAX_PKT_LEN;
            BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(TxRingPtr, BdCurPtr);
        }

        Status = XAxiDma_BdRingToHw(TxRingPtr, NUMBER_OF_BDS_TO_TRANSFER, BdPtr);
        if (Status != XST_SUCCESS) {
            xil_printf("Failed to hw, length %d\r\n",
                    (int)XAxiDma_BdGetLength(BdPtr, TxRingPtr->MaxTransferLen));
            return XST_FAILURE;
        }
        while(XAxiDma_Busy(AxiDmaInstPtr, XAXIDMA_DMA_TO_DEVICE));
    }

    TxDone = 0;
    return XST_SUCCESS;
}
  • 写回答

32条回答 默认 最新

  • M_try的小尾巴 2025-01-10 10:24
    关注

    该回答引用自Deepseek,由本人整理审核 若有帮助,望采纳。

    在SG-cyclic模式下,AXI DMA的循环传输机制允许DMA在描述符链中循环传输数据,而不需要每次传输完成后重新配置描述符链。然而,你提到的问题是FPGA无法在cyclic模式下实时更新缓存区内的数据,导致DMA一直在传输固定的第一帧数据。以下是解决这个问题的步骤和建议:

    1. 确保FPGA能够正确更新缓存区

    在cyclic模式下,DMA会循环传输描述符链中的数据。如果FPGA无法更新缓存区,可能是因为缓存区的地址没有正确映射或FPGA没有正确写入数据。确保以下几点:

    • 缓存区地址映射正确:确保FPGA和DMA共享的缓存区地址是正确的,并且FPGA能够正确写入数据。
    • 缓存区同步:在FPGA更新缓存区后,确保缓存区的数据已经同步到DMA可以访问的内存区域。可以使用Xil_DCacheFlushRange函数来刷新缓存。

    2. 中断处理与数据更新

    在cyclic模式下,DMA会在每次传输完成后触发中断。你可以在中断处理函数中更新缓存区的数据。以下是中断处理函数的伪代码:

    void DmaTxIntrHandler(void *CallbackRef)
    {
        XAxiDma *AxiDmaInstPtr = (XAxiDma *)CallbackRef;
        XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(AxiDmaInstPtr);
        XAxiDma_Bd *BdPtr;
        int Status;
    
        // 获取已完成传输的描述符
        Status = XAxiDma_BdRingFromHw(TxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
        if (Status != XST_SUCCESS) {
            xil_printf("Failed to get completed BDs\n");
            return;
        }
    
        // 更新缓存区数据
        internal_trigger_get_img_data(); // 触发FPGA更新数据
        Xil_DCacheFlushRange((UINTPTR)TX_BUFFER_BASE, NUMBER_OF_PKTS_TO_TRANSFER * MAX_PKT_LEN);
    
        // 重新提交描述符
        Status = XAxiDma_BdRingToHw(TxRingPtr, NUMBER_OF_BDS_TO_TRANSFER, BdPtr);
        if (Status != XST_SUCCESS) {
            xil_printf("Failed to submit BDs\n");
            return;
        }
    
        // 清除中断标志
        XAxiDma_BdRingAckIntr(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);
    }
    

    3. 确保DMA配置正确

    在cyclic模式下,DMA的配置需要确保描述符链是循环的。以下是配置描述符链的关键步骤:

    // 创建描述符环
    Status = XAxiDma_BdRingCreate(TxRingPtr, TX_BD_SPACE_BASE, TX_BD_SPACE_BASE, XAXIDMA_BD_MINIMUM_ALIGNMENT, TOTAL_BD_COUNT);
    if (Status != XST_SUCCESS) {
        xil_printf("Failed create BD ring, error code %d\n", Status);
        return XST_FAILURE;
    }
    
    // 启用循环模式
    XAxiDma_BdRingEnableCyclicDMA(TxRingPtr);
    XAxiDma_SelectCyclicMode(AxiDmaInstPtr, XAXIDMA_DMA_TO_DEVICE, 1);
    
    // 启动描述符环
    Status = XAxiDma_BdRingStart(TxRingPtr);
    if (Status != XST_SUCCESS) {
        xil_printf("Failed bd start\n");
        return XST_FAILURE;
    }
    

    4. 停止条件处理

    在中断处理函数中,你可以检查停止条件并停止传输。例如:

    if (img_stop) {
        XAxiDma_BdRingStop(TxRingPtr);
        return;
    }
    

    5. 调试与验证

    • 调试FPGA数据更新:确保FPGA能够正确更新缓存区数据,并且数据已经同步到DMA可以访问的内存区域。
    • 调试DMA传输:使用调试工具(如Xilinx SDK中的调试器)检查DMA是否正确地循环传输数据。

    通过以上步骤,你应该能够在cyclic模式下实时更新缓存区内的数据,并确保DMA正确传输更新后的数据。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(31条)

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 1月13日
  • 已采纳回答 1月13日
  • 创建了问题 1月10日