2025-10-10 6:05 AM
Hi all,
I’m working on a custom bootloader for STM32F7, which performs OTA updates using TCP/IP (Ethernet and/or WINC3400 WiFi). The bootloader writes the new firmware to flash, then attempts to jump to the application using the standard sequence: cleaning and disabling caches, setting VTOR, setting MSP, and jumping to the application's reset handler.
The Problem:
What I Have Tried:
Questions:
Any advice, references, or similar experiences would be greatly appreciated!
Solved! Go to Solution.
2025-10-30 9:47 AM
Hard to say, this is your design. My crystal ball say SPI from WIFI corrupt your data... Try close this ticket with jump over ethernet always work ? And create new ticket...
2025-10-10 9:42 AM
Show your code and/or follow existing examples of how to jump.
Don't jump from within an interrupt. Don't disable interrupts without enabling.
Small vs large program size is not the issue here. The jump mechanic is the same.
2025-10-10 10:10 AM
I mean issue isnt size or fw based, but bootloader . I preffer jump to app on first main lines based on some marker, then after update simply reset and no jump.
2025-10-10 8:56 PM
Hi @TDK and @MM..1 ,
Here is my current bootloader sequence for jumping to the application (after OTA update):
static void goto_application(void)
{
  printf("Boot from Application\r\n");
  /* Each vector position contains an address to the boot loader entry point */
      	// Disable all interrupts
   	__disable_irq();
   //	 Disable Systick timer
   	SysTick->CTRL = 0;
   	// Set the clock to the default state
   	HAL_RCC_DeInit();
   	// Clear Interrupt Enable Register & Interrupt Pending Register
   	for (i=0;i<5;i++)
   	{
   		NVIC->ICER[i]=0xFFFFFFFF;
   		NVIC->ICPR[i]=0xFFFFFFFF;
   	}
   	// Re-enable all interrupts
   	__enable_irq();
  void (*app_reset_handler)(void) = (void*)(*((volatile uint32_t*) (0x08100000 + 4U )));
  __set_MSP(*(volatile uint32_t*) 0x08100000);
  // Turn OFF the Green Led to tell the user that Bootloader is not running
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET );    //Green LED OFF
  /* Reset the Clock */
  HAL_RCC_DeInit();
  HAL_DeInit();
  SysTick->CTRL = 0;
  SysTick->LOAD = 0;
  SysTick->VAL = 0;
  SCB->VTOR = 0x08100000;
  /* Jump to application */
  app_reset_handler();    //call the app reset handler
}I have also verified that both the reset handler address and the stack pointer address are correct after both Ethernet and WiFi OTA updates—they point to valid locations in flash and RAM, respectively.
This sequence works perfectly with Ethernet/TCP OTA updates: the bootloader jumps and the app runs every time. But only for small-sized firmware; if it exceeds 256KB, it cannot jump.
However, when I use the same TCP update sequence over WiFi (with the WINC3400 module), the application does not start after the update, even though the firmware is written and the jump logic is the same.
If there’s anything missing or any subtle points I should handle (especially regarding peripheral state, EXTI/IRQ, or caches), please let me know.
Any advice or pointers would be appreciated—thank you!
2025-10-11 1:27 AM
Check if the code generated uses the stack pointer for variable access (look at the disassembly output). If the reset handler address is stored on the stack and then the MSP is reloaded, your code will not jump to the app entry point.
2025-10-11 7:44 AM
Are you jumping from within an interrupt?
Code seems okay. Although it would be better to de-initialize peripherals. Could be getting an unexpected interrupt when the application re-initializes them.
2025-10-12 7:09 AM
If your bootloader configures the MPU, make sure to disable or reset it before jumping to the application.
2025-10-29 3:22 AM
hi @Shirley.Ye ,
Yes, I have done this, but I am still unable to jump the application. I am not sure what the main reason is; below I am reuploading my jump sequence. In the current situation, the application runs only on the first two updates using WiFi TCP. After that, no other application runs; only the first application is run, which has a small size and was uploaded first. Why?
/// Define application start address (change if needed)
#define APP_START_ADDRESS   0x08040000U  // Application start address (sector 5)
typedef void (*pFunction)(void);
void goto_application(void)
{
    uint32_t appStack;
    pFunction appEntry;
    printf("Boot from Application firmware \r\n");
    printf("Jumping to app: SP=0x%08lx, PC=0x%08lx\r\n", *(uint32_t*)APP_START_ADDRESS, *(uint32_t*)(APP_START_ADDRESS+4));
    // 1. Disable WINC3400 IRQ line interrupt on STM32 MCU
        nm_bsp_interrupt_ctrl(0);  // 0 = disable interrupts for WINC3400 ISR line
        // 2. Deinitialize WINC3400 bus and GPIO pins
        nm_bus_deinit();      // Deinit SPI bus or related bus layer
        nm_bsp_deinit();      // Deinit GPIO pins, power down WINC3400
    /* === 1️⃣ Stop all interrupts === */
    __disable_irq();  // Disable global IRQs
    /* === 2️⃣ Deinitialize peripherals used in bootloader === */
    HAL_SPI_DeInit(&hspi1);              // Deinit SPI1
  //  HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7); // example SPI1 pins
  //  HAL_GPIO_DeInit(GPIOx_INT_PORT, GPIOx_INT_PIN); // your EXTI interrupt pin
    HAL_NVIC_DisableIRQ(EXTI1_IRQn);   // Disable EXTI for GPIO interrupt
    HAL_NVIC_DisableIRQ(SPI1_IRQn);      // Disable SPI1 IRQ
    HAL_NVIC_DisableIRQ(SysTick_IRQn);   // Disable SysTick interrupt
    HAL_DeInit();                        // Deinit HAL (SysTick, RCC, etc.)
    HAL_RCC_DeInit();                    // Reset clock config
    /* === 3️⃣ Clear all pending IRQs in NVIC === */
        for (uint8_t i = 0; i < 8; i++) {
            NVIC->ICER[i] = 0xFFFFFFFF;  // Disable all IRQs
            NVIC->ICPR[i] = 0xFFFFFFFF;  // Clear all pending
        }
    /* === 4️⃣ Read application’s stack pointer and reset vector === */
    appStack = *((__IO uint32_t*)APP_START_ADDRESS);
    appEntry = (pFunction)*((__IO uint32_t*)(APP_START_ADDRESS + 4));
    /* === 5️⃣ Remap vector table to application area === */
    SCB->VTOR = APP_START_ADDRESS;
	__enable_irq();
    /* === 6️⃣ Set new main stack pointer === */
    __set_MSP(appStack);
      /* === 7️⃣ Jump to application’s Reset_Handler === */
    appEntry();
    /* Should never return here */
    while (1);
}
This code use for the copy the firmware from sector to sector.
void OTA_Process_Main(void)
{
    if (!ota_done_flag) return;
    ota_done_flag = 0;
    printf("OTA_Process_Main: Erasing OTA scratch area (sector10..?)\r\n");
   // HAL_Delay(40);
     Erase scratch area (choose sufficient size, using OTA_MAX_SIZE)
    if (Erase_Flash_Area(FLASH_SECTOR_10_ADDR, OTA_SIZE) != 0) {
        printf("OTA_Process_Main: Erase OTA area failed\r\n");
        return;
    }
    HAL_Delay(30);
    printf("OTA_Process_Main: Writing %lu bytes to OTA scratch 0x%08lX\r\n", (unsigned long)ota_received_bytes, (unsigned long)FLASH_SECTOR_10_ADDR);
    if (ota_ram_buffer == NULL) {
        printf("OTA_Process_Main: No RAM buffer present!\r\n");
        return;
    }
    if (Flash_Write_Buffer(FLASH_SECTOR_10_ADDR, ota_ram_buffer, ota_received_bytes) != HAL_OK) {
        printf("OTA_Process_Main: Flash write to scratch failed\r\n");
        return;
    }
    uint32_t crc_scratch = Compute_CRC32_For_Flash(FLASH_SECTOR_10_ADDR, ota_received_bytes);
       printf("Scratchpad CRC = 0x%08lX\r\n", crc_scratch);
    HAL_Delay(50);
     //Optional: compute/verify CRC here against server-supplied CRC (not implemented)
    printf("OTA_Process_Main: Copying from scratch to application area 0x%08lX -> 0x%08lX size=%lu\r\n",
           (unsigned long)FLASH_SECTOR_10_ADDR, (unsigned long)FLASH_SECTOR_5_ADDR, (unsigned long)ota_received_bytes);
    if (Copy_Flash_Area(FLASH_SECTOR_10_ADDR, FLASH_SECTOR_5_ADDR, ota_received_bytes) == HAL_OK) {
        printf("OTA_Process_Main: Copy successful. Cleaning up and rebooting.\r\n");
        uint32_t crc_app = Compute_CRC32_For_Flash(FLASH_SECTOR_10_ADDR, ota_received_bytes);
              printf("Scratchpad CRC = 0x%08lX\r\n", crc_app);
        // Erase OTA scratch area after use to avoid leftover data
        if (Erase_Flash_Area(FLASH_SECTOR_10_ADDR, OTA_SIZE) != 0) {
            printf("OTA_Process_Main: Warning: erase scratch after copy failed\r\n");
        }
        if (ota_ram_buffer) {
            memset(ota_ram_buffer, 0xFF, OTA_MAX_SIZE);
            free(ota_ram_buffer);
            ota_ram_buffer = NULL;
            printf("OTA_Process_Main: freed OTA RAM buffer\r\n");
        }
        ota_received_bytes = 0;
        // Clean / invalidate caches before reset (Cortex-M7)
//#if (__CORTEX_M >= 7)
//        SCB_CleanInvalidateDCache();
//        SCB_InvalidateICache();
//#endif
        HAL_Delay(100);
        NVIC_SystemReset();
    } else {
        printf("OTA_Process_Main: Copy failed, not rebooting\r\n");
        // handle failure: maybe stay in bootloader, inform user, etc.
    }
}
*/
In the above, i also added how I use the code to save the firmware in the flash sector.
2025-10-29 8:55 AM
Primary start verify OTA after update with stlink method:
1. run tcp OTA to end no jump only mark ended for example LED light
2. connect stlink and verify HEX file over memory ...
After image is valid youtest jump simpler method. Is in your code used IWDG or WWDG ?
2025-10-29 10:16 PM
HI @MM..1 ,
Thanks for your suggestion.
I followed your steps for debugging, and here’s what I found:
I performed the full TCP OTA update (without jumping to the application) and then verified the internal flash using ST-Link as you suggested.
1. When I compared the memory region against the original .bin file, I found data mismatches — some bytes were different or missing.
2. Interestingly, when I use the Ethernet TCP/IP interface with the same code and the same firmware file, the data in flash matches perfectly -no mismatches at all.
3. This issue appears only when the firmware is received over Wi-Fi (using the WINC3400).
Could you please suggest what could cause this kind of behaviour?
#define CHUNK_SIZE                                        1024
#define APP_SECTORS_COUNT                                 2U
static uint8_t ota_buf[CHUNK_SIZE];
uint8_t *ota_ram_buffer = NULL;
/* ====== Socket callback ====== */
static void socket_cb(SOCKET sock, uint8_t u8Msg, void *pvMsg)
{
    switch (u8Msg)
    {
    case SOCKET_MSG_CONNECT:
        {
            tstrSocketConnectMsg *pstrConnect = (tstrSocketConnectMsg *)pvMsg;
            if (pstrConnect->s8Error == 0) {
                printf("✅ Connected, requesting firmware...\r\n");
                send(sock, "REQ_FW", strlen("REQ_FW"), 0);
                recv(sock, ota_buf, sizeof(ota_buf), 0);
            } else {
                printf("❌ Connection failed\r\n");
            }
            break;
        }
    case SOCKET_MSG_RECV:
        {
            tstrSocketRecvMsg *pstrRecv = (tstrSocketRecvMsg *)pvMsg;
            if (pstrRecv->s16BufferSize <= 0) {
                printf("Server closed connection\r\n");
                close(sock);
                return;
            }
            char *msg = (char *)pstrRecv->pu8Buffer;
            msg[pstrRecv->s16BufferSize] = '\0';
            /* --- Control messages --- */
            if (strncmp(msg, "FILE_SIZE:", 10) == 0) {
                 expected_Bytes = (uint32_t)atoi(msg + 10);
                printf("Firmware size: %lu bytes\r\n", (unsigned long)expected_Bytes);
                ota_received_bytes = 0;
            }
            else if (strncmp(msg, "OTA_UPDATE", 10) == 0) {
                printf("OTA update start\r\n");
                ota_in_progress = 1;
                ota_received_bytes = 0;
                // 1️⃣ Allocate buffer only now
                    ota_ram_buffer = (uint8_t *)malloc(OTA_MAX_SIZE);
                    if (ota_ram_buffer == NULL)
                    {
                        printf("❌ OTA buffer allocation failed!\r\n");
                        return;
                    }
                    printf(" OTA buffer allocated at %p\r\n", ota_ram_buffer);
                memset(ota_ram_buffer, 0, sizeof(ota_ram_buffer));  // clear RAM
                send(sock, "OTA_READY", strlen("OTA_READY"), 0);
            }
            else if (strncmp(msg, "OTA_DONE", 8) == 0) {
                printf("OTA done (%lu bytes)\r\n", (unsigned long)ota_received_bytes);
                ota_in_progress = 0;
              //  ota_done_flag = 1;   // 🔥 trigger main loop to write flash
               // send(sock, "ACK", 3, 0);
            }
            else if (ota_in_progress) {
                uint16_t len = pstrRecv->s16BufferSize;
                if ((ota_received_bytes + len) <= OTA_MAX_SIZE) {
                    memcpy(&ota_ram_buffer[ota_received_bytes], pstrRecv->pu8Buffer, len);
                    ota_received_bytes += len;
                    printf("Received %lu bytes\r\n", (unsigned long)ota_received_bytes);
                    // ✅ When full firmware is received
                    if (ota_received_bytes >= expected_Bytes) {
                        ota_in_progress = 0;
                        ota_done_flag = 1;   // mark completion for main loop
                        send(sock, "ACK", 3, 0);
                        close(sock);
                        printf("Full firmware received (%lu bytes)\r\n", (unsigned long)ota_received_bytes);
                    }
                }
                else {
                    printf("OTA buffer overflow\r\n");
                    ota_in_progress = 0;
                    send(sock, "NACK", 4, 0);
                }
            }
            recv(sock, ota_buf, sizeof(ota_buf), 0); // re-arm receive
            break;
        }
    default:
        break;
    }
}
void OTA_Process_Main(void)
{
    if (!ota_done_flag)
        return;
    ota_done_flag = 0;  // clear flag so we only run once
    printf(" Erasing OTA flash area...\r\n");
    Flash_Erase_OTA_Area();
    HAL_Delay(500);
    printf(" Writing firmware (%lu bytes)...\r\n", (unsigned long)ota_received_bytes);
    if (Flash_Write_Buffer(OTA_FLASH_START_ADDR, ota_ram_buffer, ota_received_bytes) != HAL_OK) {
        printf(" Flash write failed\r\n");
        return;
    }
    HAL_Delay(500);
    printf(" OTA firmware written successfully\r\n");
   // EraseSectors5and6();
   // HAL_Delay(500);
	if (Copy_Scrach_to_Application_Area(FLASH_SECTOR_10_ADDR,FLASH_SECTOR_5_ADDR, ota_received_bytes) == 0) {
			printf(" Firmware copied successfully, rebooting...\r\n");
			//close(sock);
			  HAL_Delay(500);
			 Flash_Erase_OTA_Area();
			 // 3️⃣ After use, free RAM
			  memset(ota_ram_buffer, 0, sizeof(ota_ram_buffer));  // clear RAM
			     free(ota_ram_buffer);
			     ota_ram_buffer = NULL;
			     printf(" OTA buffer released from RAM.\r\n");
			    ota_received_bytes = 0;
			NVIC_SystemReset();
		} else {
			printf(" Flash copy failed, rebooting anyway.\r\n");
			//close(sock);
			NVIC_SystemReset();
		}
    // Optional: verify CRC or add boot flag here
    printf(" Rebooting to no firmware...\r\n");
    HAL_Delay(100);
    NVIC_SystemReset();
}
