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.
SYSCLK_HZ is off by 2x; its interrupt table is shifted from IRQ 27; and SPI has no kernel clock. The rows this touches are marked partial with a note until the H5 fix session. See Clocks.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@044f8f3. 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.CH.*, Core.N.*. 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 | · | ● | · | ● |
| 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 | · | · | · | ○ | |
| 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 | ● | ● | ● | ● |
| 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 | · | ● | · | ● |
| SWD flash from Studio | Studio | ○ | ○ | ● | ○ |
| BLE radio | · | · | ● | · | |
| BLE advertising | · | · | ● | · | |
| BLE GATT server | · | · | ● | · | |
| FDCAN | · | · | · | ○ | |
| Advanced | |||||
| I3C controller | · | · | · | ○ | |
| I3C target | · | · | · | ○ | |
| 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 | · | · | ○ | · |
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.
STM32 ADCs can hardware-oversample 2×–256× for extra effective resolution. The HAL exposes the registers but core_adc has no convenience wrapper — callers reach into tal_adc / hal_adc to set OVSR/OVSS bits manually.
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.
startup_stm32h523xx.s carries vectors for I2C1_EV, I2C1_ER and I2C2_EV only. There is no I2C2_ER or I2C3 entry, and slot 58 is labelled as I2C2 Error shared with USART1, so target mode is restricted to I2C1 on Core.ST.H5. 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.
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_write returns -1 on Core.ST.H5 (CORE_NVM_SIZE is 0). The flash emulation behind Core.ST.L4 and Core.ST.W5 has not been ported to the H523's 8 KB sectors yet. Until it is, H5 programs that need persistent state are limited to backup registers.
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 (the L0 enabled the LSE instead of the LSI and hung; the WBA wrote CCIPR instead of BDCR1, had no RTC bus clock and a reserved Standby LPMS). The W5's Stop passed tests/hw-sleep-cycle on 2026-09-25; its Standby and all of the L0 are compile-only until they do. Core.ST.L4 passed the same test; Core.ST.H5 keeps its older sleep code until its own fix session.
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 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 is 26-56 kHz part to part and is not calibrated against HSI16, so its RTC second can be off by tens of percent). 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. A central that stayed at the 23-byte default 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.
Tier 2 wrappers assume the user has called core_serial_init on the matching handle before any send. Coregen doesn't yet read a `serial: { 1: { baud: 115200 } }` block out of config.json and emit the init in core_init(). Until it does, the bus has to be primed by hand even when using Tier 2.
All calls block. SDK roadmap Tier 1 item: "UART IRQ + coregen" for non-blocking serial (GPS, RS-485). Long reads currently stall the main loop until the buffer fills or times out.
The thin wrappers expose only the basics — RTS/CTS, parity, stop bits, 9-bit data, and inversion are reachable only via the hal_uart_config_t struct passed to 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.
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.
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).
The internal slot table is sized to 4. Designs with >4 simultaneous I2C buses (or >4 SPI buses) will silently route extras to slot 0, which corrupts the cached function pointers if the bus types differ. No current Core has that many buses but the cap is undocumented for future-proofing.
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, but DSL programs can't poll core_usb_available() / core_usb_read(). That makes 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.
These return / clear hardware flags that only matter on the very first boot iteration. Exposing them needs a story for "before studio_start runs" — Studio doesn't currently model that phase.
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_led.h, core_nvm.h, core_otp.h, core_pad.h, core_power.h, core_pwm.h, core_rng.h, core_rtc.h, core_scope.h, core_serial.h, core_spi.h, core_stepper.h, core_tiles.h, core_timer.h, core_timing.h, core_usb.h, core_usb_hid.h, core_watchdog.h — tiles@6af026f.

