第十一节,ZYNQ的AXI_DMA的使用

ZYNQAXI_DMA的使用

1 DMA控制器架构原理

AXIDMA:官方解释是为内存与AXI4-Stream外设之间提供高带宽的直接存储访问,其可选的scatter/gather功能可以将CPU从数据搬移任务中解放出来,在ZYNQ中,AXIDMA就是FPGA访问DDR3的桥梁,不过该过程受ARM的监控和管理。使用其他的IP(也是AXI4-Stream转AXI4-MM)可以不需要ARM管理。AXIDMA IP有6个接口,S_AXI_LITE是ARM配置DMA寄存器的接口,M_AXI_SG是从存储器加载buffer descriptor的接口,剩下4个接口构成两对,S2MM和MM2S表示数据的方向,AXI存储器一侧的接口,AXIS是FPGA一侧的接口。AXIDMA IP和ARM自带的DMA是很像的,只不过不具备从存储器到存储器的功能,当然也可以将S2MM和MM2S接口与AXIS接口直接相连,其结构如图1所示。

图1,AXI_DMA的IP结构

AXIDMA工作模式可以分为两种,Direct Register Mode和Scatter/Gather Mode。

Direct Register Mode具备DMA的基本功能,除了控制寄存器和状态寄存器之外,给出目的地址和传输长度之后就可以开启一次传输了。但是Direct Register Mode模式配置完一次寄存器之后只能完成存储器连续地址空间的读写,如果有需求往不同空间搬运数据的话,那就需要重新配置寄存器开启一次新的传输。

Scatter/Gather Mode配置灵活,其工作模式复杂很多。Scatter/Gather Mode把关于传输的基本参数(起始地址,传输长度,包信息等)存储在存储器中,一套参数称之为Buffer Descriptor(BD),在工作过程中通过上面提到的SG接口来加载BD且更新BD中的状态。Scatter/Gather Mode下的寄存器没有Address、Length相关寄存器了,取而代之的是CURDESC、TAILDESC。非多通道模式下的BD,主要有四部分内容:NXTDESC、BUFFER_ADDRESS、CONTROL、STATUS。NXTDESC指定下一个BD的地址,由此可以构成一个BD链条,AXIDMA可以顺着该链条依次fetch BD,BUFFER_ADDRESS指定传输的源地址或目的地址,CONTROL主要是length和包信息,STATUS反映该BD完成后的状态。AXIDMA启动后,首先从CURDESC指定的位置加载BD,完成当前BD的传输任务后根据BD链条找到下一个BD,依次完成BD指定的传输,知道遇到TALDESC指定的BD才停止。

Multichannel模式:在Scatter/Gather Mode下S2MM和MM2S都支持多个通道,Direct Register Mode不支持多通道,多通道相比非多通道,BD中增加了TID和TDEST,用来区分不同的通道。多通道支持2D-Transfer,从buffer address开始,读写HSIZE后跳过剩余的Stride – HSIZE个地址单元,下一次从buffer address + Stride位置开始,此过程迭代VSIZE此后结束该BD指定的传输。在Multichannel模式下S2MM有16个通道,每个通道都有独立的CURDESC和TAILDESC寄存器,而CR和SR则是共用的。而MM2S的多个通道共用一个CURDESC和TAILDESC寄存器,MM2S端只能等当前包传输完成才能开始下一次的传输,可能这与CPU不太容易同时操纵多个通道的数据包发送有关系。所以在实际使用时只能先执行一个通道的发送任务再执行另一个通道的发送任务。

Cyclic DMA:循环模式是在Scatter/Gather Mode模式下的一种独特工作方式,在Multichannel Mode下不可用。正常情况下的Scatter/Gather Mode模式在遇到Tail BD就应该结束当前的传输,但是如果使能了Cyclic模式的话,在遇到Tail BD时会忽略completed位,并且回到First BD,这一过程会一直持续直到遇到错误或者人为终止。Cyclic模式只需要在开启传输前设置好BD链条,工作之后就再不需要管了。

