diff --git a/src/audio/component.c b/src/audio/component.c index 4b140a8d4df1..17020cf2ffd5 100644 --- a/src/audio/component.c +++ b/src/audio/component.c @@ -495,8 +495,18 @@ int comp_copy(struct comp_dev *dev) perf_cnt_init(&dev->pcd); #endif +#ifdef CONFIG_SOF_TELEMETRY + const uint32_t begin_stamp = (uint32_t)sof_cycle_get_64(); +#endif + ret = dev->drv->ops.copy(dev); +#ifdef CONFIG_SOF_TELEMETRY + const uint32_t cycles_consumed = (uint32_t)sof_cycle_get_64() - begin_stamp; + + comp_update_performance_data(dev, cycles_consumed); +#endif + #if CONFIG_PERFORMANCE_COUNTERS perf_cnt_stamp(&dev->pcd, perf_trace_null, dev); perf_cnt_average(&dev->pcd, comp_perf_avg_info, dev); @@ -506,6 +516,44 @@ int comp_copy(struct comp_dev *dev) return ret; } +void comp_init_performance_data(struct comp_dev *dev) +{ + struct perf_data_item_comp *item = dev->perf_data.perf_data_item; + + if (item) + perf_data_item_comp_init(item, dev->ipc_config.id, 0); +} + +/* returns true if budget violation occured */ +bool update_peak_of_measured_cpc(struct comp_dev *dev, size_t measured_cpc) +{ + if (measured_cpc <= dev->perf_data.peak_of_measured_cpc) + { + return false; + } + dev->perf_data.peak_of_measured_cpc = measured_cpc; + if (measured_cpc <= dev->cpc) + { + return false; + } + return true; +} + +bool comp_update_performance_data(struct comp_dev *dev, uint32_t cycles_used) +{ + struct perf_data_item_comp *item = dev->perf_data.perf_data_item; + /* we divide by ibs so we need to check if its set */ + if (item && dev->ibs != 0) { + item->total_iteration_count++; + item->total_cycles_consumed += cycles_used; + item->item.avg_kcps = item->total_cycles_consumed * dev->ll_chunk_size_ + / (dev->ibs * item->total_iteration_count); + item->item.peak_kcps = MAX(item->item.peak_kcps, (cycles_used * dev->ll_chunk_size_) + / dev->ibs); + } + return update_peak_of_measured_cpc(dev, cycles_used); +} + inline uint32_t get_sample_group_size_in_bytes(const struct ipc4_audio_format fmt) { return ((fmt.depth >> 3) * fmt.channels_count); diff --git a/src/debug/telemetry/telemetry.c b/src/debug/telemetry/telemetry.c index 595dc8ed12dc..15f8a1809eea 100644 --- a/src/debug/telemetry/telemetry.c +++ b/src/debug/telemetry/telemetry.c @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -22,12 +23,20 @@ LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); +#define PERFORMANCE_DATA_ENTRIES_COUNT (CONFIG_MEMORY_WIN_3_SIZE / sizeof(struct perf_data_item_comp)) + +SYS_BITARRAY_DEFINE_STATIC(performance_data_bit_array, PERFORMANCE_DATA_ENTRIES_COUNT); + struct perf_bitmap { sys_bitarray_t *array; uint16_t occupied; size_t size; }; +struct perf_bitmap performance_data_bitmap; + +volatile struct perf_data_item_comp *perf_data_; + int perf_bitmap_init(struct perf_bitmap * const bitmap, sys_bitarray_t *array, size_t size) { bitmap->array = array; @@ -94,6 +103,71 @@ int perf_bitmap_is_bit_clear(struct perf_bitmap * const bitmap, size_t bit) return !val; } +struct perf_data_item_comp *perf_data_getnext(void) +{ + int idx; + int ret = perf_bitmap_alloc(&performance_data_bitmap, &idx); + + if (ret < 0) + return NULL; + /* original 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(&performance_data_bitmap, idx); + if (ret < 0) + return NULL; + return &perf_data_[idx]; +} + +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 -EINVAL; + ret = perf_bitmap_free(&performance_data_bitmap, idx); + if (ret < 0) + return -EINVAL; + + return IPC4_SUCCESS; +} + +void perf_data_item_comp_reset(struct perf_data_item_comp *perf) +{ + perf->total_iteration_count = 0; + perf->total_cycles_consumed = 0; + perf->restricted_total_iterations = 0; + perf->restricted_total_cycles = 0; + perf->restricted_peak_cycles = 0; + perf->item.peak_kcps = 0; + perf->item.avg_kcps = 0; +} + +void perf_data_item_comp_init(struct perf_data_item_comp *perf, uint32_t resource_id, + uint32_t power_mode) +{ + perf_data_item_comp_reset(perf); + perf->item.resource_id = resource_id; + perf->item.is_removed = false; + perf->item.power_mode = power_mode; +} + +int free_performance_data(struct perf_data_item_comp *item) +{ + int ret; + + if (item) { + item->item.is_removed = true; + ret = perf_data_free(item); + if (ret < 0) + return -EINVAL; + } + return IPC4_SUCCESS; +} + //============================================================================================== /* Systic variables, one set per core */ @@ -161,6 +235,12 @@ int telemetry_init(void) systick_info[i].peak_utilization_4k = 0; systick_info[i].peak_utilization_8k = 0; } + + /* init global performance measurement */ + perf_data_ = (volatile struct perf_data_item_comp *)ADSP_PMW; + perf_bitmap_init(&performance_data_bitmap, &performance_data_bit_array, + PERFORMANCE_DATA_ENTRIES_COUNT); + return 0; } diff --git a/src/include/ipc4/base_fw.h b/src/include/ipc4/base_fw.h index 913f44a4fd06..a721f44433a6 100644 --- a/src/include/ipc4/base_fw.h +++ b/src/include/ipc4/base_fw.h @@ -728,4 +728,31 @@ struct schedulers_info { struct ipc4_system_time_info *basefw_get_system_time_info(void); +struct perf_data_item { + /* ID of the FW component */ + uint32_t resource_id; + /* 0 - D0, 1 - D0i3. */ + uint32_t power_mode : 1; + uint32_t rsvd : 30; + /* the component still exists (0) or has been already deleted (1) */ + uint32_t is_removed : 1; + /* Peak KCPS captured */ + uint32_t peak_kcps; + /* Average KCPS measured */ + uint32_t avg_kcps; +} __packed __aligned(4); + +struct perf_data_item_comp { + struct perf_data_item item; + /* Total iteration count of module instance */ + uint32_t total_iteration_count; + /* Total cycles consumed by module instance */ + uint64_t total_cycles_consumed; + /* usage statistics against DSP wallclock cycles */ + uint32_t restricted_peak_cycles; + uint64_t restricted_total_iterations; + uint64_t restricted_total_cycles; + +} __packed __aligned(4); + #endif /* __SOF_IPC4_BASE_FW_H__ */ diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 7ecc4b62de75..c11ee5e89a3c 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -554,6 +555,18 @@ struct comp_ipc_config { #endif }; +struct comp_perf_data { + /* maximum measured cpc on run-time. + * + * if current measured cpc exceeds peak_of_measured_cpc_ then + * ResoruceEvent(BUDGET_VIOLATION) notification must be send. + * Otherwise there is no new information for host to care about + */ + size_t peak_of_measured_cpc; + /* Pointer to performance data structure. */ + struct perf_data_item_comp *perf_data_item; +}; + /** * Audio component base device "class" * - used by other component types. @@ -594,6 +607,8 @@ struct comp_dev { struct list_item bsource_list; /**< list of source buffers */ struct list_item bsink_list; /**< list of sink buffers */ + /* performance data*/ + struct comp_perf_data perf_data; /* Input Buffer Size for pin 0, add array for other pins if needed */ size_t ibs; /* Output Buffers Size for pin 0, add array for other pins if needed */ @@ -923,5 +938,7 @@ int comp_verify_params(struct comp_dev *dev, uint32_t flag, void comp_update_chunk_size(struct comp_dev *dev); void comp_update_ibs_obs_cpc(struct comp_dev *dev); +void comp_init_performance_data(struct comp_dev *dev); +bool comp_update_performance_data(struct comp_dev *dev, uint32_t cycles_used); #endif /* __SOF_AUDIO_COMPONENT_H__ */ diff --git a/src/include/sof/debug/telemetry/telemetry.h b/src/include/sof/debug/telemetry/telemetry.h index f3f80df83764..0434974ade82 100644 --- a/src/include/sof/debug/telemetry/telemetry.h +++ b/src/include/sof/debug/telemetry/telemetry.h @@ -6,6 +6,8 @@ #ifndef __SOF_TELEMETRY_H__ #define __SOF_TELEMETRY_H__ +#include + /* Slot in memory window 2 (Debug Window) to be used as telemetry slot */ #define SOF_DW_TELEMETRY_SLOT 1 /* Memory of average algorithm of performance queue */ @@ -89,6 +91,15 @@ struct telemetry_perf_queue { size_t sum; }; +void perf_data_item_comp_init(struct perf_data_item_comp *perf, uint32_t resource_id, + uint32_t power_mode); + void telemetry_update(uint32_t begin_ccount, uint32_t current_ccount); +struct perf_data_item_comp *perf_data_getnext(void); + +int perf_data_free(struct perf_data_item_comp *item); + +int free_performance_data(struct perf_data_item_comp *item); + #endif /*__SOF_TELEMETRY_H__ */ diff --git a/src/ipc/ipc-helper.c b/src/ipc/ipc-helper.c index 305c3623d3b6..cb8193893fd1 100644 --- a/src/ipc/ipc-helper.c +++ b/src/ipc/ipc-helper.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -281,6 +282,8 @@ int ipc_comp_free(struct ipc *ipc, uint32_t comp_id) return -EINVAL; } + free_performance_data(icd->cd->perf_data.perf_data_item); + if (!icd->cd->bsource_list.next || !icd->cd->bsink_list.next) { /* Unfortunate: the buffer list node gets initialized * at the component level and thus can contain NULLs diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index 4fd22b279556..6034e6e5f447 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -51,6 +51,8 @@ #include #include +#include + LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL); extern struct tr_ctx comp_tr; @@ -156,6 +158,14 @@ struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_init) list_init(&dev->bsource_list); list_init(&dev->bsink_list); + /* init global performance measurement */ + dev->perf_data.perf_data_item = perf_data_getnext(); + /* this can be null, just no performance measurements in this case */ + if (dev->perf_data.perf_data_item) { + dev->perf_data.perf_data_item->item.resource_id = comp_id; + comp_init_performance_data(dev); + } + ipc4_add_comp_dev(dev); comp_update_ibs_obs_cpc(dev);