AnsweredAssumed Answered

SPI DMA problem

Question asked by Austin Don on Jul 7, 2020
Latest reply on Jul 13, 2020 by Daniel Martynek

Hello,

 

I am trying to use DMA LPSPI to write to a SD card. The DMA SPI was supposed to send 512 bytes in one major loop. The major loop has 512 minor loops and each minor loop transfer 1 byte.

 

Here is my problem. I wanted to maximize my throughput that's why I set my SPI TX watermark to 3. But when I set it to 3, somehow my last byte (byte number 511) was not sent. It is oddly replaced with a random 0xFF byte. However, If I set the TX watermark to 1,2 or 4, it would work perfectly. However, I feel like setting a TX water mark of 4 will cause problems and 2 is not really efficient (the TX fifo has a size of 4). I have attached the picture of the array I get from reading the data back from the SD card. I know for sure that my read function is not the source of problem. 

I'm using the S32K148evb-q176.

 

Here is my code for SPI config.

/*
* Copyright (c) 2014 - 2016, Freescale Semiconductor, Inc.
* Copyright (c) 2016 - 2018, NXP.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY NXP "AS IS" AND ANY EXPRESSED OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL NXP OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "device_registers.h" /* include peripheral declarations */
#define PTB0 0

void LPSPI0_init_master(void)
{
/*!
* LPSPI0 Clocking:
* ===================================================
*/
PCC->PCCn[PCC_LPSPI0_INDEX] = 0; /* Disable clocks to modify PCS ( default) */
PCC->PCCn[PCC_LPSPI0_INDEX] = PCC_PCCn_PR_MASK /* (default) Peripheral is present. */
|PCC_PCCn_CGC_MASK /* Enable PCS=SPLL_DIV2 (40 MHz func'l clock) */
|PCC_PCCn_PCS(6);
/*!
* LPSPI0 Initialization:
* ===================================================
*/
LPSPI0->CR = 0x00000000; /* Disable module for configuration */
LPSPI0->IER = 0x00000000; /* Interrupts not used */
LPSPI0->DER = 0x00000000; /* DMA not used */
LPSPI0->CFGR0 = 0x00000000; /* Defaults: */
/* RDM0=0: rec'd data to FIFO as normal */
/* CIRFIFO=0; Circular FIFO is disabled */
/* HRSEL, HRPOL, HREN=0: Host request disabled */

LPSPI0->CFGR1 = LPSPI_CFGR1_MASTER_MASK | LPSPI_CFGR1_NOSTALL_MASK; /* Configurations: master mode */
/* PCSCFG=0: PCS[3:2] are enabled */
/* OUTCFG=0: Output data retains last value when CS negated */
/* PINCFG=0: SIN is input, SOUT is output */
/* MATCFG=0: Match disabled */
/* PCSPOL=0: PCS is active low */
/* NOSTALL=0: Stall if Tx FIFO empty or Rx FIFO full */
/* AUTOPCS=0: does not apply for master mode */
/* SAMPLE=0: input data sampled on SCK edge */
/* MASTER=1: Master mode */

LPSPI0->TCR = LPSPI_TCR_PRESCALE(2)
|LPSPI_TCR_PCS(0)
|LPSPI_TCR_FRAMESZ(7); /* Transmit cmd: PCS0, 16 bits, prescale func'l clk by 4, etc */
/* CPOL=0: SCK inactive state is low */
/* CPHA=0: capture data on SCK lead'g, changing on trail'g edge */
/* PRESCALE=2: Functional clock divided by 2**2 = 4 */
/* PCS=0: Transfer using PCS0 */
/* LSBF=0: Data is transfered MSB first */
/* BYSW=0: Byte swap disabled */
/* CONT, CONTC=0: Continuous transfer disabled */
/* RXMSK=0: Normal transfer: rx data stored in rx FIFO */
/* TXMSK=0: Normal transfer: data loaded from tx FIFO */
/* WIDTH=0: Single bit transfer */
/* FRAMESZ=47: # bits in frame = 47+1=48 */

LPSPI0->CCR = LPSPI_CCR_SCKPCS(4)
|LPSPI_CCR_PCSSCK(4)
|LPSPI_CCR_DBT(8)
|LPSPI_CCR_SCKDIV(8); /* Clock dividers based on prescaled func'l clk of 100 nsec */
/* SCKPCS=4: SCK to PCS delay = 4+1 = 5 (500 nsec) */
/* PCSSCK=4: PCS to SCK delay = 4+1 = 5 (500 usec) */
/* DBT=8: Delay between Transfers = 8+2 = 10 (1 usec) */
/* SCKDIV=8: SCK divider =8+2 = 10 (1 usec: 1 MHz baud rate) */

LPSPI0->FCR = LPSPI_FCR_TXWATER(3); /* RXWATER=0: Rx flags set when Rx FIFO > 0 */
/* TXWATER=3: Tx flags set when Tx FIFO <= 3 */

LPSPI0->CR = LPSPI_CR_MEN_MASK
|LPSPI_CR_DBGEN_MASK; /* Enable module for operation */
/* DBGEN=1: module enabled in debug mode */
/* DOZEN=0: module enabled in Doze mode */
/* RST=0: Master logic not reset */
/* MEN=1: Module is enabled */
}