Data Cache:在zynq内部AMR CPU和DDR3之间存在两级缓存区,分别是L1 I/D Cache和L2 Cache,它们都是32-byte line size。Data Cache的使用带来了一个问题,DMA和CPU都与DDR3有数据往来,可CPU的Cache是不知道DMA对DDR3的数据读写过程的,也就是说CPU得到的数据很可能是“假的”,这就是Cache一致性问题。解决该问题的办法是在程序中使用flush函数(invalid函数)及时将Cache的数据写入到DDR3(从DDR3读取数据到Cache),也就是说要避免该问题就需要注意编码时加上flush函数。

 AXI_DMA实验

AXI_DMA实验,设计一个最基本的DMA环路,实现DMA的环路测试,在通过PS端发送数据到DMA然后DMA在把数据发回到DDR里面,PS读取内存地址里面的数据,对比接收的数据是否和发送出去的一致。DMA的接口部分使用了data_fifo的IP链接,其数据流如图2所示。

程序设计:

PL端设计:

建立好工程后,创建一个block,然后在其中添加zynq的IP,AXI4-Stream Data FIFO的IP,AXI Direct Memory Acceess的IP,以及Concat的IP。如图3所示。然后根据需求配置好各个IP的参数。

zynq的IP的参数配置如图4所示,在配置了ZYNQ的基本最小系统后,增加一些配置,由于这个系统需要PS提供时钟给AXI_DMA,所以在Clock Configuration选项卡中配置了PL Fabric clocks->FCLK_CLK0为100M,启动1路HP接口,HP接口是ZYNQ个高速数据接口,所以在PS-PL Configuration选项卡选项中配置了一路HP Slave AXI Interface->S AXI HP0 interface,需要用到PL的中断资源,所以在Interrupts选项卡中增加了PL中断,Fabric Interrupts->PL-PS Interrupt Port->IRQ_F2P[15:0]。

 

图3 添加IP

图4 ZYNQ的配置

AXI Direct Memory Access IP的配置如图5所示,同时勾选读通道和写通道,另外设置,Wideh of buffer length register 为23bit 这个含义是2的23次方8,388,607bytes8M大小,这里设置14bit 就够用了,长度越大需要的资源也就越多

      

图5 AXI Direct Memory Access的配置

AXI4-Stream Data FIFO的IP的配置如图6所示。

图6 AXI4-Stream Data FIFO的配置

Concat的IP的配置如图7所示。

图7 Concat的IP配置

连接好物理连线,最终生成硬件版图如图8所示。

图8 硬件配置

PS端设计:

由于是一个回环设计,所以在实现DMA传输数据时,需要用到接收中断和发送中断,所以外设初始化配置和测试流程如下:

  1. 初始化AXI_DMA的数据结构配置,配置其工作模式为Direct Register Mode;
  2. 将GIC中断映射到硬件中断上;
  3. 配置好GIC中断,并注册好发送完成中断和接收完成中断的服务函数,设置好对应中断的优先级,并且开启接收和发送中断;
  4. 完成中断服务函数的编写(清除中断,解析中断源,完成中断操作);
  5. 配置发送数据缓存和接收数据缓存,设置好DMA的源地址和目的地址以及发送数据长度,触发DMA发送传输和接收传输。

步骤1函数源码:

int Xaxi_dma_init(void)

{

    int Status = 0;

    XAxiDma_Config* XaxidmaConfigInit = NULL;

    //1,find device

    XaxidmaConfigInit = XAxiDma_LookupConfig(XPAR_AXIDMA_0_DEVICE_ID);

    //2,config Xaxidma

    Status = XAxiDma_CfgInitialize(&XAxi_Dma,XaxidmaConfigInit);

    if (Status != XST_SUCCESS)

    {

        xil_printf("Initialization failed %d\r\n", Status);

        return XST_FAILURE;

    }

    //3,axidma mode is sg

    if(XAxiDma_HasSg(&XAxi_Dma))

    {

        xil_printf("Device configured as SG mode \r\n");

        return XST_FAILURE;

    }

    return XST_SUCCESS;

}

步骤2函数源码:

void Exception_Init(XScuGic* Xscugic)

{

    //1,connect to hardware intc

    Xil_ExceptionInit();

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)Xscugic);

 

    //2,enable hardware intc

    Xil_ExceptionEnable();

    Xil_ExceptionDisableMask(XIL_EXCEPTION_ID_IRQ_INT);

 

}

步骤3函数源码:

void Axidma_Gic_Init(XScuGic* Xscugic,XAxiDma *AxiDmaPtr,u16 GicID,u16 TxIntrId,u16 RxIntrId)

{

    XScuGic_Config *GicConfigInit;

    //1,find device

    GicConfigInit = XScuGic_LookupConfig(GicID);

    //2,config gicconfig

  XScuGic_CfgInitialize(Xscugic,GicConfigInit,GicConfigInit->CpuBaseAddress);

    //3,gic selftest

    XScuGic_SelfTest(Xscugic);

 

    //4,gic connect to axidma

    XScuGic_Connect(Xscugic, TxIntrId,(Xil_InterruptHandler)Dma_TxHandler,(void *)AxiDmaPtr);

    XScuGic_Connect(Xscugic, RxIntrId,(Xil_InterruptHandler)Dma_RxHandler,(void *)AxiDmaPtr);

    //5,set gic interrupt Priority and type

    XScuGic_SetPriorityTriggerType(Xscugic,TxIntrId,0xA0, 0x03);

    XScuGic_SetPriorityTriggerType(Xscugic,RxIntrId,0xA0, 0x03);

    //6,enable gic interrupt

    XScuGic_Enable(Xscugic,TxIntrId);

    XScuGic_Enable(Xscugic,RxIntrId);

}

步骤4函数源码:

void Dma_RxHandler(void* CallBackRef)

{

    u32 irqstatus = 0;

    int TimeOut = RESET_TIMEOUT_COUNTER;

    XAxiDma *XaxidmaPter = (XAxiDma*)CallBackRef;

    //1,Read pending interrupts /XAXIDMA_DEVICE_TO_DMA

    irqstatus = XAxiDma_IntrGetIrq(XaxidmaPter,XAXIDMA_DEVICE_TO_DMA);

    //2,Acknowledge pending interrupts

    XAxiDma_IntrAckIrq(XaxidmaPter,irqstatus,XAXIDMA_DEVICE_TO_DMA);

    //3,If no interrupt is asserted, we do not do anything

    if (!(irqstatus & XAXIDMA_IRQ_ALL_MASK))

    {

        return;

    }

    //4,If error interrupt is asserted, raise error flag, reset the hardware to recover from the error, and return with no further processing.

    if(irqstatus & XAXIDMA_IRQ_ERROR_MASK)

    {

        //1,flag = 1

        Error = 1;

        //2,Reset should never fail for transmit channel

        XAxiDma_Reset(XaxidmaPter);

        //3,wait time out

        TimeOut = RESET_TIMEOUT_COUNTER;

        while(TimeOut)

        {

            if (XAxiDma_ResetIsDone(XaxidmaPter))

            {

                break;

            }

            TimeOut--;

        }

        return;

    }

    //5,If Completion interrupt is asserted, then set the TxDone flag

    if(irqstatus & XAXIDMA_IRQ_IOC_MASK)

    {

        RxDone = 1;

    }

}

void Dma_TxHandler(void* CallBackRef)

{

    u32 irqstatus = 0;

    int TimeOut = RESET_TIMEOUT_COUNTER;

    XAxiDma *XaxidmaPter = (XAxiDma*)CallBackRef;

    //1,Read pending interrupts /XAXIDMA_DMA_TO_DEVICE

    irqstatus = XAxiDma_IntrGetIrq(XaxidmaPter,XAXIDMA_DMA_TO_DEVICE);

    //2,Acknowledge pending interrupts

    XAxiDma_IntrAckIrq(XaxidmaPter,irqstatus,XAXIDMA_DMA_TO_DEVICE);

 

    //3,If no interrupt is asserted, we do not do anything

    if (!(irqstatus & XAXIDMA_IRQ_ALL_MASK))

    {

        return;

    }

    //4,If error interrupt is asserted, raise error flag, reset the hardware to recover from the error, and return with no further processing.

    if(irqstatus & XAXIDMA_IRQ_ERROR_MASK)

    {

        //1,flag = 1

        Error = 1;

        //2,Reset should never fail for transmit channel

        XAxiDma_Reset(XaxidmaPter);

        //3,wait time out

        TimeOut = RESET_TIMEOUT_COUNTER;

        while(TimeOut)

        {

            if (XAxiDma_ResetIsDone(XaxidmaPter))

            {

                break;

            }

            TimeOut--;

        }

        return;

    }

    //5,If Completion interrupt is asserted, then set the TxDone flag

    if(irqstatus & XAXIDMA_IRQ_IOC_MASK)

    {

        TxDone = 1;

    }

}

