STM32H745ZI Quad-SPI: Delay before accessing MemoryMapped storage
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;
}