cancel
Showing results for 
Search instead for 
Did you mean: 

Failed to implement external loader on custom board

newbiee
Associate II

# External Loader Issue: STM32U5G9ZJT6Q + MX25L12833FZNI-10G (Quad-SPI)

## 1. Problem Summary

Hello, I'm a novice developer who recently started working with embedded systems. I've been struggling significantly with implementing an external loader for several weeks now...

I'm developing an external loader for **MX25L12833F** (16MB Quad-SPI NOR Flash) connected to **STM32U5G9ZJT6Q** via OCTOSPI1, but it continuously fails in STM32CubeProgrammer. The loader compiles successfully and generates a `.stldr` file, but programming operations fail.

The problem is sector erase. Writing, reading, verification, and mass erase all function normally, but only sector erase does not work properly. What could be the reason?

 

__attribute__((used)) int SectorErase(uint32_t EraseStartAddress, uint32_t EraseEndAddress)
{
    OSPI_RegularCmdTypeDef sCommand;
    uint32_t SectorAddr;
    uint8_t status_before = 0;
    uint8_t status_after = 0;

    g_erase_input_start = EraseStartAddress;
    g_erase_input_end = EraseEndAddress;
    g_erase_call_count++;
    g_erase_loop_count = 0;

    g_last_erase_status = 0;
    g_debug_status1 = 0;
    g_debug_status2 = 0;

    if (EraseStartAddress >= 0x90000000)
    {
        EraseStartAddress -= 0x90000000;
    }
    if (EraseEndAddress >= 0x90000000)
    {
        EraseEndAddress -= 0x90000000;
    }
    
    EraseStartAddress = EraseStartAddress - (EraseStartAddress % MEMORY_SECTOR_SIZE);
    
    while (EraseStartAddress < EraseEndAddress)
    {
        g_erase_loop_count++;
        SectorAddr = EraseStartAddress;
        g_erase_actual_addr = SectorAddr;

        OSPI_ReadBytes(SectorAddr, g_data_before_erase, 4);

        /* 1. Write Enable */
        memset(&sCommand, 0, sizeof(OSPI_RegularCmdTypeDef));

        sCommand.OperationType      = HAL_OSPI_OPTYPE_COMMON_CFG;
        sCommand.FlashId            = HAL_OSPI_FLASH_ID_1;
        sCommand.Instruction        = WRITE_ENABLE_CMD;
        sCommand.InstructionMode    = HAL_OSPI_INSTRUCTION_1_LINE;
        sCommand.InstructionSize    = HAL_OSPI_INSTRUCTION_8_BITS;
        sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
        sCommand.AddressMode        = HAL_OSPI_ADDRESS_NONE;
        sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
        sCommand.DataMode           = HAL_OSPI_DATA_NONE;
        sCommand.DummyCycles        = 0;
        sCommand.DQSMode            = HAL_OSPI_DQS_DISABLE;
        sCommand.SIOOMode           = HAL_OSPI_SIOO_INST_EVERY_CMD;

        if (HAL_OSPI_Command(&hospi1_local, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        {
            g_last_erase_status = 0x10;
            return 0;
        }

        HAL_Delay(1);

        if (OSPI_ReadStatusReg(&status_before) != HAL_OK)
        {
            g_last_erase_status = 0x11;
            return 0;
        }

        g_debug_status2 = status_before;

        if ((status_before & STATUS_REG_WEL_MASK) == 0)
        {
            g_last_erase_status = 0x12;
            return 0;
        }

        /* 2. 4KB Sector Erase Command (0x20) */
        memset(&sCommand, 0, sizeof(OSPI_RegularCmdTypeDef));

        sCommand.OperationType      = HAL_OSPI_OPTYPE_COMMON_CFG;
        sCommand.FlashId            = HAL_OSPI_FLASH_ID_1;
        sCommand.Instruction        = SECTOR_ERASE_4K_CMD;  /* ★ 0x20 ★ */
        sCommand.InstructionMode    = HAL_OSPI_INSTRUCTION_1_LINE;
        sCommand.InstructionSize    = HAL_OSPI_INSTRUCTION_8_BITS;
        sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
        sCommand.Address            = SectorAddr;
        sCommand.AddressMode        = HAL_OSPI_ADDRESS_1_LINE;
        sCommand.AddressSize        = HAL_OSPI_ADDRESS_24_BITS;
        sCommand.AddressDtrMode     = HAL_OSPI_ADDRESS_DTR_DISABLE;
        sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
        sCommand.DataMode           = HAL_OSPI_DATA_NONE;
        sCommand.DataDtrMode        = HAL_OSPI_DATA_DTR_DISABLE;
        sCommand.NbData             = 0;
        sCommand.DummyCycles        = 0;
        sCommand.DQSMode            = HAL_OSPI_DQS_DISABLE;
        sCommand.SIOOMode           = HAL_OSPI_SIOO_INST_EVERY_CMD;

        if (HAL_OSPI_Command(&hospi1_local, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
        {
            g_last_erase_status = 0x20;
            return 0;
        }

        HAL_Delay(1);
        OSPI_ReadStatusReg(&g_debug_status1);

        HAL_Delay(10);

        if (OSPI_ManualWaitReady(TIMEOUT_SECTOR_ERASE_4K) != HAL_OK)
        {
            g_last_erase_status = 0x30;
            return 0;
        }
        
        if (OSPI_ReadStatusReg(&status_after) != HAL_OK)
        {
            g_last_erase_status = 0x40;
            return 0;
        }

        if (status_after & STATUS_REG_WEL_MASK)
        {
            g_last_erase_status = 0x41;
        }

        OSPI_ReadSecurityReg(&g_security_reg);
        if (g_security_reg & SECURITY_REG_E_FAIL_MASK)
        {
            g_last_erase_status = 0x50;
            return 0;
        }

        OSPI_ReadBytes(SectorAddr, g_data_after_erase, 4);

        EraseStartAddress += MEMORY_SECTOR_SIZE;
    }
    
    return 1;
}

newbiee_0-1768298627329.pngnewbiee_1-1768298648499.png

 

If there is anything else you need besides the attached materials, please let me know.

Any guidance on potential issues or suggestions for debugging would be greatly appreciated!

1 ACCEPTED SOLUTION

Accepted Solutions
Kouthair
ST Employee

Hello newbiee,

When I compared your SectorErase function with another official external loader, I've noticed a difference in your loop condition.

Your code:

while (EraseStartAddress < EraseEndAddress)

 Official loader:

  while (EraseEndAddress>=EraseStartAddress)

The difference is the ' = ' in the official version, which ensures that the last sector is erased.

In order 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.

View solution in original post

3 REPLIES 3
Kouthair
ST Employee

Hello newbiee,

When I compared your SectorErase function with another official external loader, I've noticed a difference in your loop condition.

Your code:

while (EraseStartAddress < EraseEndAddress)

 Official loader:

  while (EraseEndAddress>=EraseStartAddress)

The difference is the ' = ' in the official version, which ensures that the last sector is erased.

In order 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.
Kouthair
ST Employee

Hello again!

Any updates on this matter?

In order 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.

My work was busy, so I was late checking this. I'm truly sorry. Even while handling other tasks, I made some gradual modifications to the sector erase section code. I believe I had also applied the code you mentioned, “while (EraseEndAddress >= EraseStartAddress)”. However, at that time, the sector erase didn't work. I might be getting confused. After applying the code you suggested, it's working normally!! Thank you so much!!! :thumbs_up: