tests/boards: Add intel_adsp board integration/smoke test
As Zephyr begins to absorb drivers for these platforms that had previously been managed by the SOF app, there's a need for a rapid board-specific smoke test to use during development. This starts with the smp_boot_delay test (itself a unit test for a SOF-derived feature) and adds a host IPC case (that needs to match code in cavstool.py on the other side of the PCI bus!). It will grow more features over time as needed. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
parent
bdce0a5742
commit
318aecb86f
8 changed files with 292 additions and 1 deletions
|
@ -82,7 +82,12 @@ def map_regs():
|
||||||
(bar4_mem, bar4_mmap) = bar_map(pcidir, 4)
|
(bar4_mem, bar4_mmap) = bar_map(pcidir, 4)
|
||||||
dsp = Regs(bar4_mem)
|
dsp = Regs(bar4_mem)
|
||||||
dsp.ADSPCS = 0x00004
|
dsp.ADSPCS = 0x00004
|
||||||
|
dsp.HIPCTDR = 0x00040 if cavs15 else 0x000c0
|
||||||
|
dsp.HIPCTDA = 0x000c4 # 1.8+ only
|
||||||
|
dsp.HIPCTDD = 0x00044 if cavs15 else 0x000c8
|
||||||
dsp.HIPCIDR = 0x00048 if cavs15 else 0x000d0
|
dsp.HIPCIDR = 0x00048 if cavs15 else 0x000d0
|
||||||
|
dsp.HIPCIDA = 0x000d4 # 1.8+ only
|
||||||
|
dsp.HIPCIDD = 0x0004c if cavs15 else 0x000d8
|
||||||
dsp.SRAM_FW_STATUS = 0x80000 # Start of first SRAM window
|
dsp.SRAM_FW_STATUS = 0x80000 # Start of first SRAM window
|
||||||
dsp.freeze()
|
dsp.freeze()
|
||||||
|
|
||||||
|
@ -246,7 +251,7 @@ def load_firmware(fw_file):
|
||||||
| (0x01 << 24) # type = PURGE_FW
|
| (0x01 << 24) # type = PURGE_FW
|
||||||
| (1 << 14) # purge_fw = 1
|
| (1 << 14) # purge_fw = 1
|
||||||
| (stream_idx << 9)) # dma_id
|
| (stream_idx << 9)) # dma_id
|
||||||
log.info(f"Sending IPC command, HIPCR = 0x{ipcval:x}")
|
log.info(f"Sending IPC command, HIPIDR = 0x{ipcval:x}")
|
||||||
dsp.HIPCIDR = ipcval
|
dsp.HIPCIDR = ipcval
|
||||||
|
|
||||||
log.info(f"Starting DMA, FW_STATUS = 0x{dsp.SRAM_FW_STATUS:x}")
|
log.info(f"Starting DMA, FW_STATUS = 0x{dsp.SRAM_FW_STATUS:x}")
|
||||||
|
@ -315,6 +320,32 @@ def winstream_read(last_seq):
|
||||||
if start1 == start and seq1 == seq:
|
if start1 == start and seq1 == seq:
|
||||||
return (seq, result.decode("utf-8"))
|
return (seq, result.decode("utf-8"))
|
||||||
|
|
||||||
|
async def ipc_delay_done():
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
dsp.HIPCTDA = 1<<31
|
||||||
|
|
||||||
|
# Super-simple command language, driven by the test code on the DSP
|
||||||
|
def ipc_command(data, ext_data):
|
||||||
|
send_msg = False
|
||||||
|
done = True
|
||||||
|
if data == 0: # noop, with synchronous DONE
|
||||||
|
pass
|
||||||
|
elif data == 1: # async command: signal DONE after a delay (on 1.8+)
|
||||||
|
if not cavs15:
|
||||||
|
done = False
|
||||||
|
asyncio.ensure_future(ipc_delay_done())
|
||||||
|
elif data == 2: # echo back ext_data as a message command
|
||||||
|
send_msg = True
|
||||||
|
else:
|
||||||
|
log.warning(f"cavstool: Unrecognized IPC command 0x{data:x} ext 0x{ext_data:x}")
|
||||||
|
|
||||||
|
dsp.HIPCTDR = 1<<31 # Ack local interrupt, also signals DONE on v1.5
|
||||||
|
if done and not cavs15:
|
||||||
|
dsp.HIPCTDA = 1<<31 # Signal done
|
||||||
|
if send_msg:
|
||||||
|
dsp.HIPCIDD = ext_data
|
||||||
|
dsp.HIPCIDR = (1<<31) | ext_data
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
global hda, sd, dsp, hda_ostream_id
|
global hda, sd, dsp, hda_ostream_id
|
||||||
try:
|
try:
|
||||||
|
@ -345,6 +376,11 @@ async def main():
|
||||||
if output:
|
if output:
|
||||||
sys.stdout.write(output)
|
sys.stdout.write(output)
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
if dsp.HIPCTDR & 0x80000000:
|
||||||
|
ipc_command(dsp.HIPCTDR & ~0x80000000, dsp.HIPCTDD)
|
||||||
|
if dsp.HIPCIDA & 0x80000000:
|
||||||
|
dsp.HIPCIDA = 1<<31 # must ACK any DONE interrupts that arrive!
|
||||||
|
|
||||||
|
|
||||||
ap = argparse.ArgumentParser(description="DSP loader/logger tool")
|
ap = argparse.ArgumentParser(description="DSP loader/logger tool")
|
||||||
ap.add_argument("-q", "--quiet", action="store_true",
|
ap.add_argument("-q", "--quiet", action="store_true",
|
||||||
|
|
7
tests/boards/intel_adsp/smoke/CMakeLists.txt
Normal file
7
tests/boards/intel_adsp/smoke/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(intel_adsp)
|
||||||
|
|
||||||
|
target_sources(app PRIVATE src/main.c src/smpboot.c src/hostipc.c)
|
4
tests/boards/intel_adsp/smoke/prj.conf
Normal file
4
tests/boards/intel_adsp/smoke/prj.conf
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
CONFIG_ZTEST=y
|
||||||
|
CONFIG_SMP=y
|
||||||
|
CONFIG_SMP_BOOT_DELAY=y
|
||||||
|
CONFIG_SCHED_CPU_MASK=y
|
108
tests/boards/intel_adsp/smoke/src/hostipc.c
Normal file
108
tests/boards/intel_adsp/smoke/src/hostipc.c
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/* Copyright (c) 2022 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <ztest.h>
|
||||||
|
#include <cavs_ipc.h>
|
||||||
|
#include "tests.h"
|
||||||
|
|
||||||
|
/* The cavstool.py script that launched us listens for a very simple
|
||||||
|
* set of IPC commands to help test. Pass one of the following values
|
||||||
|
* as the "data" argument to cavs_ipc_send_message():
|
||||||
|
*
|
||||||
|
* 0: The host takes no action, but signals DONE to complete the message
|
||||||
|
* 1: The host returns done after a short timeout
|
||||||
|
* 2: The host issues a new message with the ext_data value as its "data"
|
||||||
|
*/
|
||||||
|
enum cavstool_cmd { SIGNAL_DONE, ASYNC_DONE_DELAY, RETURN_MSG };
|
||||||
|
|
||||||
|
static volatile bool done_flag, msg_flag;
|
||||||
|
|
||||||
|
#define RETURN_MSG_SYNC_VAL 0x12345
|
||||||
|
#define RETURN_MSG_ASYNC_VAL 0x54321
|
||||||
|
|
||||||
|
static bool ipc_message(const struct device *dev, void *arg,
|
||||||
|
uint32_t data, uint32_t ext_data)
|
||||||
|
{
|
||||||
|
zassert_is_null(arg, "wrong message arg");
|
||||||
|
zassert_equal(data, ext_data, "unequal message data/ext_data");
|
||||||
|
zassert_true(data == RETURN_MSG_SYNC_VAL ||
|
||||||
|
data == RETURN_MSG_ASYNC_VAL, "unexpected msg data");
|
||||||
|
msg_flag = true;
|
||||||
|
return data == RETURN_MSG_SYNC_VAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ipc_done(const struct device *dev, void *arg)
|
||||||
|
{
|
||||||
|
zassert_is_null(arg, "wrong done arg");
|
||||||
|
zassert_false(done_flag, "done called unexpectedly");
|
||||||
|
done_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_host_ipc(void)
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
cavs_ipc_set_message_handler(CAVS_HOST_DEV, ipc_message, NULL);
|
||||||
|
cavs_ipc_set_done_handler(CAVS_HOST_DEV, ipc_done, NULL);
|
||||||
|
|
||||||
|
/* Just send a message and wait for it to complete */
|
||||||
|
printk("Simple message send...\n");
|
||||||
|
done_flag = false;
|
||||||
|
ret = cavs_ipc_send_message(CAVS_HOST_DEV, SIGNAL_DONE, 0);
|
||||||
|
zassert_true(ret, "send failed");
|
||||||
|
WAIT_FOR(cavs_ipc_is_complete(CAVS_HOST_DEV));
|
||||||
|
WAIT_FOR(done_flag);
|
||||||
|
|
||||||
|
/* Request the host to return a message which we will complete
|
||||||
|
* immediately.
|
||||||
|
*/
|
||||||
|
printk("Return message request...\n");
|
||||||
|
done_flag = false;
|
||||||
|
msg_flag = false;
|
||||||
|
ret = cavs_ipc_send_message(CAVS_HOST_DEV, RETURN_MSG,
|
||||||
|
RETURN_MSG_SYNC_VAL);
|
||||||
|
zassert_true(ret, "send failed");
|
||||||
|
WAIT_FOR(done_flag);
|
||||||
|
WAIT_FOR(cavs_ipc_is_complete(CAVS_HOST_DEV));
|
||||||
|
WAIT_FOR(msg_flag);
|
||||||
|
|
||||||
|
/* Do exactly the same thing again to check for state bugs
|
||||||
|
* (e.g. failing to signal done on one side or the other)
|
||||||
|
*/
|
||||||
|
printk("Return message request 2...\n");
|
||||||
|
done_flag = false;
|
||||||
|
msg_flag = false;
|
||||||
|
ret = cavs_ipc_send_message(CAVS_HOST_DEV, RETURN_MSG,
|
||||||
|
RETURN_MSG_SYNC_VAL);
|
||||||
|
zassert_true(ret, "send failed");
|
||||||
|
WAIT_FOR(done_flag);
|
||||||
|
WAIT_FOR(cavs_ipc_is_complete(CAVS_HOST_DEV));
|
||||||
|
WAIT_FOR(msg_flag);
|
||||||
|
|
||||||
|
/* Same, but we'll complete it asynchronously (1.8+ only) */
|
||||||
|
if (!IS_ENABLED(CONFIG_SOC_SERIES_INTEL_CAVS_V15)) {
|
||||||
|
printk("Return message request, async...\n");
|
||||||
|
done_flag = false;
|
||||||
|
msg_flag = false;
|
||||||
|
ret = cavs_ipc_send_message(CAVS_HOST_DEV, RETURN_MSG,
|
||||||
|
RETURN_MSG_ASYNC_VAL);
|
||||||
|
zassert_true(ret, "send failed");
|
||||||
|
WAIT_FOR(done_flag);
|
||||||
|
WAIT_FOR(cavs_ipc_is_complete(CAVS_HOST_DEV));
|
||||||
|
WAIT_FOR(msg_flag);
|
||||||
|
cavs_ipc_complete(CAVS_HOST_DEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now make a synchronous call with (on the host) a delayed
|
||||||
|
* completion and make sure the interrupt fires and wakes us
|
||||||
|
* up. (On 1.5 a delay isn't possible and this will complete
|
||||||
|
* immediately).
|
||||||
|
*/
|
||||||
|
printk("Synchronous message send...\n");
|
||||||
|
done_flag = false;
|
||||||
|
ret = cavs_ipc_send_message_sync(CAVS_HOST_DEV, ASYNC_DONE_DELAY, 0, K_FOREVER);
|
||||||
|
zassert_true(ret, "send failed");
|
||||||
|
zassert_true(done_flag, "done interrupt failed to fire");
|
||||||
|
zassert_true(cavs_ipc_is_complete(CAVS_HOST_DEV), "sync message incomplete");
|
||||||
|
}
|
17
tests/boards/intel_adsp/smoke/src/main.c
Normal file
17
tests/boards/intel_adsp/smoke/src/main.c
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/* Copyright (c) 2022 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <ztest.h>
|
||||||
|
#include "tests.h"
|
||||||
|
|
||||||
|
void test_main(void)
|
||||||
|
{
|
||||||
|
ztest_test_suite(intel_adsp,
|
||||||
|
ztest_unit_test(test_smp_boot_delay),
|
||||||
|
ztest_unit_test(test_post_boot_ipi),
|
||||||
|
ztest_unit_test(test_host_ipc)
|
||||||
|
);
|
||||||
|
|
||||||
|
ztest_run_test_suite(intel_adsp);
|
||||||
|
}
|
92
tests/boards/intel_adsp/smoke/src/smpboot.c
Normal file
92
tests/boards/intel_adsp/smoke/src/smpboot.c
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
/* Copyright (c) 2022 Intel Corporation.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <ztest.h>
|
||||||
|
#include "tests.h"
|
||||||
|
|
||||||
|
/* Experimentally 10ms is enough time to get the second CPU to run on
|
||||||
|
* all known platforms.
|
||||||
|
*/
|
||||||
|
#define CPU_START_DELAY 10000
|
||||||
|
|
||||||
|
/* IPIs happen much faster than CPU startup */
|
||||||
|
#define CPU_IPI_DELAY 100
|
||||||
|
|
||||||
|
BUILD_ASSERT(CONFIG_SMP);
|
||||||
|
BUILD_ASSERT(CONFIG_SMP_BOOT_DELAY);
|
||||||
|
|
||||||
|
#define STACKSZ 2048
|
||||||
|
char stack[STACKSZ];
|
||||||
|
|
||||||
|
volatile bool mp_flag;
|
||||||
|
|
||||||
|
struct k_thread cpu_thr;
|
||||||
|
K_THREAD_STACK_DEFINE(thr_stack, STACKSZ);
|
||||||
|
|
||||||
|
extern void z_smp_start_cpu(int id);
|
||||||
|
|
||||||
|
static void thread_fn(void *a, void *b, void *c)
|
||||||
|
{
|
||||||
|
int cpuid = (int) a;
|
||||||
|
|
||||||
|
zassert_true(cpuid == 0 || cpuid == arch_curr_cpu()->id,
|
||||||
|
"running on wrong cpu");
|
||||||
|
mp_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Needless to say: since this is starting the SMP CPUs, it needs to
|
||||||
|
* be the first test run!
|
||||||
|
*/
|
||||||
|
void test_smp_boot_delay(void)
|
||||||
|
{
|
||||||
|
if (CONFIG_MP_NUM_CPUS < 2) {
|
||||||
|
ztest_test_skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < CONFIG_MP_NUM_CPUS; i++) {
|
||||||
|
printk("Launch cpu%d\n", i);
|
||||||
|
mp_flag = false;
|
||||||
|
k_thread_create(&cpu_thr, thr_stack, K_THREAD_STACK_SIZEOF(thr_stack),
|
||||||
|
thread_fn, (void *)i, NULL, NULL,
|
||||||
|
0, 0, K_FOREVER);
|
||||||
|
k_thread_cpu_mask_clear(&cpu_thr);
|
||||||
|
k_thread_cpu_mask_enable(&cpu_thr, i);
|
||||||
|
k_thread_start(&cpu_thr);
|
||||||
|
|
||||||
|
/* Make sure that thread has not run (because the cpu is halted) */
|
||||||
|
k_busy_wait(CPU_START_DELAY);
|
||||||
|
zassert_false(mp_flag, "cpu %d must not be running yet", i);
|
||||||
|
|
||||||
|
/* Start the second CPU */
|
||||||
|
z_smp_start_cpu(i);
|
||||||
|
|
||||||
|
/* Verify the thread ran */
|
||||||
|
k_busy_wait(CPU_START_DELAY);
|
||||||
|
zassert_true(mp_flag, "cpu %d did not start", i);
|
||||||
|
|
||||||
|
k_thread_abort(&cpu_thr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_post_boot_ipi(void)
|
||||||
|
{
|
||||||
|
if (CONFIG_MP_NUM_CPUS < 2) {
|
||||||
|
ztest_test_skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spawn the same thread to do the same thing, but this time
|
||||||
|
* expect that the thread is going to run synchronously on
|
||||||
|
* another CPU as soon as its created. Intended to test
|
||||||
|
* whether IPIs were correctly set up on the runtime-launched
|
||||||
|
* CPU.
|
||||||
|
*/
|
||||||
|
mp_flag = false;
|
||||||
|
k_thread_create(&cpu_thr, thr_stack, K_THREAD_STACK_SIZEOF(thr_stack),
|
||||||
|
thread_fn, (void *)0, NULL, NULL,
|
||||||
|
1, 0, K_NO_WAIT);
|
||||||
|
|
||||||
|
k_busy_wait(CPU_IPI_DELAY);
|
||||||
|
zassert_true(mp_flag, "cpu did not start thread via IPI");
|
||||||
|
}
|
24
tests/boards/intel_adsp/smoke/src/tests.h
Normal file
24
tests/boards/intel_adsp/smoke/src/tests.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/* Copyright (c) 2022 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#ifndef ZEPHYR_TESTS_INTEL_ADSP_TESTS_H
|
||||||
|
#define ZEPHYR_TESTS_INTEL_ADSP_TESTS_H
|
||||||
|
|
||||||
|
/* Helper to escape from infinite polling loops with a test failure
|
||||||
|
* instead of a hang. Spins with a relaxation loop so that it works
|
||||||
|
* in interrupt context and doesn't stress shared resources like SRAM
|
||||||
|
*/
|
||||||
|
#define WAIT_FOR(expr) do { \
|
||||||
|
int i; \
|
||||||
|
for (i = 0; !(expr) && i < 10000; i++) { \
|
||||||
|
for (volatile int j = 0; j < 1000; j++) { \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
zassert_true(i < 10000, "timeout waiting for %s", #expr); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
void test_post_boot_ipi(void);
|
||||||
|
void test_smp_boot_delay(void);
|
||||||
|
void test_host_ipc(void);
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_TESTS_INTEL_ADSP_TESTS_H */
|
3
tests/boards/intel_adsp/smoke/testcase.yaml
Normal file
3
tests/boards/intel_adsp/smoke/testcase.yaml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
tests:
|
||||||
|
boards.intel_adsp:
|
||||||
|
platform_allow: intel_adsp_cavs15 intel_adsp_cavs18 intel_adsp_cavs20 intel_adsp_cavs25
|
Loading…
Add table
Add a link
Reference in a new issue