Skip to content

Add Wi-Fi firmware partition support for Pico 2 W #1969

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
800cb52
Add partitions for Wi-Fi firmware
will-v-pi Aug 16, 2024
462148f
Use common scope var for PICO_CYW43_DRIVER_CURRENT_PATH, and remove w…
will-v-pi Oct 8, 2024
2bb0f11
Use FLASH_SECTOR_SIZE instead of 4096
will-v-pi Oct 15, 2024
75d119e
Throw FATAL_ERROR when using RP2040
will-v-pi Nov 19, 2024
2cb5d14
Add Bazel todo referencing #2055
will-v-pi Nov 19, 2024
d690e3e
Change partition ID to 0x776966696669726d (wififirm) and use data UF2…
will-v-pi Nov 20, 2024
78f1b16
Review feedback
will-v-pi Nov 22, 2024
4394654
Rename CYW43_USE_PARTITION_FIRMWARE->CYW43_USE_FIRMWARE_PARTITION and…
will-v-pi Nov 22, 2024
c1bdf1b
Use hard copies of the firmware for Risc-V builds
will-v-pi Nov 22, 2024
caf5492
Requires increased bootrom stack size on Risc-V
will-v-pi Nov 22, 2024
918408a
pico_use_partition_firmware -> pico_use_wifi_firmware_partition
will-v-pi Nov 22, 2024
0fa63dd
Only embed partition table if one isn't already embedded
will-v-pi Apr 7, 2025
37ee6fa
Free extra Risc-V stack after use
will-v-pi Jun 3, 2025
f20b302
Fix Risc-V firmware build
will-v-pi Jun 5, 2025
643a925
Add error message when no firmware partition is found
will-v-pi Jun 5, 2025
dff70a9
Increase partition size for latest firmware, and add docs
will-v-pi Jun 5, 2025
1802cb9
Add CYW43_FIRMWARE_FAMILY_ID
will-v-pi Jun 19, 2025
d68e3ed
Fix family in wifi_pt.json
will-v-pi Jun 20, 2025
79ff41a
Tidy up UF2 defines a bit
will-v-pi Jun 26, 2025
116d898
Other tidyups
will-v-pi Jun 26, 2025
f258e01
Apply suggestions from code review
will-v-pi Jul 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/common/boot_uf2_headers/include/boot/uf2.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@
#define UF2_FLAG_MD5_PRESENT 0x00004000u
#define UF2_FLAG_EXTENSION_FLAGS_PRESENT 0x00008000u

// Extra family IDs
#define CYW43_FIRMWARE_FAMILY_ID 0xe48bff55u

// Bootrom supported family IDs
#define RP2040_FAMILY_ID 0xe48bff56u
#define ABSOLUTE_FAMILY_ID 0xe48bff57u
#define DATA_FAMILY_ID 0xe48bff58u
#define RP2350_ARM_S_FAMILY_ID 0xe48bff59u
#define RP2350_RISCV_FAMILY_ID 0xe48bff5au
#define RP2350_ARM_NS_FAMILY_ID 0xe48bff5bu
#define FAMILY_ID_MAX 0xe48bff5bu
#define DEFAULT_FAMILY_ID_MIN RP2040_FAMILY_ID
#define FAMILY_ID_MAX RP2350_ARM_NS_FAMILY_ID

// 04 e3 57 99
#define UF2_EXTENSION_RP2_IGNORE_BLOCK 0x9957e304
Expand Down
10 changes: 10 additions & 0 deletions src/rp2_common/pico_cyw43_driver/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,13 @@ pico_generate_pio_header(
name = "cyw43_bus_pio",
srcs = ["cyw43_bus_pio_spi.pio"],
)

# TODO: https://github.com/raspberrypi/pico-sdk/issues/2055 Support storing
# Wi-Fi firmware in a separate partition
filegroup(
name = "pico_use_partition_firmware",
srcs = [
"wifi_firmware.S",
"include/cyw43_partition_firmware.h",
]
)
84 changes: 84 additions & 0 deletions src/rp2_common/pico_cyw43_driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,89 @@ if (EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE})
)
endfunction()

set(PICO_CYW43_DRIVER_CURRENT_PATH ${CMAKE_CURRENT_LIST_DIR})
pico_register_common_scope_var(PICO_CYW43_DRIVER_CURRENT_PATH)

pico_promote_common_scope_vars()

# pico_use_wifi_firmware_partition(TARGET [NO_EMBEDDED_PT])
# \brief\ Use a partition for the Wi-Fi firmware
#
# This will read the CYW43 firmware from a partition with the ID 0x776966696669726d,
# instead of embedding the firmware blob in the binary. By default it will also embed
# a compatible partition table in the binary, but this can be disabled by passing the
# NO_EMBEDDED_PT argument (for example, if you need to chain into the binary, it
# can't contain a partition table).
#
# \param\ NO_EMBEDDED_PT If set, will not embed a partition table in the binary
function(pico_use_wifi_firmware_partition TARGET)
set(options NO_EMBEDDED_PT)
cmake_parse_arguments(PARSE_ARGV 1 OPTS "${options}" "" "")
if (PICO_PLATFORM STREQUAL "rp2040")
message(FATAL_ERROR "RP2040 does not support storing wi-fi firmware in partitions")
endif()
target_compile_definitions(${TARGET} PRIVATE CYW43_USE_FIRMWARE_PARTITION=1)

if (NOT OPTS_NO_EMBEDDED_PT)
get_target_property(picotool_embed_pt ${TARGET} PICOTOOL_EMBED_PT)
if (NOT picotool_embed_pt)
pico_embed_pt_in_binary(${TARGET} ${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_pt.json)
endif()
endif()

find_package (Python3 REQUIRED COMPONENTS Interpreter)

# CYW43 firmware blob
add_custom_target(${TARGET}_firmware_blob DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/firmware_blob.S)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/firmware_blob.S
COMMAND ${Python3_EXECUTABLE} ${PICO_CYW43_DRIVER_CURRENT_PATH}/cyw43_firmware.py ${PICO_CYW43_DRIVER_PATH}/firmware/wb43439A0_7_95_49_00_combined.h ${CMAKE_CURRENT_BINARY_DIR}/firmware_blob.S
)

