cancel
Showing results for 
Search instead for 
Did you mean: 

Firmware Error in STM32Cube_FW_H7RS_V1.1.0 package

Intector
Senior

Hello everyone,

I'm working on a project for which I designed a custom board. It consists of the following components.

  • STM32H7R3L8H6H MCU
    • 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

  • MX25UW25645GXDI00-T
    • 1.8V 256M-BIT [x 1/x 8] Read-While-Write Octal SPI Memory

  • APS256XXN-OBR-BG
    • 1.8V 256M-BIT Double-Data-Rate Octal SPI PSRAM with x8/x16 interface

  • EMMC08G-MV28-01J10
    • 8G-BYTE eMMC chip

  • LAN8742A-CZ-TR
    • MII/RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and flexPWR® Technology

  • L6364Q
    • dual channel transceiver IC for SIO and IO-Link sensor applications

  • RC-SPIRIT2-xxx
    • RC-SPIRIT2-915 module based on STMicroelectronics S2-LP transceiver

 

Here are some pictures of the board:

20250118_202320.jpg20250118_212240.jpg

 

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.

  1. Debugging unfriendliness.
  2. No error handling.

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.

Spoiler
Remember: We used the Memory 2 tab to place our PSRAM chip. At the bottom of this tab, you can select how many configurations you want for your PSRAM chip. I chose one and used it to place the settings for the Memory-Mapped mode.
Intector_0-1737750434400.png 

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.

Intector_1-1737751624605.png

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."

 

 

 

0 REPLIES 0