Skip to main content
Gpeti
Senior
September 14, 2022
Question

Problems accessing QSI Flash with consecutive reads

  • September 14, 2022
  • 2 replies
  • 1407 views

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.

This topic has been closed for replies.

2 replies

ChahinezC
Associate II
September 14, 2022

Hello @Gpeti​,

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

Thank you.

Chahinez.

Gpeti
GpetiAuthor
Senior
September 14, 2022

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
}

Tesla DeLorean
Guru
September 14, 2022

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 VenmoUp vote any posts that you find helpful, it shows what's working..
Gpeti
GpetiAuthor
Senior
September 14, 2022

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.