Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
154b84f
cmake: microwakeword: dynamic toolchain, libc shim, and compiler disc…
lrgirdwo Sep 30, 2026
fe47888
audio: intel_uaol: only advertise UAOL links that answered
lrgirdwo Sep 30, 2026
83b9fa3
audio: kpb: support multi-sink topology binding and synchronous reset…
lrgirdwo Sep 30, 2026
156677d
ipc: notifier: add D0ix power state notifier
lrgirdwo Sep 30, 2026
8412074
ipc4: helper: guard NULL module_data in ipc4_comp_connect()
lrgirdwo Sep 30, 2026
063afaf
ipc4: handler: support hostless pipelines and handle COMP_STATE_PREPARE
lrgirdwo Sep 30, 2026
a662b28
audio: wov_arbiter: notify host on detection, active slot reset, and …
lrgirdwo Sep 30, 2026
6bfcc5c
audio: ecns: support dual-rate 16k/48k processing in 20ms DP mode
lrgirdwo Sep 30, 2026
094710e
audio: microwakeword: use static storage for op resolver and interpre…
lrgirdwo Sep 30, 2026
c623875
audio: microwakeword: support D0i3 low-power listening and test trigg…
lrgirdwo Sep 30, 2026
d3a9b8a
config: disable USB Audio Offload (UAOL) by default
lrgirdwo Sep 30, 2026
f3ca82c
configs: ace30: enable DRC, TDFB, Multiband DRC, Aria, and MUX open m…
lrgirdwo Sep 30, 2026
f0d96bf
topology2: dmic-wov-multi: add dual-rate DMIC architecture and multi-…
lrgirdwo Sep 30, 2026
20d5d0c
topology2: integrate multi-slot WoV into SoundWire RT721/722 and HDA …
lrgirdwo Sep 30, 2026
739b48a
topology2: prebuilt: update 4-channel DMIC WoV topology binaries for …
lrgirdwo Sep 30, 2026
cec57e8
tools: wov_capture: add blocking read utilities and S0/D0i3 test runner
lrgirdwo Sep 30, 2026
5a254da
doc: topology2/audio: document multi-slot WoV, dual-rate DMIC, and D0…
lrgirdwo Sep 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ The script will guide you through the process of installing system dependencies,

