---
title: "Ethernet based bootloader on w55rp20 using pico sdk"
url: "https://maker.wiznet.io/forum/15922"
markdown_url: "https://maker.wiznet.io/forum/15922/md"
type: "Forum topic"
category: "MCU & Boards"
author: "UrviShah"
created: "2025-10-30T19:18:10+09:00"
last_activity: "2025-10-31T14:57:31+09:00"
language: "en"
views: 7
replies: 1
source: "WIZnet Makers forum (https://maker.wiznet.io/forum/)"
---

# Ethernet based bootloader on w55rp20 using pico sdk

## Question

Asked by UrviShah on 2025-10-30 in MCU & Boards.

Hello Everyone,

I am using w55RP20-EVB_Pico for my application. I am trying to implement ethernet based bootloader using pico-sdk. I have written one app where LED blinks at 3 seconds and bootloader main file that check for any update, if not then it jumps to main led blink program.
Now in my case, after flashing boot.uf2 via BOOTSEL method, then flashing app.uf2 i am not getting any printf outputs that i printed in my program (it even not generating any usb com port). But if I only flash boot.uf2 then it shows me printf outputs on usb com port that it switches to main app but then gets hang.

Please check my files and suggest me if there are any problems.

```plaintext
app.c:
#include "pico/stdlib.h"
#include <stdio.h>
#include "hardware/regs/m0plus.h"
#include "hardware/structs/scb.h"

#define LED_PIN 25
int main() {
stdio_init_all();
sleep_ms(2000);
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
printf("\n--- W55RP20 Application (LED Blink) ---\n");
while (1) {
gpio_put(LED_PIN, 1);
printf("LED ON\n");
sleep_ms(500);
gpio_put(LED_PIN, 0);
printf("LED OFF\n");
sleep_ms(500);
}
}
```

```plaintext
App CMakeList.txt
add_executable(led_app
    App.c
    )
    
    target_link_libraries(led_app pico_stdlib)
pico_enable_stdio_usb(led_app 1)
pico_enable_stdio_uart(led_app 0)

# Use custom linker script
pico_set_linker_script(led_app ${CMAKE_CURRENT_LIST_DIR}/app_linker.ld)

pico_add_extra_outputs(led_app)
```

```plaintext
app_linker.ld
/* Application linker script for W55RP20 app slot (at 0x10100000) */
/*
MEMORY
{
   FLASH(rx) : ORIGIN = 0x10100000, LENGTH = 0x100000  
   RAM(rwx)  : ORIGIN = 0x20000000, LENGTH = 256K
}
*/

MEMORY
{
   FLASH(rx) : ORIGIN = 0x10004000, LENGTH = 0x200000 - 0x4000  /* 2MB flash total, minus bootloader size */
   RAM(rwx)  : ORIGIN = 0x20000000, LENGTH = 256K
}

ENTRY(_entry_point)
/* Stack configuration */
__StackTop    = ORIGIN(RAM) + LENGTH(RAM);
__StackBottom = __StackTop - 0x1000;
PROVIDE(__StackTop = __StackTop);
PROVIDE(__StackBottom = __StackBottom);

SECTIONS
{
   /* Start from beginning of app slot */
   . = ORIGIN(FLASH);
      /* ---------------- Interrupt vectors ---------------- */
   .vectors :
   {
       KEEP(*(.vectors))
   } > FLASH
   
      /* ---------------- Reset handler ---------------- */
   .reset :
   {
       KEEP(*(.reset))
   } > FLASH
   
      /* ---------------- Code and constants ---------------- */
   .text :
   {
       *(.text*)
       *(.rodata*)
       KEEP(*(.init))
       KEEP(*(.fini))
   } > FLASH
   
      /* ---------------- C++ constructors/destructors ---------------- */
   .preinit_array :
   {
       . = ALIGN(4);
       __preinit_array_start = .;
       KEEP(*(.preinit_array))
       __preinit_array_end = .;
   } > FLASH
   
      .init_array :
   {
       . = ALIGN(4);
       __init_array_start = .;
       KEEP(*(SORT(.init_array.*)))
       KEEP(*(.init_array))
       __init_array_end = .;
   } > FLASH
   
      .fini_array :
   {
       . = ALIGN(4);
       __fini_array_start = .;
       KEEP(*(SORT(.fini_array.*)))
       KEEP(*(.fini_array))
       __fini_array_end = .;
   } > FLASH
   
      /* ---------------- Binary Info Section ---------------- */
   .binary_info :
   {
       . = ALIGN(4);
       __binary_info_start = .;
       KEEP(*(.binary_info*))
       __binary_info_end = .;
       . = ALIGN(4);
       __etext = .;
       __flash_binary_end = __etext;
   } > FLASH
   
      /* ---------------- Initialized data ---------------- */
   .data : AT(__etext)
   {
       . = ALIGN(4);
       __data_start__ = .;
       *(.data*)
       . = ALIGN(4);
       __data_end__ = .;
   } > RAM
   
      /* ---------------- Uninitialized data ---------------- */
   .bss (NOLOAD) :
   {
       . = ALIGN(4);
       __bss_start__ = .;
       *(.bss*)
       *(COMMON)
       . = ALIGN(4);
       __bss_end__ = .;
   } > RAM
   
      /* ---------------- Scratch / SDK helpers ---------------- */
   __scratch_x_source__ = __etext;
   __scratch_x_start__  = .;
   __scratch_x_end__    = .;
   __scratch_y_source__ = __etext;
   __scratch_y_start__  = .;
   __scratch_y_end__    = .;
}
```

