logging: adsp hda backend improvements

* Adds a default hook and init function for cavstool.
* Adds an optional padding on flush feature to ensure all data is written.
* Fixes an error in cavstool.py for correctly wrapping the ring buffer.
* The test case now ensures wraps and flushes occur numerous times.

Signed-off-by: Tom Burdick <thomas.burdick@intel.com>
This commit is contained in:
Tom Burdick 2022-08-24 16:10:23 -05:00 committed by Anas Nashif
commit 3a4864bdd2
5 changed files with 156 additions and 72 deletions

View file

@ -117,6 +117,7 @@ class HDAStream:
self.regs.BDPL = self.buf_list_addr & 0xffffffff self.regs.BDPL = self.buf_list_addr & 0xffffffff
self.regs.CBL = buf_len self.regs.CBL = buf_len
self.regs.LVI = self.n_bufs - 1 self.regs.LVI = self.n_bufs - 1
self.mem.seek(0)
self.debug() self.debug()
log.info(f"Configured stream {self.stream_id}") log.info(f"Configured stream {self.stream_id}")
@ -620,17 +621,29 @@ def ipc_command(data, ext_data):
buf[i] = i buf[i] = i
hda_streams[stream_id].write(buf) hda_streams[stream_id].write(buf)
elif data == 12: # HDA PRINT elif data == 12: # HDA PRINT
log.info("Doing HDA Print")
stream_id = ext_data & 0xFF stream_id = ext_data & 0xFF
buf_len = ext_data >> 8 & 0xFFFF buf_len = ext_data >> 8 & 0xFFFF
hda_str = hda_streams[stream_id] hda_str = hda_streams[stream_id]
# check for wrap here
pos = hda_str.mem.tell() pos = hda_str.mem.tell()
buf_data = hda_str.mem.read(buf_len).decode("utf-8", "replace") read_lens = [buf_len, 0]
log.info(f"DSP LOG MSG (idx: {pos}, len: {buf_len}): {buf_data}") if pos + buf_len >= hda_str.buf_len*2:
pos = hda_str.mem.tell() read_lens[0] = hda_str.buf_len*2 - pos
if pos >= hda_str.buf_len*2: read_lens[1] = buf_len - read_lens[0]
log.info(f"Wrapping log reader, pos {pos} len {hda_str.buf_len}") # validate the read lens
assert (read_lens[0] + pos) <= (hda_str.buf_len*2)
assert read_lens[0] % 128 == 0
assert read_lens[1] % 128 == 0
buf_data0 = hda_str.mem.read(read_lens[0])
hda_msg0 = buf_data0.decode("utf-8", "replace")
sys.stdout.write(hda_msg0)
if read_lens[1] != 0:
hda_str.mem.seek(0) hda_str.mem.seek(0)
buf_data1 = hda_str.mem.read(read_lens[1])
hda_msg1 = buf_data1.decode("utf-8", "replace")
sys.stdout.write(hda_msg1)
pos = hda_str.mem.tell()
sys.stdout.flush()
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}")
if not fw_is_alive(): if not fw_is_alive():
@ -887,6 +900,8 @@ if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(_main(None)) asyncio.get_event_loop().run_until_complete(_main(None))
except KeyboardInterrupt: except KeyboardInterrupt:
start_output = False start_output = False
except Exception as e:
log.error(e)
finally: finally:
sys.exit(0) sys.exit(0)

View file

@ -367,6 +367,20 @@ config LOG_BACKEND_ADSP_HDA_FLUSH_TIME
to hardware by informing the DMA hardware the contents of the ring to hardware by informing the DMA hardware the contents of the ring
buffer. buffer.
config LOG_BACKEND_ADSP_HDA_CAVSTOOL
bool "Log backend is to be used with cavstool"
help
Use cavstool understood IPC messages to inform setup and logging of
HDA messages.
config LOG_BACKEND_ADSP_HDA_PADDING
bool "Log backend should pad the buffer with \0 characters when flushing"
help
HDA requires transfers be 128 byte aligned such that a partial message may
never make it across unless padded with \0 characters to the next 128 byte
aligned address. This may or may not work depending on the log format
being used but should certainly work with text based logging.
endif # LOG_BACKEND_ADSP_HDA endif # LOG_BACKEND_ADSP_HDA
config LOG_BACKEND_FS config LOG_BACKEND_FS

View file

@ -25,31 +25,58 @@ static uint32_t hda_log_chan;
#define ALIGNMENT DMA_BUF_ALIGNMENT(DT_NODELABEL(hda_host_in)) #define ALIGNMENT DMA_BUF_ALIGNMENT(DT_NODELABEL(hda_host_in))
static __aligned(ALIGNMENT) uint8_t hda_log_buf[CONFIG_LOG_BACKEND_ADSP_HDA_SIZE]; static __aligned(ALIGNMENT) uint8_t hda_log_buf[CONFIG_LOG_BACKEND_ADSP_HDA_SIZE];
static volatile uint32_t hda_log_buffered; static volatile uint32_t hda_log_buffered;
static struct k_spinlock hda_log_lock;
static struct k_timer hda_log_timer; static struct k_timer hda_log_timer;
static adsp_hda_log_hook_t hook; static adsp_hda_log_hook_t hook;
static uint32_t write_idx;
static struct k_spinlock hda_log_lock;
/* atomic bit flags for state */ /* atomic bit flags for state */
#define HDA_LOG_DMA_READY 0 #define HDA_LOG_DMA_READY 0
#define HDA_LOG_PANIC_MODE 1 #define HDA_LOG_PANIC_MODE 1
static atomic_t hda_log_flags; static atomic_t hda_log_flags;
/**
* Flush the buffered log to the HDA stream
*
* If @kconfig{CONFIG_LOG_BACKEND_ADSP_HDA_PADDING} is enabled
* the buffer is extended with '\0' characters to align the buffer
* to 128 bytes.
*/
static uint32_t hda_log_flush(void) static uint32_t hda_log_flush(void)
{ {
if (hda_log_buffered == 0) {
return 0;
}
uint32_t nearest128 = hda_log_buffered & ~((128) - 1); uint32_t nearest128 = hda_log_buffered & ~((128) - 1);
if (nearest128 > 0) { #ifdef CONFIG_LOG_BACKEND_ADSP_HDA_PADDING
hda_log_buffered = hda_log_buffered - nearest128; if (nearest128 != hda_log_buffered) {
uint32_t next128 = nearest128 + 128;
uint32_t padding = next128 - hda_log_buffered;
for (int i = 0; i < padding; i++) {
hda_log_buf[write_idx] = '\0';
hda_log_buffered++;
write_idx++;
}
nearest128 = hda_log_buffered & ~((128) - 1);
}
__ASSERT(hda_log_buffered == nearest128,
"Expected flush length to be 128 byte aligned");
#endif
#if !(IS_ENABLED(CONFIG_KERNEL_COHERENCE)) #if !(IS_ENABLED(CONFIG_KERNEL_COHERENCE))
z_xtensa_cache_flush(hda_log_buf, CONFIG_LOG_BACKEND_ADSP_HDA_SIZE); z_xtensa_cache_flush(hda_log_buf, CONFIG_LOG_BACKEND_ADSP_HDA_SIZE);
#endif #endif
dma_reload(hda_log_dev, hda_log_chan, 0, 0, nearest128); dma_reload(hda_log_dev, hda_log_chan, 0, 0, nearest128);
}
hda_log_buffered = hda_log_buffered - nearest128;
return nearest128; return nearest128;
} }
static int hda_log_out(uint8_t *data, size_t length, void *ctx) static int hda_log_out(uint8_t *data, size_t length, void *ctx)
{ {
int ret; int ret;
@ -97,26 +124,29 @@ static int hda_log_out(uint8_t *data, size_t length, void *ctx)
/* If there isn't enough space for the message there's an overflow */ /* If there isn't enough space for the message there's an overflow */
if (available < length) { if (available < length) {
ret = length; ret = 0;
goto out; goto out;
} }
/* Copy over the message to the buffer */ /* Copy over the message to the buffer */
uint32_t idx = write_pos + hda_log_buffered; write_idx = write_pos + hda_log_buffered;
if (idx > sizeof(hda_log_buf)) { if (write_idx > sizeof(hda_log_buf)) {
idx -= sizeof(hda_log_buf); write_idx -= sizeof(hda_log_buf);
} }
size_t copy_len = (idx + length) < sizeof(hda_log_buf) ? length : sizeof(hda_log_buf) - idx; size_t copy_len = (write_idx + length) < sizeof(hda_log_buf) ? length
: sizeof(hda_log_buf) - write_idx;
memcpy(&hda_log_buf[idx], data, copy_len); memcpy(&hda_log_buf[write_idx], data, copy_len);
write_idx += copy_len;
/* There may be a wrapped copy */ /* There may be a wrapped copy */
size_t wrap_copy_len = length - copy_len; size_t wrap_copy_len = length - copy_len;
if (wrap_copy_len != 0) { if (wrap_copy_len != 0) {
memcpy(&hda_log_buf[0], &data[copy_len], wrap_copy_len); memcpy(&hda_log_buf[0], &data[copy_len], wrap_copy_len);
write_idx = wrap_copy_len;
} }
ret = length; ret = length;
@ -133,7 +163,8 @@ out:
atomic_test_bit(&hda_log_flags, HDA_LOG_PANIC_MODE)) atomic_test_bit(&hda_log_flags, HDA_LOG_PANIC_MODE))
&& atomic_test_bit(&hda_log_flags, HDA_LOG_DMA_READY); && atomic_test_bit(&hda_log_flags, HDA_LOG_DMA_READY);
if (do_log_flush) { /* Flush if there's a hook set AND dma is ready */
if (do_log_flush && hook != NULL) {
written = hda_log_flush(); written = hda_log_flush();
} }
@ -162,11 +193,14 @@ static void hda_log_periodic(struct k_timer *tm)
{ {
ARG_UNUSED(tm); ARG_UNUSED(tm);
uint32_t written = 0;
k_spinlock_key_t key = k_spin_lock(&hda_log_lock); k_spinlock_key_t key = k_spin_lock(&hda_log_lock);
uint32_t written = hda_log_flush();
k_spin_unlock(&hda_log_lock, key); if (hook != NULL) {
written = hda_log_flush();
}
/* The hook may have log calls and needs to be done outside of the spin /* The hook may have log calls and needs to be done outside of the spin
* lock to avoid recursion on the spin lock (deadlocks) in cases of * lock to avoid recursion on the spin lock (deadlocks) in cases of
@ -175,6 +209,8 @@ static void hda_log_periodic(struct k_timer *tm)
if (hook != NULL && written > 0) { if (hook != NULL && written > 0) {
hook(written); hook(written);
} }
k_spin_unlock(&hda_log_lock, key);
} }
static inline void dropped(const struct log_backend *const backend, static inline void dropped(const struct log_backend *const backend,
@ -283,5 +319,61 @@ void adsp_hda_log_init(adsp_hda_log_hook_t fn, uint32_t channel)
K_MSEC(CONFIG_LOG_BACKEND_ADSP_HDA_FLUSH_TIME), K_MSEC(CONFIG_LOG_BACKEND_ADSP_HDA_FLUSH_TIME),
K_MSEC(CONFIG_LOG_BACKEND_ADSP_HDA_FLUSH_TIME)); K_MSEC(CONFIG_LOG_BACKEND_ADSP_HDA_FLUSH_TIME));
printk("hda log initialized\n");
} }
#ifdef CONFIG_LOG_BACKEND_ADSP_HDA_CAVSTOOL
#include <cavs_ipc.h>
#include <cavstool.h>
#define CHANNEL 6
#define HOST_BUF_SIZE 8192
#define IPC_TIMEOUT K_MSEC(1500)
static inline void hda_ipc_msg(const struct device *dev, uint32_t data,
uint32_t ext, k_timeout_t timeout)
{
__ASSERT(cavs_ipc_send_message_sync(dev, data, ext, timeout),
"Unexpected ipc send message failure, try increasing IPC_TIMEOUT");
}
void adsp_hda_log_cavstool_hook(uint32_t written)
{
/* We *must* send this, but we may be in a timer ISR, so we are
* forced into a retry loop without timeouts and such.
*/
bool done = false;
/* Send IPC message notifying log data has been written */
do {
done = cavs_ipc_send_message(CAVS_HOST_DEV, IPCCMD_HDA_PRINT,
(written << 8) | CHANNEL);
} while (!done);
/* Wait for confirmation log data has been received */
do {
done = cavs_ipc_is_complete(CAVS_HOST_DEV);
} while (!done);
}
int adsp_hda_log_cavstool_init(const struct device *dev)
{
ARG_UNUSED(dev);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET, CHANNEL, IPC_TIMEOUT);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_CONFIG,
CHANNEL | (HOST_BUF_SIZE << 8), IPC_TIMEOUT);
adsp_hda_log_init(adsp_hda_log_cavstool_hook, CHANNEL);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_START, CHANNEL, IPC_TIMEOUT);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_PRINT,
((HOST_BUF_SIZE*2) << 8) | CHANNEL, IPC_TIMEOUT);
return 0;
}
SYS_INIT(adsp_hda_log_cavstool_init, POST_KERNEL, 99);
#endif /* CONFIG_LOG_BACKEND_ADSP_HDA_CAVSTOOL */

View file

@ -3,9 +3,11 @@ CONFIG_ZTEST_NEW_API=y
CONFIG_DMA=y CONFIG_DMA=y
CONFIG_ASSERT=y CONFIG_ASSERT=y
CONFIG_LOG=y CONFIG_LOG=y
CONFIG_LOG_MODE_DEFERRED=y CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_LOG_BACKEND_ADSP=n CONFIG_LOG_BACKEND_ADSP=n
CONFIG_LOG_BACKEND_ADSP_HDA=y CONFIG_LOG_BACKEND_ADSP_HDA=y
CONFIG_LOG_BACKEND_ADSP_HDA_SIZE=512 CONFIG_LOG_BACKEND_ADSP_HDA_SIZE=8192
CONFIG_LOG_BACKEND_ADSP_HDA_PADDING=y
CONFIG_LOG_BACKEND_ADSP_HDA_CAVSTOOL=y
CONFIG_LOG_BACKEND_ADSP_HDA_FLUSH_TIME=100 CONFIG_LOG_BACKEND_ADSP_HDA_FLUSH_TIME=100
CONFIG_XTENSA_ENABLE_BACKTRACE=n CONFIG_XTENSA_ENABLE_BACKTRACE=n

View file

@ -11,62 +11,23 @@
#include <cavs_ipc.h> #include <cavs_ipc.h>
#include "tests.h" #include "tests.h"
#define CHANNEL 6
#define HOST_BUF_SIZE 512
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(hda_test, LOG_LEVEL_DBG); LOG_MODULE_REGISTER(hda_test, LOG_LEVEL_DBG);
#define IPC_TIMEOUT K_MSEC(1500)
static void hda_log_hook(uint32_t written)
{
/* We *must* send this, but we may be in a timer ISR, so we are
* forced into a retry loop without timeouts and such.
*/
bool done = false;
/* Now send the next one */
do {
done = cavs_ipc_send_message(CAVS_HOST_DEV, IPCCMD_HDA_PRINT,
(written << 8) | CHANNEL);
} while (!done);
/* Previous message may not be done yet, wait for that */
do {
done = cavs_ipc_is_complete(CAVS_HOST_DEV);
} while (!done);
}
ZTEST(intel_adsp_hda_log, test_hda_logger) ZTEST(intel_adsp_hda_log, test_hda_logger)
{ {
const struct log_backend *hda_log_backend = log_backend_get(0); TC_PRINT("Testing hda log backend\n");
zassert_not_null(hda_log_backend, "Expected hda log backend"); /* Wait a moment so the output isn't mangled */
k_msleep(100);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET, CHANNEL, IPC_TIMEOUT); /* Ensure multiple wraps and many many logs are written */
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_CONFIG, for (int i = 0; i < 4096; i++) {
CHANNEL | (HOST_BUF_SIZE << 8), IPC_TIMEOUT);
adsp_hda_log_init(hda_log_hook, CHANNEL);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_START, CHANNEL, IPC_TIMEOUT);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_PRINT,
((HOST_BUF_SIZE*2) << 8) | CHANNEL, IPC_TIMEOUT);
printk("Testing log backend\n");
for (int i = 0; i < 512; i++) {
LOG_DBG("test hda log message %d", i); LOG_DBG("test hda log message %d", i);
} }
printk("Sleeping to let the log flush\n"); /* Wait another moment so dma may flush */
k_sleep(K_MSEC(100));
k_sleep(K_MSEC(500));
printk("Done\n");
} }
ZTEST_SUITE(intel_adsp_hda_log, NULL, NULL, NULL, NULL, NULL); ZTEST_SUITE(intel_adsp_hda_log, NULL, NULL, NULL, NULL, NULL);