上篇该系列博文中讲述W5500接收到上位机传输的数据,此后需要将数据缓存起来。当数据量较大或者其他数据带宽较高的情况下,片上缓存(OCM)已无法满足需求,这时需要将大量数据保存在外挂的DDR SDRAM中。
最简单的方式是使用Xilinx的读写地址库函数Xil_In32()和Xil_Out32(),当然不仅支持32bit位宽,还包括8 16和64bit。但这种方式每次读写都要占用CPU,无法在读写的同时接收后续数据或者对之前的数据进一步处理,也就无法形成类似FPGA逻辑设计中的“流水线结构”,此时前段数据缓存过程中,后段数据会被丢弃。所以,需要利用PS端CPU子系统内的专用硬件DMA完成高速的批量数据搬移工作。
在Xilinx SDK的system.mss页面下直接导入ps_dma示例工程。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "sleep.h" 4 #include "xparameters.h" 5 #include "xil_types.h" 6 #include "xil_assert.h" 7 #include "xil_io.h" 8 #include "xil_exception.h" 9 #include "xil_cache.h" 10 #include "xil_printf.h" 11 #include "xscugic.h" 12 #include "xdmaps.h" 13 14 /************************** Constant Definitions *****************************/ 15 /* 16 * The following constants map to the XPAR parameters created in the 17 * xparameters.h file. They are defined here such that a user can easily 18 * change all the needed parameters in one place. 19 */ 20 #define DMA_DEVICE_ID XPAR_XDMAPS_1_DEVICE_ID 21 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID 22 23 #define DMA_DONE_INTR_0 XPAR_XDMAPS_0_DONE_INTR_0 24 #define DMA_DONE_INTR_1 XPAR_XDMAPS_0_DONE_INTR_1 25 #define DMA_DONE_INTR_2 XPAR_XDMAPS_0_DONE_INTR_2 26 #define DMA_DONE_INTR_3 XPAR_XDMAPS_0_DONE_INTR_3 27 #define DMA_DONE_INTR_4 XPAR_XDMAPS_0_DONE_INTR_4 28 #define DMA_DONE_INTR_5 XPAR_XDMAPS_0_DONE_INTR_5 29 #define DMA_DONE_INTR_6 XPAR_XDMAPS_0_DONE_INTR_6 30 #define DMA_DONE_INTR_7 XPAR_XDMAPS_0_DONE_INTR_7 31 #define DMA_FAULT_INTR XPAR_XDMAPS_0_FAULT_INTR 32 33 34 35 #define TEST_ROUNDS 1 /* Number of loops that the Dma transfers run.*/ 36 #define DMA_LENGTH 1024 /* Length of the Dma Transfers */ 37 #define TIMEOUT_LIMIT 0x2000 /* Loop count for timeout */ 38 39 /**************************** Type Definitions *******************************/ 40 41 42 /***************** Macros (Inline Functions) Definitions *********************/ 43 44 45 /************************** Function Prototypes ******************************/ 46 47 int XDmaPs_Example_W_Intr(XScuGic *GicPtr, u16 DeviceId); 48 int SetupInterruptSystem(XScuGic *GicPtr, XDmaPs *DmaPtr); 49 void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd, 50 void *CallbackRef); 51 52 /************************** Macro Definitions *****************************/ 53 54 55 /************************** Variable Definitions *****************************/ 56 #ifdef __ICCARM__ 57 #pragma data_alignment=32 58 static int Src[DMA_LENGTH]; 59 static int Dst[DMA_LENGTH]; 60 #pragma data_alignment=4 61 #else 62 static int Src[DMA_LENGTH] __attribute__ ((aligned (32))); 63 static int Dst[DMA_LENGTH] __attribute__ ((aligned (32))); 64 #endif 65 66 XDmaPs DmaInstance; 67 #ifndef TESTAPP_GEN 68 XScuGic GicInstance; 69 #endif 70 71 /****************************************************************************/ 72 /** 73 * 74 * This is the main function for the DmaPs interrupt example. 75 * 76 * @param None. 77 * 78 * @return XST_SUCCESS to indicate success, otherwise XST_FAILURE. 79 * 80 * @note None. 81 * 82 ****************************************************************************/ 83 #ifndef TESTAPP_GEN 84 int main(void) 85 { 86 int Status; 87 88 Status = XDmaPs_Example_W_Intr(&GicInstance,DMA_DEVICE_ID); 89 if (Status != XST_SUCCESS) { 90 xil_printf("Error: XDMaPs_Example_W_Intr failed\r\n"); 91 return XST_FAILURE; 92 } 93 94 xil_printf("XDMaPs_Example_W_Intr passed\r\n"); 95 return XST_SUCCESS; 96 97 } 98 #endif 99 100 101 /*****************************************************************************/ 102 /** 103 * 104 * Interrupt Example to test the DMA. 105 * 106 * @param DeviceId is the Device ID of the DMA controller. 107 * 108 * @return XST_SUCCESS to indicate success, otherwise XST_FAILURE. 109 * 110 * @note None. 111 * 112 ****************************************************************************/ 113 int XDmaPs_Example_W_Intr(XScuGic *GicPtr, u16 DeviceId) 114 { 115 int Index; 116 unsigned int Channel = 0; 117 int Status; 118 int TestStatus; 119 int TestRound; 120 int TimeOutCnt; 121 volatile int Checked[XDMAPS_CHANNELS_PER_DEV]; 122 XDmaPs_Config *DmaCfg; 123 XDmaPs *DmaInst = &DmaInstance; 124 XDmaPs_Cmd DmaCmd; 125 126 memset(&DmaCmd, 0, sizeof(XDmaPs_Cmd)); 127 128 DmaCmd.ChanCtrl.SrcBurstSize = 4; 129 DmaCmd.ChanCtrl.SrcBurstLen = 4; 130 DmaCmd.ChanCtrl.SrcInc = 1; 131 DmaCmd.ChanCtrl.DstBurstSize = 4; 132 DmaCmd.ChanCtrl.DstBurstLen = 4; 133 DmaCmd.ChanCtrl.DstInc = 1; 134 DmaCmd.BD.SrcAddr = (u32) Src; 135 DmaCmd.BD.DstAddr = (u32) Dst; 136 DmaCmd.BD.Length = DMA_LENGTH * sizeof(int); 137 138 139 /* 140 * Initialize the DMA Driver 141 */ 142 DmaCfg = XDmaPs_LookupConfig(DeviceId); 143 if (DmaCfg == NULL) { 144 return XST_FAILURE; 145 } 146 147 Status = XDmaPs_CfgInitialize(DmaInst, 148 DmaCfg, 149 DmaCfg->BaseAddress); 150 if (Status != XST_SUCCESS) { 151 return XST_FAILURE; 152 } 153 154 155 /* 156 * Setup the interrupt system. 157 */ 158 Status = SetupInterruptSystem(GicPtr, DmaInst); 159 if (Status != XST_SUCCESS) { 160 return XST_FAILURE; 161 } 162 163 164 TestStatus = XST_SUCCESS; 165 166 for (TestRound = 0; TestRound < TEST_ROUNDS; TestRound++) { 167 xil_printf("Test round %d\r\n", TestRound); 168 for (Channel = 0; 169 Channel < XDMAPS_CHANNELS_PER_DEV; 170 Channel++) { 171 172 173 /* Initialize source */ 174 for (Index = 0; Index < DMA_LENGTH; Index++) 175 Src[Index] = DMA_LENGTH - Index; 176 177 /* Clear destination */ 178 for (Index = 0; Index < DMA_LENGTH; Index++) 179 Dst[Index] = 0; 180 181 Checked[Channel] = 0; 182 183 /* Set the Done interrupt handler */ 184 XDmaPs_SetDoneHandler(DmaInst, 185 Channel, 186 DmaDoneHandler, 187 (void *)Checked); 188 189 190 Status = XDmaPs_Start(DmaInst, Channel, &DmaCmd, 0); 191 if (Status != XST_SUCCESS) { 192 return XST_FAILURE; 193 } 194 195 TimeOutCnt = 0; 196 197 /* Now the DMA is done */ 198 while (!Checked[Channel] 199 && TimeOutCnt < TIMEOUT_LIMIT) { 200 TimeOutCnt++; 201 } 202 203 if (TimeOutCnt >= TIMEOUT_LIMIT) { 204 TestStatus = XST_FAILURE; 205 } 206 207 if (Checked[Channel] < 0) { 208 /* DMA controller failed */ 209 TestStatus = XST_FAILURE; 210 } 211 } 212 } 213 214 return TestStatus; 215 216 } 217 218 219 /******************************************************************************/ 220 /** 221 * 222 * This function connects the interrupt handler of the interrupt controller to 223 * the processor. This function is seperate to allow it to be customized for 224 * each application. Each processor or RTOS may require unique processing to 225 * connect the interrupt handler. 226 * 227 * @param GicPtr is the GIC instance pointer. 228 * @param DmaPtr is the DMA instance pointer. 229 * 230 * @return None. 231 * 232 * @note None. 233 * 234 ****************************************************************************/ 235 int SetupInterruptSystem(XScuGic *GicPtr, XDmaPs *DmaPtr) 236 { 237 int Status; 238 #ifndef TESTAPP_GEN 239 XScuGic_Config *GicConfig; 240 241 242 Xil_ExceptionInit(); 243 244 /* 245 * Initialize the interrupt controller driver so that it is ready to 246 * use. 247 */ 248 GicConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); 249 if (NULL == GicConfig) { 250 return XST_FAILURE; 251 } 252 253 Status = XScuGic_CfgInitialize(GicPtr, GicConfig, 254 GicConfig->CpuBaseAddress); 255 if (Status != XST_SUCCESS) { 256 return XST_FAILURE; 257 } 258 259 /* 260 * Connect the interrupt controller interrupt handler to the hardware 261 * interrupt handling logic in the processor. 262 */ 263 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, 264 (Xil_ExceptionHandler)XScuGic_InterruptHandler, 265 GicPtr); 266 #endif 267 /* 268 * Connect the device driver handlers that will be called when an interrupt 269 * for the device occurs, the device driver handler performs the specific 270 * interrupt processing for the device 271 */ 272 273 /* 274 * Connect the Fault ISR 275 */ 276 Status = XScuGic_Connect(GicPtr, 277 DMA_FAULT_INTR, 278 (Xil_InterruptHandler)XDmaPs_FaultISR, 279 (void *)DmaPtr); 280 if (Status != XST_SUCCESS) { 281 return XST_FAILURE; 282 } 283 284 /* 285 * Connect the Done ISR for all 8 channels of DMA 0 286 */ 287 Status = XScuGic_Connect(GicPtr, 288 DMA_DONE_INTR_0, 289 (Xil_InterruptHandler)XDmaPs_DoneISR_0, 290 (void *)DmaPtr); 291 Status |= XScuGic_Connect(GicPtr, 292 DMA_DONE_INTR_1, 293 (Xil_InterruptHandler)XDmaPs_DoneISR_1, 294 (void *)DmaPtr); 295 Status |= XScuGic_Connect(GicPtr, 296 DMA_DONE_INTR_2, 297 (Xil_InterruptHandler)XDmaPs_DoneISR_2, 298 (void *)DmaPtr); 299 Status |= XScuGic_Connect(GicPtr, 300 DMA_DONE_INTR_3, 301 (Xil_InterruptHandler)XDmaPs_DoneISR_3, 302 (void *)DmaPtr); 303 Status |= XScuGic_Connect(GicPtr, 304 DMA_DONE_INTR_4, 305 (Xil_InterruptHandler)XDmaPs_DoneISR_4, 306 (void *)DmaPtr); 307 Status |= XScuGic_Connect(GicPtr, 308 DMA_DONE_INTR_5, 309 (Xil_InterruptHandler)XDmaPs_DoneISR_5, 310 (void *)DmaPtr); 311 Status |= XScuGic_Connect(GicPtr, 312 DMA_DONE_INTR_6, 313 (Xil_InterruptHandler)XDmaPs_DoneISR_6, 314 (void *)DmaPtr); 315 Status |= XScuGic_Connect(GicPtr, 316 DMA_DONE_INTR_7, 317 (Xil_InterruptHandler)XDmaPs_DoneISR_7, 318 (void *)DmaPtr); 319 320 if (Status != XST_SUCCESS) 321 return XST_FAILURE; 322 323 /* 324 * Enable the interrupts for the device 325 */ 326 XScuGic_Enable(GicPtr, DMA_DONE_INTR_0); 327 XScuGic_Enable(GicPtr, DMA_DONE_INTR_1); 328 XScuGic_Enable(GicPtr, DMA_DONE_INTR_2); 329 XScuGic_Enable(GicPtr, DMA_DONE_INTR_3); 330 XScuGic_Enable(GicPtr, DMA_DONE_INTR_4); 331 XScuGic_Enable(GicPtr, DMA_DONE_INTR_5); 332 XScuGic_Enable(GicPtr, DMA_DONE_INTR_6); 333 XScuGic_Enable(GicPtr, DMA_DONE_INTR_7); 334 XScuGic_Enable(GicPtr, DMA_FAULT_INTR); 335 336 Xil_ExceptionEnable(); 337 338 return XST_SUCCESS; 339 340 } 341 342 343 /*****************************************************************************/ 344 /** 345 * 346 * DmaDoneHandler. 347 * 348 * @param Channel is the Channel number. 349 * @param DmaCmd is the Dma Command. 350 * @param CallbackRef is the callback reference data. 351 * 352 * @return None. 353 * 354 * @note None. 355 * 356 ******************************************************************************/ 357 void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd, void *CallbackRef) 358 { 359 360 /* done handler */ 361 volatile int *Checked = (volatile int *)CallbackRef; 362 int Index; 363 int Status = 1; 364 int *Src; 365 int *Dst; 366 367 Src = (int *)DmaCmd->BD.SrcAddr; 368 Dst = (int *)DmaCmd->BD.DstAddr; 369 370 /* DMA successful */ 371 /* compare the src and dst buffer */ 372 for (Index = 0; Index < DMA_LENGTH; Index++) { 373 if ((Src[Index] != Dst[Index]) || 374 (Dst[Index] != DMA_LENGTH - Index)) { 375 Status = -XST_FAILURE; 376 } 377 } 378 379 380 Checked[Channel] = Status; 381 }
其实demo中做的操作非常简单,仅仅是定义了两个数组Src和Dst,之后利用PS_DMA将Src中数据搬移到Dst中,搬移完成后进入中断函数比较两部分地址数据是否一致。Xilinx的SDK软件代码有固定的套路,“上有政策,下有对策”,我们可以将其封装成固定格式的一个个子函数,方便今后调用。这里把整个工程分为:系统中断,PS_DMA专有中断以及主函数三个部分。
1 #include "xscugic.h" 2 #include "sys_intr.h" 3 4 int sys_IntrInit(XScuGic *GicPtr) 5 { 6 XScuGic_Config *GicConfig; 7 /* 8 * Initialize the interrupt controller driver so that it is ready to 9 * use. 10 */ 11 int Status; 12 GicConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); 13 if (NULL == GicConfig) { 14 return XST_FAILURE; 15 } 16 17 Status = XScuGic_CfgInitialize(GicPtr, GicConfig, 18 GicConfig->CpuBaseAddress); 19 if (Status != XST_SUCCESS) { 20 return XST_FAILURE; 21 } 22 return XST_SUCCESS; 23 } 24 25 void setupIntrException(XScuGic *GicPtr) 26 { 27 Xil_ExceptionInit(); 28 /* 29 * Connect the interrupt controller interrupt handler to the hardware 30 * interrupt handling logic in the processor. 31 */ 32 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, 33 (Xil_ExceptionHandler)XScuGic_InterruptHandler, 34 GicPtr); 35 Xil_ExceptionEnable(); 36 }
1 #ifndef SRC_SYS_INTR_H_ 2 #define SRC_SYS_INTR_H_ 3 4 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID 5 6 int sys_IntrInit(XScuGic *GicPtr); 7 void setupIntrException(XScuGic *GicPtr); 8 9 #endif /* SRC_SYS_INTR_H_ */
1 #include "xil_types.h" 2 #include "xdmaps.h" 3 #include "xscugic.h" 4 #include "psdma_intr.h" 5 6 int PS_DMA_IntrInit(XDmaPs *DmaInst,u16 DeviceId) 7 { 8 /* 9 * Initialize the DMA Driver 10 */ 11 int Status; 12 XDmaPs_Config *DmaCfg = NULL; 13 DmaCfg = XDmaPs_LookupConfig(DeviceId); 14 if (DmaCfg == NULL) { 15 return XST_FAILURE; 16 } 17 18 Status = XDmaPs_CfgInitialize(DmaInst, 19 DmaCfg, 20 DmaCfg->BaseAddress); 21 if (Status != XST_SUCCESS) { 22 return XST_FAILURE; 23 } 24 return XST_SUCCESS; 25 } 26 27 int PS_DMA_SetupIntr(XScuGic *GicPtr,XDmaPs *DmaPtr,unsigned Channel) 28 { 29 int Status; 30 /* 31 * Connect the device driver handlers that will be called when an interrupt 32 * for the device occurs, the device driver handler performs the specific 33 * interrupt processing for the device 34 */ 35 36 /* 37 * Connect the Fault ISR 38 */ 39 Status = XScuGic_Connect(GicPtr, 40 DMA_FAULT_INTR, 41 (Xil_InterruptHandler)XDmaPs_FaultISR, 42 (void *)DmaPtr); 43 if (Status != XST_SUCCESS) { 44 return XST_FAILURE; 45 } 46 47 /* 48 * Connect the Done ISR for all 8 channels of DMA 0 49 */ 50 Status = XScuGic_Connect(GicPtr, 51 DMA_DONE_INTR_0, 52 (Xil_InterruptHandler)XDmaPs_DoneISR_0, 53 (void *)DmaPtr); 54 /*Status |= XScuGic_Connect(GicPtr, 55 DMA_DONE_INTR_1, 56 (Xil_InterruptHandler)XDmaPs_DoneISR_1, 57 (void *)DmaPtr); 58 Status |= XScuGic_Connect(GicPtr, 59 DMA_DONE_INTR_2, 60 (Xil_InterruptHandler)XDmaPs_DoneISR_2, 61 (void *)DmaPtr); 62 Status |= XScuGic_Connect(GicPtr, 63 DMA_DONE_INTR_3, 64 (Xil_InterruptHandler)XDmaPs_DoneISR_3, 65 (void *)DmaPtr); 66 Status |= XScuGic_Connect(GicPtr, 67 DMA_DONE_INTR_4, 68 (Xil_InterruptHandler)XDmaPs_DoneISR_4, 69 (void *)DmaPtr); 70 Status |= XScuGic_Connect(GicPtr, 71 DMA_DONE_INTR_5, 72 (Xil_InterruptHandler)XDmaPs_DoneISR_5, 73 (void *)DmaPtr); 74 Status |= XScuGic_Connect(GicPtr, 75 DMA_DONE_INTR_6, 76 (Xil_InterruptHandler)XDmaPs_DoneISR_6, 77 (void *)DmaPtr); 78 Status |= XScuGic_Connect(GicPtr, 79 DMA_DONE_INTR_7, 80 (Xil_InterruptHandler)XDmaPs_DoneISR_7, 81 (void *)DmaPtr);*/ 82 83 if (Status != XST_SUCCESS) 84 return XST_FAILURE; 85 86 /* Set the Done interrupt handler */ 87 XDmaPs_SetDoneHandler(DmaPtr, 88 Channel,//Channel 89 DmaDoneHandler,//真正的中断函数 90 (void *)Checked); 91 92 /* 93 * Enable the interrupts for the device 94 */ 95 XScuGic_Enable(GicPtr, DMA_DONE_INTR_0); 96 /* 97 XScuGic_Enable(GicPtr, DMA_DONE_INTR_1); 98 XScuGic_Enable(GicPtr, DMA_DONE_INTR_2); 99 XScuGic_Enable(GicPtr, DMA_DONE_INTR_3); 100 XScuGic_Enable(GicPtr, DMA_DONE_INTR_4); 101 XScuGic_Enable(GicPtr, DMA_DONE_INTR_5); 102 XScuGic_Enable(GicPtr, DMA_DONE_INTR_6); 103 XScuGic_Enable(GicPtr, DMA_DONE_INTR_7);*/ 104 XScuGic_Enable(GicPtr, DMA_FAULT_INTR); 105 106 return XST_SUCCESS; 107 } 108 109 void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd, void *CallbackRef) 110 { 111 112 /* done handler */ 113 volatile int *Checked = (volatile int *)CallbackRef; 114 //int Index; 115 int Status = 1; 116 117 xil_printf("Enter into the interrupt\n"); 118 Checked[Channel] = Status; 119 } 120 121 void PS_DMA_InitPara(XDmaPs_Cmd* DmaCmd) 122 { 123 124 memset(DmaCmd, 0, sizeof(XDmaPs_Cmd)); 125 126 DmaCmd->ChanCtrl.SrcBurstSize = 4; 127 DmaCmd->ChanCtrl.SrcBurstLen = 4; 128 DmaCmd->ChanCtrl.SrcInc = 1; 129 DmaCmd->ChanCtrl.DstBurstSize = 4; 130 DmaCmd->ChanCtrl.DstBurstLen = 4; 131 DmaCmd->ChanCtrl.DstInc = 1; 132 DmaCmd->BD.SrcAddr = (u32) Src; 133 DmaCmd->BD.DstAddr = (u32) DDR_BASEADDR;//Dst 134 DmaCmd->BD.Length = DMA_LENGTH * sizeof(int); 135 }
1 #ifndef SRC_PSDMA_INTR_H_ 2 #define SRC_PSDMA_INTR_H_ 3 4 #define DMA_DONE_INTR_0 XPAR_XDMAPS_0_DONE_INTR_0 5 #define DMA_DONE_INTR_1 XPAR_XDMAPS_0_DONE_INTR_1 6 #define DMA_DONE_INTR_2 XPAR_XDMAPS_0_DONE_INTR_2 7 #define DMA_DONE_INTR_3 XPAR_XDMAPS_0_DONE_INTR_3 8 #define DMA_DONE_INTR_4 XPAR_XDMAPS_0_DONE_INTR_4 9 #define DMA_DONE_INTR_5 XPAR_XDMAPS_0_DONE_INTR_5 10 #define DMA_DONE_INTR_6 XPAR_XDMAPS_0_DONE_INTR_6 11 #define DMA_DONE_INTR_7 XPAR_XDMAPS_0_DONE_INTR_7 12 #define DMA_FAULT_INTR XPAR_XDMAPS_0_FAULT_INTR 13 14 #define DDR_BASEADDR 0x00600000//XPAR_PS7_DDR_0_S_AXI_BASEADDR 0x00100000 15 #define DMA_LENGTH 1024 /* Length of the Dma Transfers */ 16 17 int Src[DMA_LENGTH] __attribute__ ((aligned (32))); 18 volatile int Checked[XDMAPS_CHANNELS_PER_DEV]; 19 20 int PS_DMA_IntrInit(XDmaPs *DmaInst,u16 DeviceId); 21 int PS_DMA_SetupIntr(XScuGic *GicPtr,XDmaPs *DmaPtr,unsigned Channel); 22 void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd, void *CallbackRef); 23 void PS_DMA_InitPara(XDmaPs_Cmd* DmaCmd); 24 25 #endif /* SRC_PSDMA_INTR_H_ */
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "sleep.h" 4 #include "xparameters.h" 5 #include "xil_types.h" 6 #include "xil_assert.h" 7 #include "xil_io.h" 8 #include "xil_exception.h" 9 #include "xil_cache.h" 10 #include "xil_printf.h" 11 #include "xscugic.h" 12 #include "xdmaps.h" 13 14 #include "sys_intr.h" 15 #include "psdma_intr.h" 16 17 18 #define DMA_DEVICE_ID XPAR_XDMAPS_1_DEVICE_ID 19 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID 20 21 22 #define TEST_ROUNDS 1 /* Number of loops that the Dma transfers run.*/ 23 #define TIMEOUT_LIMIT 0x2000 /* Loop count for timeout */ 24 25 26 static XScuGic GicInstance; 27 static XDmaPs DmaInstance; 28 static XDmaPs_Cmd DmaCmd; 29 unsigned int Channel = 0; 30 31 /************************** Function Prototypes ******************************/ 32 33 int PS_DMA_WriteTest(); 34 int SetupInterruptSystem(XScuGic *GicPtr, XDmaPs *DmaPtr); 35 void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd, 36 void *CallbackRef); 37 int dataCheck(u32 baseAddr,u32 len); 38 int systemInit(XScuGic *GicPtr,u16 DeviceId); 39 40 41 int main(void) 42 { 43 int Status; 44 Status = systemInit(&GicInstance,DMA_DEVICE_ID); 45 if (Status != XST_SUCCESS) { 46 xil_printf("System initialization is failed\r\n"); 47 return XST_FAILURE; 48 } 49 50 Status = PS_DMA_WriteTest(); 51 if (Status != XST_SUCCESS) { 52 xil_printf("Error: XDMaPs_Example_W_Intr failed\r\n"); 53 return XST_FAILURE; 54 } 55 xil_printf("Checking data...\n"); 56 Status = dataCheck(DDR_BASEADDR,DMA_LENGTH); 57 if(Status != XST_SUCCESS) 58 { 59 xil_printf("Error:check failed\n"); 60 return XST_FAILURE; 61 } 62 63 xil_printf("Writing data to DDR using DMA test passed!\r\n"); 64 return XST_SUCCESS; 65 66 } 67 68 int dataCheck(u32 baseAddr,u32 len) 69 { 70 u32 DDR_ReadData[1024]; 71 int i; 72 for(i=0;i<len;i++) 73 { 74 DDR_ReadData[i] = Xil_In32(baseAddr+i*4); 75 if(DDR_ReadData[i]!=Src[i]) 76 return XST_FAILURE; 77 //else //将写入DDR数据读回 并打印 78 // xil_printf("data at %x is %d\n",baseAddr+i*4,DDR_ReadData[i]); 79 } 80 return XST_SUCCESS; 81 } 82 83 84 /*****************************************************************************/ 85 /** 86 * 87 * Interrupt Example to test the DMA. 88 * 89 * @param DeviceId is the Device ID of the DMA controller. 90 * 91 * @return XST_SUCCESS to indicate success, otherwise XST_FAILURE. 92 * 93 * @note None. 94 * 95 ****************************************************************************/ 96 int PS_DMA_WriteTest() 97 { 98 int Index; 99 int Status; 100 int TestStatus; 101 int TestRound; 102 int TimeOutCnt; 103 104 TestStatus = XST_SUCCESS; 105 106 for (TestRound = 0; TestRound < TEST_ROUNDS; TestRound++) { 107 xil_printf("Test round %d\r\n", TestRound); 108 for (Channel = 0;Channel < 1;Channel++) 109 { 110 /* Initialize source */ 111 for (Index = 0; Index < DMA_LENGTH; Index++) 112 Src[Index] = DMA_LENGTH - Index; 113 114 Checked[Channel] = 0; 115 116 Status = XDmaPs_Start(&DmaInstance, Channel, &DmaCmd, 0); 117 if (Status != XST_SUCCESS) { 118 xil_printf("Starting the DMA is failed.\n"); 119 return XST_FAILURE; 120 } 121 xil_printf("Starting the DMA is successful.\n"); 122 TimeOutCnt = 0; 123 124 while (!Checked[Channel] 125 && TimeOutCnt < TIMEOUT_LIMIT) { 126 TimeOutCnt++; 127 } 128 /* Now the DMA is done */ 129 xil_printf("Jump out of the interrupt\n"); 130 if (TimeOutCnt >= TIMEOUT_LIMIT) { 131 xil_printf("Overtime!\n"); 132 TestStatus = XST_FAILURE; 133 } 134 135 if (Checked[Channel] < 0) { 136 /* DMA controller failed */ 137 xil_printf("Checking failure!\n"); 138 TestStatus = XST_FAILURE; 139 } 140 } 141 } 142 143 return TestStatus; 144 145 } 146 147 int systemInit(XScuGic *GicPtr,u16 DeviceId) 148 { 149 xil_printf("Start to initialize interrupt system.\n"); 150 151 PS_DMA_InitPara(&DmaCmd);//主要设置DMA的源目的地址 152 //xil_printf("Configuring DMA parameters is successful.\n"); 153 154 int Status; 155 156 Status = PS_DMA_IntrInit(&DmaInstance,DeviceId); 157 if (Status != XST_SUCCESS) { 158 xil_printf("DMA initialization is failed.\n"); 159 return XST_FAILURE; 160 } 161 //xil_printf("DMA initialization is successful.\n"); 162 163 Status = sys_IntrInit(GicPtr); 164 if (Status != XST_SUCCESS) { 165 xil_printf("Initialization of the interrupt system is failed.\n"); 166 return XST_FAILURE; 167 } 168 //xil_printf("Initialization of the interrupt system is successful.\n"); 169 170 setupIntrException(GicPtr); 171 172 Status = PS_DMA_SetupIntr(GicPtr,&DmaInstance,Channel);//DMA中断入口/// 173 if (Status != XST_SUCCESS) { 174 xil_printf("Setting up DMA interrupt is failed.\n"); 175 return XST_FAILURE; 176 } 177 //xil_printf("Setting up DMA interrupt is successful.\n"); 178 179 xil_printf("System initialization is finished.\n"); 180 xil_printf("------------------------------------------\n"); 181 return XST_SUCCESS; 182 }
上述代码的封装方式参考了米联客教程中的思想。先说明系统中断部分:sys_IntrInit()函数中进行查找表配置和中断控制器初始化操作,setupIntrException()函数负责使能中断异常处理。再来说说PS_DMA中断部分:PS_DMA_IntrInit()函数与系统中断中sys_IntrInit()从操作到格式几乎完成相同,亦是查找表配置和DMA的初始化。PS_DMA_SetupIntr()函数完成了中断源和中断控制器的连接和设置中断处理器,以及中断使能,也就是所有PS_DMA的专用中断操作。
PS_DMA_SetupIntr()内最重要的部分是XDmaPs_SetDoneHandler(),其相当于一个调用中断函数的通用处理框架,它的第三个参数DoneHandler才是真正的中断处理函数。这里涉及到C语言的高级话题:函数通过函数指针调用另一个函数,被函数指针调用的函数就是通常讲的“回调函数”了。指针调用函数的方式兼顾了程序的通用架构和灵活性,具体参考文章:不懂C语言回调函数,那就看这篇文章吧! - 简书 https://www.jianshu.com/p/2f695d6fd64f 在该程序中,中断回调函数为DmaDoneHandler()。
PS_DMA_InitPara()是自行添加的PS_DMA参数初始化函数,内部的参数更是重中之重了,我们来查看Xilinx官方文档ug585的DMA Controller章节。
简要来说,DMA以burst形式传输数据,意思是分批次搬移。手册说明原或目的burst_size位宽不能超过64bit,这也是其挂载AXI总线的数据位宽。PS_DMA_InitPara()里的SrcBurstSize为源突发传输位宽字节数,最大为8.SrcBurstLen是手册中所说的“burst length”,即突发传输数据个数。SrcInc表示burst types为地址自增(1)还是地址固定(0)模式。目的控制字同理。剩下的三个参数最重要:SrcAddr DstAddr Length分别代表源首地址 目的首地址和一共需要搬移的数据字节数。需要注意的是,一定要满足srcburstsize*srcburstlen == dstburstsize*dstburstlen,否则发生错误。这一点也比较好理解,相当于FPGA逻辑设计中的异步FIFO两侧数据带宽要匹配。
那么要想完成OCM到DDR的数据搬移,改动下地址就可以嘛。由于读写DDR要访问绝对地址,所以要格外注意读写操作的地址不能和DDR内存储程序代码和中间数据的地址段重叠。避免程序崩溃很简单的做法就是在XPAR_PS7_DDR_0_S_AXI_BASEADDR 的基础上加一段偏移量,具体加多少的问题本人也不是很明确,希望看到的朋友能在评论中指点一二。
明确了PS_DMA的参数和使用方式,还有一点非常重要:PS_DMA的工作时钟是多少?这就需要继续看ug585了。
DMA控制器工作在CPU_2*时钟速率下,那这个CPU_2*的频率值具体是多少呢?
从上表可以看出,CPU的时钟系统有两种时钟比例关系,分别是:6:2:1和4:2:1。对应的时钟名称依次是:CPU_6*4* CPU_3*2* CPU_2* CPU_1*。后边的N*就是该时钟频率与CPU_1*的频率的倍数。确定CPU_6*4*的数值和当前的时钟比例关系,也就确定了其他时钟的频率。PS_CLK频率与PLL Feedback Divider Value值相乘得到ARM PLL output frequency。之后经过二分频获得CPU_6*4*。在IP Integrator中打开ZYNQ的时钟配置界面:
当前使用时钟比例关系是6:2:1,PLL时钟频率是1333.333MHz,也就是CPU_6*4*的频率是1333.33/2=667MHz。综上,DMA的工作时钟CPU_2*的频率值是667/3=222MHz。
对于ZYNQ这一SOC架构来说,PS端连接如以太网,USB等高带宽外设计接口更加方便,所以PS_DMA的灵活运用还好是十分必要的,更灵活高效的利用这一硬件资源还要后期继续探索。PS端和PL端高速数据交互就需要用到另一个DMA成员AXI_DMA,可以说它利用片内总线打破了CPU+FPGA架构的性能瓶颈,该部分内容将在后续说明。