Example #1
0
/**
* Reset both TX and RX channels of a DMA engine.
*
* Any DMA transaction in progress aborts immediately. The DMA engine is in
* stop state after the reset.
*
* @param  InstancePtr is a pointer to the DMA engine instance to be worked on.
*
* @return None.
*
* @note
*         - If the hardware is not working properly, this function will enter
*           infinite loop and never return.
*         - After the reset, the Normal mode is enabled, and the overflow error
*           for both TX/RX channels are disabled.
*         - After the reset, the DMA engine is no longer in pausing state, if
*           the DMA engine is paused before the reset operation.
*         - After the reset, the coalescing count value and the delay timeout
*           value are both set to 1 for TX and RX channels.
*         - After the reset, all interrupts are disabled.
*
******************************************************************************/
void XLlDma_Reset(XLlDma * InstancePtr)
{
	u32 IrqStatus;
	XLlDma_BdRing *TxRingPtr, *RxRingPtr;

	TxRingPtr = &XLlDma_mGetTxRing(InstancePtr);
	RxRingPtr = &XLlDma_mGetRxRing(InstancePtr);

	/* Save the locations of current BDs both rings are working on
	 * before the reset so later we can resume the rings smoothly.
	 */
	XLlDma_mBdRingSnapShotCurrBd(TxRingPtr);
	XLlDma_mBdRingSnapShotCurrBd(RxRingPtr);

	/* Start reset process then wait for completion */
	XLlDma_mSetCr(InstancePtr, XLLDMA_DMACR_SW_RESET_MASK);

	/* Loop until the reset is done */
	while ((XLlDma_mGetCr(InstancePtr) & XLLDMA_DMACR_SW_RESET_MASK)) {
	}

	/* Disable all interrupts after issue software reset */
	XLlDma_mBdRingIntDisable(TxRingPtr, XLLDMA_CR_IRQ_ALL_EN_MASK);
	XLlDma_mBdRingIntDisable(RxRingPtr, XLLDMA_CR_IRQ_ALL_EN_MASK);

	/* Clear Interrupt registers of both channels, as the software reset
	 * does not clear any register values. Not doing so will cause
	 * interrupts asserted after the software reset if there is any
	 * interrupt left over before.
	 */
	IrqStatus = XLlDma_mBdRingGetIrq(TxRingPtr);
	XLlDma_mBdRingAckIrq(TxRingPtr, IrqStatus);
	IrqStatus = XLlDma_mBdRingGetIrq(RxRingPtr);
	XLlDma_mBdRingAckIrq(RxRingPtr, IrqStatus);

	/* Enable Normal mode, and disable overflow errors for both channels */
	XLlDma_mSetCr(InstancePtr, XLLDMA_DMACR_TAIL_PTR_EN_MASK |
		      XLLDMA_DMACR_RX_OVERFLOW_ERR_DIS_MASK |
		      XLLDMA_DMACR_TX_OVERFLOW_ERR_DIS_MASK);

	/* Set TX/RX Channel coalescing setting */
	XLlDma_BdRingSetCoalesce(TxRingPtr, 1, 1);
	XLlDma_BdRingSetCoalesce(RxRingPtr, 1, 1);

	TxRingPtr->RunState = XST_DMA_SG_IS_STOPPED;
	RxRingPtr->RunState = XST_DMA_SG_IS_STOPPED;
}
/**
*
* This example sends and receives a single packet in loopback mode with checksum
* offloading support.
*
* The transmit frame will be checksummed over the entire Ethernet payload
* and inserted into the last 2 bytes of the frame.
*
* On receive, HW should calculate the Ethernet payload checksum and return a
* value of 0xFFFF which means the payload data was likely not corrupted.
*
* @param    TemacInstancePtr is a pointer to the instance of the Temac
*           component.
*
* @return   XST_SUCCESS to indicate success, otherwise XST_FAILURE.
*
* @note     None.
*
******************************************************************************/
int TemacSgDmaChecksumOffloadExample(XLlTemac * TemacInstancePtr,
				     XLlDma * DmaInstancePtr)
{
	int Status;
	u32 TxFrameLength;
	int PayloadSize = 1000;
	XLlDma_BdRing *RxRingPtr = &XLlDma_mGetRxRing(DmaInstancePtr);
	XLlDma_BdRing *TxRingPtr = &XLlDma_mGetTxRing(DmaInstancePtr);
	XLlDma_Bd *BdPtr;

	/*
	 * Cannot run this example if checksum offloading support is not available
	 */
	if (!(XLlTemac_IsRxCsum(TemacInstancePtr) &&
	      XLlTemac_IsTxCsum(TemacInstancePtr))) {
		TemacUtilErrorTrap("Checksum offloading not available");
		return XST_FAILURE;
	}

	/*
	 * Clear variables shared with callbacks
	 */
	FramesRx = 0;
	FramesTx = 0;
	DeviceErrors = 0;

	/*
	 * Calculate frame length (not including FCS)
	 */
	TxFrameLength = XTE_HDR_SIZE + PayloadSize;

	/*
	 * Setup the packet to be transmitted,
	 * Last 2 bytes are reserved for checksum
	 */
	TemacUtilFrameMemClear(&TxFrame);
	TemacUtilFrameHdrFormatMAC(&TxFrame, TemacMAC);
	TemacUtilFrameHdrFormatType(&TxFrame, PayloadSize);
	TemacUtilFrameSetPayloadData(&TxFrame, PayloadSize - 2);

        /*
         * Flush the TX frame before giving it to DMA TX channel to transmit,
         * in case D-Caching is turned on.
         */
	XCACHE_FLUSH_DCACHE_RANGE(&TxFrame, TxFrameLength);

	/*
	 * Clear out receive packet memory area
	 */
	TemacUtilFrameMemClear(&RxFrame);

        /*
         * Invalidate the RX frame before giving it to DMA RX channel to
         * receive data, in case D-Caching is turned on.
         */
	XCACHE_INVALIDATE_DCACHE_RANGE(&RxFrame, TxFrameLength);

	/*
	 * Interrupt coalescing parameters are set to their default settings
	 * which is to interrupt the processor after every frame has been
	 * processed by the DMA engine.
	 */
	Status = XLlDma_BdRingSetCoalesce(TxRingPtr, 1, 1);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error setting coalescing for transmit");
		return XST_FAILURE;
	}

	Status = XLlDma_BdRingSetCoalesce(RxRingPtr, 1, 1);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error setting coalescing for recv");
		return XST_FAILURE;
	}

	/*
	 * Make sure Tx and Rx are enabled
	 */
	Status = XLlTemac_SetOptions(TemacInstancePtr,
				     XTE_RECEIVER_ENABLE_OPTION |
				     XTE_TRANSMITTER_ENABLE_OPTION);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error setting options");
		return XST_FAILURE;
	}

	/*
	 * Start the LLTEMAC and enable its ERROR interrupts
	 */
	XLlTemac_Start(TemacInstancePtr);
	XLlTemac_IntEnable(TemacInstancePtr, XTE_INT_RECV_ERROR_MASK);

	/*
	 * Allocate 1 RxBD. Note that TEMAC utilizes an in-place allocation
	 * scheme. The returned BdPtr will point to a free BD in the memory
	 * segment setup with the call to XLlTemac_SgSetSpace()
	 */
	Status = XLlDma_BdRingAlloc(RxRingPtr, 1, &BdPtr);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error allocating RxBD");
		return XST_FAILURE;
	}

	/*
	 * Setup the BD. The BD template used in the call to XLlTemac_SgSetSpace()
	 * set the SOP and EOP fields of all RxBDs.
	 */
	XLlDma_mBdSetBufAddr(BdPtr, &RxFrame);
	XLlDma_mBdSetLength(BdPtr, sizeof(RxFrame));
	XLlDma_mBdSetStsCtrl(BdPtr, XLLDMA_BD_STSCTRL_SOP_MASK |
			     XLLDMA_BD_STSCTRL_EOP_MASK);


	/*
	 * Enqueue to HW
	 */
	Status = XLlDma_BdRingToHw(RxRingPtr, 1, BdPtr);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error committing RxBD to HW");
		return XST_FAILURE;
	}

	/*
	 * Enable DMA receive related interrupts
	 */
	XLlDma_mBdRingIntEnable(RxRingPtr, XLLDMA_CR_IRQ_ALL_EN_MASK);

	/*
	 * As DMA RX channel has been started in
	 * TemacSgDmaIntrSingleFrameExample() above, there is no need to start
	 * it again here
	 */

	/*
	 * Allocate 1 TxBD
	 */
	Status = XLlDma_BdRingAlloc(TxRingPtr, 1, &BdPtr);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error allocating TxBD");
		return XST_FAILURE;
	}

	/*
	 * Setup the TxBD
	 */
	XLlDma_mBdSetBufAddr(BdPtr, &TxFrame);
	XLlDma_mBdSetLength(BdPtr, TxFrameLength);
	XLlDma_mBdSetStsCtrl(BdPtr, XLLDMA_BD_STSCTRL_SOP_MASK |
			     XLLDMA_BD_STSCTRL_EOP_MASK);

	/*
	 * Setup TxBd checksum offload attributes.
	 * Note that the checksum offload values can be set globally for all TxBds
	 * when XLlDma_BdRingClone() is called to setup Tx BD space. This would
	 * eliminate the need to set them here.
	 */

	/* Enable hardware checksum computation for the buffer descriptor */
	XLlDma_mBdWrite((BdPtr), XLLDMA_BD_STSCTRL_USR0_OFFSET,
			(XLlDma_mBdRead((BdPtr), XLLDMA_BD_STSCTRL_USR0_OFFSET)
			 | CSUM_ENABLE));
	/* Write Start Offset and Insert Offset into BD */
	XLlDma_mBdWrite(BdPtr, XLLDMA_BD_USR1_OFFSET,
					XTE_HDR_SIZE << 16 | TxFrameLength - 2);
	/* Write 0, as the seed value, to the BD */
	XLlDma_mBdWrite(BdPtr, XLLDMA_BD_USR2_OFFSET, 0);
	/*
	 * Enqueue to HW
	 */
	Status = XLlDma_BdRingToHw(TxRingPtr, 1, BdPtr);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error committing TxBD to HW");
		return XST_FAILURE;
	}

	/*
	 * Enable DMA transmit related interrupts
	 */
	XLlDma_mBdRingIntEnable(TxRingPtr, XLLDMA_CR_IRQ_ALL_EN_MASK);

	/*
	 * As DMA TX channel has been started in
	 * TemacSgDmaIntrSingleFrameExample() above, there is no need to start
	 * it again here
	 */

	/*
	 * Wait for transmission to complete
	 */
	while (!FramesTx);
	/*
	 * Now that the frame has been sent, post process our TxBDs.
	 * Since we have only submitted 2 to HW, then there should be only 2 ready
	 * for post processing.
	 */
	if (XLlDma_BdRingFromHw(TxRingPtr, 1, &BdPtr) == 0) {
		TemacUtilErrorTrap("TxBDs were not ready for post processing");
		return XST_FAILURE;
	}

	/*
	 * Examine the TxBDs.
	 *
	 * There isn't much to do. The only thing to check would be DMA exception
	 * bits. But this would also be caught in the error handler. So we just
	 * return these BDs to the free list
	 */
	Status = XLlDma_BdRingFree(TxRingPtr, 1, BdPtr);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error freeing up TxBDs");
		return XST_FAILURE;
	}

	/*
	 * Wait for Rx indication
	 */
	while (!FramesRx);
	/*
	 * Now that the frame has been received, post process our RxBD.
	 * Since we have only submitted 1 to HW, then there should be only 1 ready
	 * for post processing.
	 */
	if (XLlDma_BdRingFromHw(RxRingPtr, 1, &BdPtr) == 0) {
		TemacUtilErrorTrap("RxBD was not ready for post processing");
		return XST_FAILURE;
	}

	/*
	 * There is no device status to check. If there was a DMA error, it should
	 * have been reported to the error handler. Check the receive length against
	 * the transmitted length, then verify the data.
	 *
	 * Note in LLTEMAC case, USR4_OFFSET word in the RX BD is used to store
	 * the real length of the received packet
	 */
	if (XLlDma_mBdRead(BdPtr, XLLDMA_BD_USR4_OFFSET) != TxFrameLength) {
		TemacUtilErrorTrap("Length mismatch");
	}

	/*
	 * Verify the checksum as computed by HW. It should add up to 0xFFFF
	 * if frame was uncorrupted
	 */
	if ((u16) (XLlDma_mBdRead(BdPtr, XLLDMA_BD_USR3_OFFSET))
		!= 0xFFFF) {
		TemacUtilErrorTrap("Rx checksum incorrect");
		return XST_FAILURE;
	}

	/*
	 * Return the RxBD back to the channel for later allocation. Free the
	 * exact number we just post processed.
	 */
	Status = XLlDma_BdRingFree(RxRingPtr, 1, BdPtr);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error freeing up TxBDs");
		return XST_FAILURE;
	}

	/*
	 * Finished this example. If everything worked correctly, all TxBDs and
	 * RxBDs should be free for allocation. Stop the device.
	 */
	XLlTemac_Stop(TemacInstancePtr);
	return XST_SUCCESS;
}
/**
*
* This example sends frames with the interrupt coalescing settings altered
* from their defaults.
*
* The default settings will interrupt the processor after every frame has been
* sent. This example will increase the threshold resulting in lower CPU
* utilization since it spends less time servicing interrupts.
*
* @param    TemacInstancePtr is a pointer to the instance of the Temac
*           component.
*
* @return   XST_SUCCESS to indicate success, otherwise XST_FAILURE.
*
* @note     None.
*
******************************************************************************/
int TemacSgDmaIntrCoalescingExample(XLlTemac * TemacInstancePtr,
				    XLlDma * DmaInstancePtr)
{
	int Status;
	u32 TxFrameLength;
	int TxFramesToSend = 1000;
	int TxFramesSent = 0;
	int PayloadSize = XTE_MTU;
	u32 Index;
	u32 NumBd;
	u16 Threshold = 64;
	XLlDma_BdRing *RxRingPtr = &XLlDma_mGetRxRing(DmaInstancePtr);
	XLlDma_BdRing *TxRingPtr = &XLlDma_mGetTxRing(DmaInstancePtr);
	XLlDma_Bd *BdPtr, *BdCurPtr;

	/*
	 * Clear variables shared with callbacks
	 */
	FramesRx = 0;
	FramesTx = 0;
	DeviceErrors = 0;

	/*
	 * Calculate the frame length (not including FCS)
	 */
	TxFrameLength = XTE_HDR_SIZE + PayloadSize;

	/*
	 * Setup packet to be transmitted. The same packet will be transmitted
	 * over and over
	 */
	TemacUtilFrameHdrFormatMAC(&TxFrame, TemacMAC);
	TemacUtilFrameHdrFormatType(&TxFrame, PayloadSize);
	TemacUtilFrameSetPayloadData(&TxFrame, PayloadSize);

        /*
         * Flush the TX frame before giving it to DMA TX channel to transmit,
         * in case D-Caching is turned on.
         */
	XCACHE_FLUSH_DCACHE_RANGE(&TxFrame, TxFrameLength);

	/*
	 * We don't care about the receive channel for this example, so turn it off
	 */
	Status = XLlTemac_ClearOptions(TemacInstancePtr,
				       XTE_RECEIVER_ENABLE_OPTION);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error clearing option");
		return XST_FAILURE;
	}

	/*
	 * Set the interrupt coalescing parameters for the test. The waitbound timer
	 * is set to 1 (ms) to catch the last few frames.
	 *
	 * If you set variable Threshold to some value larger than TXBD_CNT, then
	 * there can never be enough frames sent to meet the threshold. In this case
	 * the waitbound timer will always cause the interrupt to occur.
	 */
	Status = XLlDma_BdRingSetCoalesce(TxRingPtr, Threshold, 1);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error setting coalescing settings");
		return XST_FAILURE;
	}

	/*
	 * Prime the engine, allocate all BDs and assign them to the same buffer
	 */
	Status = XLlDma_BdRingAlloc(TxRingPtr, TXBD_CNT, &BdPtr);
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error allocating TxBDs prior to starting");
		return XST_FAILURE;
	}

	/*
	 * Setup the TxBDs
	 */
	BdCurPtr = BdPtr;

	for (Index = 0; Index < TXBD_CNT; Index++) {
		XLlDma_mBdSetBufAddr(BdCurPtr, &TxFrame);
		XLlDma_mBdSetLength(BdCurPtr, TxFrameLength);
		XLlDma_mBdSetStsCtrl(BdCurPtr, XLLDMA_BD_STSCTRL_SOP_MASK |
				     XLLDMA_BD_STSCTRL_EOP_MASK);
		BdCurPtr = XLlDma_mBdRingNext(TxRingPtr, BdCurPtr);
	}

	/*
	 *  Enqueue all TxBDs to HW
	 */
	Status = XLlDma_BdRingToHw(TxRingPtr, TXBD_CNT, BdPtr);

	TxFramesSent += TXBD_CNT;
	if (Status != XST_SUCCESS) {
		TemacUtilErrorTrap("Error committing TxBDs prior to starting");
		return XST_FAILURE;
	}

	/*
	 * Enable the send interrupts. Nothing should be transmitted yet as the
	 * device has not been started
	 */
	XLlDma_mBdRingIntEnable(TxRingPtr, XLLDMA_CR_IRQ_ALL_EN_MASK);
	/* Enable temac interrupts to capture errors */
	XLlTemac_IntEnable(TemacInstancePtr, XTE_INT_RECV_ERROR_MASK);

	/*
	 * Start the device. Transmission commences now!
	 */
	XLlTemac_Start(TemacInstancePtr);

	/*
	 * As DMA TX channel has been started in
	 * TemacSgDmaIntrSingleFrameExample() above, there is no need to start
	 * it again here
	 */

	while (TxFramesSent < TxFramesToSend) {
		/*
		 * Wait for the interrupt
		 */
		while (!FramesTx);

		/*
		 * Some TxBDs are ready for post processing. Get all that are available
		 * at the moment, free them, then allocate the same number and
		 * re-enqueue back to HW. At the same time this all occurs here,
		 * the DMA engine should be processing other TxBDs. If we can stay ahead
		 * of the transmitter like this, then throughput should be at or near
		 * line-rate.
		 *
		 * Note that this retrieves up to TXBD_CNT outstanding packets,
		 * so we may be processing more packets here than the threshold
		 * value, which may result in a seemingly spurious interrupt
		 * next time if we grab twice (or more) of the threshold value
		 * this time around (clear as mud, eh!).  Bottom line is that we
		 * ignore if NumBd returns as zero.
		 */
		NumBd = XLlDma_BdRingFromHw(TxRingPtr, TXBD_CNT, &BdPtr);
		if (NumBd > 0) {
			TxFramesSent += NumBd;

			/*
			 * Don't bother to check the BDs status, just free them
			 */
			Status = XLlDma_BdRingFree(TxRingPtr, NumBd, BdPtr);
			if (Status != XST_SUCCESS) {
				TemacUtilErrorTrap("Error freeing TxBDs");
			}

			/*
			 * Allocate the same number of BDs just freed
			 */
			Status = XLlDma_BdRingAlloc(TxRingPtr, NumBd, &BdPtr);
			if (Status != XST_SUCCESS) {
				TemacUtilErrorTrap("Error allocating TxBDs");
				return XST_FAILURE;
			}

			/*
			 * Setup the TxBDs
			 */
			BdCurPtr = BdPtr;
			for (Index = 0; Index < NumBd; Index++) {
				XLlDma_mBdSetBufAddr(BdCurPtr, &TxFrame);
				XLlDma_mBdSetLength(BdCurPtr, TxFrameLength);
				XLlDma_mBdSetStsCtrl(BdCurPtr,
						     XLLDMA_BD_STSCTRL_SOP_MASK
						     |
						     XLLDMA_BD_STSCTRL_EOP_MASK);
				BdCurPtr =
					XLlDma_mBdRingNext(TxRingPtr, BdCurPtr);
			}

			/*
			 * Enqueue TxBDs to HW
			 */
			Status = XLlDma_BdRingToHw(TxRingPtr, NumBd, BdPtr);
			if (Status != XST_SUCCESS) {
				TemacUtilErrorTrap
					("Error committing TxBDs prior to starting");
				return XST_FAILURE;
			}
		}

		/*
		 * Re-enable the interrupts
		 */
		FramesTx = 0;
		XLlDma_mBdRingIntEnable(TxRingPtr, XLLDMA_CR_IRQ_ALL_EN_MASK);
	}

	/*
	 * Wait a second and collect any remaining frames that had been
	 * queued up but not post processed
	 */
#ifndef __MICROBLAZE__
	usleep(1000000);
#else
	/*
	 * Not sure if we should depend on there being a timer core in a
	 * microblaze system
	 */
	TemacUtilPhyDelay(1);
#endif

	NumBd = XLlDma_BdRingFromHw(TxRingPtr, 0xFFFFFFFF, &BdPtr);

	if (NumBd > 0) {
		Status = XLlDma_BdRingFree(TxRingPtr, NumBd, BdPtr);
		if (Status != XST_SUCCESS) {
			TemacUtilErrorTrap("Error freeing TxBDs");
		}
	}

	/*
	 * Done sending frames. Stop the device
	 */
	XLlTemac_Stop(TemacInstancePtr);

	return XST_SUCCESS;
}