# Create UF2s for regular and TBYB firmwares
add_executable(${TARGET}_firmware
${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S)
add_executable(${TARGET}_firmware_tbyb
${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S)

add_dependencies(${TARGET}_firmware ${TARGET}_firmware_blob)
add_dependencies(${TARGET}_firmware_tbyb ${TARGET}_firmware_blob)

target_include_directories(${TARGET}_firmware PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(${TARGET}_firmware_tbyb PRIVATE ${CMAKE_CURRENT_BINARY_DIR})

target_compile_definitions(${TARGET}_firmware PRIVATE
NO_PICO_PLATFORM=1
)
target_compile_definitions(${TARGET}_firmware_tbyb PRIVATE
NO_PICO_PLATFORM=1
PICO_CRT0_IMAGE_TYPE_TBYB=1
)

target_link_options(${TARGET}_firmware PRIVATE -nostartfiles -nodefaultlibs -N LINKER:--script=${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.ld)
target_link_options(${TARGET}_firmware_tbyb PRIVATE -nostartfiles -nodefaultlibs -N LINKER:--script=${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.ld)

target_link_libraries(${TARGET}_firmware boot_picobin_headers)
target_link_libraries(${TARGET}_firmware_tbyb boot_picobin_headers)

get_target_property(hasSigfile ${TARGET} PICOTOOL_SIGFILE)
if (hasSigfile)
pico_sign_binary(${TARGET}_firmware ${sigfile})
pico_sign_binary(${TARGET}_firmware_tbyb ${sigfile})
endif()

pico_hash_binary(${TARGET}_firmware)
pico_hash_binary(${TARGET}_firmware_tbyb)

pico_set_uf2_family(${TARGET}_firmware cyw43-firmware)
pico_set_uf2_family(${TARGET}_firmware_tbyb cyw43-firmware)

pico_package_uf2_output(${TARGET}_firmware 0x10000000)
pico_package_uf2_output(${TARGET}_firmware_tbyb 0x10000000)

pico_add_extra_outputs(${TARGET}_firmware)
pico_add_extra_outputs(${TARGET}_firmware_tbyb)

add_dependencies(${TARGET}
${TARGET}_firmware ${TARGET}_firmware_tbyb)
endfunction()
endif()
110 changes: 102 additions & 8 deletions src/rp2_common/pico_cyw43_driver/cyw43_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,18 @@
#define CYW43_SLEEP_CHECK_MS 50
#endif

static async_context_t *cyw43_async_context;
static async_context_t *cyw43_async_context = NULL;

#if CYW43_USE_FIRMWARE_PARTITION
#include "pico/bootrom.h"
#include "hardware/flash.h"
#include "boot/picobin.h"
#include <stdlib.h>

int32_t cyw43_wifi_fw_len;
int32_t cyw43_clm_len;
uintptr_t fw_data;
#endif

static void cyw43_sleep_timeout_reached(async_context_t *context, async_at_time_worker_t *worker);
static void cyw43_do_poll(async_context_t *context, async_when_pending_worker_t *worker);
Expand Down Expand Up @@ -104,6 +115,87 @@ static void cyw43_sleep_timeout_reached(async_context_t *context, __unused async
}

bool cyw43_driver_init(async_context_t *context) {
#if CYW43_USE_FIRMWARE_PARTITION
uint32_t buffer[(16 * 4) + 1] = {}; // maximum of 16 partitions, each with maximum of 4 words returned, plus 1
int ret = rom_get_partition_table_info(buffer, count_of(buffer), PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_PARTITION_ID);

assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_PARTITION_ID));

if (ret > 0) {
int i = 1;
int p = 0;
int picked_p = -1;
while (i < ret) {
i++;
uint32_t flags_and_permissions = buffer[i++];
bool has_id = (flags_and_permissions & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS);
if (has_id) {
uint64_t id = 0;
id |= buffer[i++];
id |= ((uint64_t)(buffer[i++]) << 32ull);
if (id == CYW43_FIRMWARE_PARTITION_ID) {
picked_p = p;
break;
}
}

p++;
}

if (picked_p >= 0) {
#ifdef __riscv
// Increased bootrom stack is required for this function
bootrom_stack_t stack = {
.base = malloc(0x400),
.size = 0x400
};
rom_set_bootrom_stack(&stack);
#endif
uint32_t* workarea = malloc(0x1000);
picked_p = rom_pick_ab_update_partition(workarea, 0x1000, picked_p);
free(workarea);
#ifdef __riscv
// Reset bootrom stack
rom_set_bootrom_stack(&stack);
free(stack.base);
#endif

if (picked_p < 0) {
if (picked_p == BOOTROM_ERROR_NOT_FOUND) {
CYW43_DEBUG("Chosen CYW43 firmware partition was not verified\n");
} else if (picked_p == BOOTROM_ERROR_NOT_PERMITTED) {
CYW43_DEBUG("Too many update boots going on at once\n");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what this error message means!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means there are multiple partitions being updated at once, so if you update the boot partition with a TBYB image and the firmware partition in the same boot you get this error.

I can't actually think of a scenario when this might happen, because you can only have one flash_update_base, but I left in the error handling for it in case it does (as I did get it to happen during my early testing, although the code has changed a lot since then)

}
return false;
}

CYW43_DEBUG("Chosen CYW43 firmware in partition %d\n", picked_p);
int ret = rom_get_partition_table_info(buffer, count_of(buffer), PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION | (picked_p << 24));
assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION));
assert(ret == 3);

uint32_t location_and_permissions = buffer[1];
uint32_t saddr = ((location_and_permissions >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) & 0x1fffu) * FLASH_SECTOR_SIZE;
uint32_t eaddr = (((location_and_permissions >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) & 0x1fffu) + 1) * FLASH_SECTOR_SIZE;
// Starts with metadata block
while(saddr < eaddr && *(uint32_t*)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr) != PICOBIN_BLOCK_MARKER_END) {
saddr += 4;
}
saddr += 4;
// Now onto data
cyw43_wifi_fw_len = *(uint32_t*)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr);
cyw43_clm_len = *(uint32_t*)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr + 4);
fw_data = XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr + 8;
} else {
CYW43_DEBUG("No CYW43 firmware partition found, so cannot get firmware from partition\n");
return false;
}
} else {
CYW43_DEBUG("No partition table, so cannot get firmware from partition - get_partition_table_info returned %d\n", ret);
return false;
}

#endif
cyw43_init(&cyw43_state);
cyw43_async_context = context;
// we need the IRQ to be on the same core as the context, because we need to be able to enable/disable the IRQ
Expand All @@ -114,13 +206,15 @@ bool cyw43_driver_init(async_context_t *context) {
}

void cyw43_driver_deinit(async_context_t *context) {
assert(context == cyw43_async_context);
async_context_remove_at_time_worker(context, &sleep_timeout_worker);
async_context_remove_when_pending_worker(context, &cyw43_poll_worker);
// the IRQ IS on the same core as the context, so must be de-initialized there
async_context_execute_sync(context, cyw43_irq_deinit, NULL);
cyw43_deinit(&cyw43_state);
cyw43_async_context = NULL;
if (cyw43_async_context != NULL) {
assert(context == cyw43_async_context);
async_context_remove_at_time_worker(context, &sleep_timeout_worker);
async_context_remove_when_pending_worker(context, &cyw43_poll_worker);
// the IRQ IS on the same core as the context, so must be de-initialized there
async_context_execute_sync(context, cyw43_irq_deinit, NULL);
cyw43_deinit(&cyw43_state);
cyw43_async_context = NULL;
}
}

// todo maybe add an #ifdef in cyw43_driver
Expand Down
44 changes: 44 additions & 0 deletions src/rp2_common/pico_cyw43_driver/cyw43_firmware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
#
# SPDX-License-Identifier: BSD-3-Clause

import sys
import re

assert len(sys.argv) == 3

cyw43_wifi_fw_len = -1
cyw43_clm_len = -1

with open(sys.argv[1], "r") as f:
data = f.read()
statements = data.split(";")
for line in statements[1].split("\n"):
if "#define CYW43_WIFI_FW_LEN" in line:
matches = re.search(r"#define\s+\S+\s+\((\S+)\)", line)
cyw43_wifi_fw_len = int(matches[1])
elif "#define CYW43_CLM_LEN" in line:
matches = re.search(r"#define\s+\S+\s+\((\S+)\)", line)
cyw43_clm_len = int(matches[1])
if cyw43_wifi_fw_len > 0 and cyw43_clm_len > 0:
break
data = statements[0]
bits = data.split(",")
bits[0] = bits[0].split("{")[-1]
bits[-1] = bits[-1].split("}")[0]
for i in range(len(bits)):
bits[i] = bits[i].strip()
bits[i] = bits[i].strip(',')
bits[i] = int(bits[i], base=0)

data = (
cyw43_wifi_fw_len.to_bytes(4, 'little', signed=True) +
cyw43_clm_len.to_bytes(4, 'little', signed=True) +
bytearray(bits)
)

with open(sys.argv[2], "w") as f:
for b in data:
f.write(f".byte 0x{b:02x}\n")
4 changes: 4 additions & 0 deletions src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,16 @@ extern "C" {
#endif

#ifndef CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE
#if CYW43_USE_FIRMWARE_PARTITION
#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "cyw43_partition_firmware.h"
#else
#if CYW43_ENABLE_BLUETOOTH
#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "wb43439A0_7_95_49_00_combined.h"
#else
#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "w43439A0_7_95_49_00_combined.h"
#endif
#endif
#endif

#ifndef CYW43_WIFI_NVRAM_INCLUDE_FILE
#define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

extern int cyw43_wifi_fw_len;
extern int cyw43_clm_len;

#define CYW43_WIFI_FW_LEN (cyw43_wifi_fw_len)
#define CYW43_CLM_LEN (cyw43_clm_len)
extern uintptr_t fw_data;

#include "boot/picobin.h"
#include "pico/bootrom.h"
8 changes: 8 additions & 0 deletions src/rp2_common/pico_cyw43_driver/include/pico/cyw43_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
#include "cyw43_configport.h"
#endif

#if CYW43_USE_FIRMWARE_PARTITION
// PICO_CONFIG: CYW43_FIRMWARE_PARTITION_ID, ID of Wi-Fi firmware partition which must match the ID used in the partition table JSON, type=int, default=0x776966696669726d, group=pico_cyw43_driver
#ifndef CYW43_FIRMWARE_PARTITION_ID
// The default 0x776966696669726d value is the ASCII encoding of "wififirm"
#define CYW43_FIRMWARE_PARTITION_ID 0x776966696669726d
#endif
#endif

#ifdef __cplusplus
extern "C" {
#endif
Expand Down
Loading
Loading