本篇筆記中的代碼來自:米聯科技的教程「第三季第一篇的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文件內部範圍內進行連接。測試
- /**
- * The XScuGic driver instance data. The user is required to allocate a
- * variable of this type for every intc device in the system. A pointer
- * to a variable of this type is then passed to the driver API functions.
- */
- typedef struct
- {
- XScuGic_Config *Config; /**< Configuration table entry */
- u32 IsReady; /**< Device is initialized and ready */
- u32 UnhandledInterrupts; /**< Intc Statistics */
- } XScuGic;
XScuGic結構體中包含了XScuGic_Config 結構體類型指針Config。XScuGic_Config 結構體以下: fetch
- typedef struct
- {
- u16 DeviceId; /**< Unique ID of device */
- u32 CpuBaseAddress; /**< CPU Interface Register base address */
- u32 DistBaseAddress; /**< Distributor Register base address */
- XScuGic_VectorTableEntry HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS];/**<
- Vector table of interrupt handlers */
- } XScuGic_Config;
1.2.DMA設備:static XAxiDma AxiDma;
- /**
- * The XAxiDma driver instance data. An instance must be allocated for each DMA
- * engine in use.
- */
- typedef struct XAxiDma {
- UINTPTR RegBase; /* Virtual base address of DMA engine */
- int HasMm2S; /* Has transmit channel */
- int HasS2Mm; /* Has receive channel */
- int Initialized; /* Driver has been initialized */
- int HasSg;
- XAxiDma_BdRing TxBdRing; /* BD container management for TX channel */
- XAxiDma_BdRing RxBdRing[]; /* BD container management for RX channel */
- int TxNumChannels;
- int RxNumChannels;
- int MicroDmaMode;
- int AddrWidth; /**< Address Width */
- } XAxiDma;
1.3.中斷向量表
- typedef struct {
- Xil_ExceptionHandler Handler;
- void *Data;
- } 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:
- /*****************************************************************************/
- /**
- * @brief Register a handler for a specific exception. This handler is being
- * called when the processor encounters the specified exception.
- *
- * @param exception_id contains the ID of the exception source and should
- * be in the range of 0 to XIL_EXCEPTION_ID_LAST.
- * See xil_exception.h for further information.
- * @param Handler to the Handler for that exception.
- * @param Data is a reference to Data that will be passed to the
- * Handler when it gets called.
- *
- * @return None.
- *
- * @note None.
- *
- ****************************************************************************/
- void Xil_ExceptionRegisterHandler(u32 Exception_id,
- Xil_ExceptionHandler Handler,
- void *Data)
- {
- XExc_VectorTable[Exception_id].Handler = Handler;
- XExc_VectorTable[Exception_id].Data = Data;
- }
從上面能夠看到Xil_ExceptionRegisterHandler()這個函數是把中斷的句柄(第二個行參「Handler」)和中斷的參數(第三個行參「Data」)放到了兩個結構體XExc_VectorTableEntry類型的數組XExc_VectorTable當中 ,XExc_VectorTableEntry結構體類型以下:
- XExc_VectorTableEntry XExc_VectorTable[XIL_EXCEPTION_ID_LAST + ] =
- {
- {Xil_ExceptionNullHandler, NULL},
- {Xil_UndefinedExceptionHandler, NULL},
- {Xil_ExceptionNullHandler, NULL},
- {Xil_PrefetchAbortHandler, NULL},
- {Xil_DataAbortHandler, NULL},
- {Xil_ExceptionNullHandler, NULL},
- {Xil_ExceptionNullHandler, NULL},
- };
Xil_ExceptionRegisterHandler()函數的第二傳參XScuGic_InterruptHandler,是一個函數指針,強制轉化成了Xil_ExceptionHandler類型,XScuGic_InterruptHandler()函數以下。
- void XScuGic_InterruptHandler(XScuGic *InstancePtr)
- {
- u32 InterruptID;
- u32 IntIDFull;
- XScuGic_VectorTableEntry *TablePtr;
- /* Assert that the pointer to the instance is valid
- */
- Xil_AssertVoid(InstancePtr != NULL);
- /*
- * Read the int_ack register to identify the highest priority interrupt ID
- * and make sure it is valid. Reading Int_Ack will clear the interrupt in the GIC.
- * 讀取 int_ack 寄存器以識別最高優先級的中斷 ID, 並確保其有效。讀取 Int_Ack 將清除 GIC 中的中斷。
* 而後看看讀出來的中斷 ID 是否大於最大的中斷值。 - */
- IntIDFull = XScuGic_CPUReadReg(InstancePtr, XSCUGIC_INT_ACK_OFFSET);
- InterruptID = IntIDFull & XSCUGIC_ACK_INTID_MASK;
- if(XSCUGIC_MAX_NUM_INTR_INPUTS < InterruptID){
- goto IntrExit;
- }
- /*
- * Execute the ISR. Jump into the Interrupt service routine based on the
- * IRQSource. A software trigger is cleared by the ACK.
- */
- TablePtr = &(InstancePtr->Config->HandlerTable[InterruptID]);
- if(TablePtr != NULL) {
- TablePtr->Handler(TablePtr->CallBackRef);
- }
- IntrExit:
- /*
- * Write to the EOI register, we are all done here.
- * Let this function return, the boot code will restore the stack.
- */
- XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_EOI_OFFSET, IntIDFull);
- }
經過程序開頭 xilinx 給出的這個XScuGic_InterruptHandler()程序的註釋能夠知道: 這個函數是基本的中斷驅動函數。 它必須 鏈接到中斷源, 以便在中斷控制器的中斷激活時被調用。 它將解決哪些中斷是活動的和啓用的, 並調用適當的中斷處理程序。 它使用中斷類型信息來肯定什麼時候確認中斷。 首先處理最高優先級的中斷。 此函數假定中斷向量表已預先初始化。 它不會在調用中斷處理程序以前驗證表中的條目是否有效。 當中斷髮生時,調用的就是上面的代碼中的語句:TablePtr->Handler(TablePtr->CallBackRef)。那麼這個Handler和CallBackRef究竟是什麼呢?也就是Handler和CallBackRef究竟是和哪段要被執行的代碼綁定在一塊兒呢?
2.1.2.中斷鏈接函數
咱們在DMA_Setup_Intr_System()函數中調用了中斷鏈接函數XScuGic_Connect();
- int DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
- {
- <...>
- /*
- * Connect the device driver handler that will be called when an
- * interrupt for the device occurs, the handler defined above performs
- * the specific interrupt processing for the device.
- */
- Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,
- (Xil_InterruptHandler)DMA_TxIntrHandler,
- AxiDmaPtr);
- if (Status != XST_SUCCESS) {
- return Status;
- }
- Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
- (Xil_InterruptHandler)DMA_RxIntrHandler,
- AxiDmaPtr);
- if (Status != XST_SUCCESS) {
- return Status;
- }
- <...>
- }
能夠看到XScuGic_Connect()函數的第三個傳參是一個Xil_InterruptHandler類型的函數指針DMA_TxIntrHandler。XScuGic_Connect()內容以下。
- s32 XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
- Xil_InterruptHandler Handler, void *CallBackRef)
- {
- /*
- * Assert the arguments
- */
- Xil_AssertNonvoid(InstancePtr != NULL);
- Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
- Xil_AssertNonvoid(Handler != NULL);
- Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
- /*
- * The Int_Id is used as an index into the table to select the proper
- * handler
- */
- InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;
- InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;
- return XST_SUCCESS;
- }
能夠看到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的也差很少。
- static void DMA_TxIntrHandler(void *Callback)
- {
- u32 IrqStatus;
- int TimeOut;
- XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
- /* Read pending interrupts */
- IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);
- /* Acknowledge pending interrupts */
- XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);
- /*
- * If no interrupt is asserted, we do not do anything
- */
- if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
- return;
- }
- /*
- * If error interrupt is asserted, raise error flag, reset the
- * hardware to recover from the error, and return with no further
- * processing.
- */
- if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
- Error = ;
- /*
- * Reset should never fail for transmit channel
- */
- XAxiDma_Reset(AxiDmaInst);
- TimeOut = RESET_TIMEOUT_COUNTER;
- while (TimeOut) {
- if (XAxiDma_ResetIsDone(AxiDmaInst)) {
- break;
- }
- TimeOut -= ;
- }
- return;
- }
- /*
- * If Completion interrupt is asserted, then set the TxDone flag
- */
- if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
- TxDone = ;
- }
- }
代碼中,在dma_intr.h中聲明瞭三個全局變量:TxDone,RxDone和Error。
dam_intr.h :
- extern volatile int TxDone;
- extern volatile int RxDone;
- extern volatile int Error;
在dma_intr.c文件的開頭,定義了這三個全局變量。
- #include "dma_intr.h"
- volatile int TxDone;
- volatile int RxDone;
- 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的數據收發。
-
xilinx DMA IP核(二) —— 文檔閱讀
本筆記不記錄DMA的Scatter/Gather特性.DMA上有三種總線:AXI4-LIte(對寄存器進行配置),AXI4-Memory Map(用於與內存交互)和AXI4 Stream(用於與外設交 ...
-
從Xilinx FFT IP核到OFDM
筆者在校的科研任務,須要用FPGA搭建OFDM通訊系統,而OFDM的核心便是IFFT和FFT運算,所以本文經過Xilinx FFT IP核的使用總結給你們開個頭,詳細內容可查看官方文檔PG109.關於 ...
-
PCIE xilinx v5 IP核使用前的研究
外帶一個月前啃的一個星期,加本星期心無旁騖,啃出些心得,廣惠後人.希望有用. trn信號是數據鏈路層的信號 TLP包是數據鏈路層傳給transaction層的包 解包須要一個transaction的協 ...
-
Xilinx DDR3 IP核使用問題彙總(持續更新)和感悟
一度由於DDR3的IP核使用而發狂. 後來由於解決問題,得一感悟.後面此貼會完整講述ddr3 ip的使用.(XILINX K7) 感悟:對於有供應商支持的產品,遇到問題找官方的流程.按照官方的指導進行 ...
-
aurora 64B/66B ip核設置與例程代碼詳解
見網頁https://blog.csdn.net/u014586651/article/details/84349328 https://blog.csdn.net/u012135070/articl ...
-
Xilinx FFT IP核縮放因子說明
以1024點FFT爲例, reg [9:0] scale_sch = 10'b11_10_01_01_01; 流水線結構中,將每一個基 2 的蝶形處理單元視爲一個階段. 每一個階段進行一次數據的縮減,縮減 ...
-
Xilinx的IP核接口命名說明
s_axis中的s表示:slave(從); m_axis中的m表示:master(主). axis表示AXI(一種總線協議) Signal.
-
xilinx IP核配置,一步一步驗證Xilinx Serdes GTX最高8.0Gbps
版權聲明:本文爲博主原創文章,未經博主容許不得轉載. https://blog.csdn.net/u010161493/article/details/77658599 目錄(?)[+] 以前 ...
-
利用ZYNQ SOC快速打開算法驗證通路(4)——AXI DMA使用解析及環路測試
一.AXI DMA介紹 本篇博文講述AXI DMA的一些使用總結,硬件IP子系統搭建與SDK C代碼封裝參考米聯客ZYNQ教程.若想讓ZYNQ的PS與PL兩部分高速數據傳輸,須要利用PS的HP(高性能 ...
-
7 Series GTP IP核使用總結 IP核配置篇
FPGA內嵌收發器至關於以太網中的PHY芯片,但更靈活更高效,線速率也在隨着FPGA芯片的發展升級.本文對7系列FPGA內部高速收發器GTP IP核的配置和使用作些簡單的總結,以備後續回顧重用.本文是 ...