Skip to content

Commit 6f17a50

Browse files
committed
zephyr: test: userspace: add tests for using Intel HDA DMA from user-space
Test the SOF DMA interface from a user-space thread. Use all the exported functionality using SOF_DMA_DEV_HOST. This test is using the zephyr/soc/intel/intel_adsp/tools/cavstool.py as the host-side tool. This is required as the DMA programming is split between host and DSP side and test sequences need a test endpoint on both sides. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
1 parent 13b3774 commit 6f17a50

File tree

3 files changed

+265
-0
lines changed

3 files changed

+265
-0
lines changed

zephyr/test/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@ if (CONFIG_SOF_BOOT_TEST)
33
vmh.c
44
)
55
endif()
6+
7+
if (CONFIG_SOF_BOOT_TEST_STANDALONE)
8+
if (CONFIG_DT_HAS_INTEL_ADSP_HDA_HOST_IN_ENABLED AND CONFIG_USERSPACE)
9+
zephyr_library_sources(userspace/test_intel_hda_dma.c)
10+
endif()
11+
endif()

zephyr/test/userspace/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
intel_hda_dma test
2+
------------------
3+
4+
This is a standalone test to exercise the Intel HDA DMA host interface
5+
from a userspace Zephyr thread.
6+
Build with ("ptl" example):
7+
8+
./scripts/xtensa-build-zephyr.py --cmake-args=-DCONFIG_SOF_BOOT_TEST_STANDALONE=y \
9+
-o app/overlays/ptl/userspace_overlay.conf -o app/winconsole_overlay.conf ptl
10+
11+
Running test:
12+
- Copy resulting firmware (sof-ptl.ri) to device under test.
13+
- Boot and run the test with cavstool.py:
14+
sudo ./cavstool.py sof-ptl.ri
15+
- Test results printed to cavstool.py
16+
17+
References to related assets in Zephyr codebase:
18+
- cavstool.py
19+
- zephyr/soc/intel/intel_adsp/tools/cavstool.py
20+
- HD DMA tests in Zephyr
21+
- zephyr/tests/boards/intel_adsp/hda/
22+
- larger set in kernel space, using DMA interface directly without SOF dependencies
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
/*
3+
* Copyright(c) 2025 Intel Corporation.
4+
*/
5+
6+
/*
7+
* Test case for user-space use of the SOF DMA interface. The tests
8+
* transfer data from DSP to host using the host HD DMA instance.
9+
* The test uses the cavstool.py infrastructure to perform host side
10+
* programming of the HDA DMA, and to verify the transferred data.
11+
*
12+
* This test is based on the Zephyr kernel tests for Intel HD DMA
13+
* driver (zephyr/tests/boards/intel_adsp/hda/) written by Tom
14+
* Burdick. This test performs only subset of flows. Driver testing
15+
* should primarily done with the Zephyr kernel tests and this test
16+
* is solely to test the added syscall layer added in SOF.
17+
*/
18+
19+
#include <sof/boot_test.h>
20+
21+
#include <zephyr/kernel.h>
22+
#include <zephyr/ztest.h>
23+
#include <zephyr/logging/log.h>
24+
#include <sof/lib/dma.h>
25+
26+
LOG_MODULE_DECLARE(sof_boot_test, LOG_LEVEL_DBG);
27+
28+
#define USER_STACKSIZE 2048
29+
#define TEST_BUF_SIZE 256
30+
#define TEST_CHANNEL 0
31+
#define HD_DMA_BUF_ALIGN 128
32+
33+
static struct k_thread user_thread;
34+
static K_THREAD_STACK_DEFINE(user_stack, USER_STACKSIZE);
35+
36+
K_SEM_DEFINE(ipc_sem_wake_user, 0, 1);
37+
K_SEM_DEFINE(ipc_sem_wake_kernel, 0, 1);
38+
39+
static void intel_hda_dma_user(void *p1, void *p2, void *p3)
40+
{
41+
uint32_t addr_align = 0x0;
42+
struct sof_dma *dma;
43+
struct dma_config config;
44+
struct dma_block_config dma_block_cfg;
45+
struct dma_status stat;
46+
uint8_t data_buf[TEST_BUF_SIZE] __aligned(HD_DMA_BUF_ALIGN);
47+
int err, channel;
48+
49+
__ASSERT(k_is_user_context(), "isn't user");
50+
51+
LOG_INF("SOF thread %s (%s)",
52+
k_is_user_context() ? "UserSpace!" : "privileged mode.",
53+
CONFIG_BOARD_TARGET);
54+
55+
/*
56+
* note: this gets a pointer to kernel memory this thread
57+
* cannot access
58+
*/
59+
dma = sof_dma_get(SOF_DMA_DIR_LMEM_TO_HMEM, 0, SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED);
60+
61+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
62+
LOG_INF("configure DMA channel");
63+
64+
channel = sof_dma_request_channel(dma, TEST_CHANNEL);
65+
LOG_INF("sof_dma_request_channel: ret %d", channel);
66+
67+
err = sof_dma_get_attribute(dma, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT,
68+
&addr_align);
69+
__ASSERT(!err, "dma-attr-alignment error");
70+
LOG_INF("DMA addr alignment %u", addr_align);
71+
__ASSERT(addr_align == HD_DMA_BUF_ALIGN, "dma-attr-alignment not correct");
72+
73+
/* set up a DMA transfer */
74+
memset(&dma_block_cfg, 0, sizeof(dma_block_cfg));
75+
dma_block_cfg.dest_address = 0; /* host fifo */
76+
dma_block_cfg.source_address = (uintptr_t)data_buf;
77+
dma_block_cfg.block_size = sizeof(data_buf);
78+
79+
/*
80+
* fill data ramp, this payload is expected by host test
81+
* harness
82+
*/
83+
for (uint32_t i = 0; i < TEST_BUF_SIZE; i++) {
84+
data_buf[i] = i & 0xff;
85+
}
86+
sys_cache_data_flush_range(data_buf, sizeof(data_buf));
87+
88+
memset(&config, 0, sizeof(config));
89+
config.channel_direction = MEMORY_TO_HOST;
90+
config.block_count = 1;
91+
config.head_block = &dma_block_cfg;
92+
93+
err = sof_dma_config(dma, channel, &config);
94+
__ASSERT(!err, "dma-config error");
95+
LOG_INF("sof_dma_config: success");
96+
97+
err = sof_dma_start(dma, channel);
98+
LOG_INF("sof_dma_start: ret %d", err);
99+
100+
k_sem_give(&ipc_sem_wake_kernel);
101+
LOG_INF("setup ready, waiting for kernel to configure host-side of the test");
102+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
103+
LOG_INF("start DMA test and transfer data");
104+
105+
err = sof_dma_get_status(dma, channel, &stat);
106+
LOG_INF("sof_dma_get_status start: ret %d pend %u free %u", err,
107+
stat.pending_length, stat.free);
108+
109+
err = sof_dma_reload(dma, channel, sizeof(data_buf));
110+
LOG_INF("sof_dma_reload: ret %d", err);
111+
112+
for (int i = 0; stat.pending_length < TEST_BUF_SIZE; i++) {
113+
err = sof_dma_get_status(dma, channel, &stat);
114+
LOG_INF("sof_dma_get_status %d: ret %d pend %u free %u", i, err,
115+
stat.pending_length, stat.free);
116+
117+
__ASSERT(i < 100, "DMA transfer completes in 100usec");
118+
119+
/* let DMA transfer complete */
120+
k_sleep(K_USEC(1));
121+
}
122+
123+
err = sof_dma_get_status(dma, channel, &stat);
124+
LOG_INF("sof_dma_get_status end: ret %d pend %u free %u", err,
125+
stat.pending_length, stat.free);
126+
127+
LOG_INF("transfer done, asking host to validate output");
128+
k_sem_give(&ipc_sem_wake_kernel);
129+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
130+
LOG_INF("test done, cleaning up resources");
131+
132+
err = sof_dma_stop(dma, channel);
133+
LOG_INF("sof_dma_stop: ret %d", err);
134+
135+
sof_dma_release_channel(dma, channel);
136+
LOG_INF("sof_dma_release_channel: success");
137+
138+
sof_dma_put(dma);
139+
LOG_INF("sof_dma_put: success");
140+
k_sem_give(&ipc_sem_wake_kernel);
141+
}
142+
143+
#define IPC_TIMEOUT K_MSEC(1500)
144+
#define DMA_BUF_SIZE 256
145+
146+
#define ALIGNMENT DMA_BUF_ADDR_ALIGNMENT(DT_NODELABEL(hda_host_in))
147+
static __aligned(ALIGNMENT) uint8_t dma_buf[DMA_BUF_SIZE];
148+
149+
#include <intel_adsp_hda.h>
150+
#include <../../../../zephyr/tests/boards/intel_adsp/hda/src/tests.h>
151+
152+
static int msg_validate_res;
153+
154+
static bool ipc_message(const struct device *dev, void *arg,
155+
uint32_t data, uint32_t ext_data)
156+
{
157+
LOG_DBG("HDA message received, data %u, ext_data %u", data, ext_data);
158+
msg_validate_res = ext_data;
159+
return true;
160+
}
161+
162+
static void intel_hda_dma_kernel(void)
163+
{
164+
const struct device *dma;
165+
166+
LOG_INF("run %s with buffer at address %p, size %d",
167+
__func__, (void *)dma_buf, DMA_BUF_SIZE);
168+
169+
intel_adsp_ipc_set_message_handler(INTEL_ADSP_IPC_HOST_DEV, ipc_message, NULL);
170+
171+
k_thread_create(&user_thread, user_stack, USER_STACKSIZE,
172+
intel_hda_dma_user, NULL, NULL, NULL,
173+
-1, K_USER, K_FOREVER);
174+
175+
k_thread_access_grant(&user_thread, &ipc_sem_wake_user);
176+
k_thread_access_grant(&user_thread, &ipc_sem_wake_kernel);
177+
178+
dma = DEVICE_DT_GET(DT_NODELABEL(hda_host_in));
179+
k_thread_access_grant(&user_thread, dma);
180+
181+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_RESET,
182+
TEST_CHANNEL, IPC_TIMEOUT);
183+
184+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_CONFIG,
185+
TEST_CHANNEL | (DMA_BUF_SIZE << 8), IPC_TIMEOUT);
186+
187+
k_thread_start(&user_thread);
188+
189+
LOG_INF("user started, waiting for it to be ready");
190+
191+
k_sem_give(&ipc_sem_wake_user);
192+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
193+
194+
LOG_INF("user ready, starting HDA test");
195+
196+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_START, TEST_CHANNEL, IPC_TIMEOUT);
197+
198+
k_sem_give(&ipc_sem_wake_user);
199+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
200+
201+
LOG_INF("transfer done, validating results");
202+
203+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_VALIDATE, TEST_CHANNEL,
204+
IPC_TIMEOUT);
205+
206+
hda_dump_regs(HOST_OUT, HDA_REGBLOCK_SIZE, TEST_CHANNEL, "host reset");
207+
208+
k_sem_give(&ipc_sem_wake_user);
209+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
210+
211+
LOG_INF("test done, terminate user");
212+
213+
k_thread_join(&user_thread, K_FOREVER);
214+
215+
__ASSERT(msg_validate_res == 1, "DMA transferred data invalid payload");
216+
}
217+
218+
ZTEST(userspace_intel_hda_dma, dma_mem_to_host)
219+
{
220+
intel_hda_dma_kernel();
221+
222+
ztest_test_pass();
223+
}
224+
225+
ZTEST_SUITE(userspace_intel_hda_dma, NULL, NULL, NULL, NULL, NULL);
226+
227+
/**
228+
* SOF main has booted up and IPC handling is stopped.
229+
* Run test suites with ztest_run_all.
230+
*/
231+
static int run_tests(void)
232+
{
233+
ztest_run_all(NULL, false, 1, 1);
234+
return 0;
235+
}
236+
237+
SYS_INIT(run_tests, APPLICATION, 99);

0 commit comments

Comments
 (0)