SAME70 XDMA hangs during QSPI transfer in SPI mode to SDRAM

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

Did anyone experienced Bus/Memory faults or incomplete NOR flash transfers via XDMA from QSPI unit in SPI mode? This only happens if target memory is SDRAM, but works fine in internal SRAM. However memory tests are passing on the SDRAM and MPU is configured as full access and INNER_NORMAL_WB_NWA_TYPE( NON_SHAREABLE ). I, D Cache are disabled.

 

Best regards,

David.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is a snippet with initialisation of QSPI
QSPI_Disable(m_qspi); 
QSPI_SwReset(m_qspi); 
QSPI_Configure(m_qspi, (QSPI_MR_SMM_SPI | QSPI_MR_LLB_DISABLED |
QSPI_MR_CSMODE_LASTXXFER | 
QSPI_MR_DLYBCT(0) |
QSPI_MR_DLYCS (20) | 
(QSPI_MR_NBBITS_8_BIT)); 
QSPI_ConfigureClock(m_qspi, ClockMode_00, QSPI_SCR_SCBR((BOARD_MCK / bps) - 1) | QSPI_SCR_DLYBS(0));

XDMA setup:

spi_status_t QSpiDma::ReadPacket(volatile uint8_t *recvbuffer, size_t rxSize) 
{
    sXdmadCfg xdmadRxCfg, xdmadTxCfg; 
    uint32_t xdmaCndc, xdmaInt; 
    uint32_t txSize = rxSize; 
    static uint32_t writech = CONFIG_SPI_MASTER_DUMMY;// needs to be in IRAM 
    m_status = SPI_ERROR_ARGUMENT; 
    m_qspi->QSPI_MR |= (QSPI_MR_WDRBT);//spi_enable_tx_on_rx_empty(m_qspi); 
    volatile uint32_t dummych = m_qspi->QSPI_RDR; //read char 
    dummych = QSPI_GetStatus(m_qspi, IsReceived); 
    /* Setup TX to send dummy chars to enable reading*/ 
    xdmadTxCfg.mbr_sa = (uint32_t)&writech; 
    xdmadTxCfg.mbr_da = (uint32_t)&m_qspi->QSPI_TDR; 
    xdmadTxCfg.mbr_ubc = XDMA_UBC_NVIEW_NDV0 | 
                                        XDMA_UBC_NDE_FETCH_DIS | 
                                        XDMA_UBC_NSEN_UPDATED | 
                                        XDMA_UBC_NDEN_UNCHANGED | 
                                        XDMAC_CUBC_UBLEN(txSize); 
    xdmadTxCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN | 
                                        XDMAC_CC_MBSIZE_SINGLE | 
                                        XDMAC_CC_DSYNC_MEM2PER | 
                                        XDMAC_CC_CSIZE_CHK_1 | 
                                        XDMAC_CC_DWIDTH_BYTE | 
                                        XDMAC_CC_SIF_AHB_IF0 | 
                                        XDMAC_CC_DIF_AHB_IF1 | 
                                        XDMAC_CC_SAM_UBS_DS_AM | 
                                        XDMAC_CC_DAM_UBS_DS_AM | XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(periphId, XDMAD_TRANSFER_TX)); 
    uint16_t sds_msp = -1; 
    uint16_t dds_msp = -1; 
    xdmadTxCfg.mbr_ds = XDMAC_CDS_MSP_DDS_MSP(dds_msp) | XDMAC_CDS_MSP_SDS_MSP(sds_msp);//Errata 61.5 
    xdmadTxCfg.mbr_bc = 0; 
    xdmadTxCfg.mbr_sus = 0; 
    xdmadTxCfg.mbr_dus = 0; 
    /* Setup RX Link List */ 
    xdmadRxCfg.mbr_ubc = XDMA_UBC_NVIEW_NDV0 | 
                                        XDMA_UBC_NDE_FETCH_DIS | 
                                        XDMA_UBC_NSEN_UNCHANGED | 
                                        XDMA_UBC_NDEN_UPDATED | 
                                        XDMAC_CUBC_UBLEN(rxSize); 
    xdmadRxCfg.mbr_da = (uint32_t)recvbuffer; 
    xdmadRxCfg.mbr_sa = (uint32_t)&m_qspi->QSPI_RDR; 
    xdmadRxCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN | 
                                        XDMAC_CC_MBSIZE_SINGLE | 
                                        XDMAC_CC_DSYNC_PER2MEM | 
                                        XDMAC_CC_CSIZE_CHK_1 | 
                                        XDMAC_CC_DWIDTH_BYTE | 
                                        XDMAC_CC_SIF_AHB_IF1 | 
                                        XDMAC_CC_DIF_AHB_IF0 | 
                                        XDMAC_CC_SAM_UBS_DS_AM | 
                                        XDMAC_CC_DAM_INCREMENTED_AM | XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(periphId, XDMAD_TRANSFER_RX)); 
    sds_msp = -1; 
    dds_msp = 0; 
    xdmadRxCfg.mbr_ds = XDMAC_CDS_MSP_DDS_MSP(dds_msp) | XDMAC_CDS_MSP_SDS_MSP(sds_msp);//Errata 61.5 
    xdmadRxCfg.mbr_bc = 0; 
    xdmadRxCfg.mbr_sus = 0; 
    xdmadRxCfg.mbr_dus = 0; 
    xdmaCndc = 0; 
    /* Put all interrupts on for non LLI list setup of DMA */ 
    xdmaInt = (XDMAC_CIE_BIE | 
                                        //XDMAC_CIE_DIE | 
                                        XDMAC_CIE_FIE | 
                                        XDMAC_CIE_RBIE | 
                                        XDMAC_CIE_WBIE | 
                                        XDMAC_CIE_ROIE); 
    XDMAD_ConfigureTransfer(&M7dev::xdmaM.Get(), spiDmaRxChannel, &xdmadRxCfg, xdmaCndc, 0, xdmaInt); 
    XDMAD_ConfigureTransfer(&M7dev::xdmaM.Get(), spiDmaTxChannel, &xdmadTxCfg, xdmaCndc, 0, 0/*xdmaInt*/); 
    /* Start DMA 0(RX) && 1(TX) */ 
    XDMAD_StartTransfer(&M7dev::xdmaM.Get(), spiDmaRxChannel); 
    XDMAD_StartTransfer(&M7dev::xdmaM.Get(), spiDmaTxChannel); 
    if (m_semaRx.Lock((rxSize+TIMEOUT_mS) / portTICK_PERIOD_MS)==false) //await transfers complete 
    { 
        //This is where it occasionally timeouts with RX channel XDMAC_CUBC = 1. 
        XDMAD_StopTransfer(&M7dev::xdmaM.Get(), spiDmaRxChannel); 
        XDMAD_StopTransfer(&M7dev::xdmaM.Get(), spiDmaTxChannel); 
        return SPI_ERROR_TIMEOUT; 
    } //await transfers complete 
    return m_status; 
} 
void QSpiDma::DMAHandler(uint32_t channel) 
{ 
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; 
    if (channel == spiDmaRxChannel) 
    { 
        m_status = XDMAD_IsTransferDone(&M7dev::xdmaM.Get(), channel) == XDMAD_OK ? SPI_OK : SPI_ERROR; 
        m_semaRx.UnlockISR(xHigherPriorityTaskWoken); 
    } 
    XDMAD_StopTransfer(&M7dev::xdmaM.Get(), channel); 
}

Sometimes the TX finishes but RX still has one byte to read but doesn't get read. This function works fine if recvbuffer is in SRAM.

 

Last Edited: Tue. Sep 5, 2017 - 12:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I do not use XDMAC for QSPI only for SPI and TWI.

Is the initialization of QSPI_Configure() ok ? Did you check the timing with a scope ?

I do not understand why you are using 2 DMAs.

I have problems reading your code because it is not formatted. You should probably use the "Add code" feature.

Unfortunately I am using the ASF functions (xdmac_channel_enable, qspi_set_chip_select_mode ...) so it is hard to compare with my code.

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Regjoe, thanks for stopping by. I realise how difficult is to read this code and  I've tried using the "Add code" but browser (Chrome, Edge, even ingognito mode) only displays an empty box and doesn't work.

The QSPI has been working fine for months actually until we moved buffers to SDRAM, timings seem ok and can read/write the flash but I will check again. The reason I use 2 DMA is one (TxDMA) is required for transmitting to enable (RxDMA) reading in SPI mode, in ASF code it's done by writing dummy values to QSPI_TDR (functionality similar to SPI peripherals, by writing SPI_TDR and then read). I need to send equal number of bytes to how many I need to read, but with above DMA code (txSize=rxSize), one last byte (in debugger sometimes was 4 bytes left) doesn't get transferred so reading stops. I tried sending txSize=rxSize+1, code worked for almost an hour then CPU went into NMI Handler. Also tried to send one byte first (QSPI_TDR = 0xff) then enable TxDMA with txSize = rxSize-1 but reading also stops. You mentioned in another post about QSPI hangs during DLYCS so I added just some Sleep(1ms) before a read, also set it to 0, but didn't help.

The configuration was done using Softpack 1.5 (very similar to ASF but seemed better functionality and less buggy to me at the time I started using it):

m_qspi = QSPI;
m_qspi->QSPI_WPMR = QSPI_WPMR_WPKEY_PASSWD & (~(QSPI_WPMR_WPEN));//disable password
//QSPI_Disable(m_qspi); 
pQspi->QSPI_CR = QSPI_CR_QSPIDIS;
while (pQspi->QSPI_SR & QSPI_SR_QSPIENS);

//QSPI_SwReset(m_qspi); 
pQspi->QSPI_CR = QSPI_CR_SWRST;
//QSPI_Configure(m_qspi, (QSPI_MR_SMM_SPI | QSPI_MR_LLB_DISABLED | QSPI_MR_CSMODE_LASTXXFER | QSPI_MR_DLYBCT(0) | QSPI_MR_DLYCS (20) | (QSPI_MR_NBBITS_8_BIT)); 
pQspi->QSPI_MR |=  (QSPI_MR_SMM_SPI | QSPI_MR_LLB_DISABLED | QSPI_MR_CSMODE_LASTXXFER | QSPI_MR_DLYBCT(0) | QSPI_MR_DLYCS (20) | (QSPI_MR_NBBITS_8_BIT); 

bps = 50000000; //S25FL256SAGMFI001
BOARD_MCK = 150000000;
ClockMode_00 = 0;
//QSPI_ConfigureClock(m_qspi, ClockMode_00, QSPI_SCR_SCBR((BOARD_MCK / bps) - 1) | QSPI_SCR_DLYBS(0));
pQspi->QSPI_SCR = ClockMode_00;
pQspi->QSPI_SCR |= QSPI_SCR_SCBR((BOARD_MCK / bps) - 1) | QSPI_SCR_DLYBS(0);

Before reading from flash I use: m_qspi->QSPI_MR |= (QSPI_MR_WDRBT);//spi_enable_tx_on_rx_empty(m_qspi); so that DMA doesn't send another byte until the other channel read it. 

Writing to flash I use m_qspi->QSPI_MR &= ~ (QSPI_MR_WDRBT);//spi_disable_tx_on_rx_empty(m_qspi);  so that I don't need to read RDR for transmitting. 

Best regards,

David.

Last Edited: Tue. Sep 5, 2017 - 12:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Still unsure about the 2 DMAs. If I want to read data from SPI device I setup and run a RX DMA.

Have you tested your DMA without RTOS ?

The NMI is weird. Can you find out the reason ?

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think NMI is called if I'm using incorrect txSize = rxSize+1 or force QSPI_TDR=0xff before starting DMA transfer with txSize=rxSize. For timeout it might be a conflict with other DMA transfers in XDMA_Handler interrupt, since this function seems to be working fine before starting other peripherals when there's more traffic. Or just maybe some interrupt pending bit is cleared, I'll be looking more into it.  

How do you setup DMA for SPI transfers? See "Receiving data cannot occur without transmitting data." in 41.6.4.1 SPI Mode Operations. 

This is how the handler is implemented in Softpack:

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
/**
 * \brief xDMA interrupt handler
 * \param pxDmad Pointer to DMA driver instance.
 */
void XDMAD_Handler(sXdmad *pDmad)
{
	Xdmac *pXdmac;
	sXdmadChannel *pCh;
	uint32_t xdmaChannelIntStatus, xdmaGlobaIntStatus, xdmaGlobalChStatus;
	uint8_t bExec = 0;
	uint32_t _iChannel;
	assert(pDmad != NULL);

	pXdmac = pDmad->pXdmacs;
	xdmaGlobaIntStatus = XDMAC_GetGIsr(pXdmac);

	if ((xdmaGlobaIntStatus & 0xFFFFFF) != 0) {
		xdmaGlobalChStatus = XDMAC_GetGlobalChStatus(pXdmac);

		for (_iChannel = 0; _iChannel < pDmad->numChannels; _iChannel ++) {
			if (!(xdmaGlobaIntStatus & (1 << _iChannel))) continue;

			pCh = &pDmad->XdmaChannels[_iChannel];

			if (pCh->state == XDMAD_STATE_FREE) return;

			if ((xdmaGlobalChStatus & (XDMAC_GS_ST0 << _iChannel)) == 0) {
				bExec = 0;
				xdmaChannelIntStatus = XDMAC_GetMaskChannelIsr(pXdmac, _iChannel);

				if (xdmaChannelIntStatus & XDMAC_CIS_BIS) {
					if ((XDMAC_GetChannelItMask(pXdmac, _iChannel) & XDMAC_CIM_LIM)
						== 0) {
						pCh->state = XDMAD_STATE_DONE;
						bExec = 1;
					}

					TRACE_DEBUG("XDMAC_CIS_BIS\n\r");
				}

				if (xdmaChannelIntStatus & XDMAC_CIS_FIS)
					TRACE_DEBUG("XDMAC_CIS_FIS\n\r");

				if (xdmaChannelIntStatus & XDMAC_CIS_RBEIS){					
					TRACE_DEBUG("XDMAC_CIS_RBEIS\n\r");
					pCh->state = XDMAD_STATE_HALTED;
				}
				if (xdmaChannelIntStatus & XDMAC_CIS_WBEIS){
					TRACE_DEBUG("XDMAC_CIS_WBEIS\n\r");
					pCh->state = XDMAD_STATE_HALTED;
				}
				if (xdmaChannelIntStatus & XDMAC_CIS_ROIS){
					TRACE_DEBUG("XDMAC_CIS_ROIS\n\r");
					pCh->state = XDMAD_STATE_HALTED;
				}
				if (xdmaChannelIntStatus & XDMAC_CIS_LIS) {
					TRACE_DEBUG("XDMAC_CIS_LIS\n\r");
					pCh->state = XDMAD_STATE_DONE;
					bExec = 1;
				}

				if (xdmaChannelIntStatus & XDMAC_CIS_DIS) {
					pCh->state = XDMAD_STATE_DONE;
					bExec = 1;
				}

			} else {
				/* Block end interrupt for LLI dma mode */
				dummyisr = XDMAC_GetChannelIsr(pXdmac, _iChannel);
				if (dummyisr & XDMAC_CIS_BIS) {
					/* Execute callback */
					pCh->fCallback(_iChannel, pCh->pArg);
				}
			}

			/* Execute callback */
			if (bExec && pCh->fCallback)
				pCh->fCallback(_iChannel, pCh->pArg);
		}
	}
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are right, I got lost in my code somewhere. I remember that I split RX and TX DMA initialization for whatever reason - LCD write probably ?

The TX data: is that just dummy data ? If so you can use for source and destination FIXED attribute ?

The errata 61.5 workaround : did you implement the patch on your own ?

 

 

 

 

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, this is my attempt. Previously I had XDMAC_CC_DAM_FIXED_AM or XDMAC_CC_SAM_FIXED_AM and mbr_ds = 0 (SDS=DDS=0) but because of Memfaults, I implemented this solution. I see no problem using fixed source (dummy data SRAM location) for Tx and fixed destination for Rx since it's a matter of writing same register multiple times with data from same location but reading needs the clocking. For LCD we use UsartSPI and only TX needs to be set, also with m_usart->US_MR &= ~(US_MR_WRDBT);//spi_disable_tx_on_rx_empty(m_usart);

DMA code is pretty much identical for SPI, QSPI, UsartSPI apart from register pointers and channel numbers. I also have test code that writes into flash, reads back and compares and it's passing. 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

According to latest SAME70 errata,  QSPI_MR_WDRBT  is not doing anything when QSPI is set in SPI mode. We no longer observed this issue once we started using QSPI quad mode for flash.