Add new async_context abstraction and refactor cyw43_arch to use it (#1177)

* Extract all poll/threadsafe_background/freertos from cyw43_arch into new abstraction async_context:
* provides support for asynchronous events (timers/IRQ notifications) to be handled in a safe context.
* now guarantees all callbacks happen on a single core.
* is reusable by multiple different libraries (stdio_usb can now be ported to this but hasn't been yet).
* supports multiple independent instances (independent instances will not block each other).
* cyw43_arch libraries cleaned up to use the new abstraction. Note each distinct cyw43_arch type is now a very thin layer that creates the right type of context and adds cyw43_driver and lwip support as appropriate.

Additionally,

* Add new pico_time and hardware_alarm APIs
* Add from_us_since_boot()
* Add alarm_pool_create_with_unused_hardware_alarm()
* Add alarm_pool_add_alarm_at_force_in_context()
* Add hardware_alarm_claim_unused()
* Add hardware_alarm_force_irq()
* Added panic_compact() and some minor comment cleanup; moved FIRST_USER_IRQ define to platform_defs.h
This commit is contained in:
Graham Sanderson
2023-01-24 12:01:24 -06:00
committed by GitHub
parent c578422528
commit a540ca905a
47 changed files with 2694 additions and 883 deletions

View File

@ -0,0 +1,86 @@
if (DEFINED ENV{PICO_CYW43_DRIVER_PATH} AND (NOT PICO_CYW43_DRIVER_PATH))
set(PICO_CYW43_DRIVER_PATH $ENV{PICO_CYW43_DRIVER_PATH})
message("Using PICO_CYW43_DRIVER_PATH from environment ('${PICO_CYW43_DRIVER_PATH}')")
endif()
set(CYW43_DRIVER_TEST_FILE "src/cyw43.h")
if (NOT PICO_CYW43_DRIVER_PATH)
set(PICO_CYW43_DRIVER_PATH ${PICO_SDK_PATH}/lib/cyw43-driver)
if (PICO_CYW43_SUPPORTED AND NOT EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE})
message(WARNING "cyw43-driver submodule has not been initialized; Pico W wireless support will be unavailable
hint: try 'git submodule update --init' from your SDK directory (${PICO_SDK_PATH}).")
endif()
elseif (NOT EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE})
message(WARNING "PICO_CYW43_DRIVER_PATH specified but content not present.")
endif()
if (EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE})
message("cyw43-driver available at ${PICO_CYW43_DRIVER_PATH}")
pico_register_common_scope_var(PICO_CYW43_DRIVER_PATH)
# base driver without our bus
add_library(cyw43_driver INTERFACE)
target_sources(cyw43_driver INTERFACE
${PICO_CYW43_DRIVER_PATH}/src/cyw43_ll.c
${PICO_CYW43_DRIVER_PATH}/src/cyw43_stats.c
${PICO_CYW43_DRIVER_PATH}/src/cyw43_lwip.c
${PICO_CYW43_DRIVER_PATH}/src/cyw43_ctrl.c
)
target_include_directories(cyw43_driver INTERFACE
${PICO_CYW43_DRIVER_PATH}/src
${PICO_CYW43_DRIVER_PATH}/firmware
)
# pico_cyw43_driver adds async_context integration to cyw43_driver
add_library(pico_cyw43_driver INTERFACE)
target_sources(pico_cyw43_driver INTERFACE
cyw43_driver.c)
target_include_directories(pico_cyw43_driver INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(pico_cyw43_driver INTERFACE cyw43_driver)
# Firmware stuff
set(CYW43_FIRMWARE_BIN 43439A0-7.95.49.00.combined)
string(REGEX REPLACE [\\\.\-] _ CYW43_FIRMWARE_BIN_ ${CYW43_FIRMWARE_BIN})
string(REGEX MATCH [^_]+_?[^_]*_?[^_]*_?[^_]*_?[^_]* CYW43_FIRMWARE_PRETTY ${CYW43_FIRMWARE_BIN_})
set(CYW43_FIRMWARE_PRETTY fw_${CYW43_FIRMWARE_PRETTY})
set(RESOURCE_SECNAME .big_const)
set(RESOURCE_SECFLAGS contents,alloc,load,readonly,data)
set(CYW43_FIRMWARE_OBJ ${CMAKE_CURRENT_BINARY_DIR}/cyw43_resource.o)
add_custom_target(cyw43_firmware_package DEPENDS ${CYW43_FIRMWARE_OBJ})
# cyw43_resource.o contains the WiFi and BT firmware as a binary blob
add_custom_command(
OUTPUT ${CYW43_FIRMWARE_OBJ}
DEPENDS ${PICO_CYW43_DRIVER_PATH}/firmware/${CYW43_FIRMWARE_BIN}
WORKING_DIRECTORY ${PICO_CYW43_DRIVER_PATH}/firmware
COMMAND ${CMAKE_OBJCOPY} -I binary -O elf32-littlearm -B arm
--readonly-text
--rename-section .data=${RESOURCE_SECNAME},${RESOURCE_SECFLAGS}
--redefine-sym _binary_${CYW43_FIRMWARE_BIN_}_start=${CYW43_FIRMWARE_PRETTY}_start
--redefine-sym _binary_${CYW43_FIRMWARE_BIN_}_end=${CYW43_FIRMWARE_PRETTY}_end
--redefine-sym _binary_${CYW43_FIRMWARE_BIN_}_size=${CYW43_FIRMWARE_PRETTY}_size
${CYW43_FIRMWARE_BIN} ${CYW43_FIRMWARE_OBJ}
VERBATIM)
# cyw43_driver_picow is cyw43_driver plus Pico W specific bus implementation, and Pico W firmware
add_library(cyw43_driver_picow INTERFACE)
target_sources(cyw43_driver_picow INTERFACE
${CMAKE_CURRENT_LIST_DIR}/cyw43_bus_pio_spi.c
)
pico_generate_pio_header(cyw43_driver_picow ${CMAKE_CURRENT_LIST_DIR}/cyw43_bus_pio_spi.pio)
add_dependencies(cyw43_driver_picow INTERFACE cyw43_firmware_package)
target_link_libraries(cyw43_driver_picow INTERFACE
${CYW43_FIRMWARE_OBJ}
)
target_link_libraries(cyw43_driver_picow INTERFACE
cyw43_driver
hardware_pio
hardware_dma
hardware_exception
)
pico_promote_common_scope_vars()
endif()

View File

@ -0,0 +1,546 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/structs/iobank0.h"
#include "hardware/sync.h"
#include "hardware/dma.h"
#include "cyw43_bus_pio_spi.pio.h"
#include "cyw43.h"
#include "cyw43_internal.h"
#include "cyw43_spi.h"
#include "cyw43_debug_pins.h"
#if CYW43_SPI_PIO
#define WL_REG_ON 23
#define DATA_OUT_PIN 24u
#define DATA_IN_PIN 24u
#define IRQ_PIN 24u
// #define MONITOR_PIN 3u
#define CLOCK_PIN 29u
#define CS_PIN 25u
#define IRQ_SAMPLE_DELAY_NS 100
#define SPI_PROGRAM_NAME spi_gap01_sample0
#define SPI_PROGRAM_FUNC __CONCAT(SPI_PROGRAM_NAME, _program)
#define SPI_PROGRAM_GET_DEFAULT_CONFIG_FUNC __CONCAT(SPI_PROGRAM_NAME, _program_get_default_config)
#define SPI_OFFSET_END __CONCAT(SPI_PROGRAM_NAME, _offset_end)
#define SPI_OFFSET_LP1_END __CONCAT(SPI_PROGRAM_NAME, _offset_lp1_end)
#define CLOCK_DIV 2
#define CLOCK_DIV_MINOR 0
#define PADS_DRIVE_STRENGTH PADS_BANK0_GPIO0_DRIVE_VALUE_12MA
#if !CYW43_USE_SPI
#error CYW43_USE_SPI should be true
#endif
#ifndef NDEBUG
//#define ENABLE_SPI_DUMPING 1
#endif
// Set to 1 to enable
#if ENABLE_SPI_DUMPING //NDEBUG
#if 0
#define DUMP_SPI_TRANSACTIONS(A) A
#else
static bool enable_spi_packet_dumping; // set to true to dump
#define DUMP_SPI_TRANSACTIONS(A) if (enable_spi_packet_dumping) {A}
#endif
static uint32_t counter = 0;
#else
#define DUMP_SPI_TRANSACTIONS(A)
#endif
//#define SWAP32(A) ((((A) & 0xff000000U) >> 8) | (((A) & 0xff0000U) << 8) | (((A) & 0xff00U) >> 8) | (((A) & 0xffU) << 8))
__force_inline static uint32_t __swap16x2(uint32_t a) {
__asm ("rev16 %0, %0" : "+l" (a) : : );
return a;
}
#define SWAP32(a) __swap16x2(a)
#ifndef CYW43_SPI_PIO_PREFERRED_PIO
#define CYW43_SPI_PIO_PREFERRED_PIO 1
#endif
static_assert(CYW43_SPI_PIO_PREFERRED_PIO >=0 && CYW43_SPI_PIO_PREFERRED_PIO < NUM_PIOS, "");
typedef struct {
pio_hw_t *pio;
uint8_t pio_func_sel;
int8_t pio_offset;
int8_t pio_sm;
int8_t dma_out;
int8_t dma_in;
} bus_data_t;
static bus_data_t bus_data_instance;
int cyw43_spi_init(cyw43_int_t *self) {
// Only does something if CYW43_LOGIC_DEBUG=1
logic_debug_init();
static_assert(NUM_PIOS == 2, "");
pio_hw_t *pios[2] = {pio0, pio1};
uint pio_index = CYW43_SPI_PIO_PREFERRED_PIO;
// Check we can add the program
if (!pio_can_add_program(pios[pio_index], &SPI_PROGRAM_FUNC)) {
pio_index ^= 1;
if (!pio_can_add_program(pios[pio_index], &SPI_PROGRAM_FUNC)) {
return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
}
}
assert(!self->bus_data);
self->bus_data = &bus_data_instance;
bus_data_t *bus_data = (bus_data_t *)self->bus_data;
bus_data->pio = pios[pio_index];
bus_data->dma_in = -1;
bus_data->dma_out = -1;
static_assert(GPIO_FUNC_PIO1 == GPIO_FUNC_PIO0 + 1, "");
bus_data->pio_func_sel = GPIO_FUNC_PIO0 + pio_index;
bus_data->pio_sm = (int8_t)pio_claim_unused_sm(bus_data->pio, false);
if (bus_data->pio_sm < 0) {
cyw43_spi_deinit(self);
return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
}
bus_data->pio_offset = pio_add_program(bus_data->pio, &SPI_PROGRAM_FUNC);
pio_sm_config config = SPI_PROGRAM_GET_DEFAULT_CONFIG_FUNC(bus_data->pio_offset);
sm_config_set_clkdiv_int_frac(&config, CLOCK_DIV, CLOCK_DIV_MINOR);
hw_write_masked(&padsbank0_hw->io[CLOCK_PIN],
(uint)PADS_DRIVE_STRENGTH << PADS_BANK0_GPIO0_DRIVE_LSB,
PADS_BANK0_GPIO0_DRIVE_BITS
);
hw_write_masked(&padsbank0_hw->io[CLOCK_PIN],
(uint)1 << PADS_BANK0_GPIO0_SLEWFAST_LSB,
PADS_BANK0_GPIO0_SLEWFAST_BITS
);
sm_config_set_out_pins(&config, DATA_OUT_PIN, 1);
sm_config_set_in_pins(&config, DATA_IN_PIN);
sm_config_set_set_pins(&config, DATA_OUT_PIN, 1);
sm_config_set_sideset(&config, 1, false, false);
sm_config_set_sideset_pins(&config, CLOCK_PIN);
sm_config_set_in_shift(&config, false, true, 32);
sm_config_set_out_shift(&config, false, true, 32);
hw_set_bits(&bus_data->pio->input_sync_bypass, 1u << DATA_IN_PIN);
pio_sm_set_config(bus_data->pio, bus_data->pio_sm, &config);
pio_sm_set_consecutive_pindirs(bus_data->pio, bus_data->pio_sm, CLOCK_PIN, 1, true);
gpio_set_function(DATA_OUT_PIN, bus_data->pio_func_sel);
gpio_set_function(CLOCK_PIN, bus_data->pio_func_sel);
// Set data pin to pull down and schmitt
gpio_set_pulls(DATA_IN_PIN, false, true);
gpio_set_input_hysteresis_enabled(DATA_IN_PIN, true);
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_set(pio_pins, 1));
bus_data->dma_out = (int8_t) dma_claim_unused_channel(false);
bus_data->dma_in = (int8_t) dma_claim_unused_channel(false);
if (bus_data->dma_out < 0 || bus_data->dma_in < 0) {
cyw43_spi_deinit(self);
return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
}
return 0;
}
void cyw43_spi_deinit(cyw43_int_t *self) {
if (self->bus_data) {
bus_data_t *bus_data = (bus_data_t *)self->bus_data;
if (bus_data->pio_sm >= 0) {
if (bus_data->pio_offset != -1)
pio_remove_program(bus_data->pio, &SPI_PROGRAM_FUNC, bus_data->pio_offset);
pio_sm_unclaim(bus_data->pio, bus_data->pio_sm);
}
if (bus_data->dma_out >= 0) {
dma_channel_unclaim(bus_data->dma_out);
bus_data->dma_out = -1;
}
if (bus_data->dma_in >= 0) {
dma_channel_unclaim(bus_data->dma_in);
bus_data->dma_in = -1;
}
self->bus_data = NULL;
}
}
static void cs_set(bool value) {
gpio_put(CS_PIN, value);
}
static __noinline void ns_delay(uint32_t ns) {
// cycles = ns * clk_sys_hz / 1,000,000,000
uint32_t cycles = ns * (clock_get_hz(clk_sys) >> 16u) / (1000000000u >> 16u);
busy_wait_at_least_cycles(cycles);
}
static void start_spi_comms(cyw43_int_t *self) {
bus_data_t *bus_data = (bus_data_t *)self->bus_data;
// Pull CS low
cs_set(false);
gpio_set_function(DATA_OUT_PIN, bus_data->pio_func_sel);
}
// we need to atomically de-assert CS and enable IRQ
static void stop_spi_comms(void) {
// from this point a positive edge will cause an IRQ to be pending
cs_set(true);
// we need to wait a bit in case the irq line is incorrectly high
ns_delay(IRQ_SAMPLE_DELAY_NS);
}
#if ENABLE_SPI_DUMPING
static void dump_bytes(const uint8_t *bptr, uint32_t len) {
unsigned int i = 0;
for (i = 0; i < len;) {
if ((i & 0x0f) == 0) {
printf("\n");
} else if ((i & 0x07) == 0) {
printf(" ");
}
printf("%02x ", bptr[i++]);
}
printf("\n");
}
#endif
int cyw43_spi_transfer(cyw43_int_t *self, const uint8_t *tx, size_t tx_length, uint8_t *rx,
size_t rx_length) {
if ((tx == NULL) && (rx == NULL)) {
return CYW43_FAIL_FAST_CHECK(-CYW43_EINVAL);
}
bus_data_t *bus_data = (bus_data_t *)self->bus_data;
start_spi_comms(self);
if (rx != NULL) {
if (tx == NULL) {
tx = rx;
assert(tx_length && tx_length < rx_length);
}
DUMP_SPI_TRANSACTIONS(
printf("[%lu] bus TX/RX %u bytes rx %u:", counter++, tx_length, rx_length);
dump_bytes(tx, tx_length);
)
assert(!(tx_length & 3));
assert(!(((uintptr_t)tx) & 3));
assert(!(((uintptr_t)rx) & 3));
assert(!(rx_length & 3));
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
pio_sm_set_wrap(bus_data->pio, bus_data->pio_sm, bus_data->pio_offset, bus_data->pio_offset + SPI_OFFSET_END - 1);
pio_sm_clear_fifos(bus_data->pio, bus_data->pio_sm);
pio_sm_set_pindirs_with_mask(bus_data->pio, bus_data->pio_sm, 1u << DATA_OUT_PIN, 1u << DATA_OUT_PIN);
pio_sm_restart(bus_data->pio, bus_data->pio_sm);
pio_sm_clkdiv_restart(bus_data->pio, bus_data->pio_sm);
pio_sm_put(bus_data->pio, bus_data->pio_sm, tx_length * 8 - 1);
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_x, 32));
pio_sm_put(bus_data->pio, bus_data->pio_sm, (rx_length - tx_length) * 8 - 1);
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_y, 32));
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_jmp(bus_data->pio_offset));
dma_channel_abort(bus_data->dma_out);
dma_channel_abort(bus_data->dma_in);
dma_channel_config out_config = dma_channel_get_default_config(bus_data->dma_out);
channel_config_set_bswap(&out_config, true);
channel_config_set_dreq(&out_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, true));
dma_channel_configure(bus_data->dma_out, &out_config, &bus_data->pio->txf[bus_data->pio_sm], tx, tx_length / 4, true);
dma_channel_config in_config = dma_channel_get_default_config(bus_data->dma_in);
channel_config_set_bswap(&in_config, true);
channel_config_set_dreq(&in_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, false));
channel_config_set_write_increment(&in_config, true);
channel_config_set_read_increment(&in_config, false);
dma_channel_configure(bus_data->dma_in, &in_config, rx + tx_length, &bus_data->pio->rxf[bus_data->pio_sm], rx_length / 4 - tx_length / 4, true);
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, true);
__compiler_memory_barrier();
dma_channel_wait_for_finish_blocking(bus_data->dma_out);
dma_channel_wait_for_finish_blocking(bus_data->dma_in);
__compiler_memory_barrier();
memset(rx, 0, tx_length); // make sure we don't have garbage in what would have been returned data if using real SPI
} else if (tx != NULL) {
DUMP_SPI_TRANSACTIONS(
printf("[%lu] bus TX only %u bytes:", counter++, tx_length);
dump_bytes(tx, tx_length);
)
assert(!(((uintptr_t)tx) & 3));
assert(!(tx_length & 3));
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
pio_sm_set_wrap(bus_data->pio, bus_data->pio_sm, bus_data->pio_offset, bus_data->pio_offset + SPI_OFFSET_LP1_END - 1);
pio_sm_clear_fifos(bus_data->pio, bus_data->pio_sm);
pio_sm_set_pindirs_with_mask(bus_data->pio, bus_data->pio_sm, 1u << DATA_OUT_PIN, 1u << DATA_OUT_PIN);
pio_sm_restart(bus_data->pio, bus_data->pio_sm);
pio_sm_clkdiv_restart(bus_data->pio, bus_data->pio_sm);
pio_sm_put(bus_data->pio, bus_data->pio_sm, tx_length * 8 - 1);
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_x, 32));
pio_sm_put(bus_data->pio, bus_data->pio_sm, 0);
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_y, 32));
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_jmp(bus_data->pio_offset));
dma_channel_abort(bus_data->dma_out);
dma_channel_config out_config = dma_channel_get_default_config(bus_data->dma_out);
channel_config_set_bswap(&out_config, true);
channel_config_set_dreq(&out_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, true));
dma_channel_configure(bus_data->dma_out, &out_config, &bus_data->pio->txf[bus_data->pio_sm], tx, tx_length / 4, true);
uint32_t fdebug_tx_stall = 1u << (PIO_FDEBUG_TXSTALL_LSB + bus_data->pio_sm);
bus_data->pio->fdebug = fdebug_tx_stall;
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, true);
while (!(bus_data->pio->fdebug & fdebug_tx_stall)) {
tight_loop_contents(); // todo timeout
}
__compiler_memory_barrier();
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
pio_sm_set_consecutive_pindirs(bus_data->pio, bus_data->pio_sm, DATA_IN_PIN, 1, false);
} else if (rx != NULL) { /* currently do one at a time */
DUMP_SPI_TRANSACTIONS(
printf("[%lu] bus TX %u bytes:", counter++, rx_length);
dump_bytes(rx, rx_length);
)
panic_unsupported();
}
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_mov(pio_pins, pio_null)); // for next time we turn output on
stop_spi_comms();
DUMP_SPI_TRANSACTIONS(
printf("RXed:");
dump_bytes(rx, rx_length);
printf("\n");
)
return 0;
}
// Initialise our gpios
void cyw43_spi_gpio_setup(void) {
// Setup WL_REG_ON (23)
gpio_init(WL_REG_ON);
gpio_set_dir(WL_REG_ON, GPIO_OUT);
gpio_pull_up(WL_REG_ON);
// Setup DO, DI and IRQ (24)
gpio_init(DATA_OUT_PIN);
gpio_set_dir(DATA_OUT_PIN, GPIO_OUT);
gpio_put(DATA_OUT_PIN, false);
// Setup CS (25)
gpio_init(CS_PIN);
gpio_set_dir(CS_PIN, GPIO_OUT);
gpio_put(CS_PIN, true);
}
// Reset wifi chip
void cyw43_spi_reset(void) {
gpio_put(WL_REG_ON, false); // off
sleep_ms(20);
gpio_put(WL_REG_ON, true); // on
sleep_ms(250);
// Setup IRQ (24) - also used for DO, DI
gpio_init(IRQ_PIN);
gpio_set_dir(IRQ_PIN, GPIO_IN);
}
static inline uint32_t make_cmd(bool write, bool inc, uint32_t fn, uint32_t addr, uint32_t sz) {
return write << 31 | inc << 30 | fn << 28 | (addr & 0x1ffff) << 11 | sz;
}
#if CYW43_VERBOSE_DEBUG
static const char *func_name(int fn) {
switch (fn)
{
case BUS_FUNCTION:
return "BUS_FUNCTION";
case BACKPLANE_FUNCTION:
return "BACKPLANE_FUNCTION";
case WLAN_FUNCTION:
return "WLAN_FUNCTION";
default:
return "UNKNOWN";
}
}
#endif
uint32_t read_reg_u32_swap(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
uint32_t buf[2] = {0};
assert(fn != BACKPLANE_FUNCTION);
buf[0] = SWAP32(make_cmd(false, true, fn, reg, 4));
int ret = cyw43_spi_transfer(self, NULL, 4, (uint8_t *)buf, 8);
if (ret != 0) {
return ret;
}
return SWAP32(buf[1]);
}
static inline uint32_t _cyw43_read_reg(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint size) {
// Padding plus max read size of 32 bits + another 4?
static_assert(WHD_BUS_SPI_BACKPLANE_READ_PADD_SIZE % 4 == 0, "");
uint32_t buf32[WHD_BUS_SPI_BACKPLANE_READ_PADD_SIZE/4 + 1 + 1];
uint8_t *buf = (uint8_t *)buf32;
const uint32_t padding = (fn == BACKPLANE_FUNCTION) ? WHD_BUS_SPI_BACKPLANE_READ_PADD_SIZE : 0; // Add response delay
buf32[0] = make_cmd(false, true, fn, reg, size + padding);
if (fn == BACKPLANE_FUNCTION) {
logic_debug_set(pin_BACKPLANE_READ, 1);
}
int ret = cyw43_spi_transfer(self, NULL, 4, buf, 8 + padding);
if (fn == BACKPLANE_FUNCTION) {
logic_debug_set(pin_BACKPLANE_READ, 0);
}
if (ret != 0) {
return ret;
}
uint32_t result = buf32[padding > 0 ? 2 : 1];
CYW43_VDEBUG("cyw43_read_reg_u%d %s 0x%lx=0x%lx\n", size * 8, func_name(fn), reg, result);
return result;
}
uint32_t cyw43_read_reg_u32(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
return _cyw43_read_reg(self, fn, reg, 4);
}
int cyw43_read_reg_u16(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
return _cyw43_read_reg(self, fn, reg, 2);
}
int cyw43_read_reg_u8(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
return _cyw43_read_reg(self, fn, reg, 1);
}
// This is only used to switch the word order on boot
int write_reg_u32_swap(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val) {
uint32_t buf[2];
// Boots up in little endian so command needs swapping too
buf[0] = SWAP32(make_cmd(true, true, fn, reg, 4));
buf[1] = SWAP32(val);
int ret = cyw43_spi_transfer(self, (uint8_t *)buf, 8, NULL, 0);
CYW43_VDEBUG("write_reg_u32_swap %s 0x%lx=0x%lx\n", func_name(fn), reg, val);
return ret;
}
static inline int _cyw43_write_reg(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val, uint size) {
uint32_t buf[2];
buf[0] = make_cmd(true, true, fn, reg, size);
buf[1] = val;
if (fn == BACKPLANE_FUNCTION) {
// In case of f1 overflow
self->last_size = 8;
self->last_header[0] = buf[0];
self->last_header[1] = buf[1];
self->last_backplane_window = self->cur_backplane_window;
}
if (fn == BACKPLANE_FUNCTION) {
logic_debug_set(pin_BACKPLANE_WRITE, 1);
}
int ret = cyw43_spi_transfer(self, (uint8_t *)buf, 8, NULL, 0);
if (fn == BACKPLANE_FUNCTION) {
logic_debug_set(pin_BACKPLANE_WRITE, 0);
}
CYW43_VDEBUG("cyw43_write_reg_u%d %s 0x%lx=0x%lx\n", size * 8, func_name(fn), reg, val);
return ret;
}
int cyw43_write_reg_u32(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val) {
return _cyw43_write_reg(self, fn, reg, val, 4);
}
int cyw43_write_reg_u16(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint16_t val) {
return _cyw43_write_reg(self, fn, reg, val, 2);
}
int cyw43_write_reg_u8(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val) {
return _cyw43_write_reg(self, fn, reg, val, 1);
}
#if MAX_BLOCK_SIZE > 0x7f8
#error Block size is wrong for SPI
#endif
// Assumes we're reading into spid_buf
int cyw43_read_bytes(cyw43_int_t *self, uint32_t fn, uint32_t addr, size_t len, uint8_t *buf) {
assert(fn != BACKPLANE_FUNCTION || (len <= 64 && (addr + len) <= 0x8000));
const uint32_t padding = (fn == BACKPLANE_FUNCTION) ? 4 : 0; // Add response delay
size_t aligned_len = (len + 3) & ~3;
assert(aligned_len > 0 && aligned_len <= 0x7f8);
self->spi_header[padding > 0 ? 0 : 1] = make_cmd(false, true, fn, addr, len + padding);
if (fn == WLAN_FUNCTION) {
logic_debug_set(pin_WIFI_RX, 1);
}
int ret = cyw43_spi_transfer(self, NULL, 4, (uint8_t *)&self->spi_header[padding > 0 ? 0 : 1], aligned_len + 4 + padding);
if (fn == WLAN_FUNCTION) {
logic_debug_set(pin_WIFI_RX, 0);
}
if (ret != 0) {
printf("cyw43_read_bytes error %d", ret);
return ret;
}
if (buf != self->spid_buf) { // avoid a copy in the usual case just to add the header
memcpy(buf, self->spid_buf, len);
}
return 0;
}
// See whd_bus_spi_transfer_bytes
// Note, uses spid_buf if src isn't using it already
// Apart from firmware download this appears to only be used for wlan functions?
int cyw43_write_bytes(cyw43_int_t *self, uint32_t fn, uint32_t addr, size_t len, const uint8_t *src) {
assert(fn != BACKPLANE_FUNCTION || (len <= 64 && (addr + len) <= 0x8000));
size_t aligned_len = (len + 3) & ~3u;
assert(aligned_len > 0 && aligned_len <= 0x7f8);
if (fn == WLAN_FUNCTION) {
// Wait for FIFO to be ready to accept data
int f2_ready_attempts = 1000;
while (f2_ready_attempts-- > 0) {
uint32_t bus_status = cyw43_read_reg_u32(self, BUS_FUNCTION, SPI_STATUS_REGISTER);
if (bus_status & STATUS_F2_RX_READY) {
logic_debug_set(pin_F2_RX_READY_WAIT, 0);
break;
} else {
logic_debug_set(pin_F2_RX_READY_WAIT, 1);
}
}
if (f2_ready_attempts <= 0) {
printf("F2 not ready\n");
return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
}
}
if (src == self->spid_buf) { // avoid a copy in the usual case just to add the header
self->spi_header[1] = make_cmd(true, true, fn, addr, len);
logic_debug_set(pin_WIFI_TX, 1);
int res = cyw43_spi_transfer(self, (uint8_t *)&self->spi_header[1], aligned_len + 4, NULL, 0);
logic_debug_set(pin_WIFI_TX, 0);
return res;
} else {
// todo: would be nice to get rid of this. Only used for firmware download?
assert(src < self->spid_buf || src >= (self->spid_buf + sizeof(self->spid_buf)));
self->spi_header[1] = make_cmd(true, true, fn, addr, len);
memcpy(self->spid_buf, src, len);
return cyw43_spi_transfer(self, (uint8_t *)&self->spi_header[1], aligned_len + 4, NULL, 0);
}
}
#endif

View File

@ -0,0 +1,61 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.program spi_gap0_sample1
.side_set 1
; always transmit multiple of 32 bytes
lp: out pins, 1 side 0
jmp x-- lp side 1
public lp1_end:
set pindirs, 0 side 0
lp2:
in pins, 1 side 1
jmp y-- lp2 side 0
public end:
.program spi_gap01_sample0
.side_set 1
; always transmit multiple of 32 bytes
lp: out pins, 1 side 0
jmp x-- lp side 1
public lp1_end:
set pindirs, 0 side 0
nop side 1
lp2:
in pins, 1 side 0
jmp y-- lp2 side 1
public end:
.program spi_gap010_sample1
.side_set 1
; always transmit multiple of 32 bytes
lp: out pins, 1 side 0
jmp x-- lp side 1
public lp1_end:
set pindirs, 0 side 0
nop side 1
nop side 0
lp2:
in pins, 1 side 1
jmp y-- lp2 side 0
public end:
.program spi_gap0_sample1_regular
.side_set 1
; always transmit multiple of 32 bytes
lp: out pins, 1 side 0
jmp x-- lp side 1
public lp1_end:
set pindirs, 0 side 0
lp2:
in pins, 1 side 1
jmp y-- lp2 side 0
public end:

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "cyw43.h"
#include "pico/cyw43_driver.h"
#ifndef CYW43_GPIO_IRQ_HANDLER_PRIORITY
#define CYW43_GPIO_IRQ_HANDLER_PRIORITY 0x40
#endif
#ifndef CYW43_SLEEP_CHECK_MS
#define CYW43_SLEEP_CHECK_MS 50
#endif
static async_context_t *cyw43_async_context;
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);
static async_at_time_worker_t sleep_timeout_worker = {
.do_work = cyw43_sleep_timeout_reached
};
static async_when_pending_worker_t cyw43_poll_worker = {
.do_work = cyw43_do_poll
};
static void cyw43_set_irq_enabled(bool enabled) {
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, enabled);
}
// GPIO interrupt handler to tell us there's cyw43 has work to do
static void cyw43_gpio_irq_handler(void)
{
uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
if (events & GPIO_IRQ_LEVEL_HIGH) {
// As we use a high level interrupt, it will go off forever until it's serviced
// So disable the interrupt until this is done. It's re-enabled again by CYW43_POST_POLL_HOOK
// which is called at the end of cyw43_poll_func
cyw43_set_irq_enabled(false);
async_context_set_work_pending(cyw43_async_context, &cyw43_poll_worker);
}
}
uint32_t cyw43_irq_init(__unused void *param) {
#ifndef NDEBUG
assert(get_core_num() == async_context_core_num(cyw43_async_context));
#endif
gpio_add_raw_irq_handler_with_order_priority(CYW43_PIN_WL_HOST_WAKE, cyw43_gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
cyw43_set_irq_enabled(true);
irq_set_enabled(IO_IRQ_BANK0, true);
return 0;
}
uint32_t cyw43_irq_deinit(__unused void *param) {
#ifndef NDEBUG
assert(get_core_num() == async_context_core_num(cyw43_async_context));
#endif
gpio_remove_raw_irq_handler(CYW43_PIN_WL_HOST_WAKE, cyw43_gpio_irq_handler);
cyw43_set_irq_enabled(false);
return 0;
}
void cyw43_post_poll_hook(void) {
#ifndef NDEBUG
assert(get_core_num() == async_context_core_num(cyw43_async_context));
#endif
cyw43_set_irq_enabled(true);
}
void cyw43_schedule_internal_poll_dispatch(__unused void (*func)(void)) {
assert(func == cyw43_poll);
async_context_set_work_pending(cyw43_async_context, &cyw43_poll_worker);
}
static void cyw43_do_poll(async_context_t *context, __unused async_when_pending_worker_t *worker) {
#ifndef NDEBUG
assert(get_core_num() == async_context_core_num(cyw43_async_context));
#endif
if (cyw43_poll) {
if (cyw43_sleep > 0) {
cyw43_sleep--;
}
cyw43_poll();
if (cyw43_sleep) {
async_context_add_at_time_worker_in_ms(context, &sleep_timeout_worker, CYW43_SLEEP_CHECK_MS);
} else {
async_context_remove_at_time_worker(context, &sleep_timeout_worker);
}
}
}
static void cyw43_sleep_timeout_reached(async_context_t *context, async_at_time_worker_t *worker) {
assert(context == cyw43_async_context);
assert(worker == &sleep_timeout_worker);
async_context_set_work_pending(cyw43_async_context, &cyw43_poll_worker);
}
bool cyw43_driver_init(async_context_t *context) {
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
// from there later
async_context_execute_sync(context, cyw43_irq_init, NULL);
async_context_add_when_pending_worker(context, &cyw43_poll_worker);
return true;
}
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;
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_CYW43_DRIVER_ASYNC_CONTEXT_H
#define _PICO_CYW43_DRIVER_ASYNC_CONTEXT_H
/** \file pico/cyw43_driver.h
* \defgroup pico_cyw43_driver pico_cyw43_driver
*
* A wrapper around the lower level cyw43_driver, that integrates it with \ref pico_async_context
* for handling background work.
*/
#include "pico.h"
#include "pico/async_context.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \brief Initializes the lower level cyw43_driver and integrates it with the provided async_context
* \ingroup pico_cyw43_driver
*
* If the initialization succeeds, \ref lwip_nosys_deinit() can be called to shutdown lwIP support
*
* \param context the async_context instance that provides the abstraction for handling asynchronous work.
* \return true if the initialization succeeded
*/
bool cyw43_driver_init(async_context_t *context);
/*! \brief De-initialize the lowever level cyw43_driver and unhooks it from the async_context
* \ingroup pico_cyw43_driver
*
* \param context the async_context the cyw43_driver support was added to via \ref cyw43_driver_init
*/
void cyw43_driver_deinit(async_context_t *context);
#ifdef __cplusplus
}
#endif
#endif