利用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核配置界面主要参数:
重点查看以下几个参数:
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概念,即分批次传输数据块。此处没有完全看明白手册的介绍,猜想是每次传输的最大数据个数。
二、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("\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中查看。
四、函数重用封装
官方的代码比较乱,都写在main函数里,米联客教程init_intr_sys()函数完成整个中断系统的建立,将官方demo中main函数dma测试之前关于中断部分的代码全部封装其中,包括dma中断初始化,中断控制器初始化,使能中断异常,连接dma发送与接收中断,dma中断使能五个过程。
五、axi总线信号ila波形分析
axi stream主要接口:
tdata:数据 tkeep:字节有效指示 tlast:帧尾指示 tready:准备就绪 tvalid:数据有效指示
一帧数据有64个,每个数据32bit(4byte),一共正好为c代码中max_pkt_len数值,即256byte。
在pl->dma->ps方向上,检测tvalid上升沿拉高则触发ila抓取信号波形。观察到一个奇怪的现象,发现dma在接收几个数据后拉低ready信号停止接收数据,一段时间后再次拉高继续接收。暂时还不清楚原因,希望知道的朋友指点一二。
后续本人会利用system generator设计算法ip,之后集成到ip integerator中作为cpu外设进行板级验证。继续学习!