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:
Andy Ross 2022-01-14 10:11:08 -08:00 committed by Anas Nashif
commit 318aecb86f
8 changed files with 292 additions and 1 deletions

View file

@ -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",

View 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)

View file

@ -0,0 +1,4 @@
CONFIG_ZTEST=y
CONFIG_SMP=y
CONFIG_SMP_BOOT_DELAY=y
CONFIG_SCHED_CPU_MASK=y

View 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");
}

View 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);
}

View 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");
}

View 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 */

View file

@ -0,0 +1,3 @@
tests:
boards.intel_adsp:
platform_allow: intel_adsp_cavs15 intel_adsp_cavs18 intel_adsp_cavs20 intel_adsp_cavs25