AnsweredAssumed Answered

BF504F long SD card writes

Question asked by -DeeT on May 17, 2016
Latest reply on Jun 6, 2016 by Jithul_Janardhanan

My application needs to write a large amount of real time data to contiguous blocks on an SD card.  The BF504F hardware reference and EZ-KIT example code describe reliable ways to perform writes, but these methods write at most 64K at a time, after which the card is permitted to perform housecleaning operations which can introduce significant time delays.  A large buffer could of course be used to get around the latency issues, but the BF504F doesn't have very much RAM.

 

The SD card WRITE_MULTIPLE_BLOCK command has no size limit and can theoretically write the entire volume in a single operation, with housecleaning only once at the end.  This, combined with the SET_WR_BLK_ERASE_COUNT (pre-erase) feature, allows for excellent throughput.  I've been able to achieve this when using an SD card in SPI mode, but the Blackfin RSI peripheral seems to impose a 64K limit on the size of the data.

 

Here is the basic method I'm trying to use.  It works fine if I set RSI_DATA_LGTH to 64K and don't restart the data path state machine at the top of the loop.  I would like to write more than 64K at a time, however, so what I'm trying to do is keep restarting the data path state machine for each block.  It sometimes works!  I've been able to write 500K+ of data in a single fast operation.  However, the RSI peripheral seems to be fussy about the exact address on the card, which really probably comes down to timing issues or other subtle things.  Certainly it seems odd that sometimes overwriting RSI_DATA_CONTROL with the same value does something, and sometimes it does nothing.

 

/*************/

/* not shown */

/*************/

 

int sd_wait(int status_flag, int timeout); /* wait for flag with timeout, clear flags */

 

/***********/

/* defines */

/***********/

 

#define ALL_STATIC_FLAGS        0x7ff

#define BYTES_PER_SECTOR        512

#define INTS_PER_SECTOR        128

#define RSI_DATA_TIMER_VALUE    1000000

#define RSI_DATA_CONTROL_WRITE  0x99

#define SECTORS_PER_BIGBUF      2

 

 

/********/

/* data */

/********/

 

typedef union sdbuf

    {

    int i[INTS_PER_SECTOR];

    unsigned char b[BYTES_PER_SECTOR];

    } SDBUF;

 

SDBUF g_bigbuf[SECTORS_PER_BIGBUF];

 

 

/***********************/

/* bigwrite() function */

/***********************/

 

int bigwrite(int sector_address, int sectors_to_write)

    {

    int sector;

 

    /* set up data path parameters */

    *pRSI_STATUSCL = ALL_STATIC_FLAGS;

    *pRSI_DATA_LGTH = BYTES_PER_SECTOR;

    *pRSI_DATA_TIMER = RSI_DATA_TIMER_VALUE;

 

    /* two block DMA buffer */

    *pDMA1_CONFIG = 0;

    *pDMA1_START_ADDR = &g_bigbuf[0].i[0];

    *pDMA1_X_COUNT = INTS_PER_SECTOR;

    *pDMA1_X_MODIFY = sizeof(int);

    *pDMA1_Y_COUNT = SECTORS_PER_BIGBUF;

    *pDMA1_Y_MODIFY = sizeof(int);

    *pDMA1_IRQ_STATUS = DMA_ERR | DMA_DONE;

    /* enable DMA channel -- this does not start the transfer yet */

    *pDMA1_CONFIG = FLOW_AUTO | DMAEN | WDSIZE_32 | DI_EN | SYNC | DMA2D | DI_SEL;

 

    /* issue write multiple blocks command */

    *pRSI_ARGUMENT = sector_address;

    *pRSI_COMMAND = CMD_WRITE_MULTIPLE_BLOCK;

    if (sd_wait(CMD_RESP_END, SD_TIMEOUT))

        return 1;              /* error */

 

    for (sector = 0; sector < sectors_to_write; ++sector)

        {

        /* start or restart data path state machine */

        /* (note: writing same value does appear to restart the peripheral) */

        /* 100 mS delay here makes it work every time! */

        ssync();

        *pRSI_DATA_CONTROL = RSI_DATA_CONTROL_WRITE;

 

        /* wait while DMA is active (hangs here on sector != 0 in the failure case) */

        while ( (*pDMA1_IRQ_STATUS & (DMA_ERR|DMA_DONE)) == 0)

            ;

        *pDMA1_IRQ_STATUS = DMA_ERR | DMA_DONE;

 

        /* place code here to overwrite g_bigbuf[sector&1] here */

 

        /* confirm successful partial write (correct CRC) */

        if (sd_wait(DAT_BLK_END, SD_TIMEOUT))

            return 2;          /* error */

        }

 

    /* disable DMA and issue the STOP TRANSMISSION command */

    *pDMA1_CONFIG = 0;

    *pRSI_STATUSCL = ALL_STATIC_FLAGS;

    *pRSI_ARGUMENT = 0;

    *pRSI_COMMAND = CMD_STOP_TRANSMISSION;

    if (sd_wait(CMD_RESP_END, SD_TIMEOUT))

        return 3;              /* error */

 

    return 0;                  /* success */

    }


Adding delay as shown in the code excerpt (at the top of the loop) makes it work every time, but of course delays are what I seek to avoid.  Something tells me the key to understanding this is knowing why sometimes rewriting RSI_DATA_CONTROL does something right away, and sometimes it only has an effect after a delay.

 

Thoughts, anyone?

Outcomes