Here’s the current readme to give you an idea of the scope though keep in mind it’s a WIP so this is subject to change:
qorvo-sdk-zephyr
A Zephyr module providing the Qorvo DW3 QM33 SDK with FiRa MAC support. This is a fork of Qorvo’s SDK 1.1.1 with patches making it actually buildable under vanilla Zephyr 4.2.
Status
Working: builds and links against vanilla Zephyr 4.2 for nRF52833 (DWM3001CDK).
Tested chips: DW3110 (via the DWM3001CDK module).
Tested host MCUs: nRF52833 only. The fork has Nordic-specific assumptions (see Limitations) and will not build on other SoC families without additional work.
Tested Zephyr version: 4.2.0. Earlier versions fail for unrelated API differences; later versions are untested.
Quick start
1. Add this fork to your west workspace
In your west.yml:
manifest:
projects:
- name: qorvo-sdk-zephyr
url: <your fork URL>
revision: main
path: modules/lib/qorvo
Then west update.
2. Describe the UWB chip in your device tree
This SDK reads the UWB chip’s wiring from device tree. Add a node with compatible = "decawave,dw3000" to the SPI bus the chip is wired to, with these properties:
reg — chip select line index on the SPI bus (usually <0>)
spi-max-frequency — SPI clock rate in Hz; 8 MHz is conservative
rstn-gpios — chip reset line (active low)
irq-gpios — chip interrupt line
The node label is yours to choose; the SDK finds the chip by compatible string, not by label.
Example: DWM3001CDK
The upstream Zephyr board file for the DWM3001CDK declares the SPI3 bus but does not declare the on-module DW3110 chip itself. Applications must provide an overlay. Save as boards/decawave_dwm3001cdk.overlay in your application:
&spi3 {
dw3110_uwb: dw3110@0 {
compatible = "decawave,dw3000";
status = "okay";
reg = <0>;
spi-max-frequency = <8000000>;
rstn-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
irq-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
};
};
Pin assignments are per the DWM3001C module schematic (Qorvo FOR-001324). The board file’s existing cs-gpios = <&gpio1 6> on &spi3 is correct and should not be overridden.
For other boards
Add an analogous node describing your hardware’s wiring. The SDK’s chip-finding logic is DT_COMPAT_GET_ANY_STATUS_OKAY(decawave_dw3000) — any node with that compatible and the required properties will be found.
3. Enable in prj.conf
CONFIG_QORVO_UWB=y
CONFIG_QORVO_UWB_DELIVERY_FIRA=y
# Required by the bundled mbedTLS-backed mcps_crypto implementation
# (Not needed if you disable CONFIG_QORVO_CRYPTO_MBEDTLS to provide
# your own crypto backend.)
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_BUILTIN=y
CONFIG_MBEDTLS_CMAC=y
CONFIG_MBEDTLS_CIPHER_CCM_ENABLED=y
That’s the minimum. Optional Kconfig values for tuning are listed below.
4. Build
west build -b decawave_dwm3001cdk
Configuration reference
All options are under the QORVO_UWB Kconfig menu. Most have sensible defaults; you only need to set them if your hardware differs from typical assumptions.
Required
| Option |
Description |
CONFIG_QORVO_UWB |
Enables this SDK |
MAC delivery (pick one)
| Option |
Description |
CONFIG_QORVO_UWB_DELIVERY_FIRA (default) |
FiRa-only MAC binary (smaller) |
CONFIG_QORVO_UWB_DELIVERY_FULL |
Full MAC (FiRa + Apple NI + automotive) |
SPI configuration
| Option |
Default |
Description |
CONFIG_UWB_SPI_FRAME_SIZE |
8 |
SPI frame width in bits; do not change |
CONFIG_UWB_SPI_INSTANCE |
1 |
SPI peripheral instance number |
CONFIG_SPI_UWB_IRQ_PRIORITY |
2 |
Interrupt priority for UWB IRQ |
CONFIG_SPI_UWB_FAST_RATE_FREQ |
8 MHz |
SPI clock after init (max 38 MHz per datasheet) |
CONFIG_SPI_UWB_SLOW_RATE_FREQ |
4 MHz |
SPI clock during cold-start (max 7 MHz per datasheet) |
Platform implementation
| Option |
Default |
Description |
CONFIG_QHAL_IMPL_ZEPHYR |
y |
OS abstraction uses Zephyr APIs |
CONFIG_QPLATFORM_IMPL_NRF |
y on Nordic SoCs |
Nordic-specific hardware defaults |
Crypto
| Option |
Default |
Description |
CONFIG_QORVO_CRYPTO_MBEDTLS |
y if MBEDTLS |
Provides mcpscrypto* via mbedTLS |
CONFIG_HEAP_MEM_POOL_ADD_SIZE_QORVO_CRYPTO |
8192 |
Heap added for crypto contexts |
Disable QORVO_CRYPTO_MBEDTLS if you want to provide the mcpscrypto_ symbols from your own implementation (e.g. a hardware crypto backend). When disabled, you must supply implementations for: mcps_crypto_init, mcps_crypto_deinit, mcps_crypto_get_random, mcps_crypto_cmac_aes_128_digest, mcps_crypto_cmac_aes_256_digest, the mcps*crypto_aead_aes_ccm_star_128*_family (5 functions), and themcps*crypto_aes_ecb_128*\* family (4 functions).
Limitations
Nordic-only platform support
The qplatform layer’s idle timer values (RTC instance, frequency, counter width) are hardcoded inside #ifdef CONFIG_QPLATFORM_IMPL_NRF. Running on a non-Nordic SoC requires either:
- Extending the
#ifdef chain in qm33_qhal_common/src/qplatform.c with values for your platform, or
- Refactoring those values into Kconfig options with per-SoC defaults (planned, not yet done).
Until then, building on STM32, NXP, ESP32, etc. will fail with #error "Please provide idle timer configuration for selected platform."
Single chip family supported
Currently only decawave,dw3000 is wired up end-to-end. The SDK source code references decawave,dw3720 (used by the QM33 family with integrated host MCU), but this fork does not yet:
- Ship a binding for
decawave,dw3720
- Auto-select the correct driver based on DT compatible
Adding a second chip family requires creating the binding, adding it to the SDK’s chip-detection paths, and ideally driving the USE_DRV_DW3xxx build flags from DT presence (dt_has_compat(...)) so users only declare the chip once in DT rather than syncing it with CMake values.
Stubbed chip-internal persistence
persistent_config.h and persistent_time.h are stubbed. The MAC’s internal calibration data (antenna delays, etc.) does not survive reboots. Affects ranging accuracy after reset; a re-calibration is needed each boot. The proper fix is implementing these against Zephyr’s settings subsystem.
Upstream Zephyr DWM3001CDK board file is incomplete
The upstream Zephyr board file for the DWM3001CDK does not declare the on-module DW3110 chip. Until that’s fixed upstream, applications targeting this board must include the DT overlay shown above. Once the upstream board file gains the chip description, the application overlay can be removed.
MAC binary is precompiled
The FiRa MAC layer ships as a precompiled .a archive. You cannot inspect its source, modify its behavior, or fully audit it. Updates require Qorvo releasing a new SDK.
CCM vs CCM* in the bundled mbedTLS implementation
mbedTLS provides standard AES-CCM, while FiRa specifies CCM*. The two differ only in whether mac_len=0 (encryption-only mode) is permitted: CCM rejects it, CCM* allows it. The bundled wrapper returns QERR_ENOTSUP and logs a warning if called with mac_len=0. In practice FiRa always authenticates frames, so this branch should not be exercised; if you see the warning at runtime, you’ve found a case that needs proper CCM* handling.
Crypto heap sizing is a starting estimate
CONFIG_HEAP_MEM_POOL_ADD_SIZE_QORVO_CRYPTO is set to 8KB by default — enough for several concurrent CCM and ECB contexts. If you run more sessions than this can support, allocations fail at runtime and the MAC silently misbehaves. Increase CONFIG_HEAP_MEM_POOL_SIZE directly if your application needs more crypto context memory than the default sum provides.
qspi async mode is sync-with-callback
The Zephyr qspi.c implementation invokes the user handler synchronously
after spi_transceive() returns rather than from interrupt context. Grep
confirms no code in the SDK currently calls qspi_irq_set_callback with a
non-NULL handler, so this is harmless today. If a future SDK update or
custom code passes a non-NULL handler expecting interrupt-context
delivery, behavior will be wrong.
qspi_close does not release the Zephyr SPI device
Zephyr’s SPI API has no explicit release primitive. If you share the UWB
SPI controller with another device, add SPI_LOCK_ON to operation flags
so transfers serialize correctly.
Architecture notes
The SDK has three layers, organized by what they abstract:
- OS abstraction (
qosal/, qhal/src/zephyr/) — wraps OS APIs (threads, GPIO, SPI, timers). Zephyr-specific implementations live under zephyr/ subdirectories.
- Chip abstraction (
qhal/src/qm33/, qplatform/qm33_qhal_zephyr/) — wraps DW3xxx-family chip operations. Builds on top of the OS abstraction.
- MAC (
uwbstack_libs/) — precompiled FiRa MAC binary. Calls into the chip abstraction.
The qm33 directory naming refers to a Qorvo product family that includes DW3110, DW3120, DW3572, etc. — not exclusively the DW3572. Code in qhal/src/qm33/ works for any DW3xxx-family chip that fits the abstraction.