cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H745ZI Quad-SPI: Delay before accessing MemoryMapped storage

ESawa.1
Associate III

Hey together,

I have a question regarding the Quad-SPI used in MemoryMapped mode. First of all some context for you. Our plan is to use an external Flash at the moment 25Q16JV to place some code/data onto this chip. Our planed processor will be the STM32H750 but for now we are using the NUCLEO-H745ZI. Actual setup:

  • NUCLEO-H745ZI
  • Winbond 25Q16JV (Connected in 4 lines to STM32)

I wrote a "External Loader" to load up the code to the 25Q16JV and it works as expected. Read/Write/Erase is no problem. It works also to jump into the Code-Section placed in QUADSPI section. But there is one thing I dont understand. After initializing the quad-spi and setting the 25Q16JV into MemoryMapped mode I have to add a 10ms delay before I can access the code or data placed in QUADSPI section. If I remove this delay the code will end in the HardFault-Handler.

Below you can see my code in main(). Nothing special, only the code generated by CubeMx and afterwards the 2 calls:

  • CSP_QUADSPI_Init(); (Complete Code placed below)
    • Reset the 25Q16JV
    • Enable 4 line mode
    • Is the same used in my "ExternalLoader"
  • CSP_QSPI_EnableMemoryMappedMode(); (Complete Code placed below)
    • Sets the comand for 4 line data read
 int main(void){
/*... Code from CubeMX*/
 
/* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART3_UART_Init();
  MX_QUADSPI_Init();
  MX_USART1_UART_Init();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */
  logInit();
  logOutput("QUAD-SPI Performance Test\n");
  cycleCounter_init();
  
  CSP_QUADSPI_Init();					/*This resets the flash ic and will enable 4 line mode*/
  CSP_QSPI_EnableMemoryMappedMode();	/*This calls only the HAL_QSPI_MemoryMapped()*/
  HAL_Delay(10);						/*Why i need this delay???*/
  
  uint32_t cycles1 = 0;
  uint32_t cycles2 = 0;
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  cycles1 = 0;
	  cycles2 = 0;
	  cycles2 = performanceInternalFlash();		/*Placed in internal flash*/
	  cycles2 += performanceInternalFlash();	/*Placed in internal flash*/
	  cycles1 = performanceQuadSPIFlash();		/*Placed in quad spi flash*/
	  cycles1 += performanceQuadSPIFlash();		/*Placed in quad spi flash*/
	  /*Generate some output logging*/
	  logOutput("\n-quad-spi cycles: ");
	  convertIntToHexString(dataString,cycles1);
	  logOutput(dataString);
	  logOutput("\n-internal cycles: ");
	  convertIntToHexString(dataString,cycles2);
	  logOutput(dataString);
	  float flPercent = ((float)cycles2)/((float)cycles1) * 100;
	  uint32_t percent = (uint32_t)flPercent;
	  float flPromile = ((float)cycles2)/((float)cycles1) * 100000;
	  flPromile -= flPercent;
	  uint32_t promile = (uint32_t)flPromile;
	  logOutput("\n-quad-spi runs at: ");
	  convertIntToHexString(dataString,percent);
	  logOutput(dataString);
	  logOutput(".");
	  convertIntToHexString(dataString,promile);
	  logOutput(dataString);
	  logOutput("%");
 
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

In the main-loop the 2 functions are called:

  • cycles2 = performanceInternalFlash();
  • cycles1 = performanceQuadSPIFlash();

This is the same function, once placed in the internal flash and once placed in the QUAD-SPI section defined in my linker script. When I add the HAL_Delay(10) after "CSP_QUADSPI_Init()" or "CSP_QSPI_EnableMemoryMappedMode()" everything works as expected. But when I remove this delay the function call "performanceQuadSPIFlash()" it will end in the HardFault-Handler. The whole code starting from 0x90000000 (QUAD-SPI_Section) displays 0xFF in this case. But it also works when I add a break point before entering "performanceQuadSPIFlash()". I found that also the most of the example codes from ST include this HAL_Delay(10).

  • It seems that the Quad-SPI peripheral needs some time but why?
  • It there a way to poll a specific flag instead of putting this random delay?
  • Is this a know bug?

Function call one for internal one for quad spi flash:

__attribute__((section(".qspi"))) uint32_t performanceQuadSPIFlash(){
	start = cycleCounter_get();
	/*Calculation*/
	float mult = 0.1234;
	float result = 1.234;
	for(int i = 0; i < MULTIPLICATIONS; ++i) result = result*mult;
	end = cycleCounter_get();
	return (end-start);
}
uint32_t performanceInternalFlash(){
	start = cycleCounter_get();
	/*Calculation*/
	float mult = 0.1234;
	float result = 1.234;
	for(int i = 0; i < MULTIPLICATIONS; ++i) result = result*mult;
	end = cycleCounter_get();
	return (end-start);
}

Linker script:

/* Entry Point */
ENTRY(Reset_Handler)
 
/* Highest address of the user mode stack */
_estack = 0x20020000;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200 ;      /* required amount of heap  */
_Min_Stack_Size = 0x400 ; /* required amount of stack */
 
/* Specify the memory areas */
MEMORY
{
FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
QSPI (rx)      : ORIGIN = 0x90000000, LENGTH = 16M
 
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
ITCMRAM (xrw)      : ORIGIN = 0x00000000, LENGTH = 64K
AXISRAM (xrw)	   : ORIGIN = 0x24000000, LENGTH = 512K
RAM_D3 (xrw)       : ORIGIN = 0x38000000, LENGTH = 64K
}
 
/* Define output sections */
SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH
 
  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
 
    KEEP (*(.init))
    KEEP (*(.fini))
 
    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH
  
    .qspi :
  {
    . = ALIGN(4);
    _qspi_start = .;        /* create a global symbol at qspi start */
   KEEP ( *(.qspi))         /* .qspi sections */
   KEEP ( *(.qspi*))        /* .qspi* sections */
    . = ALIGN(4);
    _qspi_end = .;         /* define a global symbols at end of imgqspi */
    
  } >QSPI 
  ....

Quad-SPI initialization:

uint8_t CSP_QUADSPI_Init(void) {
	//prepare QSPI peripheral for ST-Link Utility operations
	if (HAL_QSPI_DeInit(&hqspi) != HAL_OK) {
		return HAL_ERROR;
	}
 
	MX_QUADSPI_Init();
 
	if (QSPI_ResetChip() != HAL_OK) {
		return HAL_ERROR;
	}
 
	for(int i=0;i<5000;i++)__NOP();
 
	if (QSPI_AutoPollingMemReady() != HAL_OK) {
		return HAL_ERROR;
	}
 
	if (QSPI_WriteEnable() != HAL_OK) {
 
		return HAL_ERROR;
	}
 
	if (QSPI_Configuration() != HAL_OK) {
		return HAL_ERROR;
	}
 
	return HAL_OK;
}

Enable MemoryMapped:

uint8_t CSP_QSPI_EnableMemoryMappedMode(void) {
 
	QSPI_CommandTypeDef sCommand;
	QSPI_MemoryMappedTypeDef sMemMappedCfg;
 
	/* Enable Memory-Mapped mode-------------------------------------------------- */
 
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
	sCommand.DataMode = QSPI_DATA_4_LINES;
	sCommand.NbData = 0;
	sCommand.Address = 0;
	sCommand.Instruction = QUAD_OUT_FAST_READ_CMD;
	sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_QUAD;
 
	sMemMappedCfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
 
	if (HAL_QSPI_MemoryMapped(&hqspi, &sCommand, &sMemMappedCfg) != HAL_OK) {
		return HAL_ERROR;
	}
	return HAL_OK;
}

2 REPLIES 2
Andreas Bolsch
Lead II

Resetting the flash chip, write enabling and configuring the chip for 4-line mode each involves sending a command in indirect write mode to the chip.

This in turn means every time (re-) configuring the QPSI interface, supplying the necessary command, address, ... to the QSPI interface. Only when

this information has been written to the QSPI interface, the transfer actually starts but then goes on asynchronously (regarding program flow).

This implies that one should wait for the transfer to be completed *BEFORE* reconfiguring the QSPI interface for memory mapped mode. The RM says explicitly that some configuration bits can be modified only when BUSY bit is cleared. So either check BUSY bit before touching the QSPI's setting or

use a generous delay between the various activities. (The latter being the inferiour solution, of course)

It's a good idea not to touch any QSPI register (except DR and reading SR) during an ongoing transfer, as it's not clear to me whether e.g. IR or AR are double buffered or not. The RM doesn't seem to be specific about this.

ESawa.1
Associate III

Hey Andreas,

thanks a lot for your fast resonse. I will give it a try to poll the BUSY flag and also maybe the TCF flag. Maybe I tried something similar before without success but anyway I will try it again. I will let you know if this solves the issue.