tests/boards/intel_adsp: Add ipm_cavs_host test
Add a fairly simple test of the IPM-over-IPC driver. This hits all the code, but works by implementing the host side of the protocol partially in the C test code. The message is sent with an initial payload, and then IPC commands from the firmware copy the data over into the "inbox" region to simulate data being sent via the host. Then we make sure it lands correctly as if the host driver had done it directly. This requries a new command in the cavstool script that will copy a word from the "outbox" region to the "inbox" region (both are just different SRAM windows, conceptually no different than the way the script is already managing log output), but no significant surgery. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
parent
27a59ec9d5
commit
fd929f5190
6 changed files with 123 additions and 3 deletions
|
@ -20,7 +20,14 @@ PAGESZ = 4096
|
||||||
HUGEPAGESZ = 2 * 1024 * 1024
|
HUGEPAGESZ = 2 * 1024 * 1024
|
||||||
HUGEPAGE_FILE = "/dev/hugepages/cavs-fw-dma.tmp"
|
HUGEPAGE_FILE = "/dev/hugepages/cavs-fw-dma.tmp"
|
||||||
|
|
||||||
# Log is in the fourth window, they appear in 128k regions starting at 512k
|
# SRAM windows. Each appears in a 128k region starting at 512k.
|
||||||
|
#
|
||||||
|
# Window 0 is the FW_STATUS area, and 4k after that the IPC "outbox"
|
||||||
|
# Window 1 is the IPC "inbox" (host-writable memory, just 384 bytes currently)
|
||||||
|
# Window 2 is unused by this script
|
||||||
|
# Window 3 is winstream-formatted log output
|
||||||
|
OUTBOX_OFFSET = (512 + (0 * 128)) * 1024 + 4096
|
||||||
|
INBOX_OFFSET = (512 + (1 * 128)) * 1024
|
||||||
WINSTREAM_OFFSET = (512 + (3 * 128)) * 1024
|
WINSTREAM_OFFSET = (512 + (3 * 128)) * 1024
|
||||||
|
|
||||||
def map_regs():
|
def map_regs():
|
||||||
|
@ -346,6 +353,11 @@ def ipc_command(data, ext_data):
|
||||||
ext_data = t - ipc_timestamp
|
ext_data = t - ipc_timestamp
|
||||||
ipc_timestamp = t
|
ipc_timestamp = t
|
||||||
send_msg = True
|
send_msg = True
|
||||||
|
elif data == 5: # copy word at outbox[ext_data >> 16] to inbox[ext_data & 0xffff]
|
||||||
|
src = OUTBOX_OFFSET + 4 * (ext_data >> 16)
|
||||||
|
dst = INBOX_OFFSET + 4 * (ext_data & 0xffff)
|
||||||
|
for i in range(4):
|
||||||
|
bar4_mmap[dst + i] = bar4_mmap[src + i]
|
||||||
else:
|
else:
|
||||||
log.warning(f"cavstool: Unrecognized IPC command 0x{data:x} ext 0x{ext_data:x}")
|
log.warning(f"cavstool: Unrecognized IPC command 0x{data:x} ext 0x{ext_data:x}")
|
||||||
|
|
||||||
|
|
|
@ -4,4 +4,4 @@ cmake_minimum_required(VERSION 3.20.0)
|
||||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
project(intel_adsp)
|
project(intel_adsp)
|
||||||
|
|
||||||
target_sources(app PRIVATE src/main.c src/smpboot.c src/hostipc.c src/cpus.c)
|
target_sources(app PRIVATE src/main.c src/smpboot.c src/hostipc.c src/cpus.c src/ipm.c)
|
||||||
|
|
|
@ -2,3 +2,6 @@ CONFIG_ZTEST=y
|
||||||
CONFIG_SMP=y
|
CONFIG_SMP=y
|
||||||
CONFIG_SMP_BOOT_DELAY=y
|
CONFIG_SMP_BOOT_DELAY=y
|
||||||
CONFIG_SCHED_CPU_MASK=y
|
CONFIG_SCHED_CPU_MASK=y
|
||||||
|
CONFIG_IPM=y
|
||||||
|
CONFIG_IPM_CAVS_HOST=y
|
||||||
|
CONFIG_IPM_CAVS_IDC=n
|
||||||
|
|
88
tests/boards/intel_adsp/smoke/src/ipm.c
Normal file
88
tests/boards/intel_adsp/smoke/src/ipm.c
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/* Copyright (c) 2022 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <ztest.h>
|
||||||
|
#include <drivers/ipm.h>
|
||||||
|
#include "tests.h"
|
||||||
|
|
||||||
|
#define IPM_DEV device_get_binding("ipm_cavs_host")
|
||||||
|
|
||||||
|
/* Two values impossible to transmit in a cAVS ID */
|
||||||
|
#define ID_INBOUND 0xfffffff0
|
||||||
|
#define ID_INVALID 0xffffffff
|
||||||
|
|
||||||
|
static K_SEM_DEFINE(ipm_sem, 0, 1);
|
||||||
|
|
||||||
|
static const uint32_t msg[] = { 29, 15, 58, 71, 99 };
|
||||||
|
|
||||||
|
static uint32_t received_id = ID_INVALID;
|
||||||
|
static volatile uint32_t *received_data;
|
||||||
|
|
||||||
|
static void ipm_msg(const struct device *ipmdev, void *user_data,
|
||||||
|
uint32_t id, volatile void *data)
|
||||||
|
{
|
||||||
|
zassert_equal(ipmdev, IPM_DEV, "wrong device");
|
||||||
|
zassert_equal(user_data, NULL, "wrong user_data pointer");
|
||||||
|
zassert_equal(received_id, ID_INBOUND, "unexpected message");
|
||||||
|
|
||||||
|
received_id = id;
|
||||||
|
received_data = data;
|
||||||
|
|
||||||
|
k_sem_give(&ipm_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msg_transact(bool do_wait)
|
||||||
|
{
|
||||||
|
/* Send an IPCCMD_RETURN_MSG, this will send us a return
|
||||||
|
* message with msg[0] as the ID (on cAVS 1.8+, otherwise
|
||||||
|
* zero).
|
||||||
|
*/
|
||||||
|
received_id = ID_INBOUND;
|
||||||
|
ipm_send(IPM_DEV, do_wait, IPCCMD_RETURN_MSG, msg, sizeof(msg));
|
||||||
|
|
||||||
|
/* Wait for the return message */
|
||||||
|
k_sem_take(&ipm_sem, K_FOREVER);
|
||||||
|
|
||||||
|
zassert_equal(received_id,
|
||||||
|
IS_ENABLED(IPM_CAVS_HOST_REGWORD) ? msg[0] : 0,
|
||||||
|
"wrong return message ID");
|
||||||
|
|
||||||
|
received_id = ID_INVALID;
|
||||||
|
|
||||||
|
/* Now whitebox the message protocol: copy the message buffer
|
||||||
|
* (on the host side!) from the outbox to the inbox. That
|
||||||
|
* will write into our "already received" inbox buffer memory.
|
||||||
|
* We do this using the underlying cavs_ipc API, which works
|
||||||
|
* only because we know it works. Note that on cAVS 1.8+, the
|
||||||
|
* actual in-use amount of the message will be one word
|
||||||
|
* shorter (because the first word is sent as IPC ext_data),
|
||||||
|
* but it won't be inspected below.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(msg); i++) {
|
||||||
|
cavs_ipc_send_message_sync(CAVS_HOST_DEV, IPCCMD_WINCOPY,
|
||||||
|
(i << 16) | i, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate data */
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(msg); i++) {
|
||||||
|
zassert_equal(msg[i], received_data[i], "wrong message data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is a little whiteboxey. It relies on the knowledge that an
|
||||||
|
* IPM message is nothing but a IPC message with the "id" parameter
|
||||||
|
* passed as data (and, on cAVS 1.8+ only, the first word of the
|
||||||
|
* message buffer passed as ext_data).
|
||||||
|
*/
|
||||||
|
void test_ipm_cavs_host(void)
|
||||||
|
{
|
||||||
|
/* Restore IPM driver state (we've been mucking with cavs_ipc tests) */
|
||||||
|
cavs_ipc_set_message_handler(CAVS_HOST_DEV, ipm_handler, (void *)IPM_DEV);
|
||||||
|
cavs_ipc_set_done_handler(CAVS_HOST_DEV, NULL, NULL);
|
||||||
|
|
||||||
|
ipm_register_callback(IPM_DEV, ipm_msg, NULL);
|
||||||
|
|
||||||
|
/* Do it twice just for coverage on the wait parameter */
|
||||||
|
msg_transact(true);
|
||||||
|
msg_transact(false);
|
||||||
|
}
|
|
@ -6,6 +6,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "tests.h"
|
#include "tests.h"
|
||||||
|
|
||||||
|
cavs_ipc_handler_t ipm_handler;
|
||||||
|
|
||||||
static bool clock_msg(const struct device *dev, void *arg,
|
static bool clock_msg(const struct device *dev, void *arg,
|
||||||
uint32_t data, uint32_t ext_data)
|
uint32_t data, uint32_t ext_data)
|
||||||
{
|
{
|
||||||
|
@ -42,13 +44,18 @@ void test_clock_calibrate(void)
|
||||||
|
|
||||||
void test_main(void)
|
void test_main(void)
|
||||||
{
|
{
|
||||||
|
struct cavs_ipc_data *devdata = CAVS_HOST_DEV->data;
|
||||||
|
|
||||||
|
ipm_handler = devdata->handle_message;
|
||||||
|
|
||||||
ztest_test_suite(intel_adsp,
|
ztest_test_suite(intel_adsp,
|
||||||
ztest_unit_test(test_smp_boot_delay),
|
ztest_unit_test(test_smp_boot_delay),
|
||||||
ztest_unit_test(test_cpu_halt),
|
ztest_unit_test(test_cpu_halt),
|
||||||
ztest_unit_test(test_post_boot_ipi),
|
ztest_unit_test(test_post_boot_ipi),
|
||||||
ztest_unit_test(test_cpu_behavior),
|
ztest_unit_test(test_cpu_behavior),
|
||||||
ztest_unit_test(test_host_ipc),
|
ztest_unit_test(test_host_ipc),
|
||||||
ztest_unit_test(test_clock_calibrate)
|
ztest_unit_test(test_clock_calibrate),
|
||||||
|
ztest_unit_test(test_ipm_cavs_host)
|
||||||
);
|
);
|
||||||
|
|
||||||
ztest_run_test_suite(intel_adsp);
|
ztest_run_test_suite(intel_adsp);
|
||||||
|
|
|
@ -38,6 +38,9 @@ enum cavstool_cmd {
|
||||||
|
|
||||||
/* The host emits a (real/host time) timestamp into the log stream */
|
/* The host emits a (real/host time) timestamp into the log stream */
|
||||||
IPCCMD_TIMESTAMP,
|
IPCCMD_TIMESTAMP,
|
||||||
|
|
||||||
|
/* The host copies OUTBOX[ext_data >> 16] to INBOX[ext_data & 0xffff] */
|
||||||
|
IPCCMD_WINCOPY,
|
||||||
};
|
};
|
||||||
|
|
||||||
void test_post_boot_ipi(void);
|
void test_post_boot_ipi(void);
|
||||||
|
@ -45,5 +48,12 @@ void test_smp_boot_delay(void);
|
||||||
void test_host_ipc(void);
|
void test_host_ipc(void);
|
||||||
void test_cpu_behavior(void);
|
void test_cpu_behavior(void);
|
||||||
void test_cpu_halt(void);
|
void test_cpu_halt(void);
|
||||||
|
void test_ipm_cavs_host(void);
|
||||||
|
|
||||||
|
/* Cached copy of the ipm_cavs_host driver's handler. We save it at
|
||||||
|
* the start of the test because we want to do unit testing on the
|
||||||
|
* underlying cavs_ipc device, then recover it later.
|
||||||
|
*/
|
||||||
|
extern cavs_ipc_handler_t ipm_handler;
|
||||||
|
|
||||||
#endif /* ZEPHYR_TESTS_INTEL_ADSP_TESTS_H */
|
#endif /* ZEPHYR_TESTS_INTEL_ADSP_TESTS_H */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue