• 利用ZYNQ SOC快速打开算法验证通路(4)——AXI DMA使用解析及环路测试


    一、AXI DMA介绍

      本篇博文讲述AXI DMA的一些使用总结,硬件IP子系统搭建与SDK C代码封装参考米联客ZYNQ教程。若想让ZYNQ的PS与PL两部分高速数据传输,需要利用PS的HP(高性能)接口通过AXI_DMA完成数据搬移,这正符合PG021 AXI DMA v7.1 LogiCORE IP Product Guide中介绍的AXI DMA的应用场景:The AXI DMA provides high-speed data movement between system memory and an AXI4-Stream-based target IP such as AXI Ethernet.

      如图,AXI DMA主要包括Memory Map和 Stream两部分接口,前者连接PS子系统,后者则连接带有流接口的PL IP核。

      其最简单的事直接寄存器模式(Simple DMA),这里需要注意地址对齐的问题:当没有使能地址重对齐的情况下,如果AXI Memory Map数据位宽是32bit,则搬移数据所在地址必须在0x0,0x4,0x8等起始地址上。接下来关注DMA IP核配置界面主要参数:

      AXI DMA可以有两个传输方向:读通道和写通道,依次为MM2S和S2MM方向。也就是说“读”和“写”是DMA主动对CPU发起的操作。重点查看以下几个参数:

    1 Width of Buffer Length Register:

      在直接寄存器模式下,它指定在MM2S_LENGTH和S2MM_LENGTH寄存器的有效比特数。MM2S_LENGTH寄存器指定了MM2S通道传输数据字节数,当CPU写入非零值时开始进行PS到PL的数据搬移,而S2MM_LENGTH对应另一个数据流方向。比特数直接与对应寄存器可写入的最大数直接相关,传输最大字节数= 2^(Width of Buffer Length Register)。此处保持默认14bit,也就是说启动DMA传输的最大数据量是16384byte。

    2 Memory Map Data Width:

      该参数指定了Memory Map侧数据接口宽度,选定32bit后搬移数据所在内存地址必须与4对齐。

    3 Max Burst Size:

      之前在讲解PS子系统内部的DMA时介绍过DMA的Burst概念,即分批次传输数据块。官方IP核文档解释为:

      理解起来burst size确定了突发周期的最大数值,也就是burst size越大,突发粒度越大(单次传输的数据个数越多)。这与PS端DMA有所区别,显然与 PS DMA的burst length意义相近。笔者也进行过尝试,当启动传输数据量相同时,burst size设置较大情况下,每批次传输数据量更多。

     二、AXI DMA Loop IP子系统

      在利用ZYNQ搭建系统时,经常需要利用各种IP核做所谓的“计算加速”,将重复性高 计算量大 占用较大CPU资源的底层处理交给各个IP核完成。这时PS ->DMA ->PL -> DMA -> PS的环路架构非常适用。这里使用AXI Stream Data FIFO代替自定义IP核作为演示,硬件IP子系统如下:

     三、SDK 官方demo解析

      首先分析下官方的demo。

      1 /******************************************************************************
      2 *
      3 * Copyright (C) 2010 - 2016 Xilinx, Inc.  All rights reserved.
      4 *
      5 * Permission is hereby granted, free of charge, to any person obtaining a copy
      6 * of this software and associated documentation files (the "Software"), to deal
      7 * in the Software without restriction, including without limitation the rights
      8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9 * copies of the Software, and to permit persons to whom the Software is
     10 * furnished to do so, subject to the following conditions:
     11 *
     12 * The above copyright notice and this permission notice shall be included in
     13 * all copies or substantial portions of the Software.
     14 *
     15 * Use of the Software is limited solely to applications:
     16 * (a) running on a Xilinx device, or
     17 * (b) that interact with a Xilinx device through a bus or interconnect.
     18 *
     19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     22 * XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
     24 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     25 * SOFTWARE.
     26 *
     27 * Except as contained in this notice, the name of the Xilinx shall not be used
     28 * in advertising or otherwise to promote the sale, use or other dealings in
     29 * this Software without prior written authorization from Xilinx.
     30 *
     31 ******************************************************************************/
     32 /*****************************************************************************/
     33 /**
     34  *
     35  * @file xaxidma_example_simple_intr.c
     36  *
     37  * This file demonstrates how to use the xaxidma driver on the Xilinx AXI
     38  * DMA core (AXIDMA) to transfer packets.in interrupt mode when the AXIDMA core
     39  * is configured in simple mode
     40  *
     41  * This code assumes a loopback hardware widget is connected to the AXI DMA
     42  * core for data packet loopback.
     43  *
     44  * To see the debug print, you need a Uart16550 or uartlite in your system,
     45  * and please set "-DDEBUG" in your compiler options. You need to rebuild your
     46  * software executable.
     47  *
     48  * Make sure that MEMORY_BASE is defined properly as per the HW system. The
     49  * h/w system built in Area mode has a maximum DDR memory limit of 64MB. In
     50  * throughput mode, it is 512MB.  These limits are need to ensured for
     51  * proper operation of this code.
     52  *
     53  *
     54  * <pre>
     55  * MODIFICATION HISTORY:
     56  *
     57  * Ver   Who  Date     Changes
     58  * ----- ---- -------- -------------------------------------------------------
     59  * 4.00a rkv  02/22/11 New example created for simple DMA, this example is for
     60  *                  simple DMA,Added interrupt support for Zynq.
     61  * 4.00a srt  08/04/11 Changed a typo in the RxIntrHandler, changed
     62  *               XAXIDMA_DMA_TO_DEVICE to XAXIDMA_DEVICE_TO_DMA
     63  * 5.00a srt  03/06/12 Added Flushing and Invalidation of Caches to fix CRs
     64  *               648103, 648701.
     65  *               Added V7 DDR Base Address to fix CR 649405.
     66  * 6.00a srt  03/27/12 Changed API calls to support MCDMA driver.
     67  * 7.00a srt  06/18/12 API calls are reverted back for backward compatibility.
     68  * 7.01a srt  11/02/12 Buffer sizes (Tx and Rx) are modified to meet maximum
     69  *               DDR memory limit of the h/w system built with Area mode
     70  * 7.02a srt  03/01/13 Updated DDR base address for IPI designs (CR 703656).
     71  * 9.1   adk  01/07/16 Updated DDR base address for Ultrascale (CR 799532) and
     72  *               removed the defines for S6/V6.
     73  * 9.2   vak  15/04/16 Fixed compilation warnings in the example
     74  * </pre>
     75  *
     76  * ***************************************************************************
     77  */
     78 
     79 /***************************** Include Files *********************************/
     80 
     81 #include "xaxidma.h"
     82 #include "xparameters.h"
     83 #include "xil_exception.h"
     84 #include "xdebug.h"
     85 
     86 #ifdef XPAR_UARTNS550_0_BASEADDR
     87 #include "xuartns550_l.h"       /* to use uartns550 */
     88 #endif
     89 
     90 
     91 #ifdef XPAR_INTC_0_DEVICE_ID
     92  #include "xintc.h"
     93 #else
     94  #include "xscugic.h"
     95 #endif
     96 
     97 /************************** Constant Definitions *****************************/
     98 
     99 /*
    100  * Device hardware build related constants.
    101  */
    102 
    103 #define DMA_DEV_ID        XPAR_AXIDMA_0_DEVICE_ID
    104 
    105 #ifdef XPAR_AXI_7SDDR_0_S_AXI_BASEADDR
    106 #define DDR_BASE_ADDR        XPAR_AXI_7SDDR_0_S_AXI_BASEADDR
    107 #elif XPAR_MIG7SERIES_0_BASEADDR
    108 #define DDR_BASE_ADDR    XPAR_MIG7SERIES_0_BASEADDR
    109 #elif XPAR_MIG_0_BASEADDR
    110 #define DDR_BASE_ADDR    XPAR_MIG_0_BASEADDR
    111 #elif XPAR_PSU_DDR_0_S_AXI_BASEADDR
    112 #define DDR_BASE_ADDR    XPAR_PSU_DDR_0_S_AXI_BASEADDR
    113 #endif
    114 
    115 #ifndef DDR_BASE_ADDR
    116 #warning CHECK FOR THE VALID DDR ADDRESS IN XPARAMETERS.H, 
    117         DEFAULT SET TO 0x01000000
    118 #define MEM_BASE_ADDR        0x01000000
    119 #else
    120 #define MEM_BASE_ADDR        (DDR_BASE_ADDR + 0x1000000)
    121 #endif
    122 
    123 #ifdef XPAR_INTC_0_DEVICE_ID
    124 #define RX_INTR_ID        XPAR_INTC_0_AXIDMA_0_S2MM_INTROUT_VEC_ID
    125 #define TX_INTR_ID        XPAR_INTC_0_AXIDMA_0_MM2S_INTROUT_VEC_ID
    126 #else
    127 #define RX_INTR_ID        XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
    128 #define TX_INTR_ID        XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
    129 #endif
    130 
    131 #define TX_BUFFER_BASE        (MEM_BASE_ADDR + 0x00100000)
    132 #define RX_BUFFER_BASE        (MEM_BASE_ADDR + 0x00300000)
    133 #define RX_BUFFER_HIGH        (MEM_BASE_ADDR + 0x004FFFFF)
    134 
    135 #ifdef XPAR_INTC_0_DEVICE_ID
    136 #define INTC_DEVICE_ID          XPAR_INTC_0_DEVICE_ID
    137 #else
    138 #define INTC_DEVICE_ID          XPAR_SCUGIC_SINGLE_DEVICE_ID
    139 #endif
    140 
    141 #ifdef XPAR_INTC_0_DEVICE_ID
    142  #define INTC        XIntc
    143  #define INTC_HANDLER    XIntc_InterruptHandler
    144 #else
    145  #define INTC        XScuGic
    146  #define INTC_HANDLER    XScuGic_InterruptHandler
    147 #endif
    148 
    149 
    150 /* Timeout loop counter for reset
    151  */
    152 #define RESET_TIMEOUT_COUNTER    10000
    153 
    154 #define TEST_START_VALUE    0xC
    155 /*
    156  * Buffer and Buffer Descriptor related constant definition
    157  */
    158 #define MAX_PKT_LEN        0x100
    159 
    160 #define NUMBER_OF_TRANSFERS    10
    161 
    162 /* The interrupt coalescing threshold and delay timer threshold
    163  * Valid range is 1 to 255
    164  *
    165  * We set the coalescing threshold to be the total number of packets.
    166  * The receive side will only get one completion interrupt for this example.
    167  */
    168 
    169 /**************************** Type Definitions *******************************/
    170 
    171 
    172 /***************** Macros (Inline Functions) Definitions *********************/
    173 
    174 
    175 /************************** Function Prototypes ******************************/
    176 #ifndef DEBUG
    177 extern void xil_printf(const char *format, ...);
    178 #endif
    179 
    180 #ifdef XPAR_UARTNS550_0_BASEADDR
    181 static void Uart550_Setup(void);
    182 #endif
    183 
    184 static int CheckData(int Length, u8 StartValue);
    185 static void TxIntrHandler(void *Callback);
    186 static void RxIntrHandler(void *Callback);
    187 
    188 
    189 
    190 
    191 static int SetupIntrSystem(INTC * IntcInstancePtr,
    192                XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);
    193 static void DisableIntrSystem(INTC * IntcInstancePtr,
    194                     u16 TxIntrId, u16 RxIntrId);
    195 
    196 
    197 
    198 /************************** Variable Definitions *****************************/
    199 /*
    200  * Device instance definitions
    201  */
    202 
    203 
    204 static XAxiDma AxiDma;        /* Instance of the XAxiDma */
    205 
    206 static INTC Intc;    /* Instance of the Interrupt Controller */
    207 
    208 /*
    209  * Flags interrupt handlers use to notify the application context the events.
    210  */
    211 volatile int TxDone;
    212 volatile int RxDone;
    213 volatile int Error;
    214 
    215 /*****************************************************************************/
    216 /**
    217 *
    218 * Main function
    219 *
    220 * This function is the main entry of the interrupt test. It does the following:
    221 *    Set up the output terminal if UART16550 is in the hardware build
    222 *    Initialize the DMA engine
    223 *    Set up Tx and Rx channels
    224 *    Set up the interrupt system for the Tx and Rx interrupts
    225 *    Submit a transfer
    226 *    Wait for the transfer to finish
    227 *    Check transfer status
    228 *    Disable Tx and Rx interrupts
    229 *    Print test status and exit
    230 *
    231 * @param    None
    232 *
    233 * @return
    234 *        - XST_SUCCESS if example finishes successfully
    235 *        - XST_FAILURE if example fails.
    236 *
    237 * @note        None.
    238 *
    239 ******************************************************************************/
    240 int main(void)
    241 {
    242     int Status;
    243     XAxiDma_Config *Config;
    244     int Tries = NUMBER_OF_TRANSFERS;
    245     int Index;
    246     u8 *TxBufferPtr;
    247     u8 *RxBufferPtr;
    248     u8 Value;
    249 
    250     TxBufferPtr = (u8 *)TX_BUFFER_BASE ;
    251     RxBufferPtr = (u8 *)RX_BUFFER_BASE;
    252     /* Initial setup for Uart16550 */
    253 #ifdef XPAR_UARTNS550_0_BASEADDR
    254 
    255     Uart550_Setup();
    256 
    257 #endif
    258 
    259     xil_printf("
    --- Entering main() --- 
    ");
    260 
    261     Config = XAxiDma_LookupConfig(DMA_DEV_ID);
    262     if (!Config) {
    263         xil_printf("No config found for %d
    ", DMA_DEV_ID);
    264 
    265         return XST_FAILURE;
    266     }
    267 
    268     /* Initialize DMA engine */
    269     Status = XAxiDma_CfgInitialize(&AxiDma, Config);
    270 
    271     if (Status != XST_SUCCESS) {
    272         xil_printf("Initialization failed %d
    ", Status);
    273         return XST_FAILURE;
    274     }
    275 
    276     if(XAxiDma_HasSg(&AxiDma)){
    277         xil_printf("Device configured as SG mode 
    ");
    278         return XST_FAILURE;
    279     }
    280 
    281     /* Set up Interrupt system  */
    282     Status = SetupIntrSystem(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);
    283     if (Status != XST_SUCCESS) {
    284 
    285         xil_printf("Failed intr setup
    ");
    286         return XST_FAILURE;
    287     }
    288 
    289     /* Disable all interrupts before setup */
    290 
    291     XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
    292                         XAXIDMA_DMA_TO_DEVICE);
    293 
    294     XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
    295                 XAXIDMA_DEVICE_TO_DMA);
    296 
    297     /* Enable all interrupts */
    298     XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
    299                             XAXIDMA_DMA_TO_DEVICE);
    300 
    301 
    302     XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
    303                             XAXIDMA_DEVICE_TO_DMA);
    304 
    305     /* Initialize flags before start transfer test  */
    306     TxDone = 0;
    307     RxDone = 0;
    308     Error = 0;
    309 
    310     Value = TEST_START_VALUE;
    311 
    312     for(Index = 0; Index < MAX_PKT_LEN; Index ++) {
    313             TxBufferPtr[Index] = Value;
    314 
    315             Value = (Value + 1) & 0xFF;
    316     }
    317 
    318     /* Flush the SrcBuffer before the DMA transfer, in case the Data Cache
    319      * is enabled
    320      */
    321     Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);
    322 #ifdef __aarch64__
    323     Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, MAX_PKT_LEN);
    324 #endif
    325 
    326     /* Send a packet */
    327     for(Index = 0; Index < Tries; Index ++) {
    328 
    329         Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) RxBufferPtr,
    330                     MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
    331 
    332         if (Status != XST_SUCCESS) {
    333             return XST_FAILURE;
    334         }
    335 
    336         Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) TxBufferPtr,
    337                     MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
    338 
    339         if (Status != XST_SUCCESS) {
    340             return XST_FAILURE;
    341         }
    342 
    343 
    344         /*
    345          * Wait TX done and RX done
    346          */
    347         while (!TxDone && !RxDone && !Error) {
    348                 /* NOP */
    349         }
    350 
    351         if (Error) {
    352             xil_printf("Failed test transmit%s done, "
    353             "receive%s done
    ", TxDone? "":" not",
    354                             RxDone? "":" not");
    355 
    356             goto Done;
    357 
    358         }
    359 
    360         /*
    361          * Test finished, check data
    362          */
    363         Status = CheckData(MAX_PKT_LEN, 0xC);
    364         if (Status != XST_SUCCESS) {
    365             xil_printf("Data check failed
    ");
    366             goto Done;
    367         }
    368     }
    369 
    370 
    371     xil_printf("AXI DMA interrupt example test passed
    ");
    372 
    373 
    374     /* Disable TX and RX Ring interrupts and return success */
    375 
    376     DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);
    377 
    378 Done:
    379     xil_printf("--- Exiting main() --- 
    ");
    380 
    381     return XST_SUCCESS;
    382 }
    383 
    384 #ifdef XPAR_UARTNS550_0_BASEADDR
    385 /*****************************************************************************/
    386 /*
    387 *
    388 * Uart16550 setup routine, need to set baudrate to 9600 and data bits to 8
    389 *
    390 * @param    None
    391 *
    392 * @return    None
    393 *
    394 * @note        None.
    395 *
    396 ******************************************************************************/
    397 static void Uart550_Setup(void)
    398 {
    399 
    400     XUartNs550_SetBaud(XPAR_UARTNS550_0_BASEADDR,
    401             XPAR_XUARTNS550_CLOCK_HZ, 9600);
    402 
    403     XUartNs550_SetLineControlReg(XPAR_UARTNS550_0_BASEADDR,
    404             XUN_LCR_8_DATA_BITS);
    405 }
    406 #endif
    407 
    408 /*****************************************************************************/
    409 /*
    410 *
    411 * This function checks data buffer after the DMA transfer is finished.
    412 *
    413 * We use the static tx/rx buffers.
    414 *
    415 * @param    Length is the length to check
    416 * @param    StartValue is the starting value of the first byte
    417 *
    418 * @return
    419 *        - XST_SUCCESS if validation is successful
    420 *        - XST_FAILURE if validation is failure.
    421 *
    422 * @note        None.
    423 *
    424 ******************************************************************************/
    425 static int CheckData(int Length, u8 StartValue)
    426 {
    427     u8 *RxPacket;
    428     int Index = 0;
    429     u8 Value;
    430 
    431     RxPacket = (u8 *) RX_BUFFER_BASE;
    432     Value = StartValue;
    433 
    434     /* Invalidate the DestBuffer before receiving the data, in case the
    435      * Data Cache is enabled
    436      */
    437 #ifndef __aarch64__
    438     Xil_DCacheInvalidateRange((u32)RxPacket, Length);
    439 #endif
    440 
    441     for(Index = 0; Index < Length; Index++) {
    442         if (RxPacket[Index] != Value) {
    443             xil_printf("Data error %d: %x/%x
    ",
    444                 Index, RxPacket[Index], Value);
    445 
    446             return XST_FAILURE;
    447         }
    448         Value = (Value + 1) & 0xFF;
    449     }
    450 
    451     return XST_SUCCESS;
    452 }
    453 
    454 /*****************************************************************************/
    455 /*
    456 *
    457 * This is the DMA TX Interrupt handler function.
    458 *
    459 * It gets the interrupt status from the hardware, acknowledges it, and if any
    460 * error happens, it resets the hardware. Otherwise, if a completion interrupt
    461 * is present, then sets the TxDone.flag
    462 *
    463 * @param    Callback is a pointer to TX channel of the DMA engine.
    464 *
    465 * @return    None.
    466 *
    467 * @note        None.
    468 *
    469 ******************************************************************************/
    470 static void TxIntrHandler(void *Callback)
    471 {
    472 
    473     u32 IrqStatus;
    474     int TimeOut;
    475     XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
    476 
    477     /* Read pending interrupts */
    478     IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);
    479 
    480     /* Acknowledge pending interrupts */
    481 
    482 
    483     XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);
    484 
    485     /*
    486      * If no interrupt is asserted, we do not do anything
    487      */
    488     if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
    489 
    490         return;
    491     }
    492 
    493     /*
    494      * If error interrupt is asserted, raise error flag, reset the
    495      * hardware to recover from the error, and return with no further
    496      * processing.
    497      */
    498     if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
    499 
    500         Error = 1;
    501 
    502         /*
    503          * Reset should never fail for transmit channel
    504          */
    505         XAxiDma_Reset(AxiDmaInst);
    506 
    507         TimeOut = RESET_TIMEOUT_COUNTER;
    508 
    509         while (TimeOut) {
    510             if (XAxiDma_ResetIsDone(AxiDmaInst)) {
    511                 break;
    512             }
    513 
    514             TimeOut -= 1;
    515         }
    516 
    517         return;
    518     }
    519 
    520     /*
    521      * If Completion interrupt is asserted, then set the TxDone flag
    522      */
    523     if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
    524 
    525         TxDone = 1;
    526     }
    527 }
    528 
    529 /*****************************************************************************/
    530 /*
    531 *
    532 * This is the DMA RX interrupt handler function
    533 *
    534 * It gets the interrupt status from the hardware, acknowledges it, and if any
    535 * error happens, it resets the hardware. Otherwise, if a completion interrupt
    536 * is present, then it sets the RxDone flag.
    537 *
    538 * @param    Callback is a pointer to RX channel of the DMA engine.
    539 *
    540 * @return    None.
    541 *
    542 * @note        None.
    543 *
    544 ******************************************************************************/
    545 static void RxIntrHandler(void *Callback)
    546 {
    547     u32 IrqStatus;
    548     int TimeOut;
    549     XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
    550 
    551     /* Read pending interrupts */
    552     IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);
    553 
    554     /* Acknowledge pending interrupts */
    555     XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);
    556 
    557     /*
    558      * If no interrupt is asserted, we do not do anything
    559      */
    560     if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
    561         return;
    562     }
    563 
    564     /*
    565      * If error interrupt is asserted, raise error flag, reset the
    566      * hardware to recover from the error, and return with no further
    567      * processing.
    568      */
    569     if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
    570 
    571         Error = 1;
    572 
    573         /* Reset could fail and hang
    574          * NEED a way to handle this or do not call it??
    575          */
    576         XAxiDma_Reset(AxiDmaInst);
    577 
    578         TimeOut = RESET_TIMEOUT_COUNTER;
    579 
    580         while (TimeOut) {
    581             if(XAxiDma_ResetIsDone(AxiDmaInst)) {
    582                 break;
    583             }
    584 
    585             TimeOut -= 1;
    586         }
    587 
    588         return;
    589     }
    590 
    591     /*
    592      * If completion interrupt is asserted, then set RxDone flag
    593      */
    594     if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
    595 
    596         RxDone = 1;
    597     }
    598 }
    599 
    600 /*****************************************************************************/
    601 /*
    602 *
    603 * This function setups the interrupt system so interrupts can occur for the
    604 * DMA, it assumes INTC component exists in the hardware system.
    605 *
    606 * @param    IntcInstancePtr is a pointer to the instance of the INTC.
    607 * @param    AxiDmaPtr is a pointer to the instance of the DMA engine
    608 * @param    TxIntrId is the TX channel Interrupt ID.
    609 * @param    RxIntrId is the RX channel Interrupt ID.
    610 *
    611 * @return
    612 *        - XST_SUCCESS if successful,
    613 *        - XST_FAILURE.if not succesful
    614 *
    615 * @note        None.
    616 *
    617 ******************************************************************************/
    618 static int SetupIntrSystem(INTC * IntcInstancePtr,
    619                XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
    620 {
    621     int Status;
    622 
    623 #ifdef XPAR_INTC_0_DEVICE_ID
    624 
    625     /* Initialize the interrupt controller and connect the ISRs */
    626     Status = XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);
    627     if (Status != XST_SUCCESS) {
    628 
    629         xil_printf("Failed init intc
    ");
    630         return XST_FAILURE;
    631     }
    632 
    633     Status = XIntc_Connect(IntcInstancePtr, TxIntrId,
    634                    (XInterruptHandler) TxIntrHandler, AxiDmaPtr);
    635     if (Status != XST_SUCCESS) {
    636 
    637         xil_printf("Failed tx connect intc
    ");
    638         return XST_FAILURE;
    639     }
    640 
    641     Status = XIntc_Connect(IntcInstancePtr, RxIntrId,
    642                    (XInterruptHandler) RxIntrHandler, AxiDmaPtr);
    643     if (Status != XST_SUCCESS) {
    644 
    645         xil_printf("Failed rx connect intc
    ");
    646         return XST_FAILURE;
    647     }
    648 
    649     /* Start the interrupt controller */
    650     Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
    651     if (Status != XST_SUCCESS) {
    652 
    653         xil_printf("Failed to start intc
    ");
    654         return XST_FAILURE;
    655     }
    656 
    657     XIntc_Enable(IntcInstancePtr, TxIntrId);
    658     XIntc_Enable(IntcInstancePtr, RxIntrId);
    659 
    660 #else
    661 
    662     XScuGic_Config *IntcConfig;
    663 
    664 
    665     /*
    666      * Initialize the interrupt controller driver so that it is ready to
    667      * use.
    668      */
    669     IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    670     if (NULL == IntcConfig) {
    671         return XST_FAILURE;
    672     }
    673 
    674     Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
    675                     IntcConfig->CpuBaseAddress);
    676     if (Status != XST_SUCCESS) {
    677         return XST_FAILURE;
    678     }
    679 
    680 
    681     XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
    682 
    683     XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
    684     /*
    685      * Connect the device driver handler that will be called when an
    686      * interrupt for the device occurs, the handler defined above performs
    687      * the specific interrupt processing for the device.
    688      */
    689     Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,
    690                 (Xil_InterruptHandler)TxIntrHandler,
    691                 AxiDmaPtr);
    692     if (Status != XST_SUCCESS) {
    693         return Status;
    694     }
    695 
    696     Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
    697                 (Xil_InterruptHandler)RxIntrHandler,
    698                 AxiDmaPtr);
    699     if (Status != XST_SUCCESS) {
    700         return Status;
    701     }
    702 
    703     XScuGic_Enable(IntcInstancePtr, TxIntrId);
    704     XScuGic_Enable(IntcInstancePtr, RxIntrId);
    705 
    706 
    707 #endif
    708 
    709     /* Enable interrupts from the hardware */
    710 
    711     Xil_ExceptionInit();
    712     Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
    713             (Xil_ExceptionHandler)INTC_HANDLER,
    714             (void *)IntcInstancePtr);
    715 
    716     Xil_ExceptionEnable();
    717 
    718     return XST_SUCCESS;
    719 }
    720 
    721 /*****************************************************************************/
    722 /**
    723 *
    724 * This function disables the interrupts for DMA engine.
    725 *
    726 * @param    IntcInstancePtr is the pointer to the INTC component instance
    727 * @param    TxIntrId is interrupt ID associated w/ DMA TX channel
    728 * @param    RxIntrId is interrupt ID associated w/ DMA RX channel
    729 *
    730 * @return    None.
    731 *
    732 * @note        None.
    733 *
    734 ******************************************************************************/
    735 static void DisableIntrSystem(INTC * IntcInstancePtr,
    736                     u16 TxIntrId, u16 RxIntrId)
    737 {
    738 #ifdef XPAR_INTC_0_DEVICE_ID
    739     /* Disconnect the interrupts for the DMA TX and RX channels */
    740     XIntc_Disconnect(IntcInstancePtr, TxIntrId);
    741     XIntc_Disconnect(IntcInstancePtr, RxIntrId);
    742 #else
    743     XScuGic_Disconnect(IntcInstancePtr, TxIntrId);
    744     XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
    745 #endif
    746 }
    xaxidma_example_simple_intr.c

     主函数中依次完成了:DMA初始化,建立中断系统,使能DMA中断,初始化标志位及发送数据,启动DMA传输以及数据检测。中断部分的内容与PS DMA非常相近,传输完成后进入的中断函数中仅置位了发送或接收完成标志位:

      1 static void TxIntrHandler(void *Callback)
      2 {
      3 
      4     u32 IrqStatus;
      5     int TimeOut;
      6     XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
      7 
      8     /* Read pending interrupts */
      9     IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);
     10 
     11     /* Acknowledge pending interrupts */
     12 
     13 
     14     XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);
     15 
     16     /*
     17      * If no interrupt is asserted, we do not do anything
     18      */
     19     if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
     20 
     21         return;
     22     }
     23 
     24     /*
     25      * If error interrupt is asserted, raise error flag, reset the
     26      * hardware to recover from the error, and return with no further
     27      * processing.
     28      */
     29     if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
     30 
     31         Error = 1;
     32 
     33         /*
     34          * Reset should never fail for transmit channel
     35          */
     36         XAxiDma_Reset(AxiDmaInst);
     37 
     38         TimeOut = RESET_TIMEOUT_COUNTER;
     39 
     40         while (TimeOut) {
     41             if (XAxiDma_ResetIsDone(AxiDmaInst)) {
     42                 break;
     43             }
     44 
     45             TimeOut -= 1;
     46         }
     47 
     48         return;
     49     }
     50 
     51     /*
     52      * If Completion interrupt is asserted, then set the TxDone flag
     53      */
     54     if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
     55 
     56         TxDone = 1;
     57     }
     58 }
     59 
     60 /*****************************************************************************/
     61 /*
     62 *
     63 * This is the DMA RX interrupt handler function
     64 *
     65 * It gets the interrupt status from the hardware, acknowledges it, and if any
     66 * error happens, it resets the hardware. Otherwise, if a completion interrupt
     67 * is present, then it sets the RxDone flag.
     68 *
     69 * @param    Callback is a pointer to RX channel of the DMA engine.
     70 *
     71 * @return    None.
     72 *
     73 * @note        None.
     74 *
     75 ******************************************************************************/
     76 static void RxIntrHandler(void *Callback)
     77 {
     78     u32 IrqStatus;
     79     int TimeOut;
     80     XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
     81 
     82     /* Read pending interrupts */
     83     IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);
     84 
     85     /* Acknowledge pending interrupts */
     86     XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);
     87 
     88     /*
     89      * If no interrupt is asserted, we do not do anything
     90      */
     91     if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
     92         return;
     93     }
     94 
     95     /*
     96      * If error interrupt is asserted, raise error flag, reset the
     97      * hardware to recover from the error, and return with no further
     98      * processing.
     99      */
    100     if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
    101 
    102         Error = 1;
    103 
    104         /* Reset could fail and hang
    105          * NEED a way to handle this or do not call it??
    106          */
    107         XAxiDma_Reset(AxiDmaInst);
    108 
    109         TimeOut = RESET_TIMEOUT_COUNTER;
    110 
    111         while (TimeOut) {
    112             if(XAxiDma_ResetIsDone(AxiDmaInst)) {
    113                 break;
    114             }
    115 
    116             TimeOut -= 1;
    117         }
    118 
    119         return;
    120     }
    121 
    122     /*
    123      * If completion interrupt is asserted, then set RxDone flag
    124      */
    125     if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
    126 
    127         RxDone = 1;
    128     }
    129 }
    intrHandler

       DMA启动传输部分如下,调用库函数XAxiDma_SimpleTransfer。以第一个为例,是将RxBufferPtr为数据首地址,MAX_PKT_LEN为字节数,XAXIDMA_DEVICE_TO_DMA为传输方向启动DMA传输数据。MAX_PKT_LEN不能超过之前IP核配置参数指定的16384byte,XAXIDMA_DEVICE_TO_DMA和XAXIDMA_DMA_TO_DEVICE依次指PL-> DMA ->PS以及PS->DMA -> PL方向,也就是PL就是其中的DEVICE。DMA启动函数只有一个地址,这是与PS端DMA最大的区别,因为数据搬移的另一侧是带有无地址的流接口的IP核,该侧“地址”由硬件连接决定。

      再来看看搬移数据内存首地址RxBufferPtr和TxBufferPtr.从下边的定义可见MEM_BASE_ADDR是DDR_BASE_ADDR加上一段偏移量的结果,DDR基地址数值从xparameters.h中查看。

    四、函数重用封装

      官方的代码比较乱,都写在main函数里,米联客教程init_intr_sys()函数完成整个中断系统的建立,将官方demo中main函数DMA测试之前关于中断部分的代码全部封装其中,包括DMA中断初始化,中断控制器初始化,使能中断异常,连接DMA发送与接收中断,DMA中断使能五个过程。

    五、AXI总线信号ILA波形分析 

     AXI Stream主要接口:

      tdata:数据    tkeep:字节有效指示    tlast:帧尾指示    tready:准备就绪    tvalid:数据有效指示

      MM2S方向一旦tvalid拉高则触发ILA抓取信号波形。一帧数据有64个,每个数据32bit(4byte),一共正好为C代码中MAX_PKT_LEN数值,即256byte。

      其中他keep信号比较关键。如当stream位宽为16bit,传输数据量为255byte时,tkeep信号在最后一个stream数据对应位置是2'b01指示第128个16bit数中最后一个数的高字节为upsize过程中无效填充数据。

      后续本人会利用System Generator设计算法IP,之后集成到IP Integerator中作为CPU外设进行板级验证。继续学习!

  • 相关阅读:
    11.06第十次作业
    (构造方法私有化、this)10.29练习题
    10.23创造人
    10.16(RuPeng)游戏
    10.09
    作业9.25
    练习题
    (随机数之和)求一整数各位数之和1636050052王智霞
    两点之间的距离1636050052王智霞
    TabLayout 简单使用。
  • 原文地址:https://www.cnblogs.com/moluoqishi/p/9554097.html
Copyright © 2020-2023  润新知