BERGSONNE

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.

Not on every Core
SPI master is available on the M4/M33 Cores, not the M0+ (Core.ST.L0 has no SPI). Polled transfers and DMA are bench-verified on L4, W5 and H5 (the last two run the newer SPI v2 IP on GPDMA). 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 + deselect

DMA 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 a bad length
  }
  while (core_spi_busy(&spi)) { /* or go do other work */ }
DMA coverage
Full-duplex DMA is bench-verified on Core.ST.L4 (DMA1), Core.ST.W5 and Core.ST.H5 (GPDMA1), blocking and with a callback.

Cross-architecture support

Polled master transfers and DMA are verified on every Core that has SPI; interrupt and target (slave) modes are still ahead. The same core_spi contract holds where the peripheral exists:

·L0M0+n/a●L4M42/4●W5M332/4●H5M332/5WCH (RISC-V) · Nordic (nRF54) — in development

See the implementation status for the full matrix.

Known gaps

What the SDK itself lists as missing here, straight from the header:

mediumDSLTier 2 is single-byte xfer only — no bulk / persistent CS

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.

lowC API8-bit Motorola frames only

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.

mediumC APISlave mode missing

Master-only. No path for a Core to act as a SPI peripheral on another host's bus.

lowC APIQuad / Octo SPI / OctoSPI

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@217a38f.

API reference

Default-instance · Tier 2
int core_spi_xfer_byte_bus(uint8_t bus, uint8_t cs_pad, uint8_t tx);
Single-byte full-duplex transfer over `bus`, with CS auto-managed around the call (asserted before, deasserted after). Returns the received byte (0..255) on success or -1 on any error (bus undeclared, cs_pad undefined, transfer timed out). The signed return lets DSL programs branch on `< 0` without an out-pointer. Most chip protocols pair two of these (write a register address, then read or write the value). Multi-byte sequences that need CS held across them — display init streams, audio frame transfers — still need Tier 1 with manual select/deselect.
Lower-level · Tier 1
hal_status_t core_spi_init(hal_spi_t * h, SPI_TypeDef * instance, const hal_spi_config_t * cfg);
Initialize SPI in master mode (8-bit frames).
Returns: HAL_OK, or HAL_ERROR on a NULL argument
hal_status_t core_spi_configure(hal_spi_t * h, const hal_spi_config_t * cfg);
Change prescaler, SPI mode or bit order, keeping the CS pad.
Returns: HAL_OK, HAL_BUSY while a DMA transfer runs, HAL_ERROR on NULL
uint32_t core_spi_sck_hz(const hal_spi_t * h);
The SCK frequency the handle is set to, in Hz.
Returns: SYSCLK / (2..256); 0 for an uninitialized handle
void core_spi_set_cs(hal_spi_t * h, uint8_t pad);
Assign a CS pin by tile pad number.
void core_spi_select(hal_spi_t * h);
Assert CS. Everything until core_spi_deselect() is one transaction.
void core_spi_deselect(hal_spi_t * h);
Deassert CS, ending the transaction.
uint8_t core_spi_transfer(hal_spi_t * h, uint8_t tx);
Full-duplex single byte. CS untouched.
Returns: The byte received (0xFF if the transfer failed)
hal_status_t core_spi_exchange(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len);
Full-duplex transfer of `len` bytes. CS untouched.
Returns: HAL_OK, HAL_TIMEOUT (stalled), HAL_ERROR (overrun / mode fault), HAL_BUSY (a DMA transfer is running)
hal_status_t core_spi_write(hal_spi_t * h, const uint8_t * data, uint32_t len);
Write-only: send `len` bytes, discard what comes back. CS untouched.
Returns: HAL_OK, HAL_TIMEOUT, HAL_ERROR or HAL_BUSY (see core_spi_exchange)
hal_status_t core_spi_read(hal_spi_t * h, uint8_t * buf, uint32_t len);
Read-only: clock out the fill byte (0xFF) and capture `len` bytes. CS untouched.
Returns: HAL_OK, HAL_TIMEOUT, HAL_ERROR or HAL_BUSY (see core_spi_exchange)
hal_status_t core_spi_xfer(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len);
One CS-framed full-duplex transaction: select, exchange, deselect.
Returns: HAL_OK, HAL_TIMEOUT, HAL_ERROR or HAL_BUSY (see core_spi_exchange)
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);
One CS-framed command + data transaction.
Returns: HAL_OK, HAL_TIMEOUT, HAL_ERROR or HAL_BUSY (see core_spi_exchange)
hal_status_t core_spi_exchange_dma(hal_spi_t * h, const uint8_t * tx, uint8_t * rx, uint32_t len);
Full-duplex DMA transfer, blocking until the last frame is off the bus. CS untouched.
Returns: HAL_OK, HAL_TIMEOUT (stalled; channels aborted, SPI reset), HAL_BUSY (another DMA transfer is running), HAL_ERROR (DMA error)
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);
Start a full-duplex DMA transfer and return at once.
Returns: HAL_OK, HAL_BUSY if a DMA transfer is running, HAL_ERROR for a bad length
hal_status_t core_spi_dma_wait(hal_spi_t * h);
Wait for a core_spi_xfer_dma() transfer to finish (bounded).
Returns: The transfer's result (HAL_OK if none was running), or HAL_TIMEOUT after aborting a stalled transfer
int core_spi_busy(hal_spi_t * h);
Whether a DMA transfer is in progress on this handle.
Returns: 1 while core_spi_xfer_dma() is running, else 0
hal_spi_t * core_spi_handle_for_bus(uint8_t bus);
The SPI handle for a bus id declared in config.json, or NULL if the project doesn't declare that bus. Emitted per project by coregen into core_init.c (when the project declares any SPI bus); the *_bus helpers below resolve their handle through it. Forward-declared here rather than `#include "core_init.h"` so this header compiles without a project (val tests, examples without config.json). The natives-side caller is gated on CORE_HAS_SPI_BUSES, so the linker never asks for the symbol unless the dispatcher exists.
Returns: The coregen-initialized handle, or NULL.
Types
TypeKindDescription
core_spi_ttypedefSPI handle type — same as hal_spi_t under the hood.
typedef hal_spi_t core_spi_t;
core_spi_config_ttypedefSPI configuration — prescaler, clock polarity, clock phase, bit order.
typedef hal_spi_config_t core_spi_config_t;

Generated from core_spi.h — tiles@77fd3f3.