cancel
Showing results for 
Search instead for 
Did you mean: 

QUADSPI + DMA, reading too many bytes?

Chris Rice
Associate III

Hello, so I am trying to write a driver for a GigaDevice GD5F2GQ4xFxxG part, that uses the QUADSPI peripheral. So far so good, but seeing some unexpected results and when I dug into it, I think I observe that a read command is acquiring more bytes than I expect.

Specifically, the code below is called with a parameter count_of_bytes_to_transfer equal to 18, but in the destination array, I think I see that 23 bytes are actually being set... the first 18 correctly, then the next five to random values (possibly unset bytes in the FLASH).

This is my first time using QUADSPI, and I have only utilized DMA a handful of times, so I worked out the code by trial and error. I set *both* the QUADSPI->DLR and DMA->CNDTR registers to the appropriate lengths (N-1 and N, respectively, which is what the ref manual says they want), but wasn't sure I needed to set both, or which one was correct. Anyway, this doesn't explain why I could possibly be clocking out *23* bytes with this code.

Could this have to do with my misunderstanding of the interaction between QUADSPI and DMA? I'm imagining that maybe my FIFO empties out for some reason, after CNDTR and DNR are decremented to zero.

Also, my code determines that the read is complete by looking for the DMA TCIF interrupt, and then disables DMA using DMA1_Channel5->CCR &= ~(1<<0). I patterned this after some sample code I found... it seemed to work fine. But now I've noticed this overrun and I'm reconsidering things.

Anyway, does anyone have a theory as to why I'm reading more bytes than expected, or notice anything I'm doing wrong with my described approach?

Thanks very much for any help, STM32 geniuses.

 
static void StartTransfer_0xEBReadFromCacheQuad(uint16_t source_column_address, uint8_t * local_target, uint16_t count_of_bytes_to_transfer)
{
    //////////////////////////////////////////////////////////////////////////
    // activate DMA
    //////////////////////////////////////////////////////////////////////////
        
    //////////////////////////////////////////////////////////////////////////
    // 0xEB Read from cache quad IO
    //
    //      Instruction phase: yes, one line, 0xEB
    //          Address phase: yes, four lines, two bytes of column address
    //  Alternate-bytes phase: no
    //     Dummy-cycles phase: yes, two cycles (for one byte in quad mode)
    //             Data phase: yes, four lines
    //
    // notes: 1) "column address" is the byte within the page, 0-2175 when ECC 
    //           disabled, 0-2111 when ECC enabled
    //        2) since this is a read but requires an address, will be triggered
    //           by writing to the address register
    //
    ////////////////////////////////////////////////////////////////////////// 
        
    //////////////////////////////////////////////////////////////////////////
    // Set QUADSPI CCR [RM0394, 15.5.6]
    //////////////////////////////////////////////////////////////////////////
 
                                //   33222222222211111111110000000000
                                //   10987654321098765432109876543210
                                //   --------------------------------
    QUADSPI->CCR = 0x07081DEB;  // = 00000111000010000001110111101011
                                //   || | | |     | | | | | |       |
                                //   || | | |     | | | | | |       +---    7:0    INSTRUCTION  = 11101011  = 0xeb  Instruction = 0xEB
                                //   || | | |     | | | | | +-----------    9:8    IMODE        = 01        = 0x1   Instruction on a single line
                                //   || | | |     | | | | +-------------    11:10  ADMODE       = 11        = 0x3   Address on four lines
                                //   || | | |     | | | +---------------    13:12  ADSIZE       = 01        = 0x1   16-bit address
                                //   || | | |     | | +-----------------    15:14  ABMODE       = 00        = 0x0   No alternate bytes
                                //   || | | |     | +-------------------    17:16  ABSIZE       = 00        = 0x0   8-bit alternate byte
                                //   || | | |     +---------------------    22:18  DCYC         = 00010     = 0x2   2 dummy cycles
                                //   || | | +---------------------------    25:24  DMODE        = 11        = 0x3   Data on four lines
                                //   || | +-----------------------------    27:26  FMODE        = 01        = 0x1   Indirect read mode.
                                //   || +-------------------------------    28:28  SIOO         = 0         = 0x0   Send instruction on every transaction.
                                //   |+---------------------------------    30:30  DHHC         = 0         = 0x0   Delay the data output using analog delay
                                //   +----------------------------------    31:31  DDRM         = 0         = 0x0   DDR Mode disabled
 
    QUADSPI->DLR = count_of_bytes_to_transfer - 1;
    
    //////////////////////////////////////////////////////////////////////////
    // setting the address will trigger the transfer
    //////////////////////////////////////////////////////////////////////////
    
    QUADSPI->AR = source_column_address;  
 
 
    //////////////////////////////////////////////////////////////////////////
    // set the peripheral address to QuadSPI data register
    // set the memory address to the specified location
    // set the count to be moved;
    // The data is moved from this address to the memory after the peripheral event
    //////////////////////////////////////////////////////////////////////////
    
    uint32_t address_of_QUADSPI_DR = (uint32_t) &(QUADSPI->DR);
    
    DMA1_Channel5->CPAR = address_of_QUADSPI_DR; // QUADSPI->DR;
    DMA1_Channel5->CMAR = (uint32_t) local_target;
    DMA1_Channel5->CNDTR = count_of_bytes_to_transfer;
    
 
    //////////////////////////////////////////////////////////////////////////
    // Set DMA1 CCR5 [RM0394, 11.6.3]
    //////////////////////////////////////////////////////////////////////////
 
                                      //   33222222222211111111110000000000
                                      //   10987654321098765432109876543210
                                      //   --------------------------------
    DMA1_Channel5->CCR = 0x0000308B;  // = 00000000000000000011000010001011
                                      //                    | | | |||||||||
                                      //                    | | | ||||||||+---    0:0    EN       = 1    = 0x1  DMA channel enabled
                                      //                    | | | |||||||+----    1:1    TCIE     = 1    = 0x1  transfer complete interrupt (TCI) enabled
                                      //                    | | | ||||||+-----    2:2    HTIE     = 0    = 0x0  half transfer interrupt (HTI) disabled
                                      //                    | | | |||||+------    3:3    TEIE     = 1    = 0x1  transfer error interrupt (TEI) enabled
                                      //                    | | | ||||+-------    4:4    DIR      = 0    = 0x0  data transfer reads from peripheral
                                      //                    | | | |||+--------    5:5    CIRC     = 0    = 0x0  circular mode disabled
                                      //                    | | | ||+---------    6:6    PINC     = 0    = 0x0  peripheral increment mode disabled
                                      //                    | | | |+----------    7:7    MINC     = 1    = 0x1  memory increment mode enabled
                                      //                    | | | +-----------    9:8    PSIZE    = 00   = 0x0  peripheral size is 8 bits
                                      //                    | | +-------------    11:10  MSIZE    = 00   = 0x0  memory size is 8 bits
                                      //                    | +---------------    13:12  PL       = 11   = 0x3  priority level very high
                                      //                    +-----------------    14:14  MEM2MEM  = 0    = 0x0  memory-to-memory disabled
}

1 REPLY 1
Andreas Bolsch
Lead II

Are you sure that the extra bytes are indeed written by DMA? After disabling the DMA channel, check the transfer counter. Usually the DMA controller will stop when it reaches zero, regardless of the peripheral's request signal. The "buffer overflow" might simply be an error somewhere else, unrelated to the DMA transfer.

There are some silicon bugs concerning the QSPI interface, where some extra bytes are read, but as far as I remember, the errata sheets say this might happen only in very specific modes. You have to check the appropriate sheet for the MCU in question.