This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Reading SPI Flash from Bootloader

I am modifying the TI Starterware bootloader for our product, and need to efficiently read data from a SPI flash. To keep the code simple I am trying to do this using polling, not interrupts or DMA. After a little experimentation I now have this working, but there seems to be something strange when polling the fifo register and I need a little advice.
My code is using the McSPI FIFO, and also enabling the turbo mode and multiple word access. I am not sure the correct way to block waiting for the received bytes when using the FIFO and multiple word access. I think the following loop from McSPITransfer should work:
    while (MCSPI_INT_RX_FULL(SPI_CHAN) != McSPIIntStatusGet(SPI_BASE) & MCSPI_INT_RX_FULL(SPI_CHAN));
However this does not appear to do anything useful. The Starterware McSPITransfer function works, but only with certainly clock and spi bus speeds. If the SPI bus is slowed to 6kHz then the data is not read correctly, suggesting that the reference code from TI is incorrect.
I have also tried to use the FIFO empty status, as:
    while (McSPIChannelStatusGet(SPI_BASE, SPI_CHAN) & MCSPI_CH_RXFFE);
This appears to work much better, but the last word of the transfer is never read. I am assuming that the McSPI FIFO is empty (so the look does not exist), and the last word is in the receive register. This behaviour is not clear in the reference manual.
I have attached my modified bl_mcspi.c for reference, this version works with the commented work arounds.
Can you please get some advice for me on how to correctly code this.
/**
 * \file  spi.c
 *
 * \brief Spi Initialization functions.  And a funciton to copy data from Flash
 *        to the given address.
 *  
 */

/*
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*    Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
*    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.
*
*    Neither the name of Texas Instruments Incorporated 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS 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 THE COPYRIGHT
*  OWNER OR 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 "debug.h"
#include "mcspi.h"
#include "bl.h"
#include "bl_platform.h"
#include "bl_spi.h"
#include "uartStdio.h"

 
/******************************************************************************
**                     Macro Defination 
*******************************************************************************/

#define CHAR_LENGTH             0x8
#define MCSPI_IN_CLK            48000000
//#define MCSPI_OUT_CLK           6000000
//#define MCSPI_OUT_CLK           24000000
#define MCSPI_OUT_CLK           48000000

/* flash data read command */
#define SPI_FLASH_READ          0x03

/******************************************************************************
**                    Local  Declaration 
*******************************************************************************/
static void McSPITransfer(unsigned char *p_tx,
                         unsigned char *p_rx,
                         unsigned int len);

static void McSPIFastRead(unsigned char *p_rx, unsigned int len);

/*
* \brief - SPI Configures.
* \param - none.
*
* \return  none.
*/

void SPIConfigure(void)
{
    unsigned int retVal = 0;

    /* Reset the McSPI instance.*/
    McSPIReset(SPI_BASE);

    /* Enable chip select pin.*/
    McSPICSEnable(SPI_BASE);

    /* Enable master mode of operation.*/
    McSPIMasterModeEnable(SPI_BASE);

    /* Perform the necessary configuration for master mode.*/
    retVal = McSPIMasterModeConfig(SPI_BASE, MCSPI_SINGLE_CH, MCSPI_TX_RX_MODE,\
                                   MCSPI_DATA_LINE_COMM_MODE_6, SPI_CHAN);
// XXXX this is needed until I rework the spi bus
//                                 MCSPI_DATA_LINE_COMM_MODE_1, SPI_CHAN);

    /* 
    ** If combination of trm and IS,DPE0 and DPE1 is not valid then retVal is 
    ** false.
    */
    if(!retVal)
    {
        UARTPuts("Invalid McSPI config \r\n", -1);
        UARTPuts("Aborting Boot\r\n", -1);
        BootAbort();
    }

    /* 
    ** Default granularity is used. Also as per my understanding clock mode 
    ** 0 is proper.
    */    
    McSPIClkConfig(SPI_BASE, MCSPI_IN_CLK, MCSPI_OUT_CLK, 
                   SPI_CHAN, MCSPI_CLK_MODE_0);                                        

    /* Configure the word length.*/    
    McSPIWordLengthSet(SPI_BASE, MCSPI_WORD_LENGTH(8), SPI_CHAN);                

    /* Set polarity of SPIEN to low.*/
    McSPICSPolarityConfig(SPI_BASE, MCSPI_CS_POL_LOW, SPI_CHAN);

    /* Enable the transmitter FIFO of McSPI peripheral.*/
    McSPITxFIFOConfig(SPI_BASE, MCSPI_TX_FIFO_ENABLE, SPI_CHAN);

    /* Enable the receiver FIFO of McSPI peripheral.*/
    McSPIRxFIFOConfig(SPI_BASE, MCSPI_RX_FIFO_ENABLE, SPI_CHAN);     
} 

/**
* \brief - Reads bytes from SPI Flash.
* \param - offset - SPI Flash address.\n.
* \param - size - Indicates the total size needs to be read from flash.\n.
* \param - dst - Destination address where data needs to be copy.\n.
*
* \return none
**/
void BlSPIReadFlash (unsigned int offset,
                     unsigned int size,
                     unsigned char *dst)
{
    unsigned char tx_data;
    unsigned char rx_data;
    unsigned char addr[3];
    unsigned int len;

    /* The process of reading the data from the flash involves asserting
     * proper chipselect line, asserting CSHOLD, selecting correct data format.
     * Then the flash needs a command to indicate start of read and then
     * any number of bytes can be read. After the required number of bytes
     * are read, the CS needs to be de-asserted to indicate the end of transfer
     */
    McSPICSAssert(SPI_BASE, SPI_CHAN);

    /* Enable the McSPI channel for communication.*/
    McSPIChannelEnable(SPI_BASE, SPI_CHAN);

    /* Send read command to the flash (one byte) */
    tx_data =  SPI_FLASH_READ;
    McSPITransfer(&tx_data, &rx_data, 1);

    /* Send the address to start reading from (3 bytes) */
    addr[0] = (unsigned char)(offset >> 16);
    addr[1] = (unsigned char)(offset >> 8);
    addr[2] = (unsigned char)offset;
    len = 0;

    while (len < sizeof(addr))
    {
        McSPITransfer(&addr[len], &rx_data, 1);
        len++;
    }

    /* Read all the bytes */
    McSPIFastRead(dst, size);

    /* Force SPIEN line to the inactive state.*/
    McSPICSDeAssert(SPI_BASE, SPI_CHAN);

    /* Disable the McSPI channel.*/
    McSPIChannelDisable(SPI_BASE, SPI_CHAN);
}

/**
* \brief - Spi Write and Read.
* \param - p_tx - SPI transmit data address.\n.
* \param - p_rx - SPI data reception address.\n.
* \param - len - Indicates the total length the read and write has to do.\n.
*
* \return none
**/

static void McSPITransfer(unsigned char *p_tx, unsigned char *p_rx,
                          unsigned int len)
{
    while(len)
    {
	/* Wait till TX is empty. */
        while(MCSPI_INT_TX_EMPTY(SPI_CHAN) !=
              (McSPIIntStatusGet(SPI_BASE) & MCSPI_INT_TX_EMPTY(SPI_CHAN)));
        McSPITransmitData(SPI_BASE, *p_tx, SPI_CHAN);

        p_tx++;

	/* Wait till the DATA in RX. */       
        while(MCSPI_INT_RX_FULL(SPI_CHAN) !=
              (McSPIIntStatusGet(SPI_BASE) & MCSPI_INT_RX_FULL(SPI_CHAN)));
        *p_rx = McSPIReceiveData(SPI_BASE, SPI_CHAN);

	// XXXX without clearing the status then 
	McSPIIntStatusClear(SPI_BASE, MCSPI_INT_RX_FULL(SPI_CHAN));

        p_rx++;
        len--;
    }
}

static void McSPIFastRead(unsigned char *p_rx, unsigned int len)
{
	unsigned int tx_len = 0;
	unsigned int rx_len = 0;

	unsigned int *ptr = (unsigned int *)p_rx;

	/* len must by divisable by 32 bit words for mutliple word access */
	ASSERT(len & 0x3 == 0);

	McSPITurboModeEnable(SPI_BASE, SPI_CHAN);
	McSPIMultipleWordAccessConfig(SPI_BASE, MCSPI_MOA_ENABLE);

	unsigned int fill_len = len;
	if (fill_len > 32) {
		fill_len = 32;
	}
	
	/* Prime the TX FIFO with dummy data */
	while (tx_len < fill_len && !(McSPIChannelStatusGet(SPI_BASE, SPI_CHAN) & MCSPI_CH_TXFFF)) {
		McSPITransmitData(SPI_BASE, tx_len, SPI_CHAN);
		tx_len += 4;
	}

	int debug_done_break = 0;

	while (rx_len < len) {
		// XXXX this loop gets stuck when reading the last word, count is used as a workaround to break out of
		// the loop. even though RXFFE is true (indicating the fifo is empty), it appears that the last word is
		// ready to read.
		unsigned int count = 0;
		while (McSPIChannelStatusGet(SPI_BASE, SPI_CHAN) & MCSPI_CH_RXFFE) {
			if (count++ > 100) {
				debug_done_break = rx_len;
				break;
			}
		}

		// XXXX it be possible to use the rx full interrupt status to detect when a byte is available to read
		// however this never seems to block. setting varies values for AFL and AEL in MCSPI_XFERLEVEL do not
		// appear to make any difference.
		//while (MCSPI_INT_RX_FULL(SPI_CHAN) != McSPIIntStatusGet(SPI_BASE) & MCSPI_INT_RX_FULL(SPI_CHAN));

		*ptr = McSPIReceiveData(SPI_BASE, SPI_CHAN);
		ptr++;
		rx_len += 4;

		/* Keep the TX FIFO filled with dummy data */
		if (tx_len < len) {
			McSPITransmitData(SPI_BASE, tx_len, SPI_CHAN);
			tx_len += 4;
		}
	}

	if (debug_done_break) {
		UARTPuts("done break ", -1); UARTPutHexNum(debug_done_break); UARTPuts("\n", -1);
	}

	McSPIMultipleWordAccessConfig(SPI_BASE, MCSPI_MOA_DISABLE);
	McSPITurboModeDisable(SPI_BASE, SPI_CHAN);
}

/***************************** End Of File ***********************************/
  • Hi Tim,

    My recommendation would be to use the FIFO empty status method.  Can you share the content of the entire MCSPI_CHxSTAT register (both before you read the received data and when the code is blocked on the last word)? 

    Regards,

    Melissa

  • Hi Melissa,

    The MCSPI_CH0STAT register before reading the received data is:

      McSPI_CH0STAT=0x2E

    and when the code is blocked on the last word:

      McSPI_CH0STAT=0x2F

    Looking at those results, would my understanding be correct that RXFFE returns the status of the FIFO, but the FIFO transfers the words into the receiver register whose status is returned by RXS. So I should be polling until RXS=1?

    Thanks,
    Richard

  • Hi Richard,

    It seems that polling RXS would be the better option.  Can also you provide two additional values?

    1.  McSPI_CH0STAT after you successfully exist out the blocking loop and before you read the data (i.e. after receiving data prior to the last word)

    2.  McSPI_CH0STAT after reading the last word

    Regards,
    Melissa

  • Hi Melissa,

    McSPI_CH0STAT when successfully exiting out of the blocking loop prior to reading the last word is 0x0F. After the last word is read the status register is 0x2E.

    Thanks,
    Richard

  • Hi Richard,

    Thanks for these additional data points.  Yes, polling until RXS should fix the issue you originally observed.

    Regards,

    Melissa