Skip to content

Commit 820a33a

Browse files
authored
Add Wi-Fi firmware partition support for Pico 2 W (#1969)
1 parent 8fd7c60 commit 820a33a

File tree

11 files changed

+407
-9
lines changed

11 files changed

+407
-9
lines changed

src/common/boot_uf2_headers/include/boot/uf2.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,21 @@
2626
#define UF2_FLAG_MD5_PRESENT 0x00004000u
2727
#define UF2_FLAG_EXTENSION_FLAGS_PRESENT 0x00008000u
2828

29+
// Extra family IDs
30+
#define CYW43_FIRMWARE_FAMILY_ID 0xe48bff55u
31+
32+
// Bootrom supported family IDs
2933
#define RP2040_FAMILY_ID 0xe48bff56u
3034
#define ABSOLUTE_FAMILY_ID 0xe48bff57u
3135
#define DATA_FAMILY_ID 0xe48bff58u
3236
#define RP2350_ARM_S_FAMILY_ID 0xe48bff59u
3337
#define RP2350_RISCV_FAMILY_ID 0xe48bff5au
3438
#define RP2350_ARM_NS_FAMILY_ID 0xe48bff5bu
35-
#define FAMILY_ID_MAX 0xe48bff5bu
39+
#define BOOTROM_FAMILY_ID_MIN RP2040_FAMILY_ID
40+
#define BOOTROM_FAMILY_ID_MAX RP2350_ARM_NS_FAMILY_ID
41+
42+
// Defined for backwards compatibility
43+
#define FAMILY_ID_MAX BOOTROM_FAMILY_ID_MAX
3644

3745
// 04 e3 57 99
3846
#define UF2_EXTENSION_RP2_IGNORE_BLOCK 0x9957e304

src/rp2_common/pico_cyw43_driver/BUILD.bazel

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,13 @@ pico_generate_pio_header(
8383
name = "cyw43_bus_pio",
8484
srcs = ["cyw43_bus_pio_spi.pio"],
8585
)
86+
87+
# TODO: https://github.com/raspberrypi/pico-sdk/issues/2055 Support storing
88+
# Wi-Fi firmware in a separate partition
89+
filegroup(
90+
name = "pico_use_partition_firmware",
91+
srcs = [
92+
"wifi_firmware.S",
93+
"include/cyw43_partition_firmware.h",
94+
]
95+
)

src/rp2_common/pico_cyw43_driver/CMakeLists.txt

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,5 +165,89 @@ if (EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE})
165165
)
166166
endfunction()
167167

168+
set(PICO_CYW43_DRIVER_CURRENT_PATH ${CMAKE_CURRENT_LIST_DIR})
169+
pico_register_common_scope_var(PICO_CYW43_DRIVER_CURRENT_PATH)
170+
168171
pico_promote_common_scope_vars()
172+
173+
# pico_use_wifi_firmware_partition(TARGET [NO_EMBEDDED_PT])
174+
# \brief\ Use a partition for the Wi-Fi firmware
175+
#
176+
# This will read the CYW43 firmware from a partition with the ID 0x776966696669726d,
177+
# instead of embedding the firmware blob in the binary. By default it will also embed
178+
# a compatible partition table in the binary, but this can be disabled by passing the
179+
# NO_EMBEDDED_PT argument (for example, if you need to chain into the binary, it
180+
# can't contain a partition table).
181+
#
182+
# \param\ NO_EMBEDDED_PT If set, will not embed a partition table in the binary
183+
function(pico_use_wifi_firmware_partition TARGET)
184+
set(options NO_EMBEDDED_PT)
185+
cmake_parse_arguments(PARSE_ARGV 1 OPTS "${options}" "" "")
186+
if (PICO_PLATFORM STREQUAL "rp2040")
187+
message(FATAL_ERROR "RP2040 does not support storing wi-fi firmware in partitions")
188+
endif()
189+
target_compile_definitions(${TARGET} PRIVATE CYW43_USE_FIRMWARE_PARTITION=1)
190+
191+
if (NOT OPTS_NO_EMBEDDED_PT)
192+
get_target_property(picotool_embed_pt ${TARGET} PICOTOOL_EMBED_PT)
193+
if (NOT picotool_embed_pt)
194+
pico_embed_pt_in_binary(${TARGET} ${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_pt.json)
195+
endif()
196+
endif()
197+
198+
find_package (Python3 REQUIRED COMPONENTS Interpreter)
199+
200+
# CYW43 firmware blob
201+
add_custom_target(${TARGET}_firmware_blob DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/firmware_blob.S)
202+
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/firmware_blob.S
203+
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
204+
)
205+
206+
# Create UF2s for regular and TBYB firmwares
207+
add_executable(${TARGET}_firmware
208+
${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S)
209+
add_executable(${TARGET}_firmware_tbyb
210+
${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.S)
211+
212+
add_dependencies(${TARGET}_firmware ${TARGET}_firmware_blob)
213+
add_dependencies(${TARGET}_firmware_tbyb ${TARGET}_firmware_blob)
214+
215+
target_include_directories(${TARGET}_firmware PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
216+
target_include_directories(${TARGET}_firmware_tbyb PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
217+
218+
target_compile_definitions(${TARGET}_firmware PRIVATE
219+
NO_PICO_PLATFORM=1
220+
)
221+
target_compile_definitions(${TARGET}_firmware_tbyb PRIVATE
222+
NO_PICO_PLATFORM=1
223+
PICO_CRT0_IMAGE_TYPE_TBYB=1
224+
)
225+
226+
target_link_options(${TARGET}_firmware PRIVATE -nostartfiles -nodefaultlibs LINKER:--script=${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.ld)
227+
target_link_options(${TARGET}_firmware_tbyb PRIVATE -nostartfiles -nodefaultlibs LINKER:--script=${PICO_CYW43_DRIVER_CURRENT_PATH}/wifi_firmware.ld)
228+
229+
target_link_libraries(${TARGET}_firmware boot_picobin_headers)
230+
target_link_libraries(${TARGET}_firmware_tbyb boot_picobin_headers)
231+
232+
get_target_property(hasSigfile ${TARGET} PICOTOOL_SIGFILE)
233+
if (hasSigfile)
234+
pico_sign_binary(${TARGET}_firmware ${sigfile})
235+
pico_sign_binary(${TARGET}_firmware_tbyb ${sigfile})
236+
endif()
237+
238+
pico_hash_binary(${TARGET}_firmware)
239+
pico_hash_binary(${TARGET}_firmware_tbyb)
240+
241+
pico_set_uf2_family(${TARGET}_firmware cyw43-firmware)
242+
pico_set_uf2_family(${TARGET}_firmware_tbyb cyw43-firmware)
243+
244+
pico_package_uf2_output(${TARGET}_firmware 0x10000000)
245+
pico_package_uf2_output(${TARGET}_firmware_tbyb 0x10000000)
246+
247+
pico_add_extra_outputs(${TARGET}_firmware)
248+
pico_add_extra_outputs(${TARGET}_firmware_tbyb)
249+
250+
add_dependencies(${TARGET}
251+
${TARGET}_firmware ${TARGET}_firmware_tbyb)
252+
endfunction()
169253
endif()

src/rp2_common/pico_cyw43_driver/cyw43_driver.c

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,18 @@
1919
#define CYW43_SLEEP_CHECK_MS 50
2020
#endif
2121

22-
static async_context_t *cyw43_async_context;
22+
static async_context_t *cyw43_async_context = NULL;
23+
24+
#if CYW43_USE_FIRMWARE_PARTITION
25+
#include "pico/bootrom.h"
26+
#include "hardware/flash.h"
27+
#include "boot/picobin.h"
28+
#include <stdlib.h>
29+
30+
int32_t cyw43_wifi_fw_len;
31+
int32_t cyw43_clm_len;
32+
uintptr_t fw_data;
33+
#endif
2334

2435
static void cyw43_sleep_timeout_reached(async_context_t *context, async_at_time_worker_t *worker);
2536
static void cyw43_do_poll(async_context_t *context, async_when_pending_worker_t *worker);
@@ -104,6 +115,87 @@ static void cyw43_sleep_timeout_reached(async_context_t *context, __unused async
104115
}
105116

106117
bool cyw43_driver_init(async_context_t *context) {
118+
#if CYW43_USE_FIRMWARE_PARTITION
119+
uint32_t buffer[(16 * 4) + 1] = {}; // maximum of 16 partitions, each with maximum of 4 words returned, plus 1
120+
int ret = rom_get_partition_table_info(buffer, count_of(buffer), PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_PARTITION_ID);
121+
122+
assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_PARTITION_ID));
123+
124+
if (ret > 0) {
125+
int i = 1;
126+
int p = 0;
127+
int picked_p = -1;
128+
while (i < ret) {
129+
i++;
130+
uint32_t flags_and_permissions = buffer[i++];
131+
bool has_id = (flags_and_permissions & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS);
132+
if (has_id) {
133+
uint64_t id = 0;
134+
id |= buffer[i++];
135+
id |= ((uint64_t)(buffer[i++]) << 32ull);
136+
if (id == CYW43_FIRMWARE_PARTITION_ID) {
137+
picked_p = p;
138+
break;
139+
}
140+
}
141+
142+
p++;
143+
}
144+
145+
if (picked_p >= 0) {
146+
#ifdef __riscv
147+
// Increased bootrom stack is required for this function
148+
bootrom_stack_t stack = {
149+
.base = malloc(0x400),
150+
.size = 0x400
151+
};
152+
rom_set_bootrom_stack(&stack);
153+
#endif
154+
uint32_t* workarea = malloc(0x1000);
155+
picked_p = rom_pick_ab_update_partition(workarea, 0x1000, picked_p);
156+
free(workarea);
157+
#ifdef __riscv
158+
// Reset bootrom stack
159+
rom_set_bootrom_stack(&stack);
160+
free(stack.base);
161+
#endif
162+
163+
if (picked_p < 0) {
164+
if (picked_p == BOOTROM_ERROR_NOT_FOUND) {
165+
CYW43_DEBUG("Chosen CYW43 firmware partition was not verified\n");
166+
} else if (picked_p == BOOTROM_ERROR_NOT_PERMITTED) {
167+
CYW43_DEBUG("Too many update boots going on at once\n");
168+
}
169+
return false;
170+
}
171+
172+
CYW43_DEBUG("Chosen CYW43 firmware in partition %d\n", picked_p);
173+
int ret = rom_get_partition_table_info(buffer, count_of(buffer), PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION | (picked_p << 24));
174+
assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION));
175+
assert(ret == 3);
176+
177+
uint32_t location_and_permissions = buffer[1];
178+
uint32_t saddr = ((location_and_permissions >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) & 0x1fffu) * FLASH_SECTOR_SIZE;
179+
uint32_t eaddr = (((location_and_permissions >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) & 0x1fffu) + 1) * FLASH_SECTOR_SIZE;
180+
// Starts with metadata block
181+
while(saddr < eaddr && *(uint32_t*)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr) != PICOBIN_BLOCK_MARKER_END) {
182+
saddr += 4;
183+
}
184+
saddr += 4;
185+
// Now onto data
186+
cyw43_wifi_fw_len = *(uint32_t*)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr);
187+
cyw43_clm_len = *(uint32_t*)(XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr + 4);
188+
fw_data = XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE + saddr + 8;
189+
} else {
190+
CYW43_DEBUG("No CYW43 firmware partition found, so cannot get firmware from partition\n");
191+
return false;
192+
}
193+
} else {
194+
CYW43_DEBUG("No partition table, so cannot get firmware from partition - get_partition_table_info returned %d\n", ret);
195+
return false;
196+
}
197+
198+
#endif
107199
cyw43_init(&cyw43_state);
108200
cyw43_async_context = context;
109201
// we need the IRQ to be on the same core as the context, because we need to be able to enable/disable the IRQ
@@ -114,13 +206,15 @@ bool cyw43_driver_init(async_context_t *context) {
114206
}
115207

116208
void cyw43_driver_deinit(async_context_t *context) {
117-
assert(context == cyw43_async_context);
118-
async_context_remove_at_time_worker(context, &sleep_timeout_worker);
119-
async_context_remove_when_pending_worker(context, &cyw43_poll_worker);
120-
// the IRQ IS on the same core as the context, so must be de-initialized there
121-
async_context_execute_sync(context, cyw43_irq_deinit, NULL);
122-
cyw43_deinit(&cyw43_state);
123-
cyw43_async_context = NULL;
209+
if (cyw43_async_context != NULL) {
210+
assert(context == cyw43_async_context);
211+
async_context_remove_at_time_worker(context, &sleep_timeout_worker);
212+
async_context_remove_when_pending_worker(context, &cyw43_poll_worker);
213+
// the IRQ IS on the same core as the context, so must be de-initialized there
214+
async_context_execute_sync(context, cyw43_irq_deinit, NULL);
215+
cyw43_deinit(&cyw43_state);
216+
cyw43_async_context = NULL;
217+
}
124218
}
125219

126220
// todo maybe add an #ifdef in cyw43_driver
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
4+
#
5+
# SPDX-License-Identifier: BSD-3-Clause
6+
7+
import sys
8+
import re
9+
10+
assert len(sys.argv) == 3
11+
12+
cyw43_wifi_fw_len = -1
13+
cyw43_clm_len = -1
14+
15+
with open(sys.argv[1], "r") as f:
16+
data = f.read()
17+
statements = data.split(";")
18+
for line in statements[1].split("\n"):
19+
if "#define CYW43_WIFI_FW_LEN" in line:
20+
matches = re.search(r"#define\s+\S+\s+\((\S+)\)", line)
21+
cyw43_wifi_fw_len = int(matches[1])
22+
elif "#define CYW43_CLM_LEN" in line:
23+
matches = re.search(r"#define\s+\S+\s+\((\S+)\)", line)
24+
cyw43_clm_len = int(matches[1])
25+
if cyw43_wifi_fw_len > 0 and cyw43_clm_len > 0:
26+
break
27+
data = statements[0]
28+
bits = data.split(",")
29+
bits[0] = bits[0].split("{")[-1]
30+
bits[-1] = bits[-1].split("}")[0]
31+
for i in range(len(bits)):
32+
bits[i] = bits[i].strip()
33+
bits[i] = bits[i].strip(',')
34+
bits[i] = int(bits[i], base=0)
35+
36+
data = (
37+
cyw43_wifi_fw_len.to_bytes(4, 'little', signed=True) +
38+
cyw43_clm_len.to_bytes(4, 'little', signed=True) +
39+
bytearray(bits)
40+
)
41+
42+
with open(sys.argv[2], "w") as f:
43+
for b in data:
44+
f.write(f".byte 0x{b:02x}\n")

src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,16 @@ extern "C" {
6363
#endif
6464

6565
#ifndef CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE
66+
#if CYW43_USE_FIRMWARE_PARTITION
67+
#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "cyw43_partition_firmware.h"
68+
#else
6669
#if CYW43_ENABLE_BLUETOOTH
6770
#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "wb43439A0_7_95_49_00_combined.h"
6871
#else
6972
#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "w43439A0_7_95_49_00_combined.h"
7073
#endif
7174
#endif
75+
#endif
7276

7377
#ifndef CYW43_WIFI_NVRAM_INCLUDE_FILE
7478
#define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
extern int cyw43_wifi_fw_len;
8+
extern int cyw43_clm_len;
9+
10+
#define CYW43_WIFI_FW_LEN (cyw43_wifi_fw_len)
11+
#define CYW43_CLM_LEN (cyw43_clm_len)
12+
extern uintptr_t fw_data;
13+
14+
#include "boot/picobin.h"
15+
#include "pico/bootrom.h"

src/rp2_common/pico_cyw43_driver/include/pico/cyw43_driver.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020
#include "cyw43_configport.h"
2121
#endif
2222

23+
#if CYW43_USE_FIRMWARE_PARTITION
24+
// 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
25+
#ifndef CYW43_FIRMWARE_PARTITION_ID
26+
// The default 0x776966696669726d value is the ASCII encoding of "wififirm"
27+
#define CYW43_FIRMWARE_PARTITION_ID 0x776966696669726d
28+
#endif
29+
#endif
30+
2331
#ifdef __cplusplus
2432
extern "C" {
2533
#endif

0 commit comments

Comments
 (0)