2025-01-24 01:14 PM
Hello everyone,
I'm working on a project for which I designed a custom board. It consists of the following components.
Arm® Cortex®-M7 32-bit 600 MHz MCU, 64 KB flash, 620 KB RAM, Ethernet, 2x USB, 2x FD-CAN, advanced graphics, 2x12-bit ADCs
1.8V 256M-BIT [x 1/x 8] Read-While-Write Octal SPI Memory
1.8V 256M-BIT Double-Data-Rate Octal SPI PSRAM with x8/x16 interface
8G-BYTE eMMC chip
MII/RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and flexPWR® Technology
dual channel transceiver IC for SIO and IO-Link sensor applications
RC-SPIRIT2-915 module based on STMicroelectronics S2-LP transceiver
Here are some pictures of the board:
I chose this MBU because it's a potent and flexible device with many possibilities. The downside is that it is relatively new and not commonly used. During my work on this project, I encountered many challenges and obstacles. The investment of countless hours, unmeasurable volumes of coffee, and the help of this forum made it possible to overcome most of them.
There is an error in the STM32Cube_FW_H7RS_V1.1.0 firmware package regarding the initialization of the EXTMEM_MANAGER. I became aware of this when I stumbled upon a post from @williams-one in which he doubted the correctness of the documentation about the XSPI functionality in the MPU.
Here is the post, see for yourself:
Write access to PSRAM on XSPI in memory mapped mod... - STMicroelectronics Community
@KDJEM.1, a very knowledgeable ST employee, answered the post. He often helped me out of dark valleys where I probably would have been lost for good. This time, however, he missed the point that the Memory-Manager middleware should have automatically initialized and handled the PSRAM interface correctly (as long as all the provided parameters were correct).
Since my issues were the same as the ones @williams-one ran into, I dedicated some time to looking closely at this. My findings are that the culprit is the extmem_manager.c file at the end of the MX_EXTMEM_MANAGER_Init function.
I'm using both XSPI, and this is how the STM32CubeMX generated code looks in my case:
EXTMEM_Init(EXTMEMORY_1, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2));
EXTMEM_Init(EXTMEMORY_2, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1));
There are two problems with this.
Point 1 is more of an inconvenience. If one tries to "single step" through this during debugging, it "single steps" into the routine HAL_RCCEx_GetPeriphCLKFreq but fails to step to the EXTMEM_Init function. The solution would be to have STM32CubeMX generated code look like this:
uint32_t tmpPERIPHCLK_XSPI1 = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1);
uint32_t tmpPERIPHCLK_XSPI2 = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2);
EXTMEM_Init(EXTMEMORY_1, tmpPERIPHCLK_XSPI2);
EXTMEM_Init(EXTMEMORY_2, tmpPERIPHCLK_XSPI1);
The second point in my former list is masking any problems during the EXTMEM_Init. This also explains the general understanding that one must write extra code to make the PSRAM work. The fact that STM published the famous application note AN5050 and uses it as the standard GOTO for any question related to XSPI shows the broad ignorance on that issue and provides an excellent example of the effects of error suppression.
If one had tried to look under the hood of the EXTMEM_Init function, it would have become clear that, as @williams-one put it, "some docs (e.g., the reference manual)" are correct after all.
Buckle up, readers, because you're about to see things nobody alive has ever seen after you open the EXTMEM_Init door. As always in the world of c code, strange functions call even stranger functions, which then point to pointers that point to other pointers, which become functions that call functions that contain parameters that point to data nobody knows where they come from.
However, here is the path I went down to the point where I ran out of coffee and energy to stay awake:
It all begins in the main.c of the boot project. During the peripherals' initialization just after the XSPI hardware init, the MX_EXTMEM_MANAGER_Init() function is called.
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_RTC_Init();
MX_SBS_Init();
MX_XSPI1_Init();
MX_XSPI2_Init();
MX_EXTMEM_MANAGER_Init();
This function lives in the extmem_manager.c file. It will call the EXTMEM_Init function once or twice depending on your configuration.
EXTMEM_Init(EXTMEMORY_1, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2));
EXTMEM_Init(EXTMEMORY_2, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1));
The EXTMEM_Init function can be found in the realm of the Middleware. There will be a stm32_extmem.c file, which is its home. The index EXTMEMORY_1 or EXTMEMORY_2 and some defined or not defined macros, the EXTMEM_Init function will call the correct function to proceed. Our interest here lies in the PSRAM, which is the reason we follow the path to this:
#if EXTMEM_DRIVER_PSRAM == 1
case EXTMEM_PSRAM : {
/* Initialize the SFDP memory */
if (EXTMEM_DRIVER_PSRAM_OK != EXTMEM_DRIVER_PSRAM_Init(extmem_list_config[MemId].Handle,
extmem_list_config[MemId].ConfigType,
ClockInput,
&extmem_list_config[MemId].PsramObject))
{
retr = EXTMEM_ERROR_DRIVER;
}
break;
}
#endif /* EXTMEM_DRIVER_PSRAM == 1 */
The EXTMEM_DRIVER_PSRAM_Init function, which can be found in the stm32_psram_driver.c file, is the culprit of our problem. This function prepares the XSPI to communicate with our PSRAM chip and writes the correct values to the registers of the PSRAM chip itself. It uses the SAL(Software Adaptation Layer) from the stm32_sal_xspi.c file to do the dirty work. If we keep following our path, we will encounter a loop that is used to take care of all the configurations we set up.
Here is the loop which is used to write to the registers of the PSRAM chip:
/* Execute the command sequence */
for (uint8_t command_index = 0u; command_index < PsramObject->psram_public.NumberOfConfig; command_index++)
{
retr = PSRAM_ExecuteCommand(PsramObject, command_index);
if (retr != EXTMEM_DRIVER_PSRAM_OK)
{
goto error;
}
}
The function PSRAM_ExecuteCommand is located in the same file, and this is what it looks like:
EXTMEM_DRIVER_PSRAM_StatusTypeDef PSRAM_ExecuteCommand(EXTMEM_DRIVER_PSRAM_ObjectTypeDef *PsramObject, uint8_t Index)
{
EXTMEM_DRIVER_PSRAM_StatusTypeDef retr = EXTMEM_DRIVER_PSRAM_OK;
uint8_t regval[2];
if (PsramObject->psram_public.ReadREGSize > 2u)
{
retr = EXTMEM_DRIVER_PSRAM_ERROR_REGSIZE;
goto error;
}
if (HAL_OK != SAL_XSPI_Read(&PsramObject->psram_private.SALObject,
PsramObject->psram_public.ReadREG,
PsramObject->psram_public.config[Index].REGAddress,
regval, PsramObject->psram_public.ReadREGSize))
{
retr = EXTMEM_DRIVER_PSRAM_ERROR_READREG;
goto error;
}
MODIFY_REG(regval[0],
PsramObject->psram_public.config[Index].WriteMask,
PsramObject->psram_public.config[Index].WriteValue);
if (HAL_OK != SAL_XSPI_Write(&PsramObject->psram_private.SALObject,
PsramObject->psram_public.WriteREG,
PsramObject->psram_public.config[Index].REGAddress,
regval, PsramObject->psram_public.ReadREGSize))
{
retr = EXTMEM_DRIVER_PSRAM_ERROR_WRITEREG;
goto error;
}
error:
return retr;
}
It first reads the PSRAM register, modifies its value, and writes back the new value. The SAL_XSPI_Write function writes the new value back to the PSRAM register.
This is the SAL_XSPI_Write function:
HAL_StatusTypeDef SAL_XSPI_Write(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t Command, uint32_t Address, const uint8_t *Data, uint32_t DataSize)
{
HAL_StatusTypeDef retr;
XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase;
/* Initialize the read ID command */
s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, Command);
s_command.Address = Address;
s_command.DataLength = DataSize;
s_command.DummyCycles = 0u;
s_command.DQSMode = HAL_XSPI_DQS_DISABLE;
/* Configure the command */
retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE);
if (HAL_OK != retr)
{
goto error;
}
/* transmit data */
retr = XSPI_Transmit(SalXspi, Data);
error:
if (retr != HAL_OK )
{
/* abort any ongoing transaction for the next action */
(void)HAL_XSPI_Abort(SalXspi->hxspi);
}
return retr;
}
It uses the HAL functions to do the heavy lifting. If we follow the HAL_XSPI_Command function, we will find, besides a bunch of parameter asserts, a function call to wait for a flag to be set.
/* Wait till busy flag is reset */
status = XSPI_WaitFlagStateUntilTimeout(hxspi, HAL_XSPI_FLAG_BUSY, RESET, tickstart, Timeout);
In our case, we wait for the BUSY flag in the XSPI_SR register to be RESET with a timeout of 100ms.
Unfortunately, this will never happen, even if we wait forever.
We have reached the point I mentioned earlier where I ran out of coffee and energy to stay awake.
I don't expect anybody from STM to have a sufficient explanation for all this, and I probably won't live long enough to see an STM32Cube_FW_H7RS firmware update without this error. When one thinks of all the support and help STM provides to the "little" customers all the time, it's probably a good moment to remind everyone of their duty to give back. This is why I'll try to fix this issue and publish the results here for everyone.
Microsoft Corporation once said: "Knowledge shared is knowledge squared."
Stay curious, and never forget:
"Always be yourself. Unless you can be a pirate. Then always be a pirate."
Solved! Go to Solution.
2025-01-30 07:40 PM
Hello,
About one week ago, I wrote a post describing my issues with the STM32Cube_FW_H7RS_V1.1.0 package.
This is a link to my first post about the error:
Firmware Error in STM32Cube_FW_H7RS_V1.1.0 package - STMicroelectronics Community
The errors can be found in the firmware package and the example files. The famous AN5050 offers some workaround, but it is not so much a workaround as an attempt to overcome the ignorance regarding the non-working examples. The Memory Manager handles the external memory on the XSPI interfaces without adding additional code between the "USER CODE" lines. I've attached a working example project, which is based on the following hardware:
The only additional code is to test the PSRAM and fight the firmware issues. As soon as STM modifies the firmware and corrects/completes the documentation, it will work "out of the box."
However, here are my findings and solutions:
Many of the XSPI configuration parameters for the external Flash are irrelevant because the Memory Manager will use the Serial Flash Discovery Protocol to discover all crucial settings from the chip itself. The SFDP is only used if EXTMEM_NOR_SFDP is selected.
The EXTMEM_LOADER settings must be adjusted according to the used chip. These are the settings for the MX25UW25645GXDI00 chip that I've used:
One of the most misleading XSPI parameters is the ClockPrescaler if one uses the Memory Manager. According to the choice of memory chips, the following functions are called from the middleware file stm32_extmem.c:
if (EXTMEM_DRIVER_NOR_SFDP_OK != EXTMEM_DRIVER_NOR_SFDP_Init(extmem_list_config[MemId].Handle,
extmem_list_config[MemId].ConfigType,
ClockInput,
&extmem_list_config[MemId].NorSfdpObject))
{
retr = EXTMEM_ERROR_DRIVER;
}
if (EXTMEM_DRIVER_PSRAM_OK != EXTMEM_DRIVER_PSRAM_Init(extmem_list_config[MemId].Handle,
extmem_list_config[MemId].ConfigType,
ClockInput,
&extmem_list_config[MemId].PsramObject))
{
retr = EXTMEM_ERROR_DRIVER;
}
These functions use SAL_XSPI_SetClock from the software adaptation layer. Here is a closer look at this implementation:
HAL_StatusTypeDef SAL_XSPI_SetClock(SAL_XSPI_ObjectTypeDef *SalXspi, uint32_t ClockIn, uint32_t ClockRequested, uint32_t *ClockReal)
{
HAL_StatusTypeDef retr = HAL_OK;
uint32_t divider;
if (ClockRequested == 0u)
{
retr = HAL_ERROR;
}
else
{
divider = (ClockIn / ClockRequested);
if (divider >= 1u)
{
*ClockReal = ClockIn / divider;
if (*ClockReal <= ClockRequested)
{
divider--;
}
}
#if 0 /* Only used for debug purpose */
divider=+5;
divider++;
divider++;
divider++;
divider++;
divider++;
divider++;
divider++;
divider++;
#endif
/* real clock calculation */
*ClockReal = ClockIn / (divider + 1u);
DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::CLOCK::"); DEBUG_PARAM_INT(divider); DEBUG_PARAM_END();
MODIFY_REG(SalXspi->hxspi->Instance->DCR2, XSPI_DCR2_PRESCALER, (uint32_t)divider << XSPI_DCR2_PRESCALER_Pos);
}
return retr;
}
The memory's clock frequency is calculated using the function parameter SAL_XSPI_SetClock, and the ClockPrescaler parameter from the XSPI settings is not used. This is not an issue as long as it is mentioned anywhere in the documentation. The example code is using this:
EXTMEM_Init(EXTMEMORY_1, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2));
EXTMEM_Init(EXTMEMORY_2, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1));
This approach is not a good solution in several ways. The function HAL_RCCEx_GetPeriphCLKFreq returns the XSPI's "Input Frequency," which happens to be 200 MHz in my case. If one runs the MCU on 600 MHz and selects the HCLK5(the default setting), the XSPI "Input Frequency" will be 300 MHz. The memory chips will not work at this speed, and the hunt is on.
The other problem with the EXTMEM_Init implementation is that the function's return value is ignored, any error during the initialization is masked out, and the "wild goose chase" is on its way.
The Memory Manager parameter MemorySize requires the unit MBit instead of MByte. This is wrong in the firmware examples and could confuse.
Feel free to check out my attached example project and let me know your thoughts.
I want to thank @williams-one, @KDJEM.1, and @Pavel A. for all the inspiration and support.
Stay sleepless and keep in mind:
"Always be yourself unless you can be a pirate. Then always be a pirate."
2025-01-30 07:40 PM
Hello,
About one week ago, I wrote a post describing my issues with the STM32Cube_FW_H7RS_V1.1.0 package.
This is a link to my first post about the error:
Firmware Error in STM32Cube_FW_H7RS_V1.1.0 package - STMicroelectronics Community
The errors can be found in the firmware package and the example files. The famous AN5050 offers some workaround, but it is not so much a workaround as an attempt to overcome the ignorance regarding the non-working examples. The Memory Manager handles the external memory on the XSPI interfaces without adding additional code between the "USER CODE" lines. I've attached a working example project, which is based on the following hardware:
The only additional code is to test the PSRAM and fight the firmware issues. As soon as STM modifies the firmware and corrects/completes the documentation, it will work "out of the box."
However, here are my findings and solutions:
Many of the XSPI configuration parameters for the external Flash are irrelevant because the Memory Manager will use the Serial Flash Discovery Protocol to discover all crucial settings from the chip itself. The SFDP is only used if EXTMEM_NOR_SFDP is selected.
The EXTMEM_LOADER settings must be adjusted according to the used chip. These are the settings for the MX25UW25645GXDI00 chip that I've used:
One of the most misleading XSPI parameters is the ClockPrescaler if one uses the Memory Manager. According to the choice of memory chips, the following functions are called from the middleware file stm32_extmem.c:
if (EXTMEM_DRIVER_NOR_SFDP_OK != EXTMEM_DRIVER_NOR_SFDP_Init(extmem_list_config[MemId].Handle,
extmem_list_config[MemId].ConfigType,
ClockInput,
&extmem_list_config[MemId].NorSfdpObject))
{
retr = EXTMEM_ERROR_DRIVER;
}
if (EXTMEM_DRIVER_PSRAM_OK != EXTMEM_DRIVER_PSRAM_Init(extmem_list_config[MemId].Handle,
extmem_list_config[MemId].ConfigType,
ClockInput,
&extmem_list_config[MemId].PsramObject))
{
retr = EXTMEM_ERROR_DRIVER;
}
These functions use SAL_XSPI_SetClock from the software adaptation layer. Here is a closer look at this implementation:
HAL_StatusTypeDef SAL_XSPI_SetClock(SAL_XSPI_ObjectTypeDef *SalXspi, uint32_t ClockIn, uint32_t ClockRequested, uint32_t *ClockReal)
{
HAL_StatusTypeDef retr = HAL_OK;
uint32_t divider;
if (ClockRequested == 0u)
{
retr = HAL_ERROR;
}
else
{
divider = (ClockIn / ClockRequested);
if (divider >= 1u)
{
*ClockReal = ClockIn / divider;
if (*ClockReal <= ClockRequested)
{
divider--;
}
}
#if 0 /* Only used for debug purpose */
divider=+5;
divider++;
divider++;
divider++;
divider++;
divider++;
divider++;
divider++;
divider++;
#endif
/* real clock calculation */
*ClockReal = ClockIn / (divider + 1u);
DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::CLOCK::"); DEBUG_PARAM_INT(divider); DEBUG_PARAM_END();
MODIFY_REG(SalXspi->hxspi->Instance->DCR2, XSPI_DCR2_PRESCALER, (uint32_t)divider << XSPI_DCR2_PRESCALER_Pos);
}
return retr;
}
The memory's clock frequency is calculated using the function parameter SAL_XSPI_SetClock, and the ClockPrescaler parameter from the XSPI settings is not used. This is not an issue as long as it is mentioned anywhere in the documentation. The example code is using this:
EXTMEM_Init(EXTMEMORY_1, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2));
EXTMEM_Init(EXTMEMORY_2, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1));
This approach is not a good solution in several ways. The function HAL_RCCEx_GetPeriphCLKFreq returns the XSPI's "Input Frequency," which happens to be 200 MHz in my case. If one runs the MCU on 600 MHz and selects the HCLK5(the default setting), the XSPI "Input Frequency" will be 300 MHz. The memory chips will not work at this speed, and the hunt is on.
The other problem with the EXTMEM_Init implementation is that the function's return value is ignored, any error during the initialization is masked out, and the "wild goose chase" is on its way.
The Memory Manager parameter MemorySize requires the unit MBit instead of MByte. This is wrong in the firmware examples and could confuse.
Feel free to check out my attached example project and let me know your thoughts.
I want to thank @williams-one, @KDJEM.1, and @Pavel A. for all the inspiration and support.
Stay sleepless and keep in mind:
"Always be yourself unless you can be a pirate. Then always be a pirate."
2025-01-31 07:52 AM
Hello @Intector
Is the test case project that you shared reproduces the issue?
If not, could you share the project that can be used to reproduce the issue from our side?
2025-01-31 12:19 PM
Hello @Saket_Om ,
I found the solution to the issues and attached the Test-XSPI project in my earlier post is working. It can be used as starter template by anyone who don't want want to spend countless hours on finding bugs and guessing what went wrong.
2025-02-03 01:16 AM - edited 2025-02-03 01:32 AM
Hello @Intector;
Thank you for sharing an XSPI project which may help other community members.
I reported AN5050 internally for checking.
To check the HAL drivers and middleware and fix the issue in STM32CubeH7RS firmware, could you share the project that can be used to reproduce the issue as said by @Saket_Om.
Thank you for your sharing your experience with STM32H7RS MCU and for your contribution in the community.
Your posts are most welcome.
Internal ticket number: 202049 (This is an internal tracking number and is not accessible or usable by customers).
Thank you.
Kaouthar
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2025-02-03 09:39 AM
Hello @KDJEM.1
I used the Test-XSPI project to find the solution; the original no longer exists. In my earlier post, I tried to write a detailed description of my findings and the issue. The Test-XSPI project is a working solution. I also put a repo on GitHub containing the memory's schematics.
Anyone can use this example and do whatever they like with it. Unfortunately, I can't take responsibility for anything that happens while using this software.
My friends stay thirsty and try to remember:
"Always be yourself unless you can be a pirate. Then always be a pirate."
2025-02-03 11:30 PM
Hello @Intector
In this case, could you please provide more details about the solution? In which file and which lines did you make an update?
2025-02-04 06:40 AM
Hey @Saket_Om
It looks like I've opened a can of worms. I changed nothing in the "Middleware" or "Firmware" files. The changes are in the "extmem_manager.c" file.
This is the code generated by STM32CubeMX:
EXTMEM_Init(EXTMEMORY_1, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2));
EXTMEM_Init(EXTMEMORY_2, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1));
This is my modification:
// get frequency for PSRAM
uint32_t PERIPHCLK_XSPI1 = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1);
// get frequency for FLASH
uint32_t PERIPHCLK_XSPI2 = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2);
// initialize FLASH
if (EXTMEM_Init(EXTMEMORY_1, PERIPHCLK_XSPI2) != EXTMEM_OK)
{
Error_Handler();
}
// firmware fix for clock Pre-scaler, activate next line if you want to use your Pre-scaler factor
// MODIFY_REG(hxspi2.Instance->DCR2, XSPI_DCR2_PRESCALER, hxspi2.Init.ClockPrescaler << XSPI_DCR2_PRESCALER_Pos);
// initialize PSRAM
if (EXTMEM_Init(EXTMEMORY_2, PERIPHCLK_XSPI1) != EXTMEM_OK)
{
Error_Handler();
}
// firmware fix for clock Pre-scaler, activate next line if you want to use your Pre-scaler factor
// MODIFY_REG(hxspi1.Instance->DCR2, XSPI_DCR2_PRESCALER, hxspi1.Init.ClockPrescaler << XSPI_DCR2_PRESCALER_Pos);
The HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1) function returns the XSPI's "Input" frequency. The SAL_XSPI_SetClock function uses this value to calculate the prescaler factor. Here is that function:
HAL_StatusTypeDef SAL_XSPI_SetClock(SAL_XSPI_ObjectTypeDef *SalXspi, uint32_t ClockIn, uint32_t ClockRequested, uint32_t *ClockReal)
{
HAL_StatusTypeDef retr = HAL_OK;
uint32_t divider;
if (ClockRequested == 0u)
{
retr = HAL_ERROR;
}
else
{
divider = (ClockIn / ClockRequested);
if (divider >= 1u)
{
*ClockReal = ClockIn / divider;
if (*ClockReal <= ClockRequested)
{
divider--;
}
}
#if 0 /* Only used for debug purpose */
divider=+5;
divider++;
divider++;
divider++;
divider++;
divider++;
divider++;
divider++;
divider++;
#endif
/* real clock calculation */
*ClockReal = ClockIn / (divider + 1u);
DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::CLOCK::"); DEBUG_PARAM_INT(divider); DEBUG_PARAM_END();
MODIFY_REG(SalXspi->hxspi->Instance->DCR2, XSPI_DCR2_PRESCALER, (uint32_t)divider << XSPI_DCR2_PRESCALER_Pos);
}
return retr;
}
This leads the user to believe that the prescaler value in the STM32CubeMX settings is used, which is not correct.
The entered value for the Clock Prescaler is not used by the "Middleware." This means the memory always runs at the maximum frequency set by the XSPI initialization.
If one leaves the settings at HCLK5(default) and the MBU settings are maximum, the memory must run with 300 MHz.
This is because the default maximum setting for HCLK5 is 300 MHz.
In other words, memory will not function, and we will have a goose hunt, which could keep us busy for days.
This is one of the issues. The other one is the EXTMEM_Init function call itself. You remember this is what STM32CubeMX delivers when we create the code:
EXTMEM_Init(EXTMEMORY_1, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2));
EXTMEM_Init(EXTMEMORY_2, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI1));
The function returns a value from type EXTMEM_StatusTypeDef, which the MX-generated code neglects. In case of a memory initialization error, the user will see a CPU hard stop on the first memory access. This now leads to our second goose hunt. Even if the user modifies the code to catch an error, the EXTMEM_Init function changes the original error code into something else, and we're back at square one.
EXTMEM_StatusTypeDef EXTMEM_Init(uint32_t MemId, uint32_t ClockInput)
{
EXTMEM_StatusTypeDef retr = EXTMEM_ERROR_INVALID_ID;
EXTMEM_FUNC_CALL();
/* control the memory ID */
if (MemId < (sizeof(extmem_list_config) / sizeof(EXTMEM_DefinitionTypeDef)))
{
retr = EXTMEM_OK;
switch (extmem_list_config[MemId].MemType)
{
#if EXTMEM_DRIVER_NOR_SFDP == 1
case EXTMEM_NOR_SFDP:{
/* Initialize the SFDP memory */
if (EXTMEM_DRIVER_NOR_SFDP_OK != EXTMEM_DRIVER_NOR_SFDP_Init(extmem_list_config[MemId].Handle,
extmem_list_config[MemId].ConfigType,
ClockInput,
&extmem_list_config[MemId].NorSfdpObject))
{
retr = EXTMEM_ERROR_DRIVER;
}
break;
}
#endif /* EXTMEM_DRIVER_NOR_SFDP == 1 */
#if EXTMEM_DRIVER_SDCARD == 1
case EXTMEM_SDCARD:{
/* Initialize the SFDP memory */
if (EXTMEM_DRIVER_SDCARD_OK != EXTMEM_DRIVER_SDCARD_Init(extmem_list_config[MemId].Handle,
&extmem_list_config[MemId].SdCardObject))
{
retr = EXTMEM_ERROR_DRIVER;
}
break;
}
#endif /* EXTMEM_DRIVER_SDCARD == 1 */
#if EXTMEM_DRIVER_PSRAM == 1
case EXTMEM_PSRAM : {
/* Initialize the SFDP memory */
if (EXTMEM_DRIVER_PSRAM_OK != EXTMEM_DRIVER_PSRAM_Init(extmem_list_config[MemId].Handle,
extmem_list_config[MemId].ConfigType,
ClockInput,
&extmem_list_config[MemId].PsramObject))
{
retr = EXTMEM_ERROR_DRIVER;
}
break;
}
#endif /* EXTMEM_DRIVER_PSRAM == 1 */
#if EXTMEM_DRIVER_USER == 1
case EXTMEM_USER :{
/* Initialize the SFDP memory */
EXTMEM_DRIVER_USER_StatusTypeDef status;
status = EXTMEM_DRIVER_USER_Init(MemId,
&extmem_list_config[MemId].UserObject);
switch(status){
case EXTMEM_DRIVER_USER_NOTSUPPORTED:{
retr = EXTMEM_ERROR_NOTSUPPORTED;
break;
}
case EXTMEM_DRIVER_USER_OK:{
/* nothing to do */
retr = EXTMEM_OK;
break;
}
default:{
retr = EXTMEM_ERROR_DRIVER;
break;
}
}
break;
}
#endif /* EXTMEM_DRIVER_USER == 1 */
default:{
EXTMEM_DEBUG("\terror unknown type\n");
retr = EXTMEM_ERROR_UNKNOWNMEMORY;
break;
}
}
}
return retr;
}
We will know something went wrong, but what the problem was is everyone's best guess. This modification would be inside the "Middleware" files, which I haven't done.
The other thing is that the XSPI parameter for the DHQC is available despite the fact we selected an STM32H7RS device.
It was unclear that the DHQC doesn't exist on these devices, so I set this to Enabled, following the recommendation in the famous AN5050 document. The RM0477 does not have the DHQC flag listed, which made me curious, and @KDJEM.1 explained that this flag does not exist on the STM32H7RS devices. This would explain why the RM0477 recommends leaving all other bits at the TCR register at 0.
I'm unsure what happens if one sets bit 29 to 1 in an STM32H7RS device. Maybe nothing happens at all, or maybe a wormhole opens somewhere in the Andromeda galaxy.
Those are the significant things that I found that made getting my system running difficult. I published a repo with the working "Test-XSPI" project on GitHub just in case somebody else plans to use the XSPI on any STM32H7RS device. Maybe that will help those people save some time and a headache.
My Friends, stay suspicious and keep in mind:
"Always be yourself unless you can be a pirate. Then always be a pirate."
2025-02-04 06:46 AM - edited 2025-02-04 06:46 AM
Hey,
I forgot the link to the GitHub repo, but here it is:
working XSPI example for an STM32H7R3L8H6H device