Add 'turbo' logging feature. When enabled, short logs (no argument or one numeric, 32 bit argument) are handled in a special way that is much faster than the default one (5-10x faster). Additionally, there is an option to remove all other logs from the system which allows to not include almost any logging framework code in the binary (~170 bytes of code is needed). It may be especially valueable for memory constraint targets (ppr, flpr) where with only 170 byte of code (+code for each log message) we can provide limited formatted string logging support. 'Turbo' logging is using following to achieve that: - logging strings are put into a memory section and additional memory section is created which holds addresses of those strings. Index in that array is used to identify a string (32 bit address is encoded into a smaller number, 15 bits is more than enough). This index is used for a STMESP register set (there are 2^16 available). So STMESP channel encodes string. - Logging level is stringified and prepended to a string - Source ID is encoded by using DM16 (so far not used). - Log without arguments is written as DMTS16 - Log with one argumetn is written as DM16+DMTS32 Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
689 lines
18 KiB
C
689 lines
18 KiB
C
/*
|
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log_frontend.h>
|
|
#include <zephyr/logging/log_frontend_stmesp.h>
|
|
#include <zephyr/logging/log_frontend_stmesp_demux.h>
|
|
#include <zephyr/logging/log_output_dict.h>
|
|
#include <zephyr/logging/log_ctrl.h>
|
|
#include <zephyr/logging/log_msg.h>
|
|
#include <zephyr/sys/cbprintf.h>
|
|
#ifdef CONFIG_NRF_ETR
|
|
#include <zephyr/drivers/misc/coresight/nrf_etr.h>
|
|
#endif
|
|
|
|
/* Only 32 bit platforms supported. */
|
|
BUILD_ASSERT(sizeof(void *) == sizeof(uint32_t));
|
|
|
|
#define LOG_FRONTEND_STM_NO_SOURCE 0xFFFF
|
|
|
|
#define EARLY_BUF_SIZE CONFIG_LOG_FRONTEND_STMESP_EARLY_BUF_SIZE
|
|
|
|
#define LEN_SZ sizeof(uint32_t)
|
|
|
|
#define STMESP_FLUSH_WORD 0xaabbccdd
|
|
|
|
#define STM_FLAG(reg) \
|
|
stmesp_flag(reg, 1, false, IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_GUARANTEED_ACCESS))
|
|
|
|
#define STM_D8(reg, data, timestamp, marked) \
|
|
stmesp_data8(reg, data, timestamp, marked, \
|
|
IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_GUARANTEED_ACCESS))
|
|
|
|
#define STM_D16(reg, data, timestamp, marked) \
|
|
stmesp_data16(reg, data, timestamp, marked, \
|
|
IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_GUARANTEED_ACCESS))
|
|
|
|
#define STM_D32(reg, data, timestamp, marked) \
|
|
stmesp_data32(reg, data, timestamp, marked, \
|
|
IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_GUARANTEED_ACCESS))
|
|
|
|
/* Buffer for storing frontend data before STM/ETR is ready for usage.
|
|
* When notification about ETR readiness is received content of this buffer is
|
|
* written to the STM stimulus port.
|
|
*/
|
|
static atomic_t stmesp_chan_cnt;
|
|
|
|
static uint8_t early_buf[EARLY_BUF_SIZE] __aligned(sizeof(uint32_t));
|
|
static uint32_t early_buf_idx;
|
|
static struct k_spinlock lock;
|
|
|
|
/* Flag indicating that STM/ETR is ready for use. */
|
|
static bool etr_rdy;
|
|
|
|
/* Number of messages dropped due to too small early buffer. */
|
|
static uint32_t dropped;
|
|
|
|
/* Flag indicating that logging is in the panic mode. */
|
|
static bool in_panic;
|
|
|
|
static atomic_t new_data;
|
|
|
|
/* Enum used for type bit field in the message. */
|
|
enum stm_msg_type_log_dict {
|
|
/* Dictionary-based log message */
|
|
STM_MSG_TYPE_LOG_DICT = 0,
|
|
/* Reserved for future use. */
|
|
STM_MSG_TYPE_RESERVED = 1,
|
|
};
|
|
|
|
/* Descriptor of the dictionary based logging message. */
|
|
struct stm_log_dict_msg_desc {
|
|
/* Structure version. Current version 0. */
|
|
uint32_t ver: 2;
|
|
|
|
/* Type. Set 0 as this is a logging message descriptor. */
|
|
uint32_t type: 1;
|
|
|
|
/* Severity level. */
|
|
uint32_t level: 3;
|
|
|
|
/* Data length, non-zero fox hexdump message. */
|
|
uint32_t data_len: 12;
|
|
|
|
/* Source ID. Source index as ordered in the memory section. */
|
|
uint32_t source_id: 12;
|
|
|
|
/* Reserved for future use. */
|
|
uint32_t reserved: 2;
|
|
};
|
|
|
|
union stm_log_dict_hdr {
|
|
struct stm_log_dict_msg_desc hdr;
|
|
uint32_t raw;
|
|
};
|
|
|
|
/* Dictionary header initializer. */
|
|
#define DICT_HDR_INITIALIZER(_level, _source_id, _data_len) \
|
|
{ \
|
|
.hdr = {.ver = CONFIG_LOG_FRONTEND_STMESP_DICT_VER, \
|
|
.type = STM_MSG_TYPE_LOG_DICT, \
|
|
.level = _level, \
|
|
.data_len = _data_len, \
|
|
.source_id = _source_id, \
|
|
.reserved = 0 } \
|
|
}
|
|
|
|
/* Align early buffer index to a 32 bit word. */
|
|
static inline void early_buf_align_idx(void)
|
|
{
|
|
uint32_t rem = early_buf_idx & 0x3;
|
|
|
|
if (rem) {
|
|
early_buf_idx += (sizeof(uint32_t) - rem);
|
|
}
|
|
}
|
|
|
|
/* Get address where length is written. Location is used in the dictionary mode
|
|
* where length of the message is only known once whole message is written.
|
|
* Calculated length is written to the length field location.
|
|
*/
|
|
static inline uint32_t *early_buf_len_loc(void)
|
|
{
|
|
early_buf_align_idx();
|
|
|
|
uint32_t *loc = (uint32_t *)&early_buf[early_buf_idx];
|
|
|
|
early_buf_idx += LEN_SZ;
|
|
|
|
return loc;
|
|
}
|
|
|
|
/* Check if there is space for requested amount of data. */
|
|
static inline bool early_buf_has_space(uint32_t len)
|
|
{
|
|
return (EARLY_BUF_SIZE - early_buf_idx) >= len;
|
|
}
|
|
|
|
/* Calculate length of the message. It is calculated by taking current write
|
|
* location and length location (which is at the beginning of the current message.
|
|
* Used in dictionary mode.
|
|
*/
|
|
static inline uint32_t early_buf_get_len(uint32_t *len_loc)
|
|
{
|
|
if (early_buf_idx == EARLY_BUF_SIZE) {
|
|
return 0;
|
|
}
|
|
|
|
return (uint32_t)((uintptr_t)&early_buf[early_buf_idx] - (uintptr_t)len_loc - LEN_SZ);
|
|
}
|
|
|
|
/* Check if there is available space for the message. If yes, write length field
|
|
* and return true. If allocation fails it sets next length field to 0 to indicate
|
|
* that the buffer is full.
|
|
*/
|
|
static inline bool early_buf_alloc(uint32_t len)
|
|
{
|
|
early_buf_align_idx();
|
|
|
|
if (early_buf_has_space(len + LEN_SZ)) {
|
|
*(uint32_t *)&early_buf[early_buf_idx] = len;
|
|
early_buf_idx += LEN_SZ;
|
|
return true;
|
|
}
|
|
|
|
if (early_buf_has_space(LEN_SZ)) {
|
|
*(uint32_t *)&early_buf[early_buf_idx] = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Switch to read mode. Start reading from the beginning. */
|
|
static inline void early_buf_read_mode(void)
|
|
{
|
|
early_buf_idx = 0;
|
|
}
|
|
|
|
/* Get message. Returns 0 if no messages. */
|
|
static inline uint32_t early_buf_get_data(void **buf)
|
|
{
|
|
early_buf_align_idx();
|
|
|
|
if (early_buf_has_space(LEN_SZ)) {
|
|
uint32_t len = *(uint32_t *)&early_buf[early_buf_idx];
|
|
|
|
*buf = &early_buf[early_buf_idx + LEN_SZ];
|
|
early_buf_idx += len + LEN_SZ;
|
|
return len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void early_buf_put_data(const void *buf, size_t len)
|
|
{
|
|
if (early_buf_has_space(len)) {
|
|
memcpy(&early_buf[early_buf_idx], buf, len);
|
|
early_buf_idx += len;
|
|
} else {
|
|
early_buf_idx = EARLY_BUF_SIZE;
|
|
}
|
|
}
|
|
|
|
static int early_package_cb(const void *buf, size_t len, void *ctx)
|
|
{
|
|
ARG_UNUSED(ctx);
|
|
|
|
early_buf_put_data(buf, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void write_data(const void *data, size_t len, STMESP_Type *const stm_esp)
|
|
{
|
|
const uint8_t *p8 = data;
|
|
const uint32_t *p32;
|
|
uint32_t unaligned;
|
|
|
|
if (!len) {
|
|
return;
|
|
}
|
|
|
|
/* Start by writing using D8 or D16 until pointer is word aligned. */
|
|
unaligned = (uintptr_t)data & 0x00000003UL;
|
|
if (unaligned != 0) {
|
|
unaligned = 4 - unaligned;
|
|
unaligned = MIN(len, unaligned);
|
|
|
|
len -= unaligned;
|
|
|
|
switch (unaligned) {
|
|
case 3:
|
|
STM_D8(stm_esp, *p8++, false, false);
|
|
STM_D16(stm_esp, *(uint16_t *)p8, false, false);
|
|
p8 += sizeof(uint16_t);
|
|
break;
|
|
case 2:
|
|
if (len) {
|
|
STM_D16(stm_esp, *(uint16_t *)p8, false, false);
|
|
p8 += sizeof(uint16_t);
|
|
} else {
|
|
/* If len 0 then it means that even though 2 bytes are
|
|
* to be copied we can have address which is not aligned
|
|
* to 2 bytes.
|
|
*/
|
|
STM_D8(stm_esp, *p8++, false, false);
|
|
STM_D8(stm_esp, *p8++, false, false);
|
|
}
|
|
break;
|
|
default:
|
|
/* 1 byte to align. */
|
|
STM_D8(stm_esp, *p8++, false, false);
|
|
}
|
|
}
|
|
|
|
p32 = (const uint32_t *)p8;
|
|
|
|
/* Use D32 to write as much data as possible. */
|
|
while (len >= sizeof(uint32_t)) {
|
|
STM_D32(stm_esp, *p32++, false, false);
|
|
len -= sizeof(uint32_t);
|
|
}
|
|
|
|
/* Write tail using D16 or D8. Address is word aligned at that point. */
|
|
if (len) {
|
|
p8 = (const uint8_t *)p32;
|
|
switch (len) {
|
|
case 2:
|
|
STM_D16(stm_esp, *(uint16_t *)p8, false, false);
|
|
p8 += sizeof(uint16_t);
|
|
break;
|
|
case 3:
|
|
STM_D16(stm_esp, *(uint16_t *)p8, false, false);
|
|
p8 += sizeof(uint16_t);
|
|
/* fallthrough */
|
|
default:
|
|
/* 1 byte to align. */
|
|
STM_D8(stm_esp, *p8++, false, false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int package_cb(const void *buf, size_t len, void *ctx)
|
|
{
|
|
write_data(buf, len, (STMESP_Type *const)ctx);
|
|
|
|
return len;
|
|
}
|
|
|
|
/* Get STM channel to use. Ensure that channel is unique for given priority level.
|
|
* This way we know that writing to that channel will not be interrupted by
|
|
* another log message writing to the same channel.
|
|
*/
|
|
static inline uint16_t get_channel(void)
|
|
{
|
|
return (atomic_inc(&stmesp_chan_cnt) & 0x7F) + 1;
|
|
}
|
|
|
|
/* Convert pointer to the source structure to the source ID. */
|
|
static inline int16_t get_source_id(const void *source)
|
|
{
|
|
if (source != NULL) {
|
|
return log_source_id(source);
|
|
}
|
|
|
|
return LOG_FRONTEND_STM_NO_SOURCE;
|
|
}
|
|
|
|
static void packet_end(STMESP_Type *stm_esp)
|
|
{
|
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_MSG_END_TIMESTAMP)) {
|
|
STM_D8(stm_esp, 0, true, true);
|
|
} else {
|
|
STM_FLAG(stm_esp);
|
|
}
|
|
atomic_set(&new_data, 1);
|
|
}
|
|
|
|
/* Common function to end the message when STMESP is not ready. */
|
|
static inline void early_msg_end(uint32_t *len_loc)
|
|
{
|
|
*len_loc = early_buf_get_len(len_loc);
|
|
if (*len_loc == 0) {
|
|
dropped++;
|
|
}
|
|
}
|
|
|
|
#if CONFIG_LOG_FRONTEND_STMESP_FSC
|
|
void log_frontend_msg(const void *source, const struct log_msg_desc desc, uint8_t *package,
|
|
const void *data)
|
|
{
|
|
uint16_t strl[4];
|
|
union log_frontend_stmesp_demux_header hdr = {.log = {.level = desc.level}};
|
|
bool use_timestamp = desc.level != LOG_LEVEL_INTERNAL_RAW_STRING;
|
|
const char *sname;
|
|
static const char null_c = '\0';
|
|
size_t sname_len;
|
|
int package_len;
|
|
int total_len;
|
|
static const uint32_t flags =
|
|
CBPRINTF_PACKAGE_CONVERT_RW_STR | CBPRINTF_PACKAGE_CONVERT_RO_STR;
|
|
|
|
sname = log_source_name_get(0, get_source_id(source));
|
|
if (sname) {
|
|
sname_len = strlen(sname) + 1;
|
|
} else {
|
|
sname = &null_c;
|
|
sname_len = 1;
|
|
}
|
|
total_len = desc.data_len + sname_len /* null terminator */;
|
|
|
|
package_len = cbprintf_package_convert(package, desc.package_len, NULL, NULL, flags, strl,
|
|
ARRAY_SIZE(strl));
|
|
hdr.log.total_len = total_len + package_len;
|
|
hdr.log.package_len = package_len;
|
|
|
|
if ((EARLY_BUF_SIZE == 0) || etr_rdy) {
|
|
STMESP_Type *stm_esp;
|
|
int err;
|
|
|
|
err = stmesp_get_port(get_channel(), &stm_esp);
|
|
if (err < 0) {
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_MSG_END_TIMESTAMP)) {
|
|
STM_D32(stm_esp, hdr.raw, false, false);
|
|
} else {
|
|
STM_D32(stm_esp, hdr.raw, use_timestamp, true);
|
|
}
|
|
(void)cbprintf_package_convert(package, desc.package_len, package_cb, stm_esp,
|
|
flags, strl, ARRAY_SIZE(strl));
|
|
write_data(sname, sname_len, stm_esp);
|
|
if (data) {
|
|
write_data(data, desc.data_len, stm_esp);
|
|
}
|
|
packet_end(stm_esp);
|
|
|
|
} else {
|
|
k_spinlock_key_t key = k_spin_lock(&lock);
|
|
|
|
if ((EARLY_BUF_SIZE == 0) ||
|
|
(early_buf_alloc(hdr.log.total_len + sizeof(hdr)) == false)) {
|
|
dropped++;
|
|
k_spin_unlock(&lock, key);
|
|
return;
|
|
}
|
|
|
|
early_buf_put_data((const uint8_t *)&hdr, sizeof(hdr));
|
|
(void)cbprintf_package_convert(package, desc.package_len, early_package_cb, NULL,
|
|
flags, strl, ARRAY_SIZE(strl));
|
|
early_buf_put_data(sname, sname_len);
|
|
if (data) {
|
|
early_buf_put_data(data, desc.data_len);
|
|
}
|
|
|
|
k_spin_unlock(&lock, key);
|
|
}
|
|
}
|
|
|
|
#else /* CONFIG_LOG_FRONTEND_STMESP_FSC */
|
|
|
|
void log_frontend_msg(const void *source, const struct log_msg_desc desc, uint8_t *package,
|
|
const void *data)
|
|
{
|
|
static const uint32_t flags = CBPRINTF_PACKAGE_CONVERT_RW_STR;
|
|
union stm_log_dict_hdr dict_desc =
|
|
DICT_HDR_INITIALIZER(desc.level, get_source_id(source), 0);
|
|
|
|
if ((EARLY_BUF_SIZE == 0) || etr_rdy) {
|
|
STMESP_Type *stm_esp;
|
|
int err;
|
|
|
|
err = stmesp_get_port(get_channel(), &stm_esp);
|
|
if (err < 0) {
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_MSG_END_TIMESTAMP)) {
|
|
STM_D32(stm_esp, dict_desc.raw, false, false);
|
|
} else {
|
|
STM_D32(stm_esp, dict_desc.raw, true, true);
|
|
}
|
|
(void)cbprintf_package_convert(package, desc.package_len, package_cb, stm_esp,
|
|
flags, NULL, 0);
|
|
if (data) {
|
|
package_cb(data, desc.data_len, stm_esp);
|
|
}
|
|
packet_end(stm_esp);
|
|
} else {
|
|
k_spinlock_key_t key;
|
|
uint32_t *len_loc;
|
|
|
|
key = k_spin_lock(&lock);
|
|
len_loc = early_buf_len_loc();
|
|
early_package_cb(&dict_desc.raw, sizeof(dict_desc.raw), NULL);
|
|
(void)cbprintf_package_convert(package, desc.package_len, early_package_cb, NULL,
|
|
flags, NULL, 0);
|
|
if (data) {
|
|
early_package_cb(data, desc.data_len, NULL);
|
|
}
|
|
early_msg_end(len_loc);
|
|
k_spin_unlock(&lock, key);
|
|
}
|
|
}
|
|
|
|
/* Common function for optimized message (log with 0-2 arguments) which is used in
|
|
* case when STMESP is not yet ready.
|
|
*/
|
|
static inline uint32_t *early_msg_start(uint32_t level, const void *source, uint32_t package_hdr,
|
|
const char *fmt)
|
|
{
|
|
union stm_log_dict_hdr dict_desc = DICT_HDR_INITIALIZER(level, get_source_id(source), 0);
|
|
uint32_t fmt32 = (uint32_t)fmt;
|
|
uint32_t *len_loc = early_buf_len_loc();
|
|
|
|
early_package_cb(&dict_desc.raw, sizeof(dict_desc.raw), NULL);
|
|
early_package_cb(&package_hdr, sizeof(package_hdr), NULL);
|
|
early_package_cb(&fmt32, sizeof(fmt32), NULL);
|
|
|
|
return len_loc;
|
|
}
|
|
|
|
/* Common function for optimized message (log with 0-2 arguments) which writes to STMESP */
|
|
static inline void msg_start(STMESP_Type *stm_esp, uint32_t level, const void *source,
|
|
uint32_t package_hdr, const char *fmt)
|
|
|
|
{
|
|
union stm_log_dict_hdr dict_desc = DICT_HDR_INITIALIZER(level, get_source_id(source), 0);
|
|
|
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_MSG_END_TIMESTAMP)) {
|
|
STM_D32(stm_esp, dict_desc.raw, false, false);
|
|
} else {
|
|
STM_D32(stm_esp, dict_desc.raw, true, true);
|
|
}
|
|
STM_D32(stm_esp, package_hdr, false, false);
|
|
STM_D32(stm_esp, (uint32_t)fmt, false, false);
|
|
}
|
|
|
|
void log_frontend_simple_0(const void *source, uint32_t level, const char *fmt)
|
|
{
|
|
static const union cbprintf_package_hdr package_hdr = {.desc = {.len = 2}};
|
|
|
|
if ((EARLY_BUF_SIZE == 0) || etr_rdy) {
|
|
STMESP_Type *stm_esp;
|
|
int err;
|
|
|
|
err = stmesp_get_port(get_channel(), &stm_esp);
|
|
if (err < 0) {
|
|
return;
|
|
}
|
|
|
|
msg_start(stm_esp, level, source, (uint32_t)package_hdr.raw, fmt);
|
|
packet_end(stm_esp);
|
|
return;
|
|
}
|
|
|
|
uint32_t *len_loc;
|
|
k_spinlock_key_t key;
|
|
|
|
key = k_spin_lock(&lock);
|
|
len_loc = early_msg_start(level, source, (uint32_t)package_hdr.raw, fmt);
|
|
early_msg_end(len_loc);
|
|
k_spin_unlock(&lock, key);
|
|
}
|
|
|
|
void log_frontend_simple_1(const void *source, uint32_t level, const char *fmt, uint32_t arg)
|
|
{
|
|
static const union cbprintf_package_hdr package_hdr = {.desc = {.len = 2 + 1}};
|
|
|
|
if ((EARLY_BUF_SIZE == 0) || etr_rdy) {
|
|
STMESP_Type *stm_esp;
|
|
int err;
|
|
|
|
err = stmesp_get_port(get_channel(), &stm_esp);
|
|
if (err < 0) {
|
|
return;
|
|
}
|
|
|
|
msg_start(stm_esp, level, source, (uint32_t)package_hdr.raw, fmt);
|
|
STM_D32(stm_esp, arg, false, false);
|
|
packet_end(stm_esp);
|
|
return;
|
|
}
|
|
|
|
uint32_t *len_loc;
|
|
k_spinlock_key_t key;
|
|
|
|
key = k_spin_lock(&lock);
|
|
len_loc = early_msg_start(level, source, (uint32_t)package_hdr.raw, fmt);
|
|
early_package_cb(&arg, sizeof(arg), NULL);
|
|
early_msg_end(len_loc);
|
|
k_spin_unlock(&lock, key);
|
|
}
|
|
|
|
void log_frontend_simple_2(const void *source, uint32_t level, const char *fmt, uint32_t arg0,
|
|
uint32_t arg1)
|
|
{
|
|
static const union cbprintf_package_hdr package_hdr = {.desc = {.len = 2 + 2}};
|
|
|
|
if ((EARLY_BUF_SIZE == 0) || etr_rdy) {
|
|
STMESP_Type *stm_esp;
|
|
int err;
|
|
|
|
err = stmesp_get_port(get_channel(), &stm_esp);
|
|
if (err < 0) {
|
|
return;
|
|
}
|
|
|
|
msg_start(stm_esp, level, source, (uint32_t)package_hdr.raw, fmt);
|
|
STM_D32(stm_esp, arg0, false, false);
|
|
STM_D32(stm_esp, arg1, false, false);
|
|
packet_end(stm_esp);
|
|
return;
|
|
}
|
|
|
|
uint32_t *len_loc;
|
|
k_spinlock_key_t key;
|
|
|
|
key = k_spin_lock(&lock);
|
|
len_loc = early_msg_start(level, source, (uint32_t)package_hdr.raw, fmt);
|
|
early_package_cb(&arg0, sizeof(arg0), NULL);
|
|
early_package_cb(&arg1, sizeof(arg1), NULL);
|
|
early_msg_end(len_loc);
|
|
k_spin_unlock(&lock, key);
|
|
}
|
|
|
|
#endif /* CONFIG_LOG_FRONTEND_STMESP_FSC */
|
|
|
|
void log_frontend_panic(void)
|
|
{
|
|
in_panic = true;
|
|
#ifdef CONFIG_NRF_ETR
|
|
nrf_etr_flush();
|
|
#endif
|
|
}
|
|
|
|
void log_frontend_init(void)
|
|
{
|
|
#if defined(CONFIG_LOG_FRONTEND_STPESP_TURBO_SOURCE_PORT_ID) && !defined(CONFIG_NRF_ETR)
|
|
/* Send location of section with constant source data. It is used by the
|
|
* application core to retrieve source names of log messages coming from
|
|
* coprocessors (FLPR and PPR).
|
|
*/
|
|
TYPE_SECTION_START_EXTERN(struct log_source_const_data, log_const);
|
|
STMESP_Type *stm_esp;
|
|
uintptr_t log_const_start;
|
|
|
|
(void)stmesp_get_port(CONFIG_LOG_FRONTEND_STPESP_TURBO_SOURCE_PORT_ID, &stm_esp);
|
|
log_const_start = (uintptr_t)TYPE_SECTION_START(log_const);
|
|
STM_D32(stm_esp, log_const_start, false, true);
|
|
#endif
|
|
}
|
|
|
|
void log_frontend_stmesp_dummy_write(void)
|
|
{
|
|
#define STMESP_DUMMY_WORD 0xaabbccdd
|
|
|
|
STMESP_Type *stm_esp;
|
|
|
|
(void)stmesp_get_port(CONFIG_LOG_FRONTEND_STMESP_FLUSH_PORT_ID, &stm_esp);
|
|
STM_D32(stm_esp, STMESP_DUMMY_WORD, false, false);
|
|
}
|
|
|
|
void log_frontend_stmesp_pre_sleep(void)
|
|
{
|
|
bool use_stm = etr_rdy || (EARLY_BUF_SIZE == 0);
|
|
|
|
if (!use_stm || new_data == 0) {
|
|
return;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < CONFIG_LOG_FRONTEND_STMESP_FLUSH_COUNT; i++) {
|
|
log_frontend_stmesp_dummy_write();
|
|
}
|
|
|
|
atomic_set(&new_data, 0);
|
|
}
|
|
|
|
#if EARLY_BUF_SIZE > 0
|
|
int log_frontend_stmesp_etr_ready(void)
|
|
{
|
|
STMESP_Type *stm_esp;
|
|
uint16_t len;
|
|
uint32_t *buf = NULL;
|
|
int err;
|
|
|
|
err = stmesp_get_port(get_channel(), &stm_esp);
|
|
if (err < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
early_buf_read_mode();
|
|
|
|
while ((len = early_buf_get_data((void **)&buf)) > 0) {
|
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_MSG_END_TIMESTAMP)) {
|
|
STM_D32(stm_esp, *buf, false, false);
|
|
} else {
|
|
/* Write first word with Marked and timestamp. */
|
|
STM_D32(stm_esp, *buf, true, true);
|
|
}
|
|
buf++;
|
|
len -= sizeof(uint32_t);
|
|
|
|
/* Write remaining data as raw data. */
|
|
write_data(buf, len, stm_esp);
|
|
|
|
/* Flag the end. */
|
|
packet_end(stm_esp);
|
|
}
|
|
|
|
etr_rdy = true;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* EARLY_BUF_SIZE > 0 */
|
|
|
|
void log_frontend_stmesp_log0(const void *source, uint32_t x)
|
|
{
|
|
STMESP_Type *port;
|
|
int err = stmesp_get_port((uint32_t)x + 0x8000, &port);
|
|
uint16_t source_id = log_source_id(source);
|
|
|
|
__ASSERT_NO_MSG(err == 0);
|
|
if (err == 0) {
|
|
stmesp_data16(port, source_id, true, true,
|
|
IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_GUARANTEED_ACCESS));
|
|
}
|
|
}
|
|
|
|
void log_frontend_stmesp_log1(const void *source, uint32_t x, uint32_t arg)
|
|
{
|
|
STMESP_Type *port;
|
|
int err = stmesp_get_port((uint32_t)x + 0x8000, &port);
|
|
uint16_t source_id = log_source_id(source);
|
|
|
|
__ASSERT_NO_MSG(err == 0);
|
|
if (err == 0) {
|
|
stmesp_data16(port, source_id, false, true,
|
|
IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_GUARANTEED_ACCESS));
|
|
stmesp_data32(port, arg, true, true,
|
|
IS_ENABLED(CONFIG_LOG_FRONTEND_STMESP_GUARANTEED_ACCESS));
|
|
}
|
|
}
|