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 copy6 * of this software and associated documentation files (the "Software"), to deal7 * in the Software without restriction, including without limitation the rights8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell9 * copies of the Software, and to permit persons to whom the Software is10 * furnished to do so, subject to the following conditions:11 *12 * The above copyright notice and this permission notice shall be included in13 * 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, or17 * (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 OR20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL22 * XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF24 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE25 * SOFTWARE.26 *27 * Except as contained in this notice, the name of the Xilinx shall not be used28 * in advertising or otherwise to promote the sale, use or other dealings in29 * this Software without prior written authorization from Xilinx.30 *31 ******************************************************************************/32 /*****************************************************************************/33 /**34  *35  * @file xaxidma_example_simple_intr.c36  *37  * This file demonstrates how to use the xaxidma driver on the Xilinx AXI38  * DMA core (AXIDMA) to transfer packets.in interrupt mode when the AXIDMA core39  * is configured in simple mode40  *41  * This code assumes a loopback hardware widget is connected to the AXI DMA42  * 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 your46  * software executable.47  *48  * Make sure that MEMORY_BASE is defined properly as per the HW system. The49  * h/w system built in Area mode has a maximum DDR memory limit of 64MB. In50  * throughput mode, it is 512MB.  These limits are need to ensured for51  * proper operation of this code.52  *53  *54  * 
55  * MODIFICATION HISTORY:56  *57  * Ver   Who  Date     Changes58  * ----- ---- -------- -------------------------------------------------------59  * 4.00a rkv  02/22/11 New example created for simple DMA, this example is for60  *                  simple DMA,Added interrupt support for Zynq.61  * 4.00a srt  08/04/11 Changed a typo in the RxIntrHandler, changed62  *               XAXIDMA_DMA_TO_DEVICE to XAXIDMA_DEVICE_TO_DMA63  * 5.00a srt  03/06/12 Added Flushing and Invalidation of Caches to fix CRs64  *               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 maximum69  *               DDR memory limit of the h/w system built with Area mode70  * 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) and72  *               removed the defines for S6/V6.73  * 9.2   vak  15/04/16 Fixed compilation warnings in the example74  * 
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_BASEADDR87 #include "xuartns550_l.h" /* to use uartns550 */88 #endif89 90 91 #ifdef XPAR_INTC_0_DEVICE_ID92 #include "xintc.h"93 #else94 #include "xscugic.h"95 #endif96 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("\r\n--- Entering main() --- \r\n"); 260 261 Config = XAxiDma_LookupConfig(DMA_DEV_ID); 262 if (!Config) { 263 xil_printf("No config found for %d\r\n", 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\r\n", Status); 273 return XST_FAILURE; 274 } 275 276 if(XAxiDma_HasSg(&AxiDma)){ 277 xil_printf("Device configured as SG mode \r\n"); 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\r\n"); 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\r\n", 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\r\n"); 366 goto Done; 367 } 368 } 369 370 371 xil_printf("AXI DMA interrupt example test passed\r\n"); 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() --- \r\n"); 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\r\n", 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\r\n"); 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\r\n"); 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\r\n"); 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\r\n"); 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 anything18      */19     if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {20 21         return;22     }23 24     /*25      * If error interrupt is asserted, raise error flag, reset the26      * hardware to recover from the error, and return with no further27      * processing.28      */29     if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {30 31         Error = 1;32 33         /*34          * Reset should never fail for transmit channel35          */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 flag53      */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 function64 *65 * It gets the interrupt status from the hardware, acknowledges it, and if any66 * error happens, it resets the hardware. Otherwise, if a completion interrupt67 * 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 anything90      */91     if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {92         return;93     }94 95     /*96      * If error interrupt is asserted, raise error flag, reset the97      * hardware to recover from the error, and return with no further98      * 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外设进行板级验证。继续学习!


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部