Skip to content

WIP: esp32c6: add ESP32-C6 platform support and loopback test suite - #11200

Draft
lgirdwood wants to merge 5 commits into
thesofproject:mainfrom
lgirdwood:feature/esp32c6-sof-port
Draft

lgirdwood wants to merge 5 commits into
thesofproject:mainfrom
lgirdwood:feature/esp32c6-sof-port

Conversation

@lgirdwood

Copy link
Copy Markdown
Member

Summary

This draft PR introduces Sound Open Firmware (SOF) platform support for Espressif ESP32-C6 (Single-core RISC-V @ 160 MHz) development boards:

  • Seeed Studio XIAO ESP32-C6 (Default Master Tx)
  • Waveshare ESP32-C6-Zero (Default Slave Rx)

Companion Zephyr DAI driver changes are pushed to lgirdwood/zephyr on branch feature/esp32c6-dai-port.


Key Changes

  1. ESP32-C6 Platform Support:
    • Added platform initialization, mailbox stubs, memory maps, and clocks in src/platform/esp32c6/.
    • Added board configurations and devicetree overlays for xiao_esp32c6/esp32c6/hpcore and esp32c6_devkitc/esp32c6/hpcore.
  2. Static Pipeline Architecture:
    • Implemented esp32c6_pipeline_def.c with S16_LE stereo playback and capture pipelines, intermediate buffers, and static volume kcontrols.
    • Guarded UAC2-specific macros and routed direct volume/pipeline triggers when USB Audio Class stack is not present.
  3. Dynamic DAI Discovery:
    • Replaced hardcoded component IDs in sof_static_pipeline_set_clock_mode() with dynamic topology iteration matching SOF_STATIC_COMP_DAI, preventing invalid pointer casting across topologies with different component layouts.
  4. Shell & Diagnostics:
    • Added sof cap dump <count> in esp32_shell.c to inspect captured PCM stereo waveforms directly from the console.
  5. Automated Verification:
    • Added scripts/test_c6_loopback.py to automate bidirectional hardware I2S loopback testing between XIAO and Waveshare with frame synchronization, RMS/pk-pk checks, and channel symmetry verification.

Hardware Loopback Verification

Executed bidirectional loopback between Seeed XIAO and Waveshare C6-Zero connected via GPIO 18 (BCLK), GPIO 19 (WS), GPIO 20 (DOUT), and GPIO 21 (DIN):

  • Forward Loopback (XIAO Master Tx -> Waveshare Slave Rx): PASS (max|L - R| = 0, RMS 5297, pk-pk 64632).
  • Reverse Loopback (Waveshare Master Tx -> XIAO Slave Rx): PASS (max|L - R| = 0, 1000 Hz, RMS 1414, pk-pk 4000).
  • Overall test suite returned exit code 0.

@lgirdwood lgirdwood left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rebase on main as many patches have been merged.

struct dai_data *dd = comp_get_drvdata(dev);

if (dd) {
#if CONFIG_IPC_MAJOR_4

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be IPC4 only. No need for IPC3 logic.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased and updated to pure IPC4. Static pipelines and ESP32-C6 board configs now explicitly use CONFIG_IPC_MAJOR_4=y with synthesized IPC4 copier and gateway configurations.

return -ENOENT;
}

if (ctl->id == 7 || (ctl->name && !strcmp(ctl->name, "DMIC Injector Switch"))) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ctl names/mapping should be part of teh static topology. i.e. we should have switch, mixer, enum, binary kctl ops in the static ops handler

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Control declarations are defined statically within g_esp32c6_static_topology.controls using standard SOF kcontrol macros (SOF_STATIC_KCONTROL_VOLUME and SOF_STATIC_KCONTROL_ENUM). Board-specific clock mode switching is registered via .custom_control_handler = esp32c6_control_callback.

Comment thread src/audio/usb_audio.c Outdated
return 0;
}

