xilinx DMA IP核(一) —— loop测试 代码注释

本篇笔记中的代码来自:米联科技的教程“第三季第一篇的DMA_LOOP环路测试”程序员

  硬件的链接以下图所示:算法

图:DMA Loop Block Design数组

橘色的线就是DMA加FIFO组成的一个LOOP循环,红色圈圈是AXI_LITE的控制和两个读写完成的中断。多线程

  米联科技教程提供的该测试代码文件是如下四个,我删除了其中关于OLED的部分。ide

图:DMA Loop 测试源码结构函数

 1.重要的结构体oop

  1.1.中断设备:static XScuGic Intc; //GIC性能

  在main.c文件中,sataic用来修饰全局变量,造成静态全局变量,static修饰的函数/全局变量属于内连接,Intc能够在当前main.c文件内部范围内进行连接。测试

 
  1. /**
  2. * The XScuGic driver instance data. The user is required to allocate a
  3. * variable of this type for every intc device in the system. A pointer
  4. * to a variable of this type is then passed to the driver API functions.
  5. */
  6. typedef struct
  7. {
  8. XScuGic_Config *Config; /**< Configuration table entry */
  9. u32 IsReady; /**< Device is initialized and ready */
  10. u32 UnhandledInterrupts; /**< Intc Statistics */
  11. } XScuGic;
 

  XScuGic结构体中包含了XScuGic_Config 结构体类型指针Config。XScuGic_Config 结构体以下:  fetch

 
  1. typedef struct
  2. {
  3. u16 DeviceId; /**< Unique ID of device */
  4. u32 CpuBaseAddress; /**< CPU Interface Register base address */
  5. u32 DistBaseAddress; /**< Distributor Register base address */
  6. XScuGic_VectorTableEntry HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS];/**<
  7. Vector table of interrupt handlers */
  8. } XScuGic_Config;

  1.2.DMA设备:static  XAxiDma AxiDma;

 
  1. /**
  2. * The XAxiDma driver instance data. An instance must be allocated for each DMA
  3. * engine in use.
  4. */
  5. typedef struct XAxiDma {
  6. UINTPTR RegBase; /* Virtual base address of DMA engine */
  7.  
  8. int HasMm2S; /* Has transmit channel */
  9. int HasS2Mm; /* Has receive channel */
  10. int Initialized; /* Driver has been initialized */
  11. int HasSg;
  12.  
  13. XAxiDma_BdRing TxBdRing; /* BD container management for TX channel */
  14. XAxiDma_BdRing RxBdRing[]; /* BD container management for RX channel */
  15. int TxNumChannels;
  16. int RxNumChannels;
  17. int MicroDmaMode;
  18. int AddrWidth; /**< Address Width */
  19. } XAxiDma;
 

  1.3.中断向量表

 
  1. typedef struct {
  2. Xil_ExceptionHandler Handler;
  3. void *Data;
  4. } XExc_VectorTableEntry;

2.代码结构

main()

|---- init_intr_sys();

  |---- DMA_Intr_Init();  // 初始化DMA

    |---- XAxiDma_LookupConfig();  // 查找DMA设备

    |---- XAxiDma_CfgInitialize();    // 初始化DMA设备

  |---- Init_Intr_System();  //初始化中断控制器

    |---- XScuGic_LookupConfig();  // 查找中断控制器设备;带的参数为设备ID,查看中断向量是否存在

    |---- XScuGic_CfgInitialize();  // 初始化中断控制器设备

  |---- Setup_Intr_Exception();

    |---- Xil_ExceptionInit();  // 使能硬件中断

    |---- Xil_ExceptionRegisterHandler();

    |---- Xil_ExceptionEnable();

  |---- DMA_Setup_Intr_System();  // 设置DMA中断

    |---- XScuGic_SetPriorityTriggerType();

    |---- XScuGic_Connect();  // 链接中断源

    |---- XScuGic_Enable();

  |---- DMA_Intr_Enable();

    |---- XAxiDma_IntrDisable();

    |---- XAxiDma_IntrEnable();

|----axi_dma_test();

2.1.比较重要的函数

2.1.1.中断注册函数 Xil_ExceptionRegisterHandler:

 
  1. /*****************************************************************************/
  2. /**
  3. * @brief Register a handler for a specific exception. This handler is being
  4. * called when the processor encounters the specified exception.
  5. *
  6. * @param exception_id contains the ID of the exception source and should
  7. * be in the range of 0 to XIL_EXCEPTION_ID_LAST.
  8. * See xil_exception.h for further information.
  9. * @param Handler to the Handler for that exception.
  10. * @param Data is a reference to Data that will be passed to the
  11. * Handler when it gets called.
  12. *
  13. * @return None.
  14. *
  15. * @note None.
  16. *
  17. ****************************************************************************/
  18. void Xil_ExceptionRegisterHandler(u32 Exception_id,
  19. Xil_ExceptionHandler Handler,
  20. void *Data)
  21. {
  22. XExc_VectorTable[Exception_id].Handler = Handler;
  23. XExc_VectorTable[Exception_id].Data = Data;
  24. }

  从上面能够看到Xil_ExceptionRegisterHandler()这个函数是把中断的句柄(第二个行参“Handler”)和中断的参数(第三个行参“Data”)放到了两个结构体XExc_VectorTableEntry类型的数组XExc_VectorTable当中 ,XExc_VectorTableEntry结构体类型以下:

 
 
  1. XExc_VectorTableEntry XExc_VectorTable[XIL_EXCEPTION_ID_LAST + ] =
  2. {
  3. {Xil_ExceptionNullHandler, NULL},
  4. {Xil_UndefinedExceptionHandler, NULL},
  5. {Xil_ExceptionNullHandler, NULL},
  6. {Xil_PrefetchAbortHandler, NULL},
  7. {Xil_DataAbortHandler, NULL},
  8. {Xil_ExceptionNullHandler, NULL},
  9. {Xil_ExceptionNullHandler, NULL},
  10. };

  Xil_ExceptionRegisterHandler()函数的第二传参XScuGic_InterruptHandler,是一个函数指针,强制转化成了Xil_ExceptionHandler类型,XScuGic_InterruptHandler()函数以下。

 
  1. void XScuGic_InterruptHandler(XScuGic *InstancePtr)
  2. {
  3.  
  4. u32 InterruptID;
  5. u32 IntIDFull;
  6. XScuGic_VectorTableEntry *TablePtr;
  7.  
  8. /* Assert that the pointer to the instance is valid
  9. */
  10. Xil_AssertVoid(InstancePtr != NULL);
  11.  
  12. /*
  13. * Read the int_ack register to identify the highest priority interrupt ID
  14. * and make sure it is valid. Reading Int_Ack will clear the interrupt in the GIC.
  15. * 读取 int_ack 寄存器以识别最高优先级的中断 ID, 并确保其有效。读取 Int_Ack 将清除 GIC 中的中断。
         * 而后看看读出来的中断 ID 是否大于最大的中断值。
  16. */
  17. IntIDFull = XScuGic_CPUReadReg(InstancePtr, XSCUGIC_INT_ACK_OFFSET);
  18. InterruptID = IntIDFull & XSCUGIC_ACK_INTID_MASK;
  19.  
  20. if(XSCUGIC_MAX_NUM_INTR_INPUTS < InterruptID){
  21. goto IntrExit;
  22. }
  23.  
  24. /*
  25. * Execute the ISR. Jump into the Interrupt service routine based on the
  26. * IRQSource. A software trigger is cleared by the ACK.
  27. */
  28. TablePtr = &(InstancePtr->Config->HandlerTable[InterruptID]);
  29. if(TablePtr != NULL) {
  30. TablePtr->Handler(TablePtr->CallBackRef);
  31. }
  32.  
  33. IntrExit:
  34. /*
  35. * Write to the EOI register, we are all done here.
  36. * Let this function return, the boot code will restore the stack.
  37. */
  38. XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_EOI_OFFSET, IntIDFull);
  39. }
 

  经过程序开头 xilinx 给出的这个XScuGic_InterruptHandler()程序的注释能够知道: 这个函数是基本的中断驱动函数。 它必须 链接到中断源, 以便在中断控制器的中断激活时被调用。 它将解决哪些中断是活动的和启用的, 并调用适当的中断处理程序。 它使用中断类型信息来肯定什么时候确认中断。 首先处理最高优先级的中断。 此函数假定中断向量表已预先初始化。 它不会在调用中断处理程序以前验证表中的条目是否有效。 当中断发生时,调用的就是上面的代码中的语句:TablePtr->Handler(TablePtr->CallBackRef)。那么这个Handler和CallBackRef究竟是什么呢?也就是Handler和CallBackRef究竟是和哪段要被执行的代码绑定在一块儿呢?
2.1.2.中断链接函数

  咱们在DMA_Setup_Intr_System()函数中调用了中断链接函数XScuGic_Connect();

 
  1. int DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
  2. {
  3. <...>
  4. /*
  5. * Connect the device driver handler that will be called when an
  6. * interrupt for the device occurs, the handler defined above performs
  7. * the specific interrupt processing for the device.
  8. */
  9. Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,
  10. (Xil_InterruptHandler)DMA_TxIntrHandler,
  11. AxiDmaPtr);
  12. if (Status != XST_SUCCESS) {
  13. return Status;
  14. }
  15.  
  16. Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
  17. (Xil_InterruptHandler)DMA_RxIntrHandler,
  18. AxiDmaPtr);
  19. if (Status != XST_SUCCESS) {
  20. return Status;
  21. }
  22. <...>
  23. }
 

  能够看到XScuGic_Connect()函数的第三个传参是一个Xil_InterruptHandler类型的函数指针DMA_TxIntrHandler。XScuGic_Connect()内容以下。

 
  1. s32 XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
  2. Xil_InterruptHandler Handler, void *CallBackRef)
  3. {
  4. /*
  5. * Assert the arguments
  6. */
  7. Xil_AssertNonvoid(InstancePtr != NULL);
  8. Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
  9. Xil_AssertNonvoid(Handler != NULL);
  10. Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
  11.  
  12. /*
  13. * The Int_Id is used as an index into the table to select the proper
  14. * handler
  15. */
  16. InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;
  17. InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;
  18.  
  19. return XST_SUCCESS;
  20. }

  能够看到XScuGic_Connect()函数将传进来的第三个参数,Xil_InterruptHandler类型的“Handler”,绑定到InstancePtr->Config->HandlerTable[Int_Id].Handler中,第四个参数同理绑定。这里的InstancePtr是函数XScuGic_Connect()传进来的XScuGic结构体类型的指针变量,前面讲过,XScuGic结构体中还包含XScuGic_Config结构体类型的指针Config,进一步来讲,XScuGic_Connect()函数将传进来的第三个参数Handler就是绑定到XScuGic_Config结构体类型的指针Config中的HandlerTable变量。这个HandlerTable变量是一个XScuGic_VectorTableEntry类型的结构体变量。至此,中断的Handler就绑定到main.c文件开头定义的设备static XScuGic Intc当中,同时设备 XScuGic Intc也由于函数Setup_Intr_Exception()跟硬件的异常向量表绑定到一块儿了。前文提到“Handler和CallBackRef究竟是和哪段要被执行的代码绑定在一块儿呢?”,那么答案就在这里了,要执行的代码就在这里被绑定到一块儿来。

 

  因此接下来看看这个形参Handler(对应的是调用XScuGic_Connect()函数传进来的实参DMA_TxIntrHandler)指向了什么东西?

  函数指针DMA_TxIntrHandler指向的内容以下。下面代码是DMA Tx的,Rx的也差很少。

 
  1. static void DMA_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. XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);
  14.  
  15. /*
  16. * If no interrupt is asserted, we do not do anything
  17. */
  18. if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
  19.  
  20. return;
  21. }
  22.  
  23. /*
  24. * If error interrupt is asserted, raise error flag, reset the
  25. * hardware to recover from the error, and return with no further
  26. * processing.
  27. */
  28. if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
  29.  
  30. Error = ;
  31.  
  32. /*
  33. * Reset should never fail for transmit channel
  34. */
  35. XAxiDma_Reset(AxiDmaInst);
  36.  
  37. TimeOut = RESET_TIMEOUT_COUNTER;
  38.  
  39. while (TimeOut) {
  40. if (XAxiDma_ResetIsDone(AxiDmaInst)) {
  41. break;
  42. }
  43.  
  44. TimeOut -= ;
  45. }
  46.  
  47. return;
  48. }
  49.  
  50. /*
  51. * If Completion interrupt is asserted, then set the TxDone flag
  52. */
  53. if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
  54.  
  55. TxDone = ;
  56. }
  57. }

  代码中,在dma_intr.h中声明了三个全局变量:TxDone,RxDone和Error。

dam_intr.h :

 
  1. extern volatile int TxDone;
  2. extern volatile int RxDone;
  3. extern volatile int Error;

  在dma_intr.c文件的开头,定义了这三个全局变量。

 
  1. #include "dma_intr.h"
  2.  
  3. volatile int TxDone;
  4. volatile int RxDone;
  5. volatile int Error;

能够看到用关键字volatile修饰,在此回顾一下朱有鹏老师在讲解C语言的时候,总结的volatile的用法:

(1)volatile的字面意思:可变的、易变的。C语言中volatile用来修饰一个变量,表示这个变量能够被编译器以外的东西改变。编译器以内的意思是变量的值的改变是代码的做用,编译器以外的改变就是这个改变不是代码形成的,或者不是当前代码形成的,编译器在编译当前代码时没法预知。譬如在中断处理程序isr中更改了这个变量的值,譬如多线程中在别的线程更改了这个变量的值,譬如硬件自动更改了这个变量的值(通常这个变量是一个寄存器的值)
(2)以上说的三种状况(中断isr中引用的变量,多线程中共用的变量,硬件会更改的变量)都是编译器在编译时没法预知的更改,此时应用使用volatile告诉编译器这个变量属于这种(可变的、易变的)状况。编译器在遇到volatile修饰的变量时就不会对改变量的访问进行优化,就不会出现错误。
(3)编译器的优化在通常状况下很是好,能够帮助提高程序效率。可是在特殊状况(volatile)下,变量会被编译器想象以外的力量所改变,此时若是编译器没有意识到而去优化则就会形成优化错误,优化错误就会带来执行时错误。并且这种错误很难被发现。
(4)volatile是程序员意识到须要volatile而后在定义变量时加上volatile,若是你遇到了应该加volatile的状况而没有加程序可能会被错误的优化。若是在不该该加volatile而加了的状况程序不会出错只是会下降效率。因此咱们对于volatile的态度应该是:正确区分,该加的时候加不应加的时候不加,若是不能肯定该不应加为了保险起见就加上。

计划在xilinx DMA IP loop测试(二)中结合DMA的AXI4总线时序,来记录一下DMA的数据收发。

 
  1. xilinx DMA IP核(二) —— 文档阅读

    本笔记不记录DMA的Scatter/Gather特性.DMA上有三种总线:AXI4-LIte(对寄存器进行配置),AXI4-Memory Map(用于与内存交互)和AXI4 Stream(用于与外设交 ...

  2. 从Xilinx FFT IP核到OFDM

    笔者在校的科研任务,须要用FPGA搭建OFDM通讯系统,而OFDM的核心便是IFFT和FFT运算,所以本文经过Xilinx FFT IP核的使用总结给你们开个头,详细内容可查看官方文档PG109.关于 ...

  3. PCIE xilinx v5 IP核使用前的研究

    外带一个月前啃的一个星期,加本星期心无旁骛,啃出些心得,广惠后人.希望有用. trn信号是数据链路层的信号 TLP包是数据链路层传给transaction层的包 解包须要一个transaction的协 ...

  4. Xilinx DDR3 IP核使用问题汇总(持续更新)和感悟

    一度由于DDR3的IP核使用而发狂. 后来由于解决问题,得一感悟.后面此贴会完整讲述ddr3 ip的使用.(XILINX K7) 感悟:对于有供应商支持的产品,遇到问题找官方的流程.按照官方的指导进行 ...

  5. aurora 64B/66B ip核设置与例程代码详解

    见网页https://blog.csdn.net/u014586651/article/details/84349328 https://blog.csdn.net/u012135070/articl ...

  6. Xilinx FFT IP核缩放因子说明

    以1024点FFT为例, reg [9:0] scale_sch = 10'b11_10_01_01_01; 流水线结构中,将每一个基 2 的蝶形处理单元视为一个阶段. 每一个阶段进行一次数据的缩减,缩减 ...

  7. Xilinx的IP核接口命名说明

    s_axis中的s表示:slave(从); m_axis中的m表示:master(主). axis表示AXI(一种总线协议) Signal.

  8. xilinx IP核配置,一步一步验证Xilinx Serdes GTX最高8.0Gbps

    版权声明:本文为博主原创文章,未经博主容许不得转载. https://blog.csdn.net/u010161493/article/details/77658599   目录(?)[+]   以前 ...

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

    一.AXI DMA介绍 本篇博文讲述AXI DMA的一些使用总结,硬件IP子系统搭建与SDK C代码封装参考米联客ZYNQ教程.若想让ZYNQ的PS与PL两部分高速数据传输,须要利用PS的HP(高性能 ...

  10. 7 Series GTP IP核使用总结 IP核配置篇

    FPGA内嵌收发器至关于以太网中的PHY芯片,但更灵活更高效,线速率也在随着FPGA芯片的发展升级.本文对7系列FPGA内部高速收发器GTP IP核的配置和使用作些简单的总结,以备后续回顾重用.本文是 ...

相关文章
相关标签/搜索