步骤5函数源码:

int axi_dma_test()

{

    int Status;

    TxDone = 0;

    RxDone = 0;

    Error = 0;

    xil_printf("\r\n----DMA Test----\r\n");

    xil_printf("PKT_LEN=%d\r\n",MAX_PKT_LEN);

 

    for(i = 0; i < Tries; i ++)

    {

        LED2_ON;

        Value = TEST_START_VALUE + (i & 0xFF);

        for(Index = 0; Index < MAX_PKT_LEN; Index ++)

        {

                TxBufferPtr[Index] = Value;

                Value = (Value + 1) & 0xFF;

        }

        /* Flush the SrcBuffer before the DMA transfer, in case the Data Cache

         * is enabled

         */

        Xil_DCacheFlushRange((u32)TxBufferPtr, MAX_PKT_LEN);

 

        Status = XAxiDma_SimpleTransfer(&XAxi_Dma,(u32) RxBufferPtr,

                   MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

 

        if (Status != XST_SUCCESS) {

            return XST_FAILURE;

        }

        Status = XAxiDma_SimpleTransfer(&XAxi_Dma,(u32) TxBufferPtr,

                   MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

        if (Status != XST_SUCCESS) {

            return XST_FAILURE;

        }

        /*

         * Wait TX done and RX done

         */

        while (!TxDone || !RxDone) {

                /* NOP */

        }

 

        success++;

        TxDone = 0;

        RxDone = 0;

        if (Error) {

            xil_printf("Failed test transmit%s done, "

            "receive%s done\r\n", TxDone? "":" not",

                           RxDone? "":" not");

            goto Done;

        }

        /*

         * Test finished, check data

         */

        Status = DMA_CheckData(MAX_PKT_LEN, (TEST_START_VALUE + (i & 0xFF)));

        if (Status != XST_SUCCESS) {

            xil_printf("Data check failed\r\n");

            goto Done;

        }

        LED2_OFF;

    }

    xil_printf("AXI DMA interrupt example test passed\r\n");

    xil_printf("success=%d\r\n",success);

    LED1_ON;

    /* Disable TX and RX Ring interrupts and return success */

//  DMA_DisableIntrSystem(&Xgic, TX_INTC_ID, RX_INTC_ID);

Done:

    xil_printf("--- Exiting Test --- \r\n");

    return XST_SUCCESS;

}

  • 11
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Zynq SoC 上,AXI DMA(Direct Memory Access)是一种能够在外设和存储器之间进行高速数据传输的IP核。Cyclic DMA 模式允许通过循环传输数据,从而实现连续的数据流。下面是一些关于在 Zynq SoC 上编程 AXI DMA 的基本步骤: 1. 配置 AXI DMA IP:首先,在 Vivado 中配置和实例化 AXI DMA IP 核,并连接到 Zynq SoC 的 PS(Processing System)部分。确保正确连接 AXI DMA 的信号和端口,包括数据输入/输出端口、中断线和控制寄存器。 2. 设置 DMA 控制寄存器:使用 AXI DMA 的控制寄存器来配置 DMA 的工作模式和参数。在 Cyclic DMA 模式下,你需要设置循环传输模式,并指定传输的数据长度和起始地址。 3. 配置 DMA 缓冲区:为 DMA 设置输入和输出缓冲区。这些缓冲区将用于在外设和存储器之间传输数据。 4. 启动 DMA 传输:通过设置 AXI DMA 控制寄存器中的位来启动数据传输。在 Cyclic DMA 模式下,你可以选择是否在每次传输完成后自动重启传输。 5. 处理中断:如果需要,在 AXI DMA 完成传输时,你可以使用中断机制来通知处理器。在中断处理程序中,你可以执行必要的操作,如处理传输的数据或触发下一次传输。 需要注意的是,以上步骤只是一个基本的框架,具体的实现可能因应用需求而有所差异。你可以参考 Xilinx 提供的文档和示例代码来详细了解 AXI DMA 的编程和配置方法。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值