Here is the code for DMA config

#include "device_registers.h" /* include peripheral declarations */
#include "S32K148.h"
#include "dma.h"

uint8_t dummy = 0xFF;


void DMA_TCD_init_Tx(void* p_tx_data)
{
DMA->TCD[0].SADDR = (uint32_t)(p_tx_data); /* Source Address. */
DMA->TCD[0].SOFF = DMA_TCD_SOFF_SOFF(1); /* Src. addr add 4 byte after Transfers */
DMA->TCD[0].ATTR = DMA_TCD_ATTR_SMOD(0) | /* Src. modulo feature not used */
DMA_TCD_ATTR_SSIZE(0) | /* Src. read 2**2 = 4 byte per transfer */
DMA_TCD_ATTR_DMOD(0) | /* Dest. modulo feature not used */
DMA_TCD_ATTR_DSIZE(0); /* Dest. write 2**2 =4 byte per trans. */

DMA->TCD[0].NBYTES.MLNO = DMA_TCD_NBYTES_MLNO_NBYTES(1); /* Transfer 16 byte /minor loop */
DMA->TCD[0].SLAST = DMA_TCD_SLAST_SLAST(-512); /* Src addr change after major loop */

DMA->TCD[0].DADDR = DMA_TCD_DADDR_DADDR((uint32_t)&(LPSPI0->TDR)); /* Destination Address. */
DMA->TCD[0].DOFF = DMA_TCD_DOFF_DOFF(0); /* No dest adr offset after transfer */

DMA->TCD[0].CITER.ELINKNO= DMA_TCD_CITER_ELINKNO_CITER(512) | /* 11 minor loop iterations */
DMA_TCD_CITER_ELINKNO_ELINK(0); /* No minor loop chan link */

DMA->TCD[0].DLASTSGA = DMA_TCD_DLASTSGA_DLASTSGA(0); /* No dest chg after major loop */
DMA->TCD[0].CSR = DMA_TCD_CSR_START(0) | /* Clear START status flag */
DMA_TCD_CSR_INTMAJOR(0) | /* No IRQ after major loop */
DMA_TCD_CSR_INTHALF(0) | /* No IRQ after 1/2 major loop */
DMA_TCD_CSR_DREQ(1) | /* en chan after major loop */
DMA_TCD_CSR_ESG(0) | /* Disable Scatter Gather */
DMA_TCD_CSR_MAJORELINK(0) | /* No major loop chan link */
DMA_TCD_CSR_ACTIVE(0) | /* Clear ACTIVE status flag */
DMA_TCD_CSR_DONE(0) | /* Clear DONE status flag */
DMA_TCD_CSR_MAJORLINKCH(0) | /* Chan # if major loop ch link */
DMA_TCD_CSR_BWC(0); /* No eDMA stalls after R/W */

DMA->TCD[0].BITER.ELINKNO= DMA_TCD_BITER_ELINKNO_BITER(512) | /* Initial iteration count */
DMA_TCD_BITER_ELINKNO_ELINK(0); /* No minor loop chan link */
}

Here is the code to initiate the DMA using start bit

void initiate_DMA_Tx(){

while (!((DMA->TCD[0].CSR >> DMA_TCD_CSR_DONE_SHIFT) & 1)) { /* Loop till DONE = 1 */
while((LPSPI0->SR & LPSPI_SR_TDF_MASK)>>LPSPI_SR_TDF_SHIFT==0); /* Wait for tx fifo to have space */

DMA->SSRT = 0; /* Set chan 0 START bit to initiate next minor loop */

while (((DMA->TCD[0].CSR >> DMA_TCD_CSR_START_SHIFT) & 1) | /* Wait for START = 0 */
((DMA->TCD[0].CSR >> DMA_TCD_CSR_ACTIVE_SHIFT) & 1)) {} /* and ACTIVE = 0 */
/* Now minor loop has completed */
}
}

 

When TX Watermark is 1,2,4

 

When TX Watermark is 3

Outcomes