Skip to content

Commit 0a79248

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 553b0f2 commit 0a79248

File tree

3 files changed

+270
-0
lines changed

3 files changed

+270
-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_SOF_USERSPACE_INTERFACE_DMA)
9+
zephyr_library_sources(userspace/test_intel_hda_dma.c)
10+
endif()
11+
endif()

zephyr/test/userspace/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
--cmake-args=-DCONFIG_SOF_USERSPACE_INTERFACE_DMA=y \
10+
-o app/overlays/ptl/userspace_overlay.conf -o app/winconsole_overlay.conf ptl
11+
12+
Running test:
13+
- Copy resulting firmware (sof-ptl.ri) to device under test.
14+
- Boot and run the test with cavstool.py:
15+
sudo ./cavstool.py sof-ptl.ri
16+
- Test results printed to cavstool.py
17+
18+
References to related assets in Zephyr codebase:
19+
- cavstool.py
20+
- zephyr/soc/intel/intel_adsp/tools/cavstool.py
21+
- HD DMA tests in Zephyr
22+
- zephyr/tests/boards/intel_adsp/hda/
23+
- larger set in kernel space, using DMA interface directly without SOF dependencies
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
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+
uint8_t data_buf[TEST_BUF_SIZE] __aligned(HD_DMA_BUF_ALIGN);
42+
struct dma_block_config dma_block_cfg;
43+
struct dma_config config;
44+
struct dma_status stat;
45+
struct sof_dma *dma;
46+
uint32_t addr_align;
47+
int err, channel;
48+
49+
zassert_true(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+
zassert_equal(err, 0);
70+
zassert_true(addr_align == HD_DMA_BUF_ALIGN);
71+
72+
/* set up a DMA transfer */
73+
memset(&dma_block_cfg, 0, sizeof(dma_block_cfg));
74+
dma_block_cfg.dest_address = 0; /* host fifo */
75+
dma_block_cfg.source_address = (uintptr_t)data_buf;
76+
dma_block_cfg.block_size = sizeof(data_buf);
77+
78+
/*
79+
* fill data ramp, this payload is expected by host test
80+
* harness
81+
*/
82+
for (uint32_t i = 0; i < TEST_BUF_SIZE; i++) {
83+
data_buf[i] = i & 0xff;
84+
}
85+
sys_cache_data_flush_range(data_buf, sizeof(data_buf));
86+
87+
memset(&config, 0, sizeof(config));
88+
config.channel_direction = MEMORY_TO_HOST;
89+
config.block_count = 1;
90+
config.head_block = &dma_block_cfg;
91+
92+
err = sof_dma_config(dma, channel, &config);
93+
zassert_equal(err, 0);
94+
LOG_INF("sof_dma_config: success");
95+
96+
err = sof_dma_start(dma, channel);
97+
zassert_equal(err, 0);
98+
LOG_INF("sof_dma_start: ch %d", channel);
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+
zassert_equal(err, 0);
107+
LOG_INF("sof_dma_get_status start: pend %u free %u",
108+
stat.pending_length, stat.free);
109+
110+
err = sof_dma_reload(dma, channel, sizeof(data_buf));
111+
zassert_equal(err, 0);
112+
113+
for (int i = 0; stat.pending_length < TEST_BUF_SIZE; i++) {
114+
err = sof_dma_get_status(dma, channel, &stat);
115+
zassert_equal(err, 0);
116+
LOG_INF("sof_dma_get_status %d: pend %u free %u", i,
117+
stat.pending_length, stat.free);
118+
119+
zassert_true(i < 100, "DMA transfer completes in 100usec");
120+
121+
/* let DMA transfer complete */
122+
k_sleep(K_USEC(1));
123+
}
124+
125+
err = sof_dma_get_status(dma, channel, &stat);
126+
zassert_equal(err, 0);
127+
LOG_INF("sof_dma_get_status end: pend %u free %u",
128+
stat.pending_length, stat.free);
129+
130+
LOG_INF("transfer done, asking host to validate output");
131+
k_sem_give(&ipc_sem_wake_kernel);
132+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
133+
LOG_INF("test done, cleaning up resources");
134+
135+
err = sof_dma_stop(dma, channel);
136+
zassert_equal(err, 0);
137+
138+
sof_dma_release_channel(dma, channel);
139+
140+
sof_dma_put(dma);
141+
142+
LOG_INF("DMA stopped and resources freed");
143+
144+
k_sem_give(&ipc_sem_wake_kernel);
145+
}
146+
147+
#define IPC_TIMEOUT K_MSEC(1500)
148+
#define DMA_BUF_SIZE 256
149+
150+
#define ALIGNMENT DMA_BUF_ADDR_ALIGNMENT(DT_NODELABEL(hda_host_in))
151+
static __aligned(ALIGNMENT) uint8_t dma_buf[DMA_BUF_SIZE];
152+
153+
#include <intel_adsp_hda.h>
154+
#include <../../../../zephyr/tests/boards/intel_adsp/hda/src/tests.h>
155+
156+
static int msg_validate_res;
157+
158+
static bool ipc_message(const struct device *dev, void *arg,
159+
uint32_t data, uint32_t ext_data)
160+
{
161+
LOG_DBG("HDA message received, data %u, ext_data %u", data, ext_data);
162+
msg_validate_res = ext_data;
163+
return true;
164+
}
165+
166+
static void intel_hda_dma_kernel(void)
167+
{
168+
const struct device *dma;
169+
170+
LOG_INF("run %s with buffer at address %p, size %d",
171+
__func__, (void *)dma_buf, DMA_BUF_SIZE);
172+
173+
intel_adsp_ipc_set_message_handler(INTEL_ADSP_IPC_HOST_DEV, ipc_message, NULL);
174+
175+
k_thread_create(&user_thread, user_stack, USER_STACKSIZE,
176+
intel_hda_dma_user, NULL, NULL, NULL,
177+
-1, K_USER, K_FOREVER);
178+
179+
k_thread_access_grant(&user_thread, &ipc_sem_wake_user);
180+
k_thread_access_grant(&user_thread, &ipc_sem_wake_kernel);
181+
182+
dma = DEVICE_DT_GET(DT_NODELABEL(hda_host_in));
183+
k_thread_access_grant(&user_thread, dma);
184+
185+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_RESET,
186+
TEST_CHANNEL, IPC_TIMEOUT);
187+
188+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_CONFIG,
189+
TEST_CHANNEL | (DMA_BUF_SIZE << 8), IPC_TIMEOUT);
190+
191+
k_thread_start(&user_thread);
192+
193+
LOG_INF("user started, waiting for it to be ready");
194+
195+
k_sem_give(&ipc_sem_wake_user);
196+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
197+
198+
LOG_INF("user ready, starting HDA test");
199+
200+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_START, TEST_CHANNEL, IPC_TIMEOUT);
201+
202+
k_sem_give(&ipc_sem_wake_user);
203+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
204+
205+
LOG_INF("transfer done, validating results");
206+
207+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_VALIDATE, TEST_CHANNEL,
208+
IPC_TIMEOUT);
209+
210+
hda_dump_regs(HOST_OUT, HDA_REGBLOCK_SIZE, TEST_CHANNEL, "host reset");
211+
212+
k_sem_give(&ipc_sem_wake_user);
213+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
214+
215+
LOG_INF("test done, terminate user thread");
216+
217+
k_thread_join(&user_thread, K_FOREVER);
218+
219+
zassert_true(msg_validate_res == 1, "DMA transferred data invalid payload");
220+
}
221+
222+
ZTEST(userspace_intel_hda_dma, dma_mem_to_host)
223+
{
224+
intel_hda_dma_kernel();
225+
226+
ztest_test_pass();
227+
}
228+
229+
ZTEST_SUITE(userspace_intel_hda_dma, NULL, NULL, NULL, NULL, NULL);
230+
231+
/**
232+
* SOF main has booted up and IPC handling is stopped.
233+
* Run test suites with ztest_run_all.
234+
*/
235+
static int run_tests(void)
236+
{
237+
ztest_run_all(NULL, false, 1, 1);
238+
return 0;
239+
}
240+
241+
SYS_INIT(run_tests, APPLICATION, 99);

0 commit comments

Comments
 (0)