Skip to content
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

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

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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",
]
)
92 changes: 92 additions & 0 deletions src/rp2_common/pico_cyw43_driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -158,5 +158,97 @@ 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()

function(pico_use_wifi_firmware_partition TARGET)
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)
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()

find_package (Python3 REQUIRED COMPONENTS Interpreter)

# Wifi firmware blob
add_custom_target(${TARGET}_firmware_blob DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/firmware_wb_blob.S)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/firmware_wb_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_wb_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
WB_FIRMWARE=1
)
target_compile_definitions(${TARGET}_firmware_tbyb PRIVATE
NO_PICO_PLATFORM=1
PICO_CRT0_IMAGE_TYPE_TBYB=1
WB_FIRMWARE=1
)

target_link_options(${TARGET}_firmware PRIVATE -nostartfiles -nodefaultlibs -Ttext=0x20000000)
target_link_options(${TARGET}_firmware_tbyb PRIVATE -nostartfiles -nodefaultlibs -Ttext=0x20000000)

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

if (PICO_RISCV)
# Use pre-generated firmware elfs on Risc-V, as the compiler has issues with the assembly
add_custom_command(TARGET ${TARGET}_firmware POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${PICO_CYW43_DRIVER_CURRENT_PATH}/firmware.elf $<TARGET_FILE:${TARGET}_firmware>
)
add_custom_command(TARGET ${TARGET}_firmware_tbyb POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${PICO_CYW43_DRIVER_CURRENT_PATH}/firmware_tbyb.elf $<TARGET_FILE:${TARGET}_firmware_tbyb>
)
endif()

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 data)
pico_set_uf2_family(${TARGET}_firmware_tbyb data)

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

if (PICO_RISCV)
# As Arm ELFs are being used for firmware, the bin & dis output generation will fail
# with a Risc-V toolchain
picotool_postprocess_binary(${TARGET}_firmware)
picotool_postprocess_binary(${TARGET}_firmware_tbyb)
if (NOT (PICO_NO_UF2 OR PICO_NO_PICOTOOL))
pico_add_uf2_output(${TARGET}_firmware)
pico_add_uf2_output(${TARGET}_firmware_tbyb)
endif()
else()
pico_add_extra_outputs(${TARGET}_firmware)
pico_add_extra_outputs(${TARGET}_firmware_tbyb)
endif()

add_dependencies(${TARGET}
${TARGET}_firmware ${TARGET}_firmware_tbyb)
endfunction()
endif()
106 changes: 98 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,83 @@ 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);
#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 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 +202,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
38 changes: 38 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,38 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
#
# SPDX-License-Identifier: BSD-3-Clause

import sys

with open(sys.argv[1], "r") as f:
data = f.read()
lines = data.split(";")
for line in lines[1].split("\n"):
if "#define CYW43_WIFI_FW_LEN" in line:
cyw43_wifi_fw_len = int(line.split(")")[0].split("(")[-1])
if "#define CYW43_CLM_LEN" in line:
cyw43_clm_len = int(line.split(")")[0].split("(")[-1])
data = lines[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)
print(f"Start {bits[4]}, end {bits[-1]}, num {len(bits)}")
print(bits[:10])

print(f"Wifi {cyw43_wifi_fw_len}, clm {cyw43_clm_len}")

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")
Binary file added src/rp2_common/pico_cyw43_driver/firmware.elf
Binary file not shown.
Binary file not shown.
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"
7 changes: 7 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,13 @@
#include "cyw43_configport.h"
#endif

#if CYW43_USE_FIRMWARE_PARTITION
// PICO_CONFIG: CYW43_FIRMWARE_PARTITION_ID, ID of Wi-Fi firmware partition, type=int, default=0x776966696669726d (wififirm), group=pico_cyw43_driver
#ifndef CYW43_FIRMWARE_PARTITION_ID
#define CYW43_FIRMWARE_PARTITION_ID 0x776966696669726d // wififirm
#endif
#endif

#ifdef __cplusplus
extern "C" {
#endif
Expand Down
69 changes: 69 additions & 0 deletions src/rp2_common/pico_cyw43_driver/wifi_firmware.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "boot/picobin.h"

#if PICO_CRT0_IMAGE_TYPE_TBYB
#define CRT0_TBYB_FLAG PICOBIN_IMAGE_TYPE_EXE_TBYB_BITS
#else
#define CRT0_TBYB_FLAG 0
#endif

.section .text
.global _start
_start:
.word 0
.word 0
.word 0
.word 0

.p2align 2
embedded_block:
.word PICOBIN_BLOCK_MARKER_START

.byte PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE
.byte 0x1 // 1 word
.hword PICOBIN_IMAGE_TYPE_IMAGE_TYPE_AS_BITS(EXE) | \
PICOBIN_IMAGE_TYPE_EXE_CPU_AS_BITS(RISCV) | \
PICOBIN_IMAGE_TYPE_EXE_CHIP_AS_BITS(RP2350) | \
CRT0_TBYB_FLAG

// Entry point into SRAM
.byte PICOBIN_BLOCK_ITEM_1BS_ENTRY_POINT
.byte 0x3 // word size to next item
.byte 0 // pad
.byte 0 // pad
.word _start
.word 0x20082000 // stack pointer

_lm_item:
.byte PICOBIN_BLOCK_ITEM_LOAD_MAP
.byte 7
.byte 0 // pad
.byte 2 // 2 entries
// To sign the firmware
.word (_start - _lm_item)
.word _start
.word (firmware_end - _start)
// But clear SRAM if actually running this, so it doesn't boot
.word 0
.word _start
.word 0x00082000

.byte PICOBIN_BLOCK_ITEM_2BS_LAST
.hword (embedded_block_end - embedded_block - 16 ) / 4 // total size of all
.byte 0
.word 0
.word PICOBIN_BLOCK_MARKER_END
embedded_block_end:

#if WB_FIRMWARE
#include "firmware_wb_blob.S"
#else
#include "firmware_w_blob.S"
#endif

firmware_end:
Loading