See [unit testing documentation](https://thesofproject.github.io/latest/developer_guides/unit_tests.html)

### Wake-on-Voice (WoV) Multi-Slot Hardware Verification

For automated hardware testing of the 4-channel native 16 kHz DMIC Wake-on-Voice pipeline with multi-slot microWakeWord (MWW) on Intel Panther Lake (PTL), see the comprehensive runbook:
* **Documentation & Reproduction Guide**: [Wake-on-Voice (WoV) S0 / D0i3 Multi-Slot Testing & Verification](tools/topology/topology2/README.md#wake-on-voice-wov-s0--d0i3-multi-slot-testing--verification)
* **Required Kernel Branch**: `wov-ipc4-d0i3` on [`lgirdwood/linux`](https://github.com/lgirdwood/linux/tree/wov-ipc4-d0i3) (`SNDRV_PCM_INFO_NO_PERIOD_WAKEUP` + `SOF_IPC4_NOTIFY_PHRASE_DETECTED`)
* **Required Firmware Branch**: `wcl-uaol-wov-002` on [`lgirdwood/sof`](https://github.com/lgirdwood/sof/tree/wcl-uaol-wov-002)
* **Verified Results**: 20 / 20 consecutive passes in S0 mode with synthetic fake-wake detection (`CONFIG_COMP_MWW_FAKE_WAKE_MS=5000`), zero xruns, and zero kernel IPC/ASoC errors.

## Deployment

TODO: Add additional notes about how to deploy this on a live system
Expand Down
2 changes: 1 addition & 1 deletion app/boards/intel_adsp/Kconfig.defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ config MM_DRV
default y

config UAOL
default y if ACE
default n


# # Zephyr / power settings
Expand Down
5 changes: 5 additions & 0 deletions app/boards/intel_adsp_ace30_ptl.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ CONFIG_IPC_MAJOR_4=y
CONFIG_COMP_TESTER=m
CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y
CONFIG_FORMAT_CONVERT_HIFI3=n
CONFIG_COMP_DRC=m
CONFIG_COMP_TDFB=m
CONFIG_COMP_MULTIBAND_DRC=m
CONFIG_COMP_ARIA=m
CONFIG_COMP_MUX=m

# SOF / audio modules / mocks
# This mock is part of official sof-bin releases because the CI that
Expand Down
5 changes: 5 additions & 0 deletions app/boards/intel_adsp_ace30_wcl.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ CONFIG_IPC_MAJOR_4=y
CONFIG_COMP_TESTER=m
CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y
CONFIG_FORMAT_CONVERT_HIFI3=n
CONFIG_COMP_DRC=m
CONFIG_COMP_TDFB=m
CONFIG_COMP_MULTIBAND_DRC=m
CONFIG_COMP_ARIA=m
CONFIG_COMP_MUX=m

# SOF / audio modules / mocks
# This mock is part of official sof-bin releases because the CI that
Expand Down
21 changes: 21 additions & 0 deletions app/configs/ptl/modules.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
CONFIG_DTS_CODEC=m
CONFIG_SAMPLE_SMART_AMP=m
CONFIG_COMP_STUBS=y
CONFIG_COMP_MIXIN_MIXOUT=m
CONFIG_COMP_FIR=m
CONFIG_COMP_IIR=m
CONFIG_COMP_DRC=m
CONFIG_COMP_MUX=m
CONFIG_COMP_SEL=m
CONFIG_COMP_SRC=m
CONFIG_COMP_ARIA=m
CONFIG_COMP_ASRC=m
CONFIG_COMP_MFCC=m
CONFIG_COMP_TDFB=m
CONFIG_COMP_VOLUME=m
CONFIG_COMP_DCBLOCK=m
CONFIG_COMP_CROSSOVER=m
CONFIG_COMP_MULTIBAND_DRC=m
CONFIG_COMP_GOOGLE_CTC_AUDIO_PROCESSING=m
CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING=m
CONFIG_COMP_TESTER=m
19 changes: 19 additions & 0 deletions app/configs/wcl/modules.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1,21 @@
CONFIG_DTS_CODEC=m
CONFIG_SAMPLE_SMART_AMP=m
CONFIG_COMP_STUBS=y
CONFIG_COMP_MIXIN_MIXOUT=m
CONFIG_COMP_FIR=m
CONFIG_COMP_IIR=m
CONFIG_COMP_DRC=m
CONFIG_COMP_MUX=m
CONFIG_COMP_SEL=m
CONFIG_COMP_SRC=m
CONFIG_COMP_ARIA=m
CONFIG_COMP_ASRC=m
CONFIG_COMP_MFCC=m
CONFIG_COMP_TDFB=m
CONFIG_COMP_VOLUME=m
CONFIG_COMP_DCBLOCK=m
CONFIG_COMP_CROSSOVER=m
CONFIG_COMP_MULTIBAND_DRC=m
CONFIG_COMP_GOOGLE_CTC_AUDIO_PROCESSING=m
CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING=m
CONFIG_COMP_TESTER=m
13 changes: 13 additions & 0 deletions app/wov-d0i3-fake-wake.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# microWakeWord synthetic fake-wake test aid
# Synthesizes keyword detections after 5000 ms to enable automated
# Wake-on-Voice (WoV) S0 and D0i3 sleep-and-wake testing in lab environments
# without requiring physical audio hardware or a speaker/microphone.
CONFIG_COMP_MWW_FAKE_WAKE_MS=5000

# LLEXT relocatable ELF format for loadable modules on Intel ADSP ACE 3.0
CONFIG_LLEXT_TYPE_ELF_RELOCATABLE=y

# Disable USB Audio Offload (UAOL)
CONFIG_UAOL=n
CONFIG_DAI_INTEL_UAOL=n

Binary file added build/sof-ptl-dmic-wov-multi-4ch.tplg
Binary file not shown.
Binary file added build/sof-tgl-dmic-wov-multi-4ch.tplg
Binary file not shown.
Binary file added build/sof-wcl-dmic-wov-multi-4ch.tplg
Binary file not shown.
127 changes: 83 additions & 44 deletions doc/developer_guides/wov_ecns_integration_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,68 +26,107 @@ The pipeline separates real-time audio ingest into two primary scheduling domain
- **Data Processing (DP) Domain (20 ms period)**: Runs block-based processing for acoustic echo cancellation, noise suppression, circular buffering, feature extraction, and neural network keyword classification.

```mermaid
graph TD
subgraph P100["Pipeline 100 — 4ch DMIC Ingest (Core 0, LL 1ms)"]
DAI["DAI Copier (dmic01 / dmic16k)<br>4ch · 16 kHz · S16_LE<br>Ch 0,1: Microphones | Ch 2,3: Far-End Echo Ref"]
MIX100["mixin 100.1<br>(4ch Pass-through)"]
DAI --> MIX100
flowchart TD
%% Hardware PDM Interfaces
subgraph PDM_INTERFACES ["Digital Microphone Interfaces (2 Separate PDM DAIs)"]
direction LR
PDM_48K["<b>PDM Interface: dmic01</b><br/>DAI Index: 0 (PDM0)<br/>Rate: <b>48 kHz</b> · 2ch Stereo · 16-bit"]
PDM_16K["<b>PDM Interface: dmic16k</b><br/>DAI Index: 1 (PDM1)<br/>Rate: <b>16 kHz</b> · 2ch Stereo · 16-bit"]
end

%% Pipeline 110: 48 kHz DAI Capture
subgraph P110 ["Pipeline 110: 48 kHz DAI Ingest (Core 0, LL 1ms)"]
DAI_48K["dai-copier.DMIC.dmic01.capture<br/>(48 kHz · 2ch · S16_LE)"]
MIXIN_110["mixin.110.1<br/>(48 kHz · 2ch)"]
DAI_48K --> MIXIN_110
end

subgraph P105["Pipeline 105 — ECNS Processing (Core 0, DP 20ms)"]
MO105["mixout 105.1<br>(4ch input)"]
ECNS["ecns.105.1<br>(Custom ECNS DP Module, 20ms = 320 frames)<br>Echo Cancellation + Noise Reduction"]
MIX105_1["mixin 105.1<br>(Pin 0: 1ch Mono Clean)"]
MIX105_2["mixin 105.2<br>(Pin 1: 2ch Stereo Clean)"]
MO105 --> ECNS
ECNS -- "Pin 0 (1ch Mono)" --> MIX105_1
ECNS -- "Pin 1 (2ch Stereo)" --> MIX105_2
%% Pipeline 119: 16 kHz DAI Capture
subgraph P119 ["Pipeline 119: 16 kHz DAI Ingest (Core 0, LL 1ms)"]
DAI_16K["dai-copier.DMIC.dmic16k.capture<br/>(16 kHz · 2ch · S16_LE)"]
MIXIN_119["mixin.119.1<br/>(16 kHz · 2ch)"]
DAI_16K --> MIXIN_119
end

subgraph P106["Pipeline 106 — KPB History Buffer (Core 0, DP 20ms)"]
MO106["mixout 106.1<br>(1ch mono)"]
KPB["kpb.106.1<br>(2.0s mono circular history = 64 KB)<br>16 kHz · 1ch · S16_LE"]
MIX106["mixin 106.1<br>(Fan-out Bus)"]
MO106 --> KPB
KPB --> MIX106
PDM_48K --> DAI_48K
PDM_16K --> DAI_16K

%% Pipeline 115: Dual-Rate ECNS DP Pipeline
subgraph P115 ["Pipeline 115: ECNS Processing (Core 0, DP 20ms, lp_mode 1)"]
direction TB

MIXOUT_115_2["mixout.115.2<br/>(Pin 0 In: 16 kHz · 2ch stereo)"]
MIXOUT_115_1["mixout.115.1<br/>(Pin 1 In: 48 kHz · 2ch stereo)"]

ECNS["ecns.115.1 (Custom ECNS DP Module · 20ms Period)<br/>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━<br/><b>Pin 0 In:</b> 16 kHz, 2ch (IBS 1280 bytes)<br/><b>Pin 1 In:</b> 48 kHz, 2ch (IBS 3840 bytes)<br/>─────────────────────────────────<br/><b>Processing:</b><br/>• Pin 0: Extract Left Ch → 16k Mono Clean<br/>• Pin 1: 1-to-1 Stereo Copy → 48k Stereo Clean<br/>─────────────────────────────────<br/><b>Pin 0 Out:</b> 16 kHz, 1ch mono (OBS 640 bytes)<br/><b>Pin 1 Out:</b> 48 kHz, 2ch stereo (OBS 3840 bytes)"]

MIXIN_115_1["mixin.115.1 ('KPB mixin')<br/>(16 kHz · 1ch mono)"]
MIXIN_115_2["mixin.115.2 ('Host mixin')<br/>(48 kHz · 2ch stereo)"]

MIXOUT_115_2 -->|"Pin 0 In (16k)"| ECNS
MIXOUT_115_1 -->|"Pin 1 In (48k)"| ECNS

ECNS -->|"Pin 0 Out (Mono clean)"| MIXIN_115_1
ECNS -->|"Pin 1 Out (Stereo clean)"| MIXIN_115_2
end

subgraph WOV_Slots["Pipelines 101–103 — Concurrent WOV Keyword Spotters (Core 0, DP 20ms)"]
subgraph Slot0["Slot 0 (Pipeline 101)"]
MO101["mixout 101.1"] --> FE0["Feature Extract<br>(e.g. MFCC)"] --> WOV0["WOV Engine 0<br>(e.g. Strawberry)"]
MIXIN_119 --> MIXOUT_115_2
MIXIN_110 --> MIXOUT_115_1

%% Pipeline 117: Host Capture PCM 11
subgraph P117 ["Pipeline 117: ECNS Clean Host Capture (Core 0, LL 1ms)"]
MIXOUT_117["mixout.117.1<br/>(48 kHz · 2ch stereo)"]
HOST_11["host-copier.11.capture<br/>(PCM 11: hw:0,11)<br/>2ch Stereo Clean Speech<br/>capture_compatible_d0i3: true"]
MIXOUT_117 --> HOST_11
end

MIXIN_115_2 --> MIXOUT_117

%% Pipeline 116: KPB History Buffer
subgraph P116 ["Pipeline 116: KPB History Buffer (Core 0, DP 20ms)"]
MIXOUT_116["mixout.116.1<br/>(16 kHz · 1ch mono)"]
KPB["kpb.116.1<br/>(2.0s mono circular history = 64 KB)"]
MIXIN_116["mixin.116.1<br/>(Fan-out Bus)"]
MIXOUT_116 --> KPB
KPB -->|"Pin 0 (Live Feed)"| MIXIN_116
end

MIXIN_115_1 --> MIXOUT_116

%% WOV Keyword Spotter Slots
subgraph WOV_Slots ["Pipelines 111–113: Concurrent WOV Keyword Spotters (Core 0, DP 10ms)"]
subgraph Slot0 ["Slot 0 (Pipeline 111)"]
M1["mixout.111.1"] --> FE0["Feature Extract<br>(MFCC)"] --> WOV0["WOV Engine 0<br>(e.g. Strawberry)"]
end
subgraph Slot1["Slot 1 (Pipeline 102)"]
MO102["mixout 102.1"] --> FE1["Feature Extract<br>(e.g. MFCC)"] --> WOV1["WOV Engine 1<br>(e.g. Banana)"]
subgraph Slot1 ["Slot 1 (Pipeline 112)"]
M2["mixout.112.1"] --> FE1["Feature Extract<br>(MFCC)"] --> WOV1["WOV Engine 1<br>(e.g. Banana)"]
end
subgraph Slot2["Slot 2 (Pipeline 103)"]
MO103["mixout 103.1"] --> FE2["Feature Extract<br>(e.g. MFCC)"] --> WOV2["WOV Engine 2<br>(e.g. Orange)"]
subgraph Slot2 ["Slot 2 (Pipeline 113)"]
M3["mixout.113.1"] --> FE2["Feature Extract<br>(MFCC)"] --> WOV2["WOV Engine 2<br>(e.g. Orange)"]
end
end

subgraph P104["Pipeline 104 — WOV Arbiter (Core 0, DP 20ms)"]
ARB["wov-arbiter.104.1<br>(Priority / First-Detect Selector)"]
end
MIXIN_116 --> M1
MIXIN_116 --> M2
MIXIN_116 --> M3

subgraph Host_Capture["Host Capture Interfaces (ALSA PCMs)"]
HC10["host-copier.10.capture<br>(PCM 10: hw:0,10)<br>2ch Stereo Clean Speech"]
HC11["host-copier.11.capture<br>(PCM 11: hw:0,11)<br>1ch Pre-roll + Live Keyword Speech"]
%% Pipeline 114: WOV Arbiter & Host Capture PCM 12
subgraph P114 ["Pipeline 114: WOV Arbiter & Capture (Core 0, LL 1ms)"]
ARB["wov-arbiter.114.1<br/>(Priority / First-Detect Selector)"]
HOST_12["host-copier.12.capture<br/>(PCM 12: hw:0,12)<br/>1ch Pre-roll + Live Keyword Speech<br/>capture_compatible_d0i3: true"]
ARB --> HOST_12
end

MIX100 --> MO105
MIX105_1 --> MO106
MIX105_2 --> MO107["mixout 107.1"] --> HC10
MIX106 --> MO101
MIX106 --> MO102
MIX106 --> MO103
WOV0 --> ARB
WOV1 --> ARB
WOV2 --> ARB
ARB --> HC11
KPB -->|"Pin 1 (Host Sink Drain)"| ARB
WOV0 -->|"Pin 1 (Trigger 0)"| ARB
WOV1 -->|"Pin 2 (Trigger 1)"| ARB
WOV2 -->|"Pin 3 (Trigger 2)"| ARB

style ECNS fill:#1b4f72,stroke:#5dade2,color:#fff
style KPB fill:#1c4966,stroke:#5dade2,color:#fff
style ARB fill:#4a235a,stroke:#bb8fce,color:#fff
style HC10 fill:#1e8449,stroke:#58d68d,color:#fff
style HC11 fill:#2d5a27,stroke:#58d68d,color:#fff
style HOST_11 fill:#1e8449,stroke:#58d68d,color:#fff
style HOST_12 fill:#2d5a27,stroke:#58d68d,color:#fff
```

---
Expand Down
73 changes: 73 additions & 0 deletions scripts/mww_shim_flatten.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2026 Intel Corporation. All rights reserved.

"""Flatten -ffunction-sections naming in the microwakeword libc shim objects.

The shim is assembled by extracting prebuilt members out of the toolchain's
libc.a/libm.a. Those members were compiled without -mlongcalls, so their calls
to the libgcc soft-float helpers (__addsf3, __mulsf3, __adddf3, ...) are plain
CALL8 instructions with a +/-512KB reach.

Zephyr's Xtensa linker script places plain `.text` (where libgcc's helpers live)
via `*(.literal .text)`, i.e. at the very front of the .text output section, and
per-function `.text.*` via the later `*(.literal.* .text.*)` rule, i.e. at the
very back. On a large image (ptl) the two ends are more than 512KB apart and the
link fails with:

dangerous relocation: call8: call target out of range: __addsf3

Renaming the shim's `.text.<fn>`/`.literal.<fn>` back to plain `.text`/`.literal`
puts these few functions in the same early cluster as libgcc, so the calls stay
in range. Order within a single `*(...)` wildcard follows input order, so each
object's `.literal` still precedes its `.text` and l32r reach is unaffected.
"""

import pathlib
import struct
import subprocess
import sys


def section_names(path):
"""Return the section names of a 32-bit little-endian ELF object."""
data = path.read_bytes()
if data[:4] != b'\x7fELF' or data[4] != 1 or data[5] != 1:
return []

e_shoff, = struct.unpack_from('<I', data, 0x20)
e_shentsize, e_shnum, e_shstrndx = struct.unpack_from('<HHH', data, 0x2e)
if not e_shoff or not e_shnum:
return []

strtab_off, = struct.unpack_from('<I', data, e_shoff + e_shstrndx * e_shentsize + 0x10)

names = []
for i in range(e_shnum):
sh_name, = struct.unpack_from('<I', data, e_shoff + i * e_shentsize)
start = strtab_off + sh_name
end = data.index(b'\0', start)
names.append(data[start:end].decode())
return names


def main():
if len(sys.argv) != 3:
sys.exit(f'usage: {sys.argv[0]} <objcopy> <dir>')

objcopy, directory = sys.argv[1], pathlib.Path(sys.argv[2])

for obj in sorted(directory.glob('*.o')):
args = []
for name in section_names(obj):
for prefix in ('.text.', '.literal.'):
if name.startswith(prefix):
args += ['--rename-section',
f'{name}={prefix.rstrip(".")}']
break
if args:
subprocess.run([objcopy] + args + [str(obj)], check=True)


if __name__ == '__main__':
main()
Loading
Loading