• S03_CH01_AXI_DMA_LOOP 环路测试


    S03_CH01_AXI_DMA_LOOP 环路测试

    1.1概述

    本课程是本季课程里面最简单,也是后面DMA课程的基础,读者务必认真先阅读和学习。

    本课程的设计原理分析。

    本课程是设计一个最基本的DMA环路,实现DMA的环路测试,在SDK里面发送数据到DMA然后DMA在把数据发回到DDR里面,SDK读取内存地址里面的数据,对比接收的数据是否和发送出去的一致。DMA的接口部分使用了data_fifo IP链接。本课程会详细介绍创建工程的每个步骤,后面的课程将不再详细介绍创建工程的步骤。

    1.2搭建硬件系统

    1.2.1新建VIVADO工程

    Step1:启动VIVADO,单击Create New Project

    wps2A3.tmp

    Step2:单击NEXT

    wps2A4.tmp

    Step3:创建名为Miz_sys的工程到对应的文件目录,之后单击NEXT

    wps2B5.tmp

    Step4:选择RTL Project并且勾选复选框,之后单击NEXT

    wps2B6.tmp

    Step5:选择芯片的型号和封装速度等级。

    MIZ702/MIZ702N选择Zynq-7000-xc7z020clg484-1

    MIZ701N-7010选择Zynq-7000-xc7z010clg400-1

    MIZ701N-7020选择Zynq-7000-xc7z020clg400-2

    wps2B7.tmpwps2B8.tmp wps2C9.tmp

    Step:6 单机Finish完成工程创建。

    1.2.2创建VIVADO硬件构架

    Step1:单击 Create Block Design

    wps2CA.tmp

    Step2:命名为system 之后单击OK

    wps2CB.tmp

    Step3:创建完成后如下图所示

    wps2CC.tmp

    Step3:添加各个模块如图:

    wps2DC.tmp

    Step4双击ZYNQ IP 进行如下步骤配置

    Step5: MIZ702和MIZ702N的输入时钟是333.333333MHZ

    wps2DD.tmp

    Step6:MIZ701N PS的输入时钟是50MHZ

    wps2DE.tmp

    Step7:MIZ702的开发板采用的是单片256MB的MT41K128M16JI-125

    wps2EF.tmp

    wps2F0.tmp

    Step8:MIZ701N和MIZ702N的内存型号一样,都是单片512MB的MT41K256M16RE-125

    wps2F1.tmp

    Step9:PS的PLL提供本系统的时钟100MHZ

    wps2F2.tmp

    Step10:启动1路HP接口,HP接口是ZYNQ个高速数据接口

    wps302.tmp

    Step11:勾选PL到PS的中断资源(关于中断,在第二季的课程中有详细讲解,不熟悉的读者可以到第二季课程中温习一下)

    wps303.tmp

    Step12:设置完成后单击OK

    Step13:双击DMA  IP 设置如下:

    下图中,同时勾选读通道和写通道,另外设置,Wideh of buffer length register 为23bit 这个含义是2的23次方8,388,607bytes 8M大小,这里设置14bit 就够用了,长度越大需要的资源也就越多。

    wps304.tmp

    Step14:Data FIFO 设置

    wps315.tmp

    Step15:Concat IP设置

    Concate IP实现了单个分散的信号,整合成总线信号。这里2个独立的中断信号,可以合并在一起介入到ZYNQ IP的中断信号上。

    wps316.tmp

    Step16:Run Automation 自动配置ZYNQ IP 如下图所示

    wps317.tmp

    Step17:单击Run Connection Automation 自动连线,只要软件提示你需要自动连线,一般都需要进行自动连线,除非自己知道如何连线,有特殊需求。

    wps318.tmp

    Step18:如果还有提示需要自动连线的继续让软件自动连线,直到出下如下。可以看到,还有未连线的模块。

    wps329.tmp

    Step19:把DMA收发中断信号,通过contact IP连接到ZYNQ

    wps32A.tmp

    Step20:

    连接FIFO的S_AXIS(写端口)到DMA的M_AXIS(DMA读端口);

    连接FIFO的M_AXIS(读端口)到DMA的S_AXIS(DMA写端口);

    连接FIFO 的a_axis_aresetn到 复位IP的peripheral aresetn ;

    连接FIFO的s_axis_ack到ZYNQ  IP 的FCLK0;

    连接完成后如下图

    wps32B.tmp

    Step21:把OLED 模块的IO引出来,后面C代码部分会用OLED显示一些信息(MIZ701N需要配OLED模块)。连接完成后的工程如下图

    wps33B.tmp

    Step22:未来调试的时候可以观察到中断信号的产生,添加ila 调试IP并且进行如下设置

    wps33C.tmp

    wps33D.tmp

    Step23:把中断信号连接到 ila IP上,另外,把时钟信号也连接起来。

    wps33E.tmp

    Step24:

    以上就完成独立工程的创建。

    之后的过程是Validate Design->Gerate Out products->Create wrappers->Generate Bitstream 产生完成后导入到SDK进行软件开发。

    1.3 PS部分软件分析

    1.3.1新建SDK工程

    Step1:新建一个名为AXI_DMA_Test的空的软件工程

    wps34F.tmp

    wps350.tmp

    Step2:直接把源码复制过来,软件会自动编译

    wps351.tmp

    1.3.2 main.c源码的分析

    init_intr_sys();是对中断资源的初始化,使能中断资源。这个函数里面调用的函数是笔者封装好的初始化函数,使用起来比较方便。一般只要给出中断对象,中断号,就可以对中断进行初始化。

    DMA_Intr_Init(&AxiDma,0);中第一参数是DMA的对象,第二参数是硬件ID

    Init_Intr_System(&Intc); 对象是中断对象

    DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID); //注册中断函数,最后2个参数是中断号

    DMA_Intr_Enable(&Intc,&AxiDma); 就是启动DMA传输

    表1-3-2-1 init_intr_sys函数

    int init_intr_sys(void)

    {

    DMA_Intr_Init(&AxiDma,0);//initial interrupt system

    Init_Intr_System(&Intc); // initial DMA interrupt system

    Setup_Intr_Exception(&Intc);

    DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID);//setup dma interrpt system

    DMA_Intr_Enable(&Intc,&AxiDma);

    }

    为了发送的数据是已知是确定数据,先对TxBufferPtr 发送缓冲进行初始化,初始化后用Xil_DCacheFlushRange 函数把数据全部刷到DDR中。

    XAxiDma_SimpleTransfer 函数为启动一次DMA接收传输。

    XAxiDma_SimpleTransfer 函数为启动一次DMA发送传输

    DMA_CheckData 函数为对接收的数据进行校验和对比。

    表1-3-2-1 DMA_CheckData

    int axi_dma_test()

    {

    int Status;

    TxDone = 0;

    RxDone = 0;

    Error = 0;

    xil_printf(" ----DMA Test---- ");

    print_message( "----DMA Test----",0);//oled print

    xil_printf("PKT_LEN=%d ",MAX_PKT_LEN);

    sprintf(oled_str,"PKT_LEN=%d",MAX_PKT_LEN);

    print_message(oled_str,1);//oled print

    //while(1)

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

    {

    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(&AxiDma,(u32) RxBufferPtr,

    MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

    if (Status != XST_SUCCESS) {

    return XST_FAILURE;

    }

    Status = XAxiDma_SimpleTransfer(&AxiDma,(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 ", 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 ");

    goto Done;

    }

    }

    xil_printf("AXI DMA interrupt example test passed ");

    xil_printf("success=%d ",success);

    sprintf(oled_str,"success=%d",success);

    print_message(oled_str,2);

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

    DMA_DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);

    Done:

    xil_printf("--- Exiting Test --- ");

    print_message("--Exiting Test---",3);

    return XST_SUCCESS;

    }

    int init_intr_sys(void)

    {

    DMA_Intr_Init(&AxiDma,0);//initial interrupt system

    Init_Intr_System(&Intc); // initial DMA interrupt system

    Setup_Intr_Exception(&Intc);

    DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID);//setup dma interrpt system

    DMA_Intr_Enable(&Intc,&AxiDma);

    }

    int main(void)

    {

    init_intr_sys();

    oled_fresh_en();// enable oled

    axi_dma_test();

    }

    1.3.3 dma_intr.c 源码分析

    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;这句代码是为了获取当前中断的对象。void *Callback是一个无符号的指针,传递进来的阐述可以强制转换成其他任何的对象,这里就是强制转换成 XAxiDma 对象了。

    IrqStatus =XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE)这个函数获取当前中断号。

    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);这个函数是响应当前中断,通知CPU 当前中断已经被接收,并且清除中断标志位。

    如果中断全部正确,TxDone将被置为1表示发送中断完成。

    如果有错误,则复位DMA,并且设置超时参数

    表1-3-3-1 DMA_TxIntrHandler函数

    /*****************************************************************************/

    /*

    *

    * This is the DMA TX Interrupt handler function.

    *

    * It gets the interrupt status from the hardware, acknowledges it, and if any

    * error happens, it resets the hardware. Otherwise, if a completion interrupt

    * is present, then sets the TxDone.flag

    *

    * @param Callback is a pointer to TX channel of the DMA engine.

    *

    * @return None.

    *

    * @note None.

    *

    ******************************************************************************/

    static void DMA_TxIntrHandler(void *Callback)

    {

    u32 IrqStatus;

    int TimeOut;

    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

    /* Read pending interrupts */

    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);

    /* Acknowledge pending interrupts */

    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);

    /*

    * If no interrupt is asserted, we do not do anything

    */

    if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {

    return;

    }

    /*

    * 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)) {

    Error = 1;

    /*

    * Reset should never fail for transmit channel

    */

    XAxiDma_Reset(AxiDmaInst);

    TimeOut = RESET_TIMEOUT_COUNTER;

    while (TimeOut) {

    if (XAxiDma_ResetIsDone(AxiDmaInst)) {

    break;

    }

    TimeOut -= 1;

    }

    return;

    }

    /*

    * If Completion interrupt is asserted, then set the TxDone flag

    */

    if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

    TxDone = 1;

    }

    }

    接收中断函数的原理和发送一样

    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;这句代码是为了获取当前中断的对象。void *Callback是一个无符号的指针,传递进来的阐述可以强制转换成其他任何的对象,这里就是强制转换成 XAxiDma 对象了。

    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);这个函数是获取当前中断号。

    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);这个函数是响应当前中断,通知CPU 当前中断已经被接收,并且清除中断标志位。

    如果中断全部正确,RxDone将被置为1表示接收中断完成。

    如果有错误,则复位DMA,并且设置超时参数

    表1-3-3-2 DMA_RxIntrHandler函数

    /*****************************************************************************/

    /*

    *

    * This is the DMA RX interrupt handler function

    *

    * It gets the interrupt status from the hardware, acknowledges it, and if any

    * error happens, it resets the hardware. Otherwise, if a completion interrupt

    * is present, then it sets the RxDone flag.

    *

    * @param Callback is a pointer to RX channel of the DMA engine.

    *

    * @return None.

    *

    * @note None.

    *

    ******************************************************************************/

    static void DMA_RxIntrHandler(void *Callback)

    {

    u32 IrqStatus;

    int TimeOut;

    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

    /* Read pending interrupts */

    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);

    /* Acknowledge pending interrupts */

    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);

    /*

    * If no interrupt is asserted, we do not do anything

    */

    if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {

    return;

    }

    /*

    * 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)) {

    Error = 1;

    /* Reset could fail and hang

    * NEED a way to handle this or do not call it??

    */

    XAxiDma_Reset(AxiDmaInst);

    TimeOut = RESET_TIMEOUT_COUNTER;

    while (TimeOut) {

    if(XAxiDma_ResetIsDone(AxiDmaInst)) {

    break;

    }

    TimeOut -= 1;

    }

    return;

    }

    /*

    * If completion interrupt is asserted, then set RxDone flag

    */

    if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

    RxDone = 1;

    }

    }

    表1-3-3-3 DMA_CheckData函数

    /*****************************************************************************/

    /*

    *

    * This function checks data buffer after the DMA transfer is finished.

    *

    * We use the static tx/rx buffers.

    *

    * @param Length is the length to check

    * @param StartValue is the starting value of the first byte

    *

    * @return

    * - XST_SUCCESS if validation is successful

    * - XST_FAILURE if validation is failure.

    *

    * @note None.

    *

    ******************************************************************************/

    int DMA_CheckData(int Length, u8 StartValue)

    {

    u8 *RxPacket;

    int Index = 0;

    u8 Value;

    RxPacket = (u8 *) RX_BUFFER_BASE;

    Value = StartValue;

    /* Invalidate the DestBuffer before receiving the data, in case the

    * Data Cache is enabled

    */

    #ifndef __aarch64__

    Xil_DCacheInvalidateRange((u32)RxPacket, Length);

    #endif

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

    if (RxPacket[Index] != Value) {

    xil_printf("Data error %d: %x/%x ",

        Index, RxPacket[Index], Value);

    return XST_FAILURE;

    }

    Value = (Value + 1) & 0xFF;

    }

    return XST_SUCCESS;

    }

    1.3.4 dam_intr.h 文件分析

    一般把DMA相关变量、常量、函数的声明或者定义放到头文件中,dam_intr.h比较关键的参数有

    TX_BUFFER_BASE定义了DMA发送缓存的基地址

    RX_BUFFER_BASE 定义了DMA接收缓存的基地址

    MAX_PKT_LEN 表示每一包数据传输的长度

    NUMBER_OF_TRANSFERS 用在连续测试的时候的测试次数

    TEST_START_VALUE 用于 测试的起始参数

    int  DMA_CheckData(int Length, u8 StartValue); 对数据进行对比

    int  DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);DMA 中断注册

    int  DMA_Intr_Enable(XScuGic * IntcInstancePtr,XAxiDma *DMAPtr); DMA中断使能

    int  DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId);DMA中断初始化

    表1-3-4 dam_intr.h

    /*

    *

    * www.osrc.cn

    * www.milinker.com

    * copyright by nan jin mi lian dian zi www.osrc.cn

    */

    #ifndef DMA_INTR_H

    #define DMA_INTR_H

    #include "xaxidma.h"

    #include "xparameters.h"

    #include "xil_exception.h"

    #include "xdebug.h"

    #include "xscugic.h"

    /************************** Constant Definitions *****************************/

    /*

    * Device hardware build related constants.

    */

    #define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID

    #define MEM_BASE_ADDR 0x01000000

    #define RX_INTR_ID XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR

    #define TX_INTR_ID XPAR_FABRIC_AXI_DMA_0_MM2S_INTROUT_INTR

    #define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000)

    #define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000)

    #define RX_BUFFER_HIGH (MEM_BASE_ADDR + 0x004FFFFF)

    /* Timeout loop counter for reset

    */

    #define RESET_TIMEOUT_COUNTER 10000

    /* test start value

    */

    #define TEST_START_VALUE 0xC

    /*

    * Buffer and Buffer Descriptor related constant definition

    */

    #define MAX_PKT_LEN 256//4MB

    /*

    * transfer times

    */

    #define NUMBER_OF_TRANSFERS 100000

    extern volatile int TxDone;

    extern volatile int RxDone;

    extern volatile int Error;

    int  DMA_CheckData(int Length, u8 StartValue);

    int  DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);

    int  DMA_Intr_Enable(XScuGic * IntcInstancePtr,XAxiDma *DMAPtr);

    int  DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId);

    #endif

    1.4测试结果

    Step1:在VIVADO工程中点击Open Target 然后点击Auto Connect(前面必须先启动SDK)

    wps371.tmp

    Step2:连接成功后入下图

    wps372.tmp

    Step3:设置中断条件,以及观察波形的偏移为500,当中断触发的时候,如下图所示

    wps373.tmp

    点击添加接收内存部分地址用于观察内存中的数据 地址为 0X01300000

    wps374.tmp

    为了观察到以下数据,设置断点,之让收发程序先跑一次,可以看到第一个数据是0X0C 后面是依次加1

    wps385.tmp

  • 相关阅读:
    Photoshop 基础三 制作简单按钮
    Photoshop 基础二 快捷键
    Photoshop 基础一 安装
    Winform 基础二 最小化 最大化 关闭 点击任务栏隐藏显示 点击鼠标左键移动窗体
    Winform 基础一 panel
    ASP 基础三 SQL指令
    回文&升降 数位dp
    双向链表()
    Two strings HDU
    Just do it HDU
  • 原文地址:https://www.cnblogs.com/milinker/p/6484011.html
Copyright © 2020-2023  润新知