• zynq DMA 函数XAxiDma_SimpleTransfer的用法


    1 代码理解和使用

    在使用zynq中DMA控制器的时候,会用到XAxiDma_SimpleTransfer这个函数。学习zynq中的例子程序,大概明白了这个函数的用法。

    这个函数只能用在DMA控制器配置为简单模式(simple mode)的情况下。先看这个函数的源代码:

    /*****************************************************************************/
    /**
     * This function does one simple transfer submission
     *
     * It checks in the following sequence:
     *	- if engine is busy, cannot submit
     *	- if engine is in SG mode , cannot submit
     *
     * @param	InstancePtr is the pointer to the driver instance
     * @param	BuffAddr is the address of the source/destination buffer
     * @param	Length is the length of the transfer
     * @param	Direction is DMA transfer direction, valid values are
     *			- XAXIDMA_DMA_TO_DEVICE.
     *			- XAXIDMA_DEVICE_TO_DMA.
    
     * @return
     *		- XST_SUCCESS for success of submission
     *		- XST_FAILURE for submission failure, maybe caused by:
     *		Another simple transfer is still going
     *		- XST_INVALID_PARAM if:Length out of valid range [1:8M]
     *		Or, address not aligned when DRE is not built in
     *
     * @note	This function is used only when system is configured as
     *		Simple mode.
     *
     *****************************************************************************/
    u32 XAxiDma_SimpleTransfer(XAxiDma *InstancePtr, UINTPTR BuffAddr, u32 Length,
    	int Direction)
    {
    	u32 WordBits;
    	int RingIndex = 0;
    
    	/* If Scatter Gather is included then, cannot submit
    	 */
    	if (XAxiDma_HasSg(InstancePtr)) {
    		xdbg_printf(XDBG_DEBUG_ERROR, "Simple DMA mode is not"
    							" supported\r\n");
    
    		return XST_FAILURE;
    	}
    
    	if(Direction == XAXIDMA_DMA_TO_DEVICE){
    		if ((Length < 1) ||
    			(Length > InstancePtr->TxBdRing.MaxTransferLen)) {
    			return XST_INVALID_PARAM;
    		}
    
    		if (!InstancePtr->HasMm2S) {
    			xdbg_printf(XDBG_DEBUG_ERROR, "MM2S channel is not"
    							"supported\r\n");
    
    			return XST_FAILURE;
    		}
    
    		/* If the engine is doing transfer, cannot submit
    		 */
    
    		if(!(XAxiDma_ReadReg(InstancePtr->TxBdRing.ChanBase,
    				XAXIDMA_SR_OFFSET) & XAXIDMA_HALTED_MASK)) {
    			if (XAxiDma_Busy(InstancePtr,Direction)) {
    				xdbg_printf(XDBG_DEBUG_ERROR,
    							"Engine is busy\r\n");
    				return XST_FAILURE;
    			}
    		}
    
    		if (!InstancePtr->MicroDmaMode) {
    			WordBits = (u32)((InstancePtr->TxBdRing.DataWidth) - 1);
    		}
    		else {
    			WordBits = XAXIDMA_MICROMODE_MIN_BUF_ALIGN;
    		}
    
    		if ((BuffAddr & WordBits)) {
    
    			if (!InstancePtr->TxBdRing.HasDRE) {
    				xdbg_printf(XDBG_DEBUG_ERROR,
    					"Unaligned transfer without"
    					" DRE %x\r\n",(unsigned int)BuffAddr);
    
    				return XST_INVALID_PARAM;
    			}
    		}
    
    
    		XAxiDma_WriteReg(InstancePtr->TxBdRing.ChanBase,
    				 XAXIDMA_SRCADDR_OFFSET, LOWER_32_BITS(BuffAddr));
    		if (InstancePtr->AddrWidth > 32)
    			XAxiDma_WriteReg(InstancePtr->TxBdRing.ChanBase,
    					 XAXIDMA_SRCADDR_MSB_OFFSET,
    					 UPPER_32_BITS(BuffAddr));
    
    		XAxiDma_WriteReg(InstancePtr->TxBdRing.ChanBase,
    				XAXIDMA_CR_OFFSET,
    				XAxiDma_ReadReg(
    				InstancePtr->TxBdRing.ChanBase,
    				XAXIDMA_CR_OFFSET)| XAXIDMA_CR_RUNSTOP_MASK);
    
    		/* Writing to the BTT register starts the transfer
    		 */
    		XAxiDma_WriteReg(InstancePtr->TxBdRing.ChanBase,
    					XAXIDMA_BUFFLEN_OFFSET, Length);
    	}
    	else if(Direction == XAXIDMA_DEVICE_TO_DMA){
    		if ((Length < 1) ||
    			(Length >
    			InstancePtr->RxBdRing[RingIndex].MaxTransferLen)) {
    			return XST_INVALID_PARAM;
    		}
    
    
    		if (!InstancePtr->HasS2Mm) {
    			xdbg_printf(XDBG_DEBUG_ERROR, "S2MM channel is not"
    							" supported\r\n");
    
    			return XST_FAILURE;
    		}
    
    		if(!(XAxiDma_ReadReg(InstancePtr->RxBdRing[RingIndex].ChanBase,
    				XAXIDMA_SR_OFFSET) & XAXIDMA_HALTED_MASK)) {
    			if (XAxiDma_Busy(InstancePtr,Direction)) {
    				xdbg_printf(XDBG_DEBUG_ERROR,
    							"Engine is busy\r\n");
    				return XST_FAILURE;
    			}
    		}
    
    		if (!InstancePtr->MicroDmaMode) {
    			WordBits =
    			 (u32)((InstancePtr->RxBdRing[RingIndex].DataWidth) - 1);
    		}
    		else {
    			WordBits = XAXIDMA_MICROMODE_MIN_BUF_ALIGN;
    		}
    
    		if ((BuffAddr & WordBits)) {
    
    			if (!InstancePtr->RxBdRing[RingIndex].HasDRE) {
    				xdbg_printf(XDBG_DEBUG_ERROR,
    					"Unaligned transfer without"
    				" DRE %x\r\n", (unsigned int)BuffAddr);
    
    				return XST_INVALID_PARAM;
    			}
    		}
    
    
    		XAxiDma_WriteReg(InstancePtr->RxBdRing[RingIndex].ChanBase,
    				 XAXIDMA_DESTADDR_OFFSET, LOWER_32_BITS(BuffAddr));
    		if (InstancePtr->AddrWidth > 32)
    			XAxiDma_WriteReg(InstancePtr->RxBdRing[RingIndex].ChanBase,
    					 XAXIDMA_DESTADDR_MSB_OFFSET,
    					 UPPER_32_BITS(BuffAddr));
    
    		XAxiDma_WriteReg(InstancePtr->RxBdRing[RingIndex].ChanBase,
    				XAXIDMA_CR_OFFSET,
    			XAxiDma_ReadReg(InstancePtr->RxBdRing[RingIndex].ChanBase,
    			XAXIDMA_CR_OFFSET)| XAXIDMA_CR_RUNSTOP_MASK);
    		/* Writing to the BTT register starts the transfer
    		 */
    		XAxiDma_WriteReg(InstancePtr->RxBdRing[RingIndex].ChanBase,
    					XAXIDMA_BUFFLEN_OFFSET, Length);
    
    	}
    
    	return XST_SUCCESS;
    }
    /** @} */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168

    在函数的注释里面其实写得很清楚,每次调用这个函数的时候,就会在系统中提交一次DMA的传输行为的申请。这是需要稍微理解一下的。当系统接收到申请的时候,会去按照如下顺序查询相应的寄存器的状态:

    1. 如果DMA控制器正忙,无法提交传输申请;
    2. 如果DMA控制器处于SG模式,无法提交传输申请。

    没有上面的两种情况,那就开始按照函数中的参数进行一次DMA传输。

    传输的方向有两种:

    XAXIDMA_DMA_TO_DEVICE    //DDR到外设,通过DMA进行搬运
    XAXIDMA_DEVICE_TO_DMA    //外设到DDR,通过DMA进行搬运
    
    • 1
    • 2

    2 中断程序的设置

    在DMA传输的时候会产生两个中断:

    1. S2MM(Stream to Memory Mapped,数据流向内存映射)中断

    2. MM2S(Memory Mapped to Stream,内存映射向数据流)中断

    对DMA控制器来说,S2MM就是从外设(比如PL端的FIFO,或者高速AD,比如AD9361等)接收数据,并且将数据放到相应的内存空间中的过程。在S2MM中断程序中,可以设置一个接收完成变量,当传输完成时,将此变量置1,可以通过查询此变量来判断传输是否完成。

    同理,MM2S就是将内存空间中的数据搬运到外设(比如PL端的FIFO,高速DA的FIFO等)的过程。在MM2S中断程序中,可以设置一个发送完成变量,当传输完成时,将此变量置1,可以通过查询此变量来判断传输是否完成。

    在Xilinx提供的例子程序中,两个中断程序如下:

    1. 接收中断程序:
    /*****************************************************************************/
    /*
     *
     * This is the DMA RX interrupt handler function
     *
     * It gets the interrupt status from the hardware, acknowledges it, and if any
     * error happens, it resets the hardware. Otherwise, if a completion interrupt
     * is present, then it sets the RxDone flag.
     *
     * @param	Callback is a pointer to RX channel of the DMA engine.
     *
     * @return	None.
     *
     * @note		None.
     *
     ******************************************************************************/
    static void RxIntrHandler(void *Callback) {
    	u32 IrqStatus;
    	int TimeOut;
    	XAxiDma *AxiDmaInst = (XAxiDma *) Callback;
    
    	/* Read pending interrupts */
    	IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);
    
    	/* Acknowledge pending interrupts */
    	XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);
    
    	/*
    	 * 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 = 1;
    
    		/* Reset could fail and hang
    		 * NEED a way to handle this or do not call it??
    		 */
    		XAxiDma_Reset(AxiDmaInst);
    
    		TimeOut = RESET_TIMEOUT_COUNTER;
    
    		while (TimeOut) {
    			if (XAxiDma_ResetIsDone(AxiDmaInst)) {
    				break;
    			}
    
    			TimeOut -= 1;
    		}
    
    		return;
    	}
    
    	/*
    	 * If completion interrupt is asserted, then set RxDone flag
    	 */
    	if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
    
    		RxDone = 1;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    其中的RxDone变量即传输是否完成的变量。

    1. 发送中断程序如下:
    /*****************************************************************************/
    /*
     *
     * This is the DMA TX Interrupt handler function.
     *
     * It gets the interrupt status from the hardware, acknowledges it, and if any
     * error happens, it resets the hardware. Otherwise, if a completion interrupt
     * is present, then sets the TxDone.flag
     *
     * @param	Callback is a pointer to TX channel of the DMA engine.
     *
     * @return	None.
     *
     * @note		None.
     *
     ******************************************************************************/
    static void 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 = 1;
    
    		/*
    		 * Reset should never fail for transmit channel
    		 */
    		XAxiDma_Reset(AxiDmaInst);
    
    		TimeOut = RESET_TIMEOUT_COUNTER;
    
    		while (TimeOut) {
    			if (XAxiDma_ResetIsDone(AxiDmaInst)) {
    				break;
    			}
    
    			TimeOut -= 1;
    		}
    
    		return;
    	}
    
    	/*
    	 * If Completion interrupt is asserted, then set the TxDone flag
    	 */
    	if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
    
    		TxDone = 1;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    其中的TxDone即发送完成的变量。

    3 小结

    在使用过程中,按照例子,设置好传输的方向,对相应的变量进行判断,就能够很好的完成DMA的传输。

  • 相关阅读:
    微服务的艺术:构建可扩展和弹性的分布式应用
    Excel-VBA 快速上手(二、条件判断和循环)
    软件需求工程R 第十一、十二、十三章
    深入了解JVM和垃圾回收算法
    以技术面试官的经验分享毕业生及三年以下的程序员通过面试的技巧
    Unity 工具 之 Azure 微软 【GPT4o】HttpClient 异步流式请求的简单封装
    如何监听页面可见性,如处于后台、标签页切换、最小化、锁屏
    传统算法与神经网络算法,神经网络是谁提出的
    想知道电脑录屏软件哪个好用?这三个工具轻松实现屏幕录制
    Chrome的使用技巧
  • 原文地址:https://blog.csdn.net/little_soldier/article/details/126560676