SPI
Full-duplex synchronous serial for fast peripherals — flash, displays, IMUs. core_spi is a blocking SPI master with software-managed chip-select; hal_spi and ll_spi sit underneath. Use the Core / HAL / LL toggle at the top of the sidebar to switch.
Overview
You pick an SPI instance (SPI1, …), a clock prescaler, and the clock polarity/phase (CPOL / CPHA — the four standard SPI modes). Chip-select is a regular GPIO you drive around each transaction, so one bus can address many devices.
Core.ST.L0 has no SPI). DMA is verified on L4; the newer SPI v2 IP on W5/H5 is still being brought up. The support matrix below has the specifics.Transfers
Init, point chip-select at a tile pad, then transact. transfer is one full-duplex byte (send and receive at once); xfer wraps a multi-byte transfer in select/deselect for you:
#include "core.h"
hal_spi_t spi;
hal_spi_config_t cfg = { .prescaler = LL_SPI_PRESCALER_8, .cpol = 0, .cpha = 0 };
core_spi_init(&spi, SPI1, &cfg);
core_spi_set_cs(&spi, 8); // chip-select on tile pad 8
core_spi_select(&spi);
uint8_t id = core_spi_transfer(&spi, 0x9F); // e.g. flash JEDEC ID
core_spi_deselect(&spi);
uint8_t tx[4] = { 0x03, 0, 0, 0 }, rx[4];
core_spi_xfer(&spi, tx, rx, sizeof(tx)); // select + xfer + deselectDMA transfers
For large transfers, hand the buffers to DMA and get a callback on completion — the CPU is free in the meantime. Both buffers must stay valid until the callback fires:
static void on_done(void *ctx) { /* transfer complete */ }
if (core_spi_xfer_dma(&spi, tx, rx, len, on_done, NULL) != HAL_OK) {
// busy or unsupported on this Core
}
while (core_spi_busy(&spi)) { /* or go do other work */ }Core.ST.L4; on the SPI v2 Cores it falls back or returns an error while the GPDMA path is finished.Cross-architecture support
Polling master transfers are the baseline on the Cores that have SPI; DMA and the SPI v2 bring-up vary. The same core_spi contract holds where the peripheral exists:
See the implementation status for the full matrix.
Known gaps
What the SDK itself lists as missing here, straight from the header:
The Tier 2 surface is core_spi_xfer_byte_bus: one byte, CS auto- managed around the call. DSL programs that need to push a multi-byte payload with CS held (display init streams, SD-card sectors, audio frame transfers) drop back to Tier 1 (core_spi_xfer, core_spi_write_read, core_spi_exchange_dma). Bulk variants need the array-IN / array-OUT host-call ABI prototyped on the tile- driver side — track with the DSL Capability Coverage close.
Core.ST.L4 passes tests/hw-spi-loopback (polled + DMA, modes 0-3, 1-4096 bytes, timeouts). Core.ST.W5 builds the same code (TSIZE sessions, GPDMA) but has not run it on hardware yet; the camera tile's LL half-duplex path is the only W5 SPI use proven on silicon. Core.ST.H5: the kernel clock is now per_ck (the HSI), but on the bench SPI1 still never clocks a frame and a transfer returns HAL_TIMEOUT (2026-09-26, only tried on a board started by the ROM bootloader).
core_spi_exchange_dma / core_spi_xfer_dma run on Core.ST.L4 (DMA1) and Core.ST.W5 (GPDMA1 CH6/CH7) and return HAL_ERROR on Core.ST.H5, where polled transfers are the only path.
Frames are 8 bits. No 16-bit frames, hardware CRC, TI frame format or hardware NSS at the core level; the LL layer has the Core.ST.W5 half-duplex (1-line) mode.
Master-only. No path for a Core to act as a SPI peripheral on another host's bus.
SDK roadmap Tier 2: QUADSPI / OctoSPI (memory-mapped external flash for NOR / PSRAM tiles) is wired only on Core.ST.L4 + Core.ST.H5 silicon and isn't wrapped here.
From the @studio unsupported notes in core_spi.h — tiles@6af026f.
API reference
int core_spi_xfer_byte_bus(uint8_t bus, uint8_t cs_pad, uint8_t tx);hal_status_t core_spi_init(hal_spi_t * h, SPI_TypeDef * instance, const hal_spi_config_t * cfg);hal_status_t core_spi_configure(hal_spi_t * h, const hal_spi_config_t * cfg);uint32_t core_spi_sck_hz(const hal_spi_t * h);void core_spi_set_cs(hal_spi_t * h, uint8_t pad);void core_spi_select(hal_spi_t * h);void core_spi_deselect(hal_spi_t * h);uint8_t core_spi_transfer(hal_spi_t * h, uint8_t tx);hal_status_t core_spi_exchange(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len);hal_status_t core_spi_write(hal_spi_t * h, const uint8_t * data, uint32_t len);hal_status_t core_spi_read(hal_spi_t * h, uint8_t * buf, uint32_t len);hal_status_t core_spi_xfer(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len);hal_status_t core_spi_write_read(hal_spi_t * h, const uint8_t * tx, uint32_t tx_len, uint8_t * rx, uint32_t rx_len);hal_status_t core_spi_exchange_dma(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len);hal_status_t core_spi_xfer_dma(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len, hal_callback_t cb, void * ctx);hal_status_t core_spi_dma_wait(hal_spi_t * h);int core_spi_busy(hal_spi_t * h);hal_spi_t * core_spi_handle_for_bus(uint8_t bus);Generated from core_spi.h — tiles@003821d.

