cancel
Showing results for 
Search instead for 
Did you mean: 

Problems accessing QSI Flash with consecutive reads

Gpeti
Senior II

I have a Cypress S25FL064 flash memory connected through QSPI interface to a STM32H753.

The memory is booting in "single IO" mode (ie. classical SPI protocol with MISO/MOSI signals).

In this mode I am able to erase, program and read the memory through its command set.

Then I switch to "quad io" and "memory mapped" mode.

In this configuration:

  • when the CPU sends "short" read commands (ex: I read a byte in my software -> the CPU sends a 4 bytes read command to the memory) everything works fine
  • when the CPU sends "long" read commands (ex. i do a memcpy from flash to internal RAM -> the CPU sends a single N bytes read command) the read data is corrupted after 10 bytes or so. However the signals on a scope seem correct.

The SPI clock is pretty slow (6.25MHz) compared to QPSI peripheral clock (200MHz) and CPU clock (400MHz).

About GPIOs I've let all IOs in high impedance mode (ie. bits 00 in GPIOx_PUPDR register) as adviced in the flash user manual. Pins frequency is medium.

The flash user manual mentions explicitely that it is possible to read the whole memory in a single command.

Any idea why "long" read commands would fail ? It looks like a synchronisation issue but I don't see what setting could cause that.

5 REPLIES 5
ChahinezC
Lead

Hello @Gpeti​,

Can you please share your QSPI configuration and the main function.

Thank you.

Chahinez.

Gpeti
Senior II

Here is the code (sorry, I removed the comments that were in french):

#define K_QSPI_CR1V                 0x02
#define K_QSPI_DCR                  (22 << QUADSPI_DCR_FSIZE_Pos)
#define K_QSPI_INSTRUCTION_MODE_CONFIG     QUADSPI_CCR_IMODE_0
#define K_QSPI_DATA_MODE_CONFIG     QUADSPI_CCR_DMODE_0
#define K_QSPI_ADRESSE_MODE_CONFIG  QUADSPI_CCR_ADMODE_0
#define K_QSPI_CMD_WRR              0x01
#define K_QSPI_CCR_MEMORY_MAPPED    (QUADSPI_CCR_FMODE | QUADSPI_CCR_DMODE | (8<<QUADSPI_CCR_DCYC_Pos) | QUADSPI_CCR_ABMODE | QUADSPI_CCR_ADSIZE_1 | QUADSPI_CCR_ADMODE | QUADSPI_CCR_IMODE_0 | 0xEB)
 
 
STATIC_ void envoi_commande_sans_data(octet O_INSTRUCTION)
{
    ECRIRE_REG(QUADSPI.DLR, 0);                                            
    ECRIRE_REG(QUADSPI.CCR, (K_QSPI_INSTRUCTION_MODE_CONFIG | O_INSTRUCTION) );    
    while( (QUADSPI.SR & QUADSPI_SR_TCF) == 0 );                           
    ECRIRE_REG(QUADSPI.FCR, QUADSPI_FCR_CTCF);                              
    while ((QUADSPI.SR & QUADSPI_SR_BUSY) != 0);
}
 
_STATIC_ void config_quadio(void)
{
    envoi_commande_sans_data((octet)K_QSPI_CMD_WRENV);
 
    ECRIRE_REG(QUADSPI.DLR, 1);                                             
    ECRIRE_REG(QUADSPI.CCR, (K_QSPI_DATA_MODE_CONFIG | K_QSPI_INSTRUCTION_MODE_CONFIG | K_QSPI_CMD_WRR) );
    *((__IO octet *)&(QUADSPI.DR)) = 0;                                     
    *((__IO octet *)&(QUADSPI.DR)) = K_QSPI_CR1V;                          
 
    while( (QUADSPI.SR & QUADSPI_SR_TCF) == 0 );
    ECRIRE_REG(QUADSPI.FCR, QUADSPI_FCR_CTCF);
    while ((QUADSPI.SR & QUADSPI_SR_BUSY) != 0);
}
 
 
void main(void)
{
#ifdef _TEST_MEM_CHAR_
    octet *PO_LECTURE;
    naturel4 U4_INDEX;
    octet O_ID1;
    octet O_ID2;
    octet O_ID3;
#endif
 
    /* init STM32 */
    (...)
 
    
    __IO naturel4 U4_REG_TEMP;
    SET_BIT(RCC.AHB3ENR, RCC_AHB3ENR_QSPIEN);
    U4_REG_TEMP = LIRE_BIT(RCC.AHB3ENR, RCC_AHB3ENR_QSPIEN);
    (void)U4_REG_TEMP;
 
    /* Reset */
    SET_BIT(RCC.AHB3RSTR, RCC_AHB3RSTR_QSPIRST);
    CLEAR_BIT(RCC.AHB3RSTR, RCC_AHB3RSTR_QSPIRST);
 
    ECRIRE_REG(QUADSPI.CR, (31U << QUADSPI_CR_PRESCALER_Pos) | (8 << QUADSPI_CR_PRESCALER_Pos));
 
    ECRIRE_REG(QUADSPI.DCR, (22 << QUADSPI_DCR_FSIZE_Pos));
 
    SET_BIT(QUADSPI.CR, QUADSPI_CR_EN);
 
    config_quadio();
 
    while( (QUADSPI.SR & QUADSPI_SR_BUSY) != 0 ); 
    ECRIRE_REG(QUADSPI.CCR, K_QSPI_CCR_MEMORY_MAPPED);
 
#ifdef _TEST_MEM_CHAR_
    // PO_LECTURE = (octet *)0x90000000;
    // if (*PO_LECTURE != 0) while(1);
    
    
    // PO_LECTURE = (octet *)0x90000010;
    // if (*PO_LECTURE != (octet)0x10) while(1);
    
    // PO_LECTURE = (octet *)0x90000100;
    // if (*PO_LECTURE != (octet)0x100) while(1);
    
    // PO_LECTURE = (octet *)0x90000233;
    // if (*PO_LECTURE != (octet)0x233) while(1);
 
    PO_LECTURE = (octet *)0x90000000;
    U4_INDEX = 0;
    while (U4_INDEX < K_MEM_CHAR_TEST_SIZE)
    {
        TO_TEST_BUFFER[U4_INDEX] = 0;
        U4_INDEX += KU4_UN;
    }
 
    U4_INDEX = 0;
    while (U4_INDEX < K_MEM_CHAR_TEST_SIZE)
    {
        TO_TEST_BUFFER[U4_INDEX] = *PO_LECTURE;
        U4_INDEX += KU4_UN;
        PO_LECTURE += KU4_UN;
    }
 
    U4_INDEX = 0;
    while (U4_INDEX < K_MEM_CHAR_TEST_SIZE)
    {
        if ( TO_TEST_BUFFER[U4_INDEX] != (octet)U4_INDEX )
        {
            while(1);
        }
        U4_INDEX += KU4_UN;
    }
#endif
}

Yeah, I don't know you're going to get a lot of support for register level code, or odd libraries.

Depending on the read command, and the memory, you can exhaust the buffering of the chips, but you say it is clocking is super-slow. On faster interfaces you use read commands with dummy cycles behind them to allow the memory to prefetch into a buffer.

How does it work with HAL code examples?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Yes, the memory requires 8 dummy cycles before returning its data. But it is the same whether you are reading 1 byte or many.

Didn't try with the HAL, but I looked at it for QSPI configuration.

I'd have thought the memory could sustain 25-50 mbps without being out-run.

You're using 0xEB Quad IO Fast

Prior thread so I can find it later

https://community.st.com/s/question/0D53W00001nkh8sSAA/stm32h7-qspi-kernel-frequency-too-slow-leads-to-read-errors-

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..