static int usb_audio_copy(struct comp_dev *dev)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a big function, can we split into smaller functions.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Split usb_audio_copy() into distinct usb_audio_copy_playback() and usb_audio_copy_capture() helper functions.

Comment thread src/audio/usb_audio.c Outdated
struct usb_audio_ring_buffer *ring = &uad->ring;
k_spinlock_key_t key = k_spin_lock(&ring->lock);

if (s_tone_enabled) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like test code ? all test code should be in the ztests.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the test tone generator from usb_audio.c. The test tone generator is now part of the interactive diagnostic shell in src/debug/shell/esp32_shell.c (sof tone on/off).

Comment thread src/audio/usb_audio.c Outdated
} else if (val <= -32768.0f) {
s16_buf[i] = -32768;
} else {
#if defined(__riscv) && defined(__riscv_flen)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to use standard sof maths apis here, these should map at build time to correct SIMD flavour

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Removed inline RISC-V fcvt.w.s assembly and switched to standard SOF math sat_int16().

extern const struct sof_uuid volume_uuid;
extern const struct sof_uuid dai_uuid;

/* -------------------------------------------------------------------------

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should really have an ascii art description of the topology.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added ASCII art pipeline graphs for both playback (USB_PB -> Buffer -> VOL_PB -> Buffer -> DAI_I2S_PB) and capture (DAI_I2S_CAP -> Buffer -> VOL_CAP -> Buffer -> USB_CAP) pipelines at the top of esp32c6_pipeline_def.c.

Comment thread scripts/test_c6_loopback.py Outdated
Automated Hardware Loopback Test Suite for ESP32-C6 Sound Open Firmware (SOF)

Validates bidirectional I2S digital audio transmission across cross-connected
ESP32-C6 development boards:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets state thses can be any C6 variants, but example uses these boards connected as described.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated module docstring to explicitly note that the loopback test suite is generic across any cross-connected ESP32-C6 variants, referencing Seeed Studio XIAO and Waveshare C6-Zero as the reference hardware setup.

@@ -0,0 +1,770 @@
// SPDX-License-Identifier: BSD-3-Clause
/*

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already upstream, need to rebase.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased on main. Dropped the standalone sof_static_pipeline.c file as static pipeline loader support is now upstream in static_pipeline_loader.c.

Update west manifest to use lgirdwood/zephyr on branch
feature/esp32c6-dai-port and lgirdwood/hal_espressif on branch
esp32p4-uac2 for ESP32-C6 DAI driver and peripheral support.

Signed-off-by: Liam Girdwood <lgirdwood@gmail.com>
…stry entries

- Register usb_audio and bt_audio UUIDs in uuid-registry.txt.
- Define DCACHE_LINE_SIZE 32 for ESP32-C6 in rtos/cache.h.
- Add static heapmem buffer for ESP32 platforms in zephyr/lib/alloc.c.
- Update zephyr/CMakeLists.txt to handle SOF_FULL_ZEPHYR_APPLICATION.

Signed-off-by: Liam Girdwood <lgirdwood@gmail.com>
- Add COMP_USB_AUDIO pseudo-host streaming endpoint component for platforms
  without hardware USB Audio Class 2.0 device controllers, with separated
  playback and capture processing paths and standard SOF saturation math.
- In dai-zephyr.c, add SOF_DAI_ESP32_I2S and SOF_DAI_ESP32_PDM support, allow
  bursting up to 4 periods on ESP32 to absorb scheduling jitter, and ensure
  static pipelines update DAI component trigger states.
- In ipc4/dai.c and ipc3/dai.c, handle SOF_DAI_ESP32_I2S and SOF_DAI_ESP32_PDM
  in dai_config_dma_channel() and ipc_dai_data_config().
- In ipc4/helper.c, preserve stream direction in ipc4_base_module_cfg_to_stream_params().
- In pipeline-params.c, preserve stream_direction during component parameter evaluation.
- In static_pipeline_loader.c, set is_config_blob for DAI component creation.
- Add SOF_DAI_ESP32_I2S / SOF_DAI_ESP32_PDM to ipc/dai.h and
  SOF_DMA_DEV_I2S / SOF_DMA_DEV_PDM to sof/lib/dma.h.

Signed-off-by: Liam Girdwood <lgirdwood@gmail.com>
- Add CONFIG_PLATFORM_ESP32C6 to src/platform/Kconfig selecting
  ZEPHYR_LOG, ZEPHYR_NATIVE_DRIVERS, STATIC_PIPELINE, and IPC_MAJOR_4.
- Add src/platform/esp32c6 platform support with clock control,
  interrupt handlers, mailbox stubs, memory mappings, and platform init.
- Define static S16_LE stereo playback and capture pipelines with
  ASCII art topology architecture, Q1.31 volume controls, and custom
  kcontrol callback handler in esp32c6_pipeline_def.c.
- Add board configurations and overlays for Seeed Studio XIAO ESP32-C6
  (xiao_esp32c6/esp32c6/hpcore) and Waveshare ESP32-C6-Zero
  (esp32c6_devkitc/esp32c6/hpcore).
- Add DMA driver registration for ESP32 GDMA in zephyr/lib/dma.c.
- Initialize static pipelines and board default clock modes in
  platform_boot_complete().

Signed-off-by: Liam Girdwood <lgirdwood@gmail.com>
…oopback test suite

- Add esp32_shell.c providing interactive SOF shell commands:
  sof status, play start/stop, cap start/stop/stats/dump, vol, mute,
  mode, tone, and register dumping.
- Add scripts/test_c6_loopback.py automated bidirectional hardware I2S
  loopback test suite validating forward (XIAO -> Waveshare) and
  reverse (Waveshare -> XIAO) audio transmission with channel symmetry
  and waveform verification across any ESP32-C6 variants.

Signed-off-by: Liam Girdwood <lgirdwood@gmail.com>
@lgirdwood
lgirdwood force-pushed the feature/esp32c6-sof-port branch from 5d0d566 to 45f5af3 Compare November 10, 2026 13:40
@lgirdwood

Copy link
Copy Markdown
Member Author

Rebased on latest main and addressed all review comments:

  • Static Pipeline & IPC4: Switched to pure IPC4 (CONFIG_IPC_MAJOR_4=y). Dropped standalone sof_static_pipeline.c in favor of upstream static pipeline loader and registered static controls via g_esp32c6_static_topology with .custom_control_handler = esp32c6_control_callback.
  • USB Audio Component: Split usb_audio_copy() into distinct usb_audio_copy_playback() and usb_audio_copy_capture() paths. Replaced inline RISC-V assembly with standard SOF math sat_int16(). Removed tone generator test code in favor of the interactive shell.
  • Topology Description: Added ASCII art pipeline routing diagrams at the top of esp32c6_pipeline_def.c.
  • Volume & Trigger Fixes: Corrected volume kcontrol scaling to Q1.31 (INT32_MAX 0 dB unity gain) and ensured DAI components properly transition trigger states under static pipelines.
  • Loopback Test Suite: Clarified generic applicability across ESP32-C6 variants in scripts/test_c6_loopback.py.

Hardware Verification

Rebuilt and flashed both development boards:

  • Seeed Studio XIAO ESP32-C6 (xiao_esp32c6/esp32c6/hpcore)
  • Waveshare ESP32-C6-Zero (esp32c6_devkitc/esp32c6/hpcore)

Executed scripts/test_c6_loopback.py:

  • Forward Loopback (XIAO Master Tx -> Waveshare Slave Rx): PASS (1000 Hz tone, RMS 1414, pk-pk 4000, bit-exact L/R symmetry).
  • Reverse Loopback (Waveshare Master Tx -> XIAO Slave Rx): PASS (1000 Hz tone, RMS 1414, pk-pk 4000, bit-exact L/R symmetry).
  • Overall Result: ALL TESTS PASSED [SUCCESS].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant