diff --git a/app/boards/intel_adsp_ace15_mtpm.conf b/app/boards/intel_adsp_ace15_mtpm.conf index 31009202196a..f4aa71fdc236 100644 --- a/app/boards/intel_adsp_ace15_mtpm.conf +++ b/app/boards/intel_adsp_ace15_mtpm.conf @@ -47,6 +47,7 @@ CONFIG_AMS=y CONFIG_COUNTER=y CONFIG_SOF_TELEMETRY=y CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=y +CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=y CONFIG_HEAP_MEM_POOL_SIZE=8192 CONFIG_L3_HEAP=y diff --git a/app/boards/intel_adsp_ace20_lnl.conf b/app/boards/intel_adsp_ace20_lnl.conf index a5d7ce9ce269..9c7f843af67c 100644 --- a/app/boards/intel_adsp_ace20_lnl.conf +++ b/app/boards/intel_adsp_ace20_lnl.conf @@ -41,6 +41,7 @@ CONFIG_AMS=y CONFIG_COUNTER=y CONFIG_SOF_TELEMETRY=y CONFIG_SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS=y +CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS=y CONFIG_HEAP_MEM_POOL_SIZE=8192 CONFIG_L3_HEAP=y diff --git a/src/audio/base_fw.c b/src/audio/base_fw.c index 04df6fecfe1f..39da6b1c7492 100644 --- a/src/audio/base_fw.c +++ b/src/audio/base_fw.c @@ -460,6 +460,45 @@ static int global_perf_data_get(uint32_t *data_off_size, char *data) #endif } +static int io_global_perf_state_get(uint32_t *data_off_size, char *data) +{ +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + *data = io_perf_monitor_get_state(); + *data_off_size = sizeof(enum ipc4_perf_measurements_state_set); + + return IPC4_SUCCESS; +#else + return IPC4_UNAVAILABLE; +#endif +} + +static int io_global_perf_data_get(uint32_t *data_off_size, char *data) +{ +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + int ret; + struct io_global_perf_data *perf_data = (struct io_global_perf_data *)data; + + ret = io_perf_monitor_get_performance_data(perf_data); + if (ret < 0) + return IPC4_ERROR_INVALID_PARAM; + *data_off_size = sizeof(*perf_data) + + perf_data->perf_item_count * sizeof(*perf_data->perf_items); + + return IPC4_SUCCESS; +#else + return IPC4_UNAVAILABLE; +#endif +} + +static int io_perf_monitor_state_set(const char *data) +{ +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + return io_perf_monitor_set_state((enum ipc4_perf_measurements_state_set)*data); +#else + return IPC4_UNAVAILABLE; +#endif +} + static int basefw_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, @@ -503,6 +542,11 @@ static int basefw_get_large_config(struct comp_dev *dev, return extended_global_perf_data_get(data_offset, data); case IPC4_GLOBAL_PERF_DATA: return global_perf_data_get(data_offset, data); + case IPC4_IO_PERF_MEASUREMENTS_STATE: + return io_global_perf_state_get(data_offset, data); + case IPC4_IO_GLOBAL_PERF_DATA: + return io_global_perf_data_get(data_offset, data); + /* TODO: add more support */ case IPC4_DSP_RESOURCE_STATE: case IPC4_NOTIFICATION_MASK: @@ -574,6 +618,8 @@ static int basefw_set_large_config(struct comp_dev *dev, return basefw_dma_control(first_block, last_block, data_offset, data); case IPC4_PERF_MEASUREMENTS_STATE: return set_perf_meas_state(data); + case IPC4_IO_PERF_MEASUREMENTS_STATE: + return io_perf_monitor_state_set(data); case IPC4_SYSTEM_TIME: return basefw_set_system_time(param_id, first_block, last_block, data_offset, data); diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index aea7a64d1031..89185c3f466a 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -41,6 +41,8 @@ #include #include +#include + /* note: if this macro is not defined * then that means the HOST and the DSP * have the same view of the address space. @@ -383,6 +385,10 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, /* update host position (in bytes offset) for drivers */ dd->total_data_processed += bytes; } +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(dd->io_perf_bytes_count, bytes); +#endif return dma_status; } @@ -478,6 +484,48 @@ int dai_common_new(struct dai_data *dd, struct comp_dev *dev, dd->xrun = 0; dd->chan = NULL; + /* I/O performance init, keep it last so the function does not reach this in case + * of return on error, so that we do not waste a slot + */ +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + enum io_perf_data_item_id perf_type; + enum io_perf_data_item_dir perf_dir; + + switch (dai_cfg->type) { + case SOF_DAI_INTEL_SSP: + perf_type = IO_PERF_I2S_ID; + break; + case SOF_DAI_INTEL_ALH: + perf_type = IO_PERF_SOUND_WIRE_ID; + break; + case SOF_DAI_INTEL_DMIC: + perf_type = IO_PERF_DMIC_ID; + break; + case SOF_DAI_INTEL_HDA: + perf_type = IO_PERF_HDA_ID; + break; + + default: + perf_type = IO_PERF_INVALID_ID; + comp_warn(dev, "Unsupported DAI type"); + } + if (dai_cfg->direction == SOF_IPC_STREAM_PLAYBACK) + perf_dir = IO_PERF_OUTPUT_DIRECTION; + else + perf_dir = IO_PERF_INPUT_DIRECTION; + + /* ignore perf meas init on case of other dai types */ + if (perf_type != IO_PERF_INVALID_ID) { + struct io_perf_data_item init_data = {perf_type, + cpu_get_id(), + perf_dir, + IO_PERF_POWERED_UP_ENABLED, + IO_PERF_D0IX_POWER_MODE, + 0, 0, 0 }; + io_perf_monitor_init_data(&dd->io_perf_bytes_count, &init_data); + } +#endif + return 0; } @@ -523,6 +571,10 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, void dai_common_free(struct dai_data *dd) { +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + io_perf_monitor_release_slot(dd->io_perf_bytes_count); +#endif + if (dd->group) dai_group_put(dd->group); diff --git a/src/debug/telemetry/Kconfig b/src/debug/telemetry/Kconfig index 6aca1db92916..6e6a55d18f3c 100644 --- a/src/debug/telemetry/Kconfig +++ b/src/debug/telemetry/Kconfig @@ -18,3 +18,11 @@ config SOF_TELEMETRY_PERFORMANCE_MEASUREMENTS Performance records are stored in the limited number of slots in Memory Window 3, so only a certain number (PERFORMANCE_DATA_ENTRIES_COUNT) of components can be measured. +config SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + bool "enable I/O performance measurements" + help + Enables IO performance measurements. Each data interface will have its data throughput + measured (IPC/IDC and GPIO will measure number of messages/state changes). + Disabled by default and enabled with IPC. Measurements can be extracted also by IPC. + Interfaces measured: IPC, IDC, DMIC, I2S, SNDW, HDA, USB, GPIO, I2c, I3C, UART, SPI, CSI_2, DTF. + diff --git a/src/debug/telemetry/performance_monitor.c b/src/debug/telemetry/performance_monitor.c index c99cc5eb1193..dc5e80c7c253 100644 --- a/src/debug/telemetry/performance_monitor.c +++ b/src/debug/telemetry/performance_monitor.c @@ -38,6 +38,13 @@ struct perf_bitmap { size_t size; }; +struct io_perf_monitor_ctx { + struct k_spinlock lock; + enum ipc4_perf_measurements_state_set state; + struct perf_bitmap io_performance_data_bitmap; + struct io_perf_data_item *io_perf_data; +}; + struct perf_bitmap performance_data_bitmap; struct perf_data_item_comp *perf_data; @@ -93,6 +100,7 @@ static int perf_bitmap_setbit(struct perf_bitmap * const bitmap, size_t bit) return sys_bitarray_set_bit(bitmap->array, bit); } +__attribute__((unused)) static int perf_bitmap_clearbit(struct perf_bitmap * const bitmap, size_t bit) { return sys_bitarray_clear_bit(bitmap->array, bit); @@ -138,12 +146,8 @@ struct perf_data_item_comp *perf_data_getnext(void) int perf_data_free(struct perf_data_item_comp * const item) { /* find index of item */ - int idx = (item - perf_data) / sizeof(*item); - int ret = perf_bitmap_clearbit(&performance_data_bitmap, idx); - - if (ret < 0) - return ret; - ret = perf_bitmap_free(&performance_data_bitmap, idx); + int idx = (item - perf_data); + int ret = perf_bitmap_free(&performance_data_bitmap, idx); if (ret < 0) return ret; @@ -397,3 +401,223 @@ int performance_monitor_init(void) /* init performance monitor using Zephyr */ SYS_INIT(performance_monitor_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + +SYS_BITARRAY_DEFINE_STATIC(io_performance_data_bit_array, PERFORMANCE_DATA_ENTRIES_COUNT); + +#define IO_PERFORMANCE_ALLOC_BYTES 0x1000 +#define IO_PERFORMANCE_MAX_ENTRIES (IO_PERFORMANCE_ALLOC_BYTES / sizeof(struct io_perf_data_item)) + +static struct io_perf_monitor_ctx perf_monitor_ctx; +static struct io_perf_data_item io_perf_data_items[IO_PERFORMANCE_MAX_ENTRIES]; + +int io_perf_monitor_init(void) +{ + int ret; + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + + k_spinlock_init(&self->lock); + k_spinlock_key_t key = k_spin_lock(&self->lock); + + self->io_perf_data = (struct io_perf_data_item *)io_perf_data_items; + self->state = IPC4_PERF_MEASUREMENTS_DISABLED; + + ret = perf_bitmap_init(&self->io_performance_data_bitmap, &io_performance_data_bit_array, + IO_PERFORMANCE_MAX_ENTRIES); + + k_spin_unlock(&self->lock, key); + return ret; +} + +static struct io_perf_data_item *io_perf_monitor_get_next_slot(struct io_perf_monitor_ctx *self) +{ + int idx; + int ret; + k_spinlock_key_t key = k_spin_lock(&self->lock); + + ret = perf_bitmap_alloc(&self->io_performance_data_bitmap, &idx); + if (ret < 0) + return NULL; + /* ref. FW did not set the bits, but here we do it to not have to use + * isFree() check that the bitarray does not provide yet. Instead we will use isClear + * ,and always set bit on bitmap alloc. + */ + + ret = perf_bitmap_setbit(&self->io_performance_data_bitmap, idx); + if (ret < 0) + return NULL; + + k_spin_unlock(&self->lock, key); + return &self->io_perf_data[idx]; +} + +int io_perf_monitor_release_slot(struct io_perf_data_item *item) +{ + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + int idx; + int ret; + k_spinlock_key_t key; + + if (!item) { + tr_err(&ipc_tr, "perf_data_item is null"); + return -EINVAL; + } + + item->is_removed = true; + + key = k_spin_lock(&self->lock); + idx = item - self->io_perf_data; + k_spin_unlock(&self->lock, key); + + /* we assign data items ourselves so neither of those should ever fail */ + ret = perf_bitmap_free(&self->io_performance_data_bitmap, idx); + assert(!ret); + return ret; +} + +int +io_perf_monitor_get_performance_data(struct io_global_perf_data *io_global_perf_data) +{ + if (!io_global_perf_data) + return 0; + + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + k_spinlock_key_t key = k_spin_lock(&self->lock); + + size_t slot_idx = 0; + size_t slots_count = self->io_performance_data_bitmap.occupied; + size_t entries_cont = self->io_performance_data_bitmap.size; + + for (size_t idx = 0; idx < entries_cont && slot_idx < slots_count; ++idx) { + if (perf_bitmap_is_bit_clear(&self->io_performance_data_bitmap, idx)) + continue; + io_global_perf_data->perf_items[slot_idx] = self->io_perf_data[idx]; + ++slot_idx; + } + io_global_perf_data->perf_item_count = slots_count; + + k_spin_unlock(&self->lock, key); + return 0; +} + +static int +io_perf_monitor_disable(struct io_perf_monitor_ctx *self) +{ + return 0; +} + +static int +io_perf_monitor_stop(struct io_perf_monitor_ctx *self) +{ + size_t slot_idx = 0; + size_t slots_count = self->io_performance_data_bitmap.occupied; + size_t entries_cont = self->io_performance_data_bitmap.size; + + for (size_t idx = 0; idx < entries_cont && slot_idx < slots_count; ++idx) { + if (perf_bitmap_is_bit_clear(&self->io_performance_data_bitmap, idx)) + continue; + + self->io_perf_data[idx].data = 0; + ++slot_idx; + } + + return 0; +} + +static int +io_perf_monitor_start(struct io_perf_monitor_ctx *self) +{ + return 0; +} + +static int +io_perf_monitor_pause(struct io_perf_monitor_ctx *self) +{ + return 0; +} + +int io_perf_monitor_set_state(enum ipc4_perf_measurements_state_set state) +{ + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + int ret = 0; + k_spinlock_key_t key = k_spin_lock(&self->lock); + + switch (state) { + case IPC4_PERF_MEASUREMENTS_DISABLED: + ret = io_perf_monitor_disable(self); + break; + case IPC4_PERF_MEASUREMENTS_STOPPED: + ret = io_perf_monitor_stop(self); + break; + case IPC4_PERF_MEASUREMENTS_STARTED: + ret = io_perf_monitor_start(self); + break; + case IPC4_PERF_MEASUREMENTS_PAUSED: + ret = io_perf_monitor_pause(self); + break; + default: + ret = -EINVAL; + } + + if (ret == 0) + self->state = state; + + k_spin_unlock(&self->lock, key); + return ret; +} + +inline enum ipc4_perf_measurements_state_set io_perf_monitor_get_state(void) +{ + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + + return self->state; +} + +int io_perf_monitor_init_data(struct io_perf_data_item **slot_id, + struct io_perf_data_item *init_data) +{ + if (!slot_id) + return IPC4_ERROR_INVALID_PARAM; + + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + struct io_perf_data_item *new_slot = io_perf_monitor_get_next_slot(self); + + if (!new_slot) + return IPC4_FAILURE; + + new_slot->id = init_data->id; + new_slot->instance = init_data->instance; + new_slot->direction = init_data->direction; + new_slot->state = init_data->state; + new_slot->power_mode = init_data->power_mode; + new_slot->is_removed = false; + new_slot->data = 0; + + *slot_id = new_slot; + + return 0; +} + +void io_perf_monitor_update_data(struct io_perf_data_item *slot_id, uint32_t increment) +{ + if (!slot_id) + return; + + struct io_perf_monitor_ctx *self = &perf_monitor_ctx; + + /* this does not need a lock if each perf slot has only one user */ + if (self->state == IPC4_PERF_MEASUREMENTS_STARTED) + slot_id->data += increment; +} + +inline void io_perf_monitor_update_io_state(struct io_perf_data_item *slot_id, bool const power_up) +{ + slot_id->state = power_up; +} + +inline void io_perf_monitor_update_power_mode(struct io_perf_data_item *slot_id, + bool const power_mode) +{ + slot_id->power_mode = power_mode; +} +#endif /* CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS */ diff --git a/src/idc/idc.c b/src/idc/idc.c index 69c7ad1f84bd..4ace39cf3fc9 100644 --- a/src/idc/idc.c +++ b/src/idc/idc.c @@ -34,6 +34,8 @@ #include #include +#include + LOG_MODULE_REGISTER(idc, CONFIG_SOF_LOG_LEVEL); /** \brief IDC message payload per core. */ @@ -481,6 +483,11 @@ static void idc_complete(void *data) uint32_t type = iTS(idc->received_msg.header); k_spinlock_key_t key; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(idc->io_perf_out_msg_count, 1); +#endif + switch (type) { case iTS(IDC_MSG_IPC): /* Signal the host */ @@ -511,6 +518,18 @@ int idc_init(void) /* initialize idc data */ (*idc)->payload = platform_shared_get(static_payload, sizeof(static_payload)); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + struct io_perf_data_item init_data = {IO_PERF_IDC_ID, + cpu_get_id(), + IO_PERF_INPUT_DIRECTION, + IO_PERF_POWERED_UP_ENABLED, + IO_PERF_D0IX_POWER_MODE, + 0, 0, 0 }; + io_perf_monitor_init_data(&(*idc)->io_perf_in_msg_count, &init_data); + init_data.direction = IO_PERF_OUTPUT_DIRECTION; + io_perf_monitor_init_data(&(*idc)->io_perf_out_msg_count, &init_data); +#endif + /* process task */ #ifndef __ZEPHYR__ schedule_task_init_edf(&(*idc)->idc_task, SOF_UUID(idc_cmd_task_uuid), diff --git a/src/idc/zephyr_idc.c b/src/idc/zephyr_idc.c index 74a0da0f5bab..664d09a260ca 100644 --- a/src/idc/zephyr_idc.c +++ b/src/idc/zephyr_idc.c @@ -33,6 +33,8 @@ #include #include +#include + LOG_MODULE_REGISTER(zephyr_idc, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(zephyr_idc); @@ -88,6 +90,11 @@ static void idc_handler(struct k_p4wq_work *work) idc->received_msg.header = msg->header; idc->received_msg.extension = msg->extension; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(idc->io_perf_in_msg_count, 1); +#endif + switch (msg->header) { case IDC_MSG_POWER_UP: /* Run the core initialisation? */ @@ -151,6 +158,11 @@ int idc_send_msg(struct idc_msg *msg, uint32_t mode) k_p4wq_submit(q_zephyr_idc + target_cpu, work); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(idc->io_perf_out_msg_count, 1); +#endif + switch (mode) { case IDC_BLOCKING: ret = k_p4wq_wait(work, K_USEC(IDC_TIMEOUT)); diff --git a/src/include/sof/debug/telemetry/performance_monitor.h b/src/include/sof/debug/telemetry/performance_monitor.h index ef2b02799d47..337e353dafc4 100644 --- a/src/include/sof/debug/telemetry/performance_monitor.h +++ b/src/include/sof/debug/telemetry/performance_monitor.h @@ -8,6 +8,8 @@ #ifndef __SOF_PERFORMANCE_MONITOR_H__ #define __SOF_PERFORMANCE_MONITOR_H__ +#include + /* to be moved to Zephyr */ #define WIN3_MBASE DT_REG_ADDR(DT_PHANDLE(DT_NODELABEL(mem_window3), memory)) #define ADSP_PMW ((volatile uint32_t *) \ @@ -138,4 +140,156 @@ static inline void disable_performance_counters(void) {} #endif +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + +struct io_perf_data_item { + /* ID of interface */ + uint32_t id : 8; + /* Instance of interface / bus */ + uint32_t instance : 8; + /* I/O direction from ACE perspective: 0 - Input, 1 - Output */ + uint32_t direction : 1; + /* I/O state: 0 - powered down / disabled, 1 - powered up / enabled */ + uint32_t state : 1; + /* Power Mode: 0 - D0, 1 - D0ix (clock gating enabled), */ + uint32_t power_mode : 2; + uint32_t rsvd : 11; + /* The component still exists (0) or has been already deleted (1) */ + uint32_t is_removed : 1; + /* Performance data */ + /* + * I/O (id) - ID - Units - Description + * + * Host IPC - 0 - Count - Counter of Host IPC messages incoming and outcoming + * IDC - 1 - Count - Counter of IDC messages incoming and outcoming per DSP core + * DMIC - 2 - Bytes - Counter of bytes transferred over DMIC interface + * I2S - 3 - Bytes - Counter of bytes transferred over I2S interface + * SoundWire - 4 - Bytes - Counter of bytes transferred over SoundWire interface + * HD/A - 5 - Bytes - Counter of bytes transferred over HD/A interface + * USB - 6 - Bytes - Counter of bytes transferred over USB interface + * GPIO - 7 - Count - Counter of GPIO interrupts or triggers + * I2C - 8 - Bytes - Counter of bytes transferred over I2C interface + * I3C - 9 - Bytes - Counter of bytes transferred over I3C interface + * I3C interrupt - 10 - Bytes - Counter of I3C interrupts + * UART - 11 - Bytes - Counter of bytes transferred over UART interface + * SPI - 12 - Bytes - Counter of bytes transferred over SPI interface + * CSI-2 - 13 - Bytes - Counter of bytes transferred over CSI-2 interface + * DTF - 14 - Bytes - Counter of bytes transferred over DTF interface + */ + uint64_t data; +} __packed; + +/* those below are used for bits in io_perf_data_item, it just uses 32bit vars specifically */ +enum io_perf_data_item_dir { + IO_PERF_INPUT_DIRECTION = 0, + IO_PERF_OUTPUT_DIRECTION = 1, +}; + +enum io_perf_data_item_state { + IO_PERF_POWERED_DOWN_DISABLED = 0, + IO_PERF_POWERED_UP_ENABLED = 1, +}; + +enum io_perf_data_item_power_mode { + IO_PERF_D0_POWER_MODE = 0, + IO_PERF_D0IX_POWER_MODE = 1, +}; + +enum io_perf_data_item_id { + IO_PERF_IPC_ID = 0, + IO_PERF_IDC_ID = 1, + IO_PERF_DMIC_ID = 2, + IO_PERF_I2S_ID = 3, + IO_PERF_SOUND_WIRE_ID = 4, + IO_PERF_HDA_ID = 5, + IO_PERF_USB_ID = 6, + IO_PERF_GPIO_ID = 7, + IO_PERF_I2C_ID = 8, + IO_PERF_I3C_ID = 9, + IO_PERF_I3C_INTERRUPT_ID = 10, + IO_PERF_UART_ID = 11, + IO_PERF_SPI_ID = 12, + IO_PERF_CSI_2_ID = 13, + IO_PERF_DTF_ID = 14, + IO_PERF_INVALID_ID = 0xFF +}; + +struct io_global_perf_data { + /* Number of statistics */ + uint32_t perf_item_count; + /* Performance statistics per I/O */ + struct io_perf_data_item perf_items[0]; +}; + +/* I/O Performance Monitor initialization. */ +/* + * @return ErrorCode + */ +int io_perf_monitor_init(void); + +/* Release slot */ +/* + * @param [in] slot_id pointer to io_perf_data_item + * @return ErrorCode + */ +int io_perf_monitor_release_slot(struct io_perf_data_item *slot_id); + +/* Get I/O performance data */ +/* + * @param [in] io_global_perf_data point to io_global_perf_data + * @return ErrorCode + */ +int +io_perf_monitor_get_performance_data(struct io_global_perf_data *io_global_perf_data); + +/* Set control state of I/o performance measurements process */ +/* + * @param [in] state a state to set + * @return ErrorCode + */ +int +io_perf_monitor_set_state(enum ipc4_perf_measurements_state_set state); + +/* Get control state of I/o performance measurements process */ +/* + * @return ipc4_perf_measurements_state_set + */ +enum ipc4_perf_measurements_state_set io_perf_monitor_get_state(void); + +/* Initialization of I/O performance data */ +/* + * @param [out] slot_id pointer to io_perf_data_item + * @param [in] init_data pointer to init data + */ +int io_perf_monitor_init_data(struct io_perf_data_item **slot_id, + struct io_perf_data_item *init_data); + +/* Update of I/O performance data */ +/* + * @param [in] slot_id pointer to io_perf_data_item + * @param [in] increment + * @note IMPORTANT: this function assumes each perf slot has only one user + * (use this only once per slot) + */ +void io_perf_monitor_update_data(struct io_perf_data_item *slot_id, + uint32_t increment); + +/* Update of I/0 state */ +/* + * @param [in] slot_id pointer to io_perf_data_item + * @param [in] power_up 0 - powered down / disabled, 1 - powered up / enabled + */ +void io_perf_monitor_update_io_state(struct io_perf_data_item *slot_id, + bool const power_up); + +/* Update of I/0 power mode */ +/* + * @param [in] slot_id pointer to io_perf_data_item + * @param [in] power_mode 0 - D0, 1 - D0ix (clock gating enabled) + */ +void io_perf_monitor_update_power_mode(struct io_perf_data_item *slot_id, + bool const power_mode); + +#endif //SUPPORTED(IO_PERFORMANCE_MEASUREMENTS) + #endif diff --git a/src/include/sof/ipc/common.h b/src/include/sof/ipc/common.h index 09bf075ff301..43ad023da1ef 100644 --- a/src/include/sof/ipc/common.h +++ b/src/include/sof/ipc/common.h @@ -70,6 +70,12 @@ struct ipc { /* processing task */ struct task ipc_task; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* io performance measurement */ + struct io_perf_data_item *io_perf_in_msg_count; + struct io_perf_data_item *io_perf_out_msg_count; +#endif + #ifdef __ZEPHYR__ struct k_work_delayable z_delayed_work; #endif diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index 797d1754080c..5af6f5aeec24 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -35,6 +35,8 @@ #include #include +#include + /** \addtogroup sof_dai_drivers DAI Drivers * DAI Drivers API specification. * @{ @@ -157,6 +159,10 @@ struct dai_data { struct llp_slot_info slot_info; /* fast mode, use one byte memory to save repreated cycles */ bool fast_mode; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* io performance measurement */ + struct io_perf_data_item *io_perf_bytes_count; +#endif }; /* these 3 are here to satisfy clk.c and ssp.h interconnection, will be removed leter */ diff --git a/src/init/init.c b/src/init/init.c index e6a68dcc7f0f..77665faee975 100644 --- a/src/init/init.c +++ b/src/init/init.c @@ -303,6 +303,11 @@ static int primary_core_init(int argc, char *argv[], struct sof *sof) trace_point(TRACE_BOOT_SYS_POWER); pm_runtime_init(sof); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* init I/O performance before any I/O interfaces */ + io_perf_monitor_init(); +#endif + /* init the platform */ if (platform_init(sof) < 0) sof_panic(SOF_IPC_PANIC_PLATFORM); diff --git a/src/ipc/ipc-common.c b/src/ipc/ipc-common.c index 0bb57b277ae6..bf94e0481ec0 100644 --- a/src/ipc/ipc-common.c +++ b/src/ipc/ipc-common.c @@ -32,6 +32,8 @@ #include #include +#include + LOG_MODULE_REGISTER(ipc, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(ipc); @@ -154,9 +156,14 @@ void ipc_send_queued_msg(void) msg = list_first_item(&ipc->msg_list, struct ipc_msg, list); - if (ipc_platform_send_msg(msg) == 0) + if (ipc_platform_send_msg(msg) == 0) { /* Remove the message from the list if it has been successfully sent. */ list_item_del(&msg->list); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(ipc->io_perf_out_msg_count, 1); +#endif + } out: k_spin_unlock(&ipc->lock, key); } @@ -274,6 +281,18 @@ int ipc_init(struct sof *sof) list_init(&sof->ipc->msg_list); list_init(&sof->ipc->comp_list); +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + struct io_perf_data_item init_data = {IO_PERF_IPC_ID, + cpu_get_id(), + IO_PERF_INPUT_DIRECTION, + IO_PERF_POWERED_UP_ENABLED, + IO_PERF_D0IX_POWER_MODE, + 0, 0, 0 }; + io_perf_monitor_init_data(&sof->ipc->io_perf_in_msg_count, &init_data); + init_data.direction = IO_PERF_OUTPUT_DIRECTION; + io_perf_monitor_init_data(&sof->ipc->io_perf_out_msg_count, &init_data); +#endif + #ifdef __ZEPHYR__ k_work_init_delayable(&sof->ipc->z_delayed_work, ipc_work_handler); #endif @@ -317,6 +336,11 @@ static enum task_state ipc_do_cmd(void *data) { struct ipc *ipc = data; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* Increment performance counters */ + io_perf_monitor_update_data(ipc->io_perf_in_msg_count, 1); +#endif + /* * 32-bit writes are atomic and at the moment no IPC processing is * taking place, so, no need for a lock. diff --git a/zephyr/include/rtos/idc.h b/zephyr/include/rtos/idc.h index 178048eb0cbd..8fdfb09f3c99 100644 --- a/zephyr/include/rtos/idc.h +++ b/zephyr/include/rtos/idc.h @@ -20,6 +20,8 @@ #include #include +#include + /** \brief IDC send blocking flag. */ #define IDC_BLOCKING 0 @@ -171,6 +173,11 @@ struct idc { struct task idc_task; /**< IDC processing task */ struct idc_payload *payload; int irq; +#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS + /* io performance measurement */ + struct io_perf_data_item *io_perf_in_msg_count; + struct io_perf_data_item *io_perf_out_msg_count; +#endif }; /* idc trace context, used by multiple units */