一、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 }
主函数中依次完成了: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 }
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外设进行板级验证。继续学习!