Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions app/boards/intel_adsp_ace15_mtpm.conf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ CONFIG_FORMAT_CONVERT_HIFI3=n
CONFIG_SAMPLE_KEYPHRASE=y
CONFIG_COMP_PHASE_VOCODER=y
CONFIG_COMP_STFT_PROCESS=y
CONFIG_SOF_STAGING=y
CONFIG_CPP=y
CONFIG_STD_CPP17=y
CONFIG_COMP_TENSORFLOW=m

# SOF / audio modules / mocks
# This mock is part of official sof-bin releases because the CI that
Expand Down
8 changes: 7 additions & 1 deletion app/boards/intel_adsp_ace30_ptl.conf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ CONFIG_FORMAT_CONVERT_HIFI3=n
CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING=m
CONFIG_GOOGLE_RTC_AUDIO_PROCESSING_MOCK=y
CONFIG_COMP_STFT_PROCESS=y
CONFIG_SOF_STAGING=y
CONFIG_COMP_KPB=y
CONFIG_COMP_TENSORFLOW=y
CONFIG_COMP_MFCC=y
CONFIG_COMP_VOLUME=y
CONFIG_COMP_GAIN=y

# SOF / infrastructure
CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL=n
Expand All @@ -29,7 +35,7 @@ CONFIG_COLD_STORE_EXECUTE_DRAM=y
CONFIG_INTEL_MODULES=y
CONFIG_LIBRARY_AUTH_SUPPORT=y
CONFIG_LIBRARY_MANAGER=y
CONFIG_LIBRARY_BASE_ADDRESS=0xa0688000
CONFIG_LIBRARY_BASE_ADDRESS=0xa0700000
CONFIG_LIBRARY_BUILD_LIB=y
CONFIG_LIBRARY_DEFAULT_MODULAR=y

Expand Down
1 change: 1 addition & 0 deletions app/llext_relocatable.conf
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
CONFIG_LLEXT_TYPE_ELF_RELOCATABLE=y
CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y
23 changes: 15 additions & 8 deletions scripts/llext_link_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ def main():

command = [args.command]

is_relocatable = '-r' in args.params

executable = []
writable = []
readonly = []
Expand Down Expand Up @@ -111,7 +113,8 @@ def main():
text_found = True
text_addr = max_alignment(text_addr, 0x1000, s_alignment)
text_size = s_size
command.append(f'-Wl,-Ttext=0x{text_addr:x}')
if not is_relocatable:
command.append(f'-Wl,-Ttext=0x{text_addr:x}')
else:
executable.append(section)

Expand Down Expand Up @@ -164,7 +167,8 @@ def main():

dram_addr = align_up(dram_addr, s_alignment)

command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}')
if not is_relocatable:
command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}')

dram_addr += section.header['sh_size']

Expand All @@ -177,7 +181,8 @@ def main():

dram_addr = align_up(dram_addr, s_alignment)

command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}')
if not is_relocatable:
command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}')

dram_addr += section.header['sh_size']

Expand All @@ -189,7 +194,8 @@ def main():

start_addr = align_up(start_addr, s_alignment)

command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}')
if not is_relocatable:
command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}')

start_addr += section.header['sh_size']

Expand All @@ -201,10 +207,11 @@ def main():

start_addr = align_up(start_addr, s_alignment)

if s_name == '.data':
command.append(f'-Wl,-Tdata=0x{start_addr:x}')
else:
command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}')
if not is_relocatable:
if s_name == '.data':
command.append(f'-Wl,-Tdata=0x{start_addr:x}')
else:
command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}')

start_addr += section.header['sh_size']

Expand Down
3 changes: 3 additions & 0 deletions scripts/llext_offset_calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ def get_elf_size(elf_name):
if section.header['sh_addr'] + section.header['sh_size'] > end:
end = section.header['sh_addr'] + section.header['sh_size']

if start == 0xffffffff:
return 0

size = end - start

return size
Expand Down
156 changes: 144 additions & 12 deletions src/audio/tensorflow/README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,154 @@
# TensorFlow Lite Micro (TFLM) Architecture
# TensorFlow Lite Micro (TFLM) & Wake-on-Voice (WoV) Architecture

This directory acts as the bridge for running ML models.
This directory provides the TensorFlow Lite for Microcontrollers (TFLM) classification module (`TFLMCLY`) for Sound Open Firmware (SOF), including integration with MFCC feature extraction, mtrace logging, IPC host notifications, and Key Phrase Buffer (KPB) Wake-on-Voice (WoV) trigger infrastructure.

---

## Overview

Integrates TensorFlow Lite for Microcontrollers into the SOF audio pipeline. Evaluates pre-trained neural network topologies inline with the audio stream for tasks like wake-word, noise cancellation, or sound classification.
The TFLM module evaluates pre-trained micro speech neural network models inline within the SOF audio processing graph. It receives pre-processed audio feature tensors (e.g. 40-bin mel spectrograms from the MFCC component), runs model inference, logs keyword detections to `mtrace`, issues IPC4 notifications to the host audio driver, and signals the KPB module to drain buffered pre-keyword audio for Wake-on-Voice.

---

## Architecture & Data Flow

## Architecture Diagram
### Dual-Path Wake-on-Voice (WoV) Architecture

To allow continuous keyword evaluation without streaming audio to the host until a keyword is detected, the pipeline separates real-time keyword detection from host PCM draining via KPB:

```mermaid
graph LR
Feat[Audio Features] --> TFLM[TFLM Inference Engine]
SubGraph[FlatBuffer Model] -.-> TFLM
TFLM --> Out[Inference Labels/Scores]
graph TD
DAI[HDA Mic DAI] --> Gain[Gain Component]
Gain --> KPB[KPB Buffer Module]

subgraph "Real-Time Detection Path (KPB Pin 1)"
KPB -- Live Audio Stream --> SRC[SRC: 48kHz -> 16kHz]
SRC --> MFCC[MFCC Feature Extractor]
MFCC -- 40-bin Mel Tensors --> TFLM[TFLM Classifier: tflmcly]
TFLM --> VSink[Virtual Sink: virtual.tflm_sink]
end

subgraph "Host Draining Path (KPB Pin 2)"
KPB -- History Draining Stream --> Host[Host Copier: PCM Capture]
end

TFLM -- "1. Log to mtrace (comp_info)" --> MTrace[mtrace / SOF Trace Log]
TFLM -- "2. IPC4 Host Notification" --> IPC[Host Audio Driver]
TFLM -- "3. KPB_EVENT_BEGIN_DRAINING (notifier_event)" --> KPB
```

---

## Pipeline Execution & Event Flow

1. **Continuous Real-Time Listening**:
- Live microphone audio is captured by the HDA DAI and passed into `KPB` (`kpb.2.1`).
- KPB Output Pin 1 continuously streams audio to `SRC` (resampling 48kHz $\to$ 16kHz), `MFCC` (generating 40-bin `int8_t` mel spectrogram features), and `TFLM` (`tflmcly.1.1`).
- KPB stores the raw PCM audio continuously in its circular history buffer (e.g. 2100ms – 3000ms history).

2. **Inference & Keyword Detection**:
- `tflm_process()` feeds 1960-byte feature tensors ($40 \text{ features} \times 49 \text{ windows}$) into the Micro Speech TFLM interpreter (`[1, 49, 40]` `int8_t` input tensor).
- Upon `TF_ProcessClassify()`, predictions are evaluated across categories:
- `0`: `silence`
- `1`: `unknown`
- `2`: `yes` (Keyword)
- `3`: `no` (Keyword)

3. **Wake-on-Voice Trigger & Notification**:
When a keyword (`yes` or `no`) is detected with $\ge 0.70$ confidence:
- **mtrace Logging**: Logs detection with keyword label and confidence score via `comp_info()`:
`"TFLM keyword detected: yes (confidence 0.852)"`
- **Host IPC Notification**: Sends an IPC4 module notification (`SOF_IPC4_MODULE_NOTIFICATION`) to inform the host driver.
- **KPB Draining Signal**: Fires a system notification event (`NOTIFIER_ID_KPB_CLIENT_EVT`) with `KPB_EVENT_BEGIN_DRAINING`.
- **Host PCM Draining**: KPB opens Output Pin 2 to `host-copier`, draining pre-keyword history buffer audio followed by live mic audio to the host capture stream.

---

## Topology v2 Integration & Usage

### 1. Component Widget (`include/components/tflm.conf`)

Defines `Class.Widget."tflmcly"`:
- **UUID**: `42:c6:1d:c5:e1:a2:df:48:a4:90:e2:74:8c:b6:36:3e` (`c51dc642-a2e1-48df-a490e2748cb6363e`)
- **Type**: `effect`

### 2. Detection Pipeline Template (`include/pipelines/cavs/host-gateway-src-mfcc-tflm-capture.conf`)

Instantiates the real-time detection graph:
```conf
Object.Widget {
virtual."1" { name "virtual.tflm_sink" }
src."1" { ... }
mfcc."1" { ... }
tflmcly."1" { ... }
}
```

## Configuration and Scripts
### 3. Top-Level Topology Configuration (`sof-hda-tflm.conf`)

Instantiates the complete HDA Mic WoV topology with dual-path KPB routing:
```conf
Object.Base.route [
# DAI -> Gain -> KPB
{ source "dai-copier.HDA.Analog.capture"; sink "gain.2.1" }
{ source "gain.2.1"; sink "kpb.2.1" }

# KPB Pin 1 -> Real-time Detection Path
{ source "kpb.2.1"; sink "src.1.1" }

# KPB Pin 2 -> Host WoV Draining Path
{ source "kpb.2.1"; sink "host-copier.0.capture" }
]
```

---

## Building and Testing Topologies

### Pre-processing and Compiling with `alsatplg`

Build `sof-hda-tflm.tplg` using the Topology v2 pre-processor:

```bash
ALSA_CONFIG_DIR=tools/topology/topology2 \
tools/bin/alsatplg \
-I tools/topology/topology2/ \
-p -c tools/topology/topology2/sof-hda-tflm.conf \
-o build/sof-hda-tflm.tplg
```

### Inspecting Decoded Topology Graphs

Decode and verify the compiled `.tplg` binary:

```bash
tools/bin/alsatplg -d build/sof-hda-tflm.tplg -o decoded.txt
grep -A 20 "SectionGraph" decoded.txt
```

Expected graph output:
```text
SectionGraph {
set0 {
gain.2.1 <- dai-copier.HDA.Analog.capture
kpb.2.1 <- gain.2.1
src.1.1 <- kpb.2.1 (Real-Time Detection Path)
host-copier.0.capture <- kpb.2.1 (Host WoV Draining Path)
}
set1 {
mfcc.1.1 <- src.1.1
tflmcly.1.1 <- mfcc.1.1
virtual.tflm_sink <- tflmcly.1.1 (Detection Sink Termination)
}
}
```

---

## Source Files

- **Kconfig**: Enforces requirements for C++17 support and core framework staging logic (`COMP_TENSORFLOW`).
- **CMakeLists.txt**: An intricate build specification linking the Tensilica neural network library block computations (`nn_hifi_lib`) and the TensorFlow Lite micro core engine (`tflm_lib`). Also hooks the `tflm-classify.c` SOF adapter via compiler flags explicitly enforcing memory, precision, and XTENSA optimizations.
- **tflmcly.toml**: Topology definition for the specific TFLM Classifier implementation binding the engine against the UUID `UUIDREG_STR_TFLMCLY`.
- **[tflm-classify.c](file:///home/lrg/work/sof-ptl/sof/src/audio/tensorflow/tflm-classify.c)**: SOF module adapter implementation for TFLM.
- **[speech.cc](file:///home/lrg/work/sof-ptl/sof/src/audio/tensorflow/speech.cc)** / **[speech.h](file:///home/lrg/work/sof-ptl/sof/src/audio/tensorflow/speech.h)**: TFLM C++ API bridge & micro speech tensor wrapper.
- **[tflm.conf](file:///home/lrg/work/sof-ptl/sof/tools/topology/topology2/include/components/tflm.conf)**: Topology v2 widget class definition.
- **[host-gateway-src-mfcc-tflm-capture.conf](file:///home/lrg/work/sof-ptl/sof/tools/topology/topology2/include/pipelines/cavs/host-gateway-src-mfcc-tflm-capture.conf)**: Detection pipeline template.
- **[sof-hda-tflm.conf](file:///home/lrg/work/sof-ptl/sof/tools/topology/topology2/sof-hda-tflm.conf)**: Top-level WoV topology configuration.
Loading
Loading