1、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核。app
其最簡單的事直接寄存器模式(Simple DMA),這裏須要注意地址對齊的問題:當沒有使能地址重對齊的狀況下,若是AXI Memory Map數據位寬是32bit,則搬移數據所在地址必須在0x0,0x4,0x8等起始地址上。接下來關注DMA IP核配置界面主要參數:ide
AXI DMA能夠有兩個傳輸方向:讀通道和寫通道,依次爲MM2S和S2MM方向。也就是說「讀」和「寫」是DMA主動對CPU發起的操做。重點查看如下幾個參數:函數
1 Width of Buffer Length Register:oop
在直接寄存器模式下,它指定在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設置較大狀況下,每批次傳輸數據量更多。
2、AXI DMA Loop IP子系統
在利用ZYNQ搭建系統時,常常須要利用各類IP核作所謂的「計算加速」,將重複性高 計算量大 佔用較大CPU資源的底層處理交給各個IP核完成。這時PS ->DMA ->PL -> DMA -> PS的環路架構很是適用。這裏使用AXI Stream Data FIFO代替自定義IP核做爲演示,硬件IP子系統以下:
3、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("\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 }
主函數中依次完成了: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中查看。
4、函數重用封裝
官方的代碼比較亂,都寫在main函數裏,米聯客教程init_intr_sys()函數完成整個中斷系統的創建,將官方demo中main函數DMA測試以前關於中斷部分的代碼所有封裝其中,包括DMA中斷初始化,中斷控制器初始化,使能中斷異常,鏈接DMA發送與接收中斷,DMA中斷使能五個過程。
5、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外設進行板級驗證。繼續學習!