cancel
Showing results for 
Search instead for 
Did you mean: 

Title: NUCLEO-H7S3L8: HardFault/Timeout when using XSPI2 in XIP Application

YoungKwan
Associate II

Hello,

I'm currently testing a bootloader-XIP application architecture on a NUCLEO-H7S3L8 board. The MRAM (XSPI2) test works perfectly in the bootloader, but when I move the same code to the XIP application, the system hangs during XSPI2 initialization. I would appreciate any advice on this issue.

System Environment

  • MCU: STM32H7S3L8

  • Board: NUCLEO-H7S3L8

  • IDE: STM32CubeIDE v1.19.0

  • Firmware: STM32Cube_FW_H7RS_V1.2.0

Project Goal and Progress

  1. I have successfully completed all communication mode tests for the MRAM (XSPI2) in the bootloader (exercise_Boot).

    • The bootloader runs from internal flash, and through mram_spi_test.c, it has fully tested the MRAM connected to XSPI2 in all SDR/DDR and SPI/DSPI/QSPI modes, confirming perfect operation.

  2. I have migrated the verified test code to the XIP application (exercise_Appli).

    • The bootloader loads the application to the external NOR flash (connected to XSPI1) and jumps to it in XIP mode.

    • I have successfully confirmed that the application boots in XIP mode and can output printf logs.


The Problem

In the application's main() function, the system hangs the moment MX_XSPI2_Init() is called to run the same MRAM test that was used in the bootloader.

Since MX_XSPI1_Init() and MX_XSPI2_Init() have already been executed in the bootloader, I expected that the hardware would be ready and calling MX_XSPI2_Init() again in the application should not be a problem, but it results in a system fault.


Key Questions

  1. What is the standard initialization procedure for reliably using a secondary peripheral (like XSPI2) in an application that is running via XIP, where the peripherals were initially configured by a bootloader? I am looking for the correct way for the application to inherit and use the state set up by the bootloader.

  2. A method is needed to initialize XSPI2 and safely change its communication speed in an application running via XIP, without calling the full MX_XSPI2_Init() function.

  3. I am also curious if the issues described in questions 1 and 2 could be resolved if the application is run using a "Load and Run" approach (where the bootloader copies the app from NOR flash to internal RAM, and then executes from RAM) instead of XIP.

Below is the structure of the main function in exercise_Appli that is currently causing the hang. Any advice would be greatly appreciated. Thank you.

Current structure of exercise_Appli/main.c:

int main(void)
{
MPU_Config();
HAL_Init();

// Clock configuration function, identical to the one in the bootloader
SystemClock_Config();

// Other peripheral initializations
MX_GPIO_Init();

// ‌‌ The system hangs the moment this function is called for the MRAM test.
MX_XSPI2_Init();

// The MRAM test function is never reached
MRAM_Run_All_Tests();

while (1);
}

 

Note: The mram_spi_test.c file is verified code that passed all tests when run from the bootloader.

6 REPLIES 6
Saket_Om
ST Employee

Hello @YoungKwan 

Could you try to de-initialize the XSPI2 before jumping to application?

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.
Saket_Om
YoungKwan
Associate II

Hello @Saket_Om 
I added HAL_XSPI_DeInit(&hxspi2); before if (HAL_XSPI_Init(&hxspi2) != HAL_OK)

but problem occured at if (HAL_XSPIM_Config(&hxspi2, &sXspiManagerCfg, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) .

I called with &hxspi2 but HAL_XSPIM_Config try to config both hxsp1 and 2 than hang occured at 
  CLEAR_BIT(XSPI1->CR, XSPI_CR_EN). 

Thanks.

static void MX_XSPI2_Init(void)
{

 

    .....
     hxspi2.Init.MemorySelect = HAL_XSPI_CSSEL_NCS1;
     HAL_XSPI_DeInit(&hxspi2); // Added De-init

      if (HAL_XSPI_Init(&hxspi2) != HAL_OK)
      {
      Error_Handler();
      }
      sXspiManagerCfg.nCSOverride = HAL_XSPI_CSSEL_OVR_NCS1;
      sXspiManagerCfg.IOPort = HAL_XSPIM_IOPORT_1;
       if (HAL_XSPIM_Config(&hxspi2, &sXspiManagerCfg, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
       ....

}

HAL_StatusTypeDef HAL_XSPIM_Config(XSPI_HandleTypeDef *const hxspi, XSPIM_CfgTypeDef *const pCfg, uint32_t Timeout)
{

      /********** Disable both XSPI to configure XSPI IO Manager **********/
      if ((XSPI1->CR & XSPI_CR_EN) != 0U)
      {
          CLEAR_BIT(XSPI1->CR, XSPI_CR_EN); // <-- Hang occured
          xspi_enabled |= 0x1U;
       }
       if ((XSPI2->CR & XSPI_CR_EN) != 0U)
       {
            CLEAR_BIT(XSPI2->CR, XSPI_CR_EN);
            xspi_enabled |= 0x2U;
       }

 

Hello @Saket_Om 

 

If i change EXTMEM_MANAGER > Bot usecase from Execute In Place to Load and Run then is it possilbe to use MX_XSPI2_Init() without hang?

Thanks.

Hello @YoungKwan 

According to the reference manual the two XSPI interfaces should be disabled before configuring IO manager. 

Saket_Om_0-1756111705522.png

 

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.
Saket_Om
YoungKwan
Associate II

Hello @Saket_Om 

 

I found a way to initialize the XSPI2 after booting application with Load and Run mode .

Please review below document.

I want a way to initialize the XSPI1 after booting application with Load and Run mode still .

Regards.

Analysis and Solution for XSPI2 Initialization Hang

1. Problem Description and Cause Analysis

This document addresses a hang phenomenon that occurs when initializing XSPI2 in main.c after the application is loaded and run from the bootloader on a NUCLEO-H7S3L8 board.

Note: A solution for the hang that occurs when initializing XSPI1 has not yet been identified.

Cause

The core of the problem lies in the design of the HAL_XSPIM_Config function in the standard HAL driver provided by STMicroelectronics. To safely modify the XSPI IO Manager (XSPIM) registers, this function attempts to disable both instances, regardless of whether the request is for XSPI1 or XSPI2.

However, a problem arises when MX_XSPI2_Init() calls HAL_XSPIM_Config in a situation where XSPI1 has already been initialized by the bootloader and is actively communicating with external NOR flash in memory-mapped mode. When HAL_XSPIM_Config tries to forcibly disable the active XSPI1, the XSPI1 hardware fails to complete its ongoing transaction and enters a BUSY state, resulting in a deadlock.

Consequently, the attempt to initialize XSPI2 interferes with the already-in-use XSPI1, causing the entire system to hang.

Problematic Code (stm32h7rsxx_hal_xspi.c)

HAL_StatusTypeDef HAL_XSPIM_Config(XSPI_HandleTypeDef *const hxspi, XSPIM_CfgTypeDef *const pCfg, uint32_t Timeout)
{
  // ... (Initial setup)

  /********** Disable both XSPI to configure XSPI IO Manager **********/
  if ((XSPI1->CR & XSPI_CR_EN) != 0U)
  {
    CLEAR_BIT(XSPI1->CR, XSPI_CR_EN); // <--- Attempts to disable active XSPI1 (Problem point)
    xspi_enabled |= 0x1U;
  }
  if ((XSPI2->CR & XSPI_CR_EN) != 0U)
  {
    CLEAR_BIT(XSPI2->CR, XSPI_CR_EN); // <--- Attempts to disable XSPI2
    xspi_enabled |= 0x2U;
  }

  // ... (Subsequent configuration)
}

2. Solution

The key idea to solve this problem is to "selectively disable only the specific XSPI instance that needs configuration." Instead of modifying the standard HAL function directly, we create and use a new user-defined function, USER_HAL_XSPIM_One_Config, that implements this logic.

This approach has the advantage of preserving the original ST HAL library, ensuring that the code remains safe during future HAL library updates.

User-defined function with improved logic

// user_hal_xspi.c
HAL_StatusTypeDef USER_HAL_XSPIM_One_Config(...)
{
  // ... (Initial setup)

  /* 2) Disable ONLY the target XSPI instance to safely touch XSPIM */
  // Check if the hxspi handle points to XSPI1 or XSPI2
  if (hxspi->Instance == XSPI1)
  {
    // Disable XSPI1 only when configuring XSPI1
    if ((XSPI1->CR & XSPI_CR_EN) != 0U)
    {
      CLEAR_BIT(XSPI1->CR, XSPI_CR_EN);
      xspi_enabled |= 0x1U;
    }
  }
  else if (hxspi->Instance == XSPI2)
  {
    // Disable XSPI2 only when configuring XSPI2
    if ((XSPI2->CR & XSPI_CR_EN) != 0U)
    {
      CLEAR_BIT(XSPI2->CR, XSPI_CR_EN);
      xspi_enabled |= 0x2U;
    }
  }
  
  // ... (Subsequent configuration and re-enabling)
}

3. Implementation

3.1. Add User-Defined HAL Functions

Add the following two files to your project.

user_hal_xspi.h

/* user_hal_xspi.h */
#pragma once
#include "stm32h7rsxx_hal.h"

HAL_StatusTypeDef USER_HAL_XSPIM_One_Config(XSPI_HandleTypeDef *const hxspi,
                                            XSPIM_CfgTypeDef *const pCfg,
                                            uint32_t Timeout);

user_hal_xspi.c

/*
 * user_hal_xspi.c
 * Safe user-side reimplementation of HAL_XSPIM_Config() from STM HAL.
 */
#include "user_hal_xspi.h"

#ifndef XSPI_NB_INSTANCE
#define XSPI_NB_INSTANCE 2U
#endif

/* Local copy of the static helper from HAL (XSPIM_GetConfig) */
static void USER_XSPIM_GetConfig(uint8_t instance_nb, XSPIM_CfgTypeDef *const pCfg)
{
   uint32_t mux;
   uint32_t mode;
   if (instance_nb == 1U)
   {
     if ((XSPIM->CR & XSPIM_CR_MODE) == 0U)
     {
       pCfg->IOPort = HAL_XSPIM_IOPORT_1;
     }
     else
     {
       pCfg->IOPort = HAL_XSPIM_IOPORT_2;
     }
     if ((XSPIM->CR & XSPIM_CR_CSSEL_OVR_EN) != XSPIM_CR_CSSEL_OVR_EN)
     {
       pCfg->nCSOverride = HAL_XSPI_CSSEL_OVR_DISABLED;
     }
     else if ((XSPIM->CR & XSPIM_CR_CSSEL_OVR_O1) == XSPIM_CR_CSSEL_OVR_O1)
     {
       pCfg->nCSOverride = HAL_XSPI_CSSEL_OVR_NCS2;
     }
     else
     {
       pCfg->nCSOverride = HAL_XSPI_CSSEL_OVR_NCS1;
     }
   }
   else
   {
     mux = (XSPIM->CR & XSPIM_CR_MUXEN);
     mode = ((XSPIM->CR & XSPIM_CR_MODE) >> XSPIM_CR_MODE_Pos);
     if (mux != mode)
     {
       pCfg->IOPort = HAL_XSPIM_IOPORT_1;
     }
     else
     {
       pCfg->IOPort = HAL_XSPIM_IOPORT_2;
     }
     if ((XSPIM->CR & XSPIM_CR_CSSEL_OVR_EN) != XSPIM_CR_CSSEL_OVR_EN)
     {
       pCfg->nCSOverride = HAL_XSPI_CSSEL_OVR_DISABLED;
     }
     else if ((XSPIM->CR & XSPIM_CR_CSSEL_OVR_O2) == XSPIM_CR_CSSEL_OVR_O2)
     {
       pCfg->nCSOverride = HAL_XSPI_CSSEL_OVR_NCS2;
     }
     else
     {
       pCfg->nCSOverride = HAL_XSPI_CSSEL_OVR_NCS1;
     }
   }
}

/* User-side reimplementation of HAL_XSPIM_Config */
HAL_StatusTypeDef USER_HAL_XSPIM_One_Config(XSPI_HandleTypeDef *const hxspi,
                                            XSPIM_CfgTypeDef *const pCfg,
                                            uint32_t Timeout)
{
  HAL_StatusTypeDef status = HAL_OK;
  uint8_t index;
  uint8_t xspi_enabled = 0U;
  XSPIM_CfgTypeDef IOM_cfg[XSPI_NB_INSTANCE] = {0};

  UNUSED(Timeout);

  assert_param(IS_XSPIM_NCS_OVR(pCfg->nCSOverride));
  assert_param(IS_XSPIM_IO_PORT(pCfg->IOPort));
  assert_param(IS_XSPIM_REQ2ACKTIME(pCfg->Req2AckTime));

  /* 1) Get current configuration of both instances */
  for (index = 0U; index < XSPI_NB_INSTANCE; index++)
  {
    USER_XSPIM_GetConfig(index + 1U, &(IOM_cfg[index]));
  }

  /* 2) Disable ONLY the target XSPI instance to safely touch XSPIM */
  if (hxspi->Instance == XSPI1)
  {
    if ((XSPI1->CR & XSPI_CR_EN) != 0U)
    {
      CLEAR_BIT(XSPI1->CR, XSPI_CR_EN);
      xspi_enabled |= 0x1U;
    }
  }
  else if (hxspi->Instance == XSPI2)
  {
    if ((XSPI2->CR & XSPI_CR_EN) != 0U)
    {
      CLEAR_BIT(XSPI2->CR, XSPI_CR_EN);
      xspi_enabled |= 0x2U;
    }
  }
  else
  {
    hxspi->ErrorCode |= HAL_XSPI_ERROR_INVALID_PARAM;
    return HAL_ERROR;
  }

  /* 3) Deactivate previous XSPIM configuration entirely */
  CLEAR_REG(XSPIM->CR);

  /* 4) Apply new common configuration: Req->Ack time (minus 1 coded) */
  MODIFY_REG(XSPIM->CR, XSPIM_CR_REQ2ACK_TIME,
             ((pCfg->Req2AckTime - 1U) << XSPIM_CR_REQ2ACK_TIME_Pos));

  /* 5) Per-instance overrides (select ioport, optional nCS override) */
  if (hxspi->Instance == XSPI1)
  {
    IOM_cfg[0].IOPort = pCfg->IOPort;
    if (pCfg->nCSOverride != HAL_XSPI_CSSEL_OVR_DISABLED)
    {
      MODIFY_REG(XSPIM->CR,
                 (XSPIM_CR_CSSEL_OVR_O1 | XSPIM_CR_CSSEL_OVR_EN),
                 (pCfg->nCSOverride));
    }
  }
  else /* XSPI2 */
  {
    IOM_cfg[1].IOPort = pCfg->IOPort;
    if (pCfg->nCSOverride != HAL_XSPI_CSSEL_OVR_DISABLED)
    {
      MODIFY_REG(XSPIM->CR,
                 (XSPIM_CR_CSSEL_OVR_O2 | XSPIM_CR_CSSEL_OVR_EN),
                 (pCfg->nCSOverride));
    }
  }

  /* 6) MUX/MODE bits according to both instance IO port selection */
  for (index = 0U; index < (XSPI_NB_INSTANCE - 1U); index++)
  {
    if (IOM_cfg[index].IOPort == IOM_cfg[index + 1U].IOPort)
    {
      SET_BIT(XSPIM->CR, XSPIM_CR_MUXEN);
    }
    if (IOM_cfg[0].IOPort == HAL_XSPIM_IOPORT_2)
    {
      SET_BIT(XSPIM->CR, XSPIM_CR_MODE);
    }
  }

  /* 7) Re-enable the instance(s) that were enabled before */
  if ((xspi_enabled & 0x1U) != 0U)
  {
    SET_BIT(XSPI1->CR, XSPI_CR_EN);
  }
  if ((xspi_enabled & 0x2U) != 0U)
  {
    SET_BIT(XSPI2->CR, XSPI_CR_EN);
  }

  return status;
}

3.2. Modify main.c

In main.c, replace the existing MX_XSPI2_Init() call with the safe initialization procedure below and add the necessary helper functions.

Modification in main() function

// main.c
int main(void)
{
  // ... (Initial setup)

  /* Initialize all configured peripherals */
  // ...

  // MX_XSPI2_Init(); // Calling this function directly causes a hang
  
  /* --- Start of safe initialization code to replace MX_XSPI2_Init() --- */
  
  // 1. Reconnect the software handle to the actual hardware to synchronize the state
  if (App_XSPI_Init_Reconnect(&hxspi2, XSPI2) != HAL_OK) Error_Handler();
  
  // 2. Abort any ongoing memory-mapped mode operation
  if (HAL_XSPI_Abort(&hxspi2) != HAL_OK) Error_Handler();
  
  // 3. Cleanly de-initialize the peripheral
  if (HAL_XSPI_DeInit(&hxspi2) != HAL_OK) Error_Handler();
  
  // 4. Safely initialize with the new configuration (using the user-defined function)
  MX_XSPI_Init_New_Configuration(&hxspi2, XSPI2);
  
  /* --- End of safe initialization code --- */

  // ... (Subsequent code)
}

Helper functions to add to main.c

// main.c

/**
  * @brief  "Reconnects" the application's XSPI handle to already-initialized hardware.
  * @note   Synchronizes the application's handle state with the hardware state already set by the bootloader.
  */
HAL_StatusTypeDef App_XSPI_Init_Reconnect(XSPI_HandleTypeDef* hxspi, XSPI_TypeDef* xspi)
{
  hxspi->Instance = xspi;
  hxspi->Init.FifoThresholdByte = 1;
  hxspi->Init.MemoryMode = HAL_XSPI_SINGLE_MEM;
  hxspi->Init.MemoryType = HAL_XSPI_MEMTYPE_MICRON;
  hxspi->Init.MemorySize = HAL_XSPI_SIZE_1MB;
  hxspi->Init.ChipSelectHighTimeCycle = 2;
  hxspi->Init.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE;
  hxspi->Init.ClockMode = HAL_XSPI_CLOCK_MODE_0;
  hxspi->Init.WrapSize = HAL_XSPI_WRAP_NOT_SUPPORTED;
  hxspi->Init.ClockPrescaler = 3;
  hxspi->Init.SampleShifting = HAL_XSPI_SAMPLE_SHIFT_HALFCYCLE;
  hxspi->Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_DISABLE;
  hxspi->Init.ChipSelectBoundary = HAL_XSPI_BONDARYOF_NONE;
  hxspi->Init.MaxTran = 0;
  hxspi->Init.Refresh = 0;
  hxspi->Init.MemorySelect = HAL_XSPI_CSSEL_NCS1;
  if (HAL_XSPI_Init(hxspi) != HAL_OK)
  {
    Error_Handler();
  }
  return HAL_OK;
}

/**
  * @brief  Initializes XSPI with a new configuration. (Uses USER_HAL_XSPIM_One_Config)
  * @note   Avoids the hang issue by calling the safely modified function instead of the standard HAL_XSPIM_Config.
  */
HAL_StatusTypeDef MX_XSPI_Init_New_Configuration(XSPI_HandleTypeDef* hxspi, XSPI_TypeDef* xspi)
{
  XSPIM_CfgTypeDef sXspiManagerCfg = {0};
  hxspi->Instance = xspi;
  hxspi->Init.FifoThresholdByte = 1;
  hxspi->Init.MemoryMode = HAL_XSPI_SINGLE_MEM;
  hxspi->Init.MemoryType = HAL_XSPI_MEMTYPE_MICRON;
  hxspi->Init.MemorySize = HAL_XSPI_SIZE_1MB;
  hxspi->Init.ChipSelectHighTimeCycle = 2;
  hxspi->Init.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE;
  hxspi->Init.ClockMode = HAL_XSPI_CLOCK_MODE_0;
  hxspi->Init.WrapSize = HAL_XSPI_WRAP_NOT_SUPPORTED;
  hxspi->Init.ClockPrescaler = 3;
  hxspi->Init.SampleShifting = HAL_XSPI_SAMPLE_SHIFT_HALFCYCLE;
  hxspi->Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_DISABLE;
  hxspi->Init.ChipSelectBoundary = HAL_XSPI_BONDARYOF_NONE;
  hxspi->Init.MaxTran = 0;
  hxspi->Init.Refresh = 0;
  hxspi->Init.MemorySelect = HAL_XSPI_CSSEL_NCS1;
  if (HAL_XSPI_Init(hxspi) != HAL_OK)
  {
    Error_Handler();
  }
  sXspiManagerCfg.nCSOverride = HAL_XSPI_CSSEL_OVR_NCS1;
  sXspiManagerCfg.IOPort = HAL_XSPIM_IOPORT_1;
  
  // *** Core change: Call the safe user-defined function ***
  if (USER_HAL_XSPIM_One_Config(hxspi, &sXspiManagerCfg, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    Error_Handler();
  }
  return HAL_OK;
}

4. Conclusion

The standard HAL driver can cause a system hang because it does not fully consider the independent operation of two XSPI instances. The proposed solution safely and effectively resolves this issue by using a user-defined function that controls only the specific XSPI instance requiring configuration. This approach is a good practice for enhancing code maintainability as it avoids direct modification of the vendor-provided library.

Hello @YoungKwan 

As mentioned in the reference manual the configuration can be changed only when XSPIs are disabled. 

Saket_Om_0-1756384908323.png

 

 

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