```plaintext
boot.c
#include "pico/stdlib.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "wizchip_port.h"
#include <stdio.h>

#define APP_START_ADDR  0x10100000
typedef void (*app_entry_t)(void);
 
 void jump_to_app(void) {
    uint32_t app_stack = *(uint32_t *)(APP_START_ADDR + 0);
    uint32_t app_reset = *(uint32_t *)(APP_START_ADDR + 4);
    printf("Jumping to app: SP=0x%08X  Reset=0x%08X\n", (unsigned)app_stack, (unsigned)app_reset);
    
    // Disable interrupts
    __asm volatile ("cpsid i" : : : "memory");
    
    // Try to gracefully disconnect USB (if bootloader used TinyUSB)
    // if (tud_enabled()) tud_disconnect();
    // small delay
    for (volatile int i=0;i<100000;i++);
    
    // Set VTOR to application vector table
    *((volatile uint32_t *)0xE000ED08u) = APP_START_ADDR;
    __asm volatile ("dsb 0xF":::"memory");
    __asm volatile ("isb 0xF":::"memory");
    
    // Set MSP
   __asm volatile ("msr msp, %0" :: "r" (app_stack) : );
   
   // Jump to reset handler
   app_entry_t entry = (app_entry_t)app_reset;
   entry();
   
   // never returns
   for (;;) __wfi();
   }
   
   int main(void) {
   stdio_init_all();
   sleep_ms(2000);
   printf("\n--- W55RP20 Ethernet Bootloader ---\n");
   
   if (wizchip_init() != 0) {
         printf("Ethernet init failed!\n");
         while (1);
   }
   
   printf("Ethernet ready. IP: 192.168.11.2\n");
   printf("Listening for firmware update...\n");
   uint32_t timeout = 0;
   while (timeout < 5000) { // Wait ~5 seconds for update request
   wizchip_poll_for_update();
   sleep_ms(100);
   timeout += 100;
   }
   
   printf("No update request, jumping to app.\n");
   jump_to_app();
   
   while (1) { tight_loop_contents(); }
   }
```

```plaintext
boot CMakeLists.txt

add_executable(led_bootloader
    boot.c
    wizchip_port.c
)

# Use a custom linker script for the bootloader at flash address 0x10000000
# pico_set_linker_script(led_bootloader ${CMAKE_CURRENT_LIST_DIR}/bootloader.ld)

target_link_libraries(led_bootloader pico_stdlib hardware_flash)

pico_enable_stdio_usb(led_bootloader 1)
pico_enable_stdio_uart(led_bootloader 0)

pico_add_extra_outputs(led_bootloader)

# Bootloader memory map (default, starts at 0x10000000)
```

```plaintext
wizchip_port.c:


#include <stdio.h>
#include "wizchip_port.h"
#include "pico/stdlib.h"

int wizchip_init(void) {
    // Stub initialization (you’ll add real WIZchip init here)
    printf("Initializing WIZchip...\n");
    sleep_ms(500);
    return 0; // success
}

void wizchip_poll_for_update(void) {
    // Stub polling logic — future version will handle TCP/UDP update commands
}
```

```plaintext
wizchip_port.h:

#ifndef _WIZCHIP_PORT_H_
#define _WIZCHIP_PORT_H_

int wizchip_init(void);
void wizchip_poll_for_update(void);

#endif
```

```plaintext
printf outputs i am getting:

.--- W55RP20 Ethernet Bootloader --- .Initializing WIZchip... .Ethernet ready. IP: 192.168.11.2 .Listening for firmware update... .No update request, jumping to app. .Jumping to app: SP=0x20040000 Reset=0x101000E3
```

## Replies

### Reply 1 by Lihan__, 2025-10-31

The information you provided is too limited.

I roughly understand what kind of bootloader and firmware you intend to build.
However, based on the code you’ve shared so far, it seems that many essential parts are missing.

From what I can tell, you’ve just started developing the bootloader and firmware,
and are currently testing a simple architecture where the bootloader jumps to the application.

If that’s correct,

1. The problem you’re facing now seems to be that the program jumps to the wrong address during the jump process.

2. There’s one part I don’t quite understand.
   You mentioned, “in my case, after flashing boot.uf2 via BOOTSEL method, then flashing app.uf2.”
   But if you flash two UF2 files this way, the second upload will overwrite and erase the previously flashed UF2 file.

Did I understand correctly?

---

Source: https://maker.wiznet.io/forum/15922
