Implementation status
The same core_* API and behavior DSL target every Core — this page is the honest map of how far each one is along, and where the platform is headed across architectures. One contract, many implementations.
power.stop (the H5 moved to the shared, watchdog-safe sleep code on 13 October and hasn’t been re-benched) and power.rtc_wake. See Power.Overview
A peripheral feature is “done” on a Core when the layers beneath the core_* API are implemented for that MCU and the result is checked on hardware. Because the contract is shared, this is the only thing that varies between Cores — so a single matrix captures the whole platform.
Today the shipping Cores are STMicroelectronics Arm Cortex-M parts. WCH (RISC-V) and Nordic (nRF54) Cores are in development; when they arrive they populate the same matrix without changing a line of your firmware.
tiles@e539b7b. Verified means checked on real hardware; compiles means the code builds but hasn’t been hardware-verified yet. Hover a dot for its note: most name the bench test and date.Cores by architecture
Cores are named by vendor and series — Core.ST.L4, Core.ST.W5, Core.ST.H5. The vendor segment is the architecture seam: everything above the core_* line is shared, everything below is re-implemented per family.
STMicroelectronics
· Arm Cortex-MShippingThe Cores shipping today, from an ultra-low-power M0+ up to an M33 rated for 250 MHz. The full feature matrix below is measured against these.
WCH
· RISC-VIn developmentRISC-V Cores bringing a lower-cost architecture to the same tile platform. The core_* API and behavior DSL are unchanged — only the layers beneath are re-implemented.
core_* contract, drivers via the PAL.Nordic
· Arm Cortex-M33In developmentnRF54 Cores adding modern wireless (BLE / 802.15.4) alongside the Arm M33 line. Same contract, same drivers via the PAL.
core_* contract, drivers via the PAL.Feature support matrix
Features by group, against the shipping Cores. Each entry carries the SDK layer it reaches (LL → HAL → TAL → Core); a layer implies everything beneath it.
| Feature | Layer | L0M0+ | L4M4 | W5M33 | H5M33 |
|---|---|---|---|---|---|
| System | |||||
| core initialization | Core | ● | ● | ● | ● |
| LED | Studio | ● | ● | ● | ● |
| delay (ms / µs) | Studio | ● | ● | ● | ● |
| SysTick | LL | ● | ● | ● | ● |
| fault handlers | ● | ● | ● | ● | |
| unique device ID | Core | ● | ● | ● | ● |
| brick recovery | Core | · | ● | · | ● |
| software reset | Studio | ● | ● | ● | ● |
| reset reason | Studio | ● | ● | ● | ● |
| SWO / ITM debug output | HAL | · | ● | ● | ● |
| WAMR (Wasm) runtime | Studio | · | · | ● | ● |
| Clocks | |||||
| HSI (internal RC) | LL | ● | ● | ● | ● |
| HSE (external crystal) | LL | · | · | ● | · |
| MSI / MSIS (low-power) | LL | ● | ● | · | · |
| CSI (low-power internal) | LL | · | · | · | ● |
| PLL (high-speed) | LL | ● | ● | ● | ● |
| GPIO | |||||
| digital I/O (read / write) | Studio | ● | ● | ● | ● |
| EXTI (external interrupts) | Studio | ● | ● | ● | ● |
| alternate function config | LL | ● | ● | ● | ● |
| open-drain / pull config | Core | ● | ● | ● | ● |
| UART | |||||
| TX / RX (polling) | Core | ● | ● | ● | ● |
| interrupt-driven | Core | ● | ● | ● | ● |
| DMA | Core | ○ | ○ | ○ | ○ |
| hardware flow control | ○ | ○ | ○ | ○ | |
| LPUART (low-power) | Core | ● | ● | · | ● |
| I2C | |||||
| standard mode (100 kHz) | Core | ● | ● | ● | ● |
| fast mode (400 kHz) | Core | ● | ● | ● | ● |
| fast-mode plus (1 MHz) | · | ● | · | ● | |
| interrupt-driven | Core | ○ | ○ | ○ | ○ |
| target / device mode | Core | ● | ● | ● | ● |
| DMA | ○ | ○ | ○ | ○ | |
| 10-bit addressing | Core | ○ | ○ | ○ | ○ |
| SMBus mode | ○ | ○ | ○ | ○ | |
| SPI | |||||
| master TX / RX (polling) | Core | · | ● | ● | ● |
| interrupt-driven | Core | · | ○ | ○ | ○ |
| DMA | Core | · | ● | ● | ● |
| slave mode | · | ○ | ○ | ○ | |
| OctoSPI / QSPI | · | · | · | ○ | |
| I3C | |||||
| SDR controller | Core | · | · | · | ● |
| dynamic addressing | Core | · | · | · | ● |
| CCCs | Core | · | · | · | ● |
| private read / write | Core | · | · | · | ● |
| in-band interrupts (IBI) | Core | · | · | · | ● |
| tile drivers over I3C | Core | · | · | · | ● |
| target mode | · | · | · | ○ | |
| HDR modes (DDR) | · | · | · | · | |
| Timers | |||||
| basic timebase / tick | Studio | ● | ● | ● | ● |
| PWM output | Studio | ● | ● | ● | ● |
| STEP/DIR stepper pulses | Core | ● | ● | ● | ● |
| input capture | Core | ● | ● | ● | ● |
| one-shot pulse | ○ | ○ | ○ | ○ | |
| encoder mode | ○ | ○ | ○ | ○ | |
| LPTIM (low-power timer) | ○ | ○ | ○ | ○ | |
| IWDG (watchdog) | Studio | ● | ● | ● | ● |
| ADC | |||||
| single-shot raw read | Studio | ● | ● | ● | ● |
| millivolt read (VREFINT) | Studio | ◐ | ● | ● | ● |
| temperature sensor | Studio | ● | ● | ● | ● |
| resolution (8 / 10 / 12-bit) | Core | ● | ● | ◐ | ● |
| oversampling / burst | Core | ● | ● | ● | ● |
| DMA | Core | ● | ● | ◐ | ● |
| DMA results in millivolts | Core | ◐ | ● | ● | ● |
| continuous / scan mode | Core | ● | ● | ● | ● |
| external trigger (TIMx TRGO / EXTI) | Core | ● | ◐ | ◐ | ◐ |
| analog watchdog | ○ | ○ | ○ | ○ | |
| Audio | |||||
| PDM microphone capture | Core | · | · | ● | · |
| NVM | |||||
| true EEPROM | Core | ● | · | · | · |
| flash emulation | Core | · | ● | ● | ● |
| OTP identity slots | Core | · | ○ | ◐ | ○ |
| Project Generation (Coregen) | |||||
| GPIO pad auto-init | Core | ● | ● | ● | ● |
| I2C auto-init | Core | ● | ● | ● | ● |
| analog pad init | Core | ● | ● | ● | ● |
| I3C auto-init | Core | · | · | · | ● |
| SPI auto-init | Core | · | ● | ● | ● |
| UART auto-init | Core | ● | ● | ● | ● |
| timer / PWM auto-init | Core | ● | ● | ● | ● |
| Power Management | |||||
| sleep mode | Core | ● | ● | ● | ● |
| STOP mode | Core | ● | ● | ● | ◐ |
| standby / shutdown | Core | ● | ● | ● | ● |
| RTC wakeup | Studio | ● | ● | ● | ◐ |
| EXTI wakeup | Core | ● | ● | ● | ● |
| wakeup pin (Standby) | Core | ○ | ● | ○ | ○ |
| RTC Alarm A | Studio | ● | ● | ● | ● |
| backup registers | Studio | ● | ● | ● | ● |
| Security | |||||
| hardware RNG | · | ● | ● | ● | |
| AES (hardware) | · | ○ | ◐ | · | |
| PKA / ECC | · | · | ○ | ○ | |
| HASH (SHA-256) | · | · | ○ | ○ | |
| Connectivity | |||||
| USB CDC serial | Studio | · | ● | · | ● |
| Scope over USB | Core | · | ● | · | ● |
| Scope over Bluetooth | Core | · | · | ● | · |
| USB HID | · | ● | · | ● | |
| USB-reachable sleep | Core | · | ◐ | · | ○ |
| USB MSC | · | ○ | · | ○ | |
| ROM DFU bootloader | · | ● | · | ● | |
| serial update (USB CDC) | HAL | · | ● | · | ● |
| firmware update over BLE | Core | · | · | ● | · |
| remote reset over USB serial | HAL | · | ● | · | ● |
| remote reset over BLE | Core | · | · | ● | · |
| SWD flash from Studio | Studio | ○ | ○ | ● | ○ |
| BLE radio | · | · | ● | · | |
| BLE advertising | · | · | ● | · | |
| BLE GATT server | · | · | ● | · | |
| FDCAN | · | · | · | ○ | |
| Advanced | |||||
| COMP (analog comparator) | ○ | ○ | ○ | · | |
| DAC | Studio | · | · | · | ● |
| Bluetooth LE | |||||
| advertising | Core | · | · | ● | · |
| GATT services + characteristics | Core | · | · | ● | · |
| notify / on-write callbacks | Core | · | · | ● | · |
| pairing + bonding | Core | · | · | ◐ | · |
| central / scanner role | Core | · | · | ○ | · |
| Thread | |||||
| network (fixed dataset) | Core | · | · | ● | · |
| UDP send / broadcast / receive | Core | · | · | ● | · |
| role + link diagnostics | Core | · | · | ● | · |
| settings in flash | Core | · | · | ◐ | · |
| BLE + Thread in one image | Core | · | · | ● | · |
| provisioning (BLE + C API) | Core | · | · | ● | · |
| commissioning (joiner / commissioner) | Core | · | · | ○ | · |
Legend
Status of each feature on each Core:
- ●Verified on hardware
- ●Compiles (not yet hardware-verified)
- ◐Partial
- ○Planned
- ·Not applicable to this MCU
The Layer column uses the SDK’s stack vocabulary — see SDK layers & API tiers. A higher layer implies the ones beneath it are present too.
Known gaps
The matrix says how far each feature has got; these say what’s missing. Each SDK header lists its own gaps (@studio unsupported), and they are published here as written. High / medium / low is how much closing the gap is worth; C API / DSL / wire is where it sits (the C API, the Studio DSL, or the Scope wire format). The subsystem guides show their own.
read_pad / read_mv_pad use the per-pad slider on the chip view, but temp_decidegc and vdd_mv return fixed values (25.0 °C / 3300 mV). No UI affordance to vary them yet — DSL programs that branch on under-/over-temp or low-VDD logic always see nominal in the IDE.
Tier 2 calls the default instance at its compile-time resolution (12-bit) and per-pad sampling speed set during init. SAMP_FAST / MED / SLOW / VERY_SLOW and ADC_6/8/10/12BIT aliases are reachable only via the explicit-handle Tier 1 API.
Tier 2 wrappers always hit core_adc1. Cores with multiple ADCs (Core.ST.H5 has ADC1 + ADC2) need per-pad peripheral tagging in tile JSON before coregen can emit the dispatch. Until then DSL programs are limited to whichever pads coregen routed to ADC1.
Hardware oversampling (2x to 256x; 1024x on Core.ST.H5) works through the HAL on the same handle: hal_adc_set_oversample(&core_adc1, ratio) averages, hal_adc_set_oversample_ex() also widens the result. There is no core_adc_* wrapper or DSL surface for it.
Regular-group external triggers ARE supported: core_adc_set_trigger() paces DMA conversions from a TIMx TRGO or EXTI line. Injected groups (a second, higher-priority conversion sequence that preempts the regular one) are not surfaced. Bring-your-own register writes if you need a preempting sample inside a control loop.
read/write round-trip within a single run, but the per-slot store is cleared on every project reload — DSL programs that rely on backup state surviving a soft reset (e.g., boot counters across the user clicking Reset in the IDE) can't be exercised end-to-end.
The TAMP block on WBA/H5 hosts the backup registers but also drives tamper detection (active edge, anti-tamper erase, time-stamping on tamper). None of that is wrapped — backup is read/write only.
DSL coverage landed: a project declares a contract, gets palette blocks to publish and receive, and can bind a characteristic to a variable so coregen generates the publisher. What is still missing is the simulator side, so a DSL program that uses BLE cannot be exercised without flashing a board.
core_ble_on_connect() and core_ble_on_disconnect() take a callback pointer rather than overriding a weak symbol, so coregen has no seam to emit a dispatcher against. Every other event in the DSL arrives through that weak-symbol pipeline. Until this is wired the only way to notice a connection from the DSL is to poll.
A `bytes` characteristic can be declared and published from C, but the DSL has no type that carries a buffer and a length, so it gets neither a publish block nor a write handler, and it cannot be bound to a variable. Scalars and strings both work.
Peripheral-only today. No scanning, no central-role connections, no GATT-client reads/writes. Tracked as its own initiative — most Bergsonne use-cases are peripheral-role (sensor advertising to a phone). Apps that need central-role drop into the WBA BLE stack.
core_ble_enable_pairing() does Just Works *legacy* pairing with bonding: characteristics require an encrypted link and the keys persist in flash NVM, so a bonded host reconnects without re-prompting. Not yet exposed: LE Secure Connections (numeric-comparison / passkey MITM protection) and directed advertising for fast reconnect to a known bonded central.
Narrower than it used to be. A contract pins its own 16-bit ids, custom (placed in the Bergsonne base UUID) or SIG-adopted, and core_ble_add_service_id() / _sig() take them verbatim, so ids never shift and deployed clients keep working. What is still missing is a fully arbitrary 128-bit UUID, which is what interop with an existing app that expects some other vendor's base requires.
The WBA radio supports LE Audio (LC3), extended advertising / 2M PHY / coded PHY, and multi-link (multiple simultaneous connections). None of that is exposed.
Single-sample writes only. STM32H5's DAC supports DMA-fed sample buffers + the internal noise / triangle wave generators — none of which are wrapped. Audio + arbitrary-waveform tiles need this.
The DAC peripheral has two channels that can be triggered together for stereo / I-Q output. Only single-channel writes are exposed.
Debug output is intentionally Tier 1 — the DSL doesn't have a notion of "debug print to a hardware probe." DSL programs use Core.USB.print for visible output; SWO is reserved for users who are already in C and have a probe attached.
All output goes to ITM stimulus port 0 with no timestamp packets. Multi-channel routing (e.g., separate streams for log vs. data) and ETM/CYCCNT correlation aren't wrapped.
The default handler dumps to USB CDC at runtime on the L4 only (the W5, H5 and L0 lose the cause), and there's no crash log that survives reset for post-mortem analysis. A small ring buffer in backup or NVM (with the captured PC / LR / xPSR / fault status registers) would make field debugging tractable.
The current Tier 2 surface is write_byte_bus / read_byte_bus / probe_bus. DSL programs that need to push a multi-byte payload (display init sequences, IMU FIFO drains) or discover what's on a bus (`scan -> int[]`) drop back to Tier 1 with a hal_i2c_t handle. Adds need the array-IN / array-OUT host-call ABI prototyped on the tile-driver side — track with the DSL Capability Coverage close.
All current operations are polled. A non-blocking variant (with completion callback or status poll) would let DSL programs interleave bus traffic without stalling the main loop. Tracked on the SDK roadmap as 'I2C interrupt-driven'.
No DMA path for bulk reads/writes. Long FIFO drains (e.g., IMU water-level batch reads) currently block on polled byte loops.
Target (device) mode landed at Tier 1: core_i2c_target_init() lets a Core answer as an I2C device on a host bus, using a register-file model with an optional ISR callback. There is no Tier 2 / DSL surface, because a register buffer and a callback have no representation in the DSL host-call ABI. A Studio-level version would need a declarative "expose these variables as registers" binding, close to what the BLE characteristic binding does.
Target mode runs on I2C1 only on Core.ST.H5 (driver-deferred). startup_stm32h523xx.s now has the I2C2 and I2C3 event and error vectors (51-54, 80-81, RM0481 Table 147), but hal_i2c.c registers a target on I2C1 alone there. I2C1 + I2C3 both work on Core.ST.L4 and Core.ST.W5; Core.ST.L0 has only I2C1.
API takes uint8_t addr — 7-bit only. No path for 10-bit-addressed peripherals (rare in practice but in spec).
No PEC, no Alert Response, no block read/write protocol framing. Not requested by any current tile.
The H5 I3C peripheral is SDR-only (RM0481 §49.2): no HDR-DDR, HDR-TSL/TSP or HDR-BT, although targets such as the ICM-42686-P offer DDR.
The peripheral can send legacy I2C messages (MTYPE 0100), but this SDK does not expose them: coregen keeps a bus on I2C unless every tile on it speaks I3C, so a mixed bus never happens.
FIFOs are serviced by polling (the controller stalls SCL for up to ~100 µs while it waits, §49.16.21); hot-join requests are NACKed (HJACK 0) and controller-role requests are not acknowledged.
Nothing here carries `@studio expose`, and Studio cannot request I3C for a bus yet; a hand-written config.json can ("i3c": true).
core_led_sos is `__attribute__((noreturn))` so it can't be exposed safely — DSL programs that "call SOS" would deadlock the worker. Reserved for fault-handler escape-to-C use.
The onboard LED is hard-wired as a digital push-pull output. No PWM dimming wrapper, no RGB/RGBW support (tiles with addressable LEDs use Disp.RGBW or similar, not core_led).
Tier 2 covers the byte-level pair (read_byte, write_byte) — enough for boot counters, single flags, small struct fields. Multi-byte read_buf / write_buf still need the array-IN / array-OUT host-call ABI prototyped on the tile-driver side. Tracked with the DSL Capability Coverage close.
read_byte / write_byte round-trip within a single run, but the per-slot store is cleared on every project reload — DSL programs that rely on NVM state surviving a soft reset (boot counters, crash flags) can't be exercised end-to-end in the IDE. Same issue the Backup register gap calls out; closing both needs persistent per-slot state across worker resets.
core_nvm_erase_all clears the region, but nothing reports the page erase count or the remaining write budget. Long-running data loggers budget their write rate from the figures in core_nvm.h.
CORE_OTP_* is backed only on Core.ST.W5 (STM32WBA55, 512 B @ 0x0BF90000). L0 / L4 / H5 report size 0 and every read/program returns an error until their OTP geometry is added. WBA55 is the only Core that currently ships a product using OTP identity.
Reads (size, slot_count, slot_is_blank, read) are safe to surface, but the burn (core_otp_program_slot) is irreversible and write-once, so it is intentionally NOT exposed to the DSL — a stray palette call could permanently consume a slot. Provisioning burns happen from C / the production programmer, not from a Studio program.
Tier 2 exposes write/read/toggle only. core_pad_speed, core_pad_output_od pull-config, and the analog-mode setter are reachable only by escaping to C. A Tier 2 wrapper (or @studio expose annotation) for slew-rate would let DSL programs tune SPI/UART rise times without leaving the IDE.
The single-pad DSL ops do a pad→port/pin lookup each call. A C fast path now exists — core_pad_resolve() + core_pad_fast_* (resolved {port,mask} handle, direct BSRR/IDR) and core_pad_bulk_write() (one atomic BSRR for many pins) — used by bitbang inner loops like the CMSIS-DAP probe. Still no DSL surface (pointer/struct ABI), so synchronized-bus / LED-matrix updates from the IDE remain a gap.
core_standby_for / core_standby reset on wake — they never return on success and can't be safely exposed to the DSL, since the simulator would deadlock on a call that never returns. Recoverable variants (sleep, stop_for) are Tier 2; reaching Standby from a DSL program needs an "escape-to-C" fallthrough or a separate "would have entered Standby" event the simulator can model.
core_stop_until_on_change blocks until a configured pad edge wakes the chip. The DSL surface is straightforward in spirit but needs the EXTI handler bridge to express "block until" cleanly across the host-call boundary. Tracked alongside the broader DSL event- model story.
The register maps behind stop_for / standby_for / RTC / backup were corrected against RM0377 and RM0493 on 2026-09-25. Stop with the watchdog running passed tests/hw-sleep-cycle on Core.ST.L4 and Core.ST.W5 (2026-09-25) and Core.ST.L0 (2026-09-26). Standby on the L0 and the W5 is compile-only. Core.ST.H5 moved to the same fed, chunked Stop code on 2026-10-13 and has not run it on a board yet; its core_standby_for() still has no watchdog check.
The IWDG keeps counting in Standby (always on the L0, by option byte on the L4/WBA) and nothing feeds it until main() runs again, so with the watchdog on (the Studio default, 5 s) core_standby_for refuses anything longer than half the timeout and returns HAL_ERROR, and core_standby_until_on_change returns without sleeping. Long sleeps use core_stop_for, which wakes to feed. Changing the IWDG_STDBY option byte would lift this on the L4/WBA; the SDK doesn't touch option bytes.
core_standby_until_on_change is implemented for STM32L422 only — Core.ST.L4.2's PA0/PA2 WKUP pins. Other Cores hit the (void) fallback and return without entering Standby.
The SDK roadmap flags LPTIM as Tier 1 high-impact: it runs in Stop without the RTC and gives finer wakeup granularity than seconds. Not wrapped here yet.
The WBA's Stop2 autonomous mode (ADC / SPI / I2C / UART operating while the CPU sleeps) is on the roadmap as Tier 3 — none of it is surfaced through core_power yet.
Tier 2 only exposes duty. Frequency is fixed at coregen time from config.json's timer.freq_hz. DSL programs that want to sweep frequency (e.g., a buzzer playing notes) need set_freq exposed per-pad — the underlying tal_pwm has the path, but the wrapper + default-instance dispatch don't.
core_every_us is Tier 1 only — it takes a C function pointer for the ISR callback. The DSL surface is `Core.Timer.tick` (in core_timer.h) which fires at config.json's timer.tick_ms; finer custom periods aren't reachable.
SDK roadmap Tier 2 item: TIM1's complementary outputs + dead-time insertion + break input are needed for half-bridge / motor / power-converter tiles. The PWM wrapper is single-channel only.
Edge-aligned PWM only. STM32 timers can do center-aligned (Mode 1 / 2 / 3) and per-channel phase offsets — useful for low-EMI switching. Not surfaced.
The four Cores set one flag for a power-on and a brownout (BORRSTF on the L4, W5 and H5, PORRSTF on the L0), so core_reset_reason() can't tell them apart and reports both as power-on. Hardware limit, not a driver gap.
Core.Power.reset() and Core.Power.reset_reason() run on the board; the simulator needs its own handlers (restart the program, report power-on) in web apps/studio/src/lib/simulator/runtime.ts.
core_reset(), the USB reset frame and the BLE R frame compile on every Core and pass the host tests (make -C sdk/serial_update test); none has run on a board yet (2026-10-16). tests/hw-reset is the L4 bench test.
The hardware exposes a CED (clock error) and SEIS (seed error) bit which core_rng_error checks, but there's no NIST SP 800-90B continuous-health-test wrapper. Crypto-grade users should consume the raw words through a vetted DRBG (mbedTLS, etc.).
The C surface has core_rtc_get_time / get_date taking out-pointers; they're not exposed because the host-call ABI doesn't map cleanly to multi-out values. The newer multi-scalar-out ABI (see DSL Capability Coverage close, 2026-05-03) could land them.
core_rtc_init always selects LSI (~32 kHz RC, ±5% typical drift on the L4/WBA). The L0's LSI is 26-56 kHz part to part; it is measured once against SYSCLK (ll_lsi_hz(), sdk/hal/hal_lsi.c) and the RTC prescaler is set from that, so the second is as good as SYSCLK at that moment (HSI16 at high / max, MSI at low / medium) and drifts with temperature. Cores with an LSE crystal need ll_rtc_init(1) — no Tier 2 wrapper that takes a clock-source argument.
The wrapper exposes whole-second resolution. STM32 RTCs have a subsecond register (1/PREDIV_S) that's reachable from ll_rtc but not from this header — wall-clock timestamps round to the second.
The stream is one-way. A host cannot start, stop or re-rate it, or pick channels; core_scope_enable() is firmware-side only. USB has an RX path and BLE would need a write characteristic beside Studio Scope.
The SDK is integer-only by convention (milli-units), so a channel in mdeg plots in mdeg. A per-channel unit + scale frame is planned (docs/scope-protocol.md, reserved frame types).
core_ble does not report the ATT MTU, so the BLE link sends 180-byte notifications. The Core requests a 251-byte MTU itself after connecting (core_ble_set_auto_mtu), but a central that answers below 183, or a project that turns the request off, would see nothing but CRC failures. Driver-deferred: needs core_ble_mtu().
The wire has no 8-byte type. Watching one is a compile error by design.
Tier 2 wraps the write side (print_bus, putc_bus) only. getc_bus, read_bus, available_bus need a Twin input affordance to be meaningful — without one the read functions would just return -1/0 always, defeating the point. Adding a "serial input" pane (analog of the GPIO toggle / ADC slider) closes both ends.
A USART<n>.TX / .RX pad in config.json makes coregen emit the handle (core_usart<n>) and its init in core_init(), with `baud` and `rx_interrupt` from interfaces.USART<n>. LPUART1 pads get no handle or init: call core_serial_init() on it by hand.
TX is polled: core_serial_write() and print return once the last byte is in the data register. RX is interrupt-driven into a ring buffer only when `rx_interrupt` is set (or hal_uart_config_t.rx_interrupt); otherwise reads poll the data register. hal_uart_tx_dma() is a stub that returns HAL_ERROR.
ll_uart_init() sets 8 data bits, no parity, 1 stop bit. hal_uart_config_t carries only `baud` and `rx_interrupt`, so RTS/CTS, parity, other stop bits, 9-bit data and inversion need register writes after init.
SDK roadmap Tier 2: LPUART works in Stop mode for wake-on-serial. Compile-only on every Core today; not surfaced through this 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.
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.
Everything takes a core_stepper_t*. Coregen does not yet read a `stepper` block from config.json, so there is no `core_stepper_move(pad, steps)`-style wrapper and nothing in the Studio palette. The natural Tier 2 shape is one axis per config.json entry with STEP/DIR pads, initialised in core_init().
The pulse is made by a compare channel, so STEP is restricted to pads carrying a TIMx.y function. There is no GPIO-bitbang fallback for arbitrary pads; DIR and ENABLE can be any pad.
The profile is trapezoidal (constant acceleration). No S-curve, no per-move acceleration override, no synchronised multi-axis moves.
Drivers' nFAULT / ALM outputs and end-stop switches are left to the application (core_pad_on_change + core_stepper_halt).
With "ble_provisioning" any phone that connects can move the node to another Thread network: there is no pairing or authorization yet. The network key cannot be read back. core_thread_prov_allow_write() is the hook for a gate; pairing-protected writes are planned.
Thread is C-only (Tier 1) for now, like BLE: no palette blocks, no twin. The role and UDP callbacks need the host-call ABI to grow first.
Every node built from the same config.json joins the same network with the same network key, unless it is moved at run time (core_thread_set_dataset(), or the open BLE provisioning service); there is no joiner / commissioner yet. Development only.
With BLE in the same image, OpenThread is rebuilt without the commissioner, joiner, CoAP secure (DTLS), SRP and DNS clients, TCP, ping, mesh and link-metrics diagnostics, with 64 message buffers instead of 128, so both stacks fit in RAM with room for an application. A full router (FTD) all the same; Thread-only builds keep ST's complete library.
The device is always a full Thread device with its receiver on; battery end devices that sleep between polls are deferred.
The prebuilt mbedTLS calls the ST HAL for the PKA; the SDK answers those calls with an error, so commissioning, SRP client keys and CoAP secure fail. AES (MAC and MLE security) runs on the AES peripheral.
Present in the OpenThread library and callable through core_thread_instance() and the OpenThread headers, but not wrapped.
Each bus type keeps its own table of 4 PALs. A fifth distinct bus of the same type gets the first bus's PAL back, so its tile driver silently talks to the first bus. No current Core has more than 4 buses of one type.
Tile drivers receive a bare tiles_pal_t* and a tile JSON; there's no wrapper here that walks coregen's tile_handles.h to enumerate "what tiles are on what bus." Each test builds the wiring by hand.
The DSL's `Core.Timer.tick` event fires at the period baked into coregen from config.json's timer.tick_ms. There's no `Core.Timer.set_tick_ms()` host call to retune at runtime — the period is fixed for the life of the program.
core_timer_capture_init / capture_read / pwm_set are Tier 1 — they take a core_timer_t* the DSL can't construct. PWM has its own default-instance helper (Core.PWM.duty); capture / measurement has no DSL surface.
The hardware supports incremental encoder mode (CH1+CH2 quadrature counting). The wrapper exposes neither encoder init nor read. Motion / rotary-encoder tiles can't use it without dropping into ll_tim.
STM32 timers can fire a single pulse of programmable width and then auto-disable — useful for camera trigger / strobe / ultrasound ranging. Not wrapped.
millis wraps at ~49.7 days. Long-running systems (industrial / datalogger) need either a 64-bit upcounter or a wrap-aware helper for diff-since-start.
The Core.USB.receive event handles inbound bytes one at a time, and Core.USB.line delivers newline-terminated text commands, but DSL programs can't poll core_usb_available() / core_usb_read(). That makes binary message-framed protocols (read N bytes after a header) awkward — the workaround is to accumulate bytes in a DSL array inside the receive handler.
print_int / float / bool emit one value per call, each on its own line. For multi-field log lines DSL programs concatenate with string ops (or call print multiple times). A `print_fmt(template, ...args)` host call would be the next step.
SDK roadmap Tier 3: drag-and-drop firmware / datalog volume needs SCSI + FAT. A feature gap, not a workflow blocker — projects that need persistent storage today reach for NVM or SD via SPI.
Core.ST.H5 has DRD silicon but the wrapper is device-only. Most projects don't need host mode; the few that do (USB-keyboard / thumb-drive readers) drop into the lower-layer USB stack.
Host-side baud changes (1200-touch bootloader trigger included) land in the lower stack. The header doesn't expose a hook for programs that want to react to baud / DTR changes.
Sending a HID report needs a buffer pointer + length. The DSL's array-host ABI (used for tile reads) could carry it, but no default-instance wrapper is wired up.
The HID descriptor is hard-coded as a 64-byte vendor report with usage page 0xFF00. No way to register custom reports (keyboard, mouse, gamepad) — that needs the full HID descriptor tooling.
Both stay C-only. The DSL reads the same answer from Core.Power.reset_reason() (2 = watchdog), which core_init() keeps for the whole run, so there is no "before studio_start" phase to model.
STM32 also has a windowed watchdog (must feed within a window, not just before the deadline). Useful for catching feed-too-fast bugs. Not wrapped here.
From the @studio unsupported notes in core_adc.h, core_backup.h, core_ble.h, core_dac.h, core_debug.h, core_fault.h, core_i2c.h, core_i3c.h, core_led.h, core_nvm.h, core_otp.h, core_pad.h, core_power.h, core_pwm.h, core_reset.h, core_rng.h, core_rtc.h, core_scope.h, core_serial.h, core_spi.h, core_stepper.h, core_thread.h, core_tiles.h, core_timer.h, core_timing.h, core_usb.h, core_usb_hid.h, core_watchdog.h — tiles@217a38f.

