From e6c3decd38fdaa773e0c06db61a0b8dee2850fb2 Mon Sep 17 00:00:00 2001 From: Marcin Szkudlinski Date: Fri, 15 Sep 2023 14:32:48 +0200 Subject: [PATCH] dp_queue: buf calculation size, add list, cosmetic changes buf calculation size has been changed to be always 2xmax(IBS,OBS) to allow free read/write in various data chunk sizes and execution periods (of course in/out data rates must be same) detailed examples in dp_queue.h file method and fields allowing connection dp_queues to a list added Signed-off-by: Marcin Szkudlinski --- src/audio/dp_queue.c | 22 +++++----- src/include/sof/audio/dp_queue.h | 69 +++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 18 deletions(-) diff --git a/src/audio/dp_queue.c b/src/audio/dp_queue.c index 07fa67fc0169..8001594a27bc 100644 --- a/src/audio/dp_queue.c +++ b/src/audio/dp_queue.c @@ -73,7 +73,7 @@ static inline void dp_queue_writeback_shared(struct dp_queue *dp_queue, } static inline -uint8_t __sparse_cache *dp_queue_get_pointer(struct dp_queue *dp_queue, uint32_t offset) +uint8_t __sparse_cache *dp_queue_get_pointer(struct dp_queue *dp_queue, size_t offset) { /* check if offset is not in "double area" * lines below do a quicker version of offset %= dp_queue->data_buffer_size; @@ -84,7 +84,7 @@ uint8_t __sparse_cache *dp_queue_get_pointer(struct dp_queue *dp_queue, uint32_t } static inline -uint32_t dp_queue_inc_offset(struct dp_queue *dp_queue, uint32_t offset, uint32_t inc) +size_t dp_queue_inc_offset(struct dp_queue *dp_queue, size_t offset, size_t inc) { assert(inc <= dp_queue->data_buffer_size); offset += inc; @@ -161,20 +161,20 @@ static int dp_queue_get_data(struct sof_source *source, size_t req_size, void const **data_ptr, void const **buffer_start, size_t *buffer_size) { struct dp_queue *dp_queue = dp_queue_from_source(source); - __sparse_cache void *_data_ptr; + __sparse_cache void *data_ptr_c; CORE_CHECK_STRUCT(dp_queue); if (req_size > dp_queue_get_data_available(source)) return -ENODATA; - _data_ptr = dp_queue_get_pointer(dp_queue, dp_queue->_read_offset); + data_ptr_c = dp_queue_get_pointer(dp_queue, dp_queue->_read_offset); /* clean cache in provided data range */ - dp_queue_invalidate_shared(dp_queue, _data_ptr, req_size); + dp_queue_invalidate_shared(dp_queue, data_ptr_c, req_size); *buffer_start = (__sparse_force void *)dp_queue->_data_buffer; *buffer_size = dp_queue->data_buffer_size; - *data_ptr = (__sparse_force void *)_data_ptr; + *data_ptr = (__sparse_force void *)data_ptr_c; return 0; } @@ -262,24 +262,22 @@ struct dp_queue *dp_queue_create(size_t min_available, size_t min_free_space, ui CORE_CHECK_STRUCT_INIT(dp_queue, flags & DP_QUEUE_MODE_SHARED); - /* initiate sink/source */ + /* initiate structures */ source_init(dp_queue_get_source(dp_queue), &dp_queue_source_ops, &dp_queue->audio_stream_params); sink_init(dp_queue_get_sink(dp_queue), &dp_queue_sink_ops, &dp_queue->audio_stream_params); + list_init(&dp_queue->list); + /* set obs/ibs in sink/source interfaces */ sink_set_min_free_space(&dp_queue->_sink_api, min_free_space); source_set_min_available(&dp_queue->_source_api, min_available); uint32_t max_ibs_obs = MAX(min_available, min_free_space); - uint32_t min_ibs_obs = MIN(min_available, min_free_space); /* calculate required buffer size */ - if (max_ibs_obs % min_ibs_obs == 0) - dp_queue->data_buffer_size = 2 * max_ibs_obs; - else - dp_queue->data_buffer_size = 3 * max_ibs_obs; + dp_queue->data_buffer_size = 2 * max_ibs_obs; /* allocate data buffer - always in cached memory alias */ dp_queue->data_buffer_size = ALIGN_UP(dp_queue->data_buffer_size, PLATFORM_DCACHE_ALIGN); diff --git a/src/include/sof/audio/dp_queue.h b/src/include/sof/audio/dp_queue.h index ed56ed3ceb0d..b187eb7a6f86 100644 --- a/src/include/sof/audio/dp_queue.h +++ b/src/include/sof/audio/dp_queue.h @@ -25,9 +25,38 @@ * 1) incoming and outgoing data rate MUST be the same * 2) Both data consumer and data producer declare max chunk sizes they want to use (IBS/OBS) * - * required Buffer size: - * - 2*MAX(IBS,OBS) if the larger of IBS/OBS is multiplication of smaller - * - 3*MAX(IBS,OBS) otherwise + * required Buffer size is 2*MAX(IBS,OBS) to allow free read/write in various data chunk sizes + * and execution periods (of course in/out data rates must be same) + * example: + * Consumer reads 5bytes each 3 cycles (IBS = 5) + * producer writes 3bytes every 5 cycles (OBS = 3) + * - cycle0 buffer empty, producer starting processing, consumer must wait + * - cycle3 produce 3 bytes (buf occupation = 3) + * - cycle6 produce 3 bytes (buf occupation = 6), consumer becomes ready + * in DP thread will start now - asyn to LL cycles + * in this example assuming it consumes data in next cycle + * - cycle7 consume 5 bytes, (buf occupation = 1) + * - cycle9 produce 3 bytes (buf occupation = 4) + * - cycle12 (producer goes first) produce 3 bytes (buf occupation = 7) + * consume 5 bytes (buf occupation = 2) + * - cycle15 produce 3 bytes (buf occupation = 5) + * consumer has enough data, but is busy processing prev data + * - cycle15 consume 5 bytes (buf occupation = 0) + * + * ===> max buf occupation = 7 + * + * The worst case is when IBS=OBS and equal periods of consumer/producer + * the buffer must be 2*MAX(IBS,OBS) as we do not know who goes first - consumer or producer, + * especially when both are located on separate cores and EDF scheduling is used + * + * Consumer reads 5 bytes every cycle (IBS = 5) + * producer writes 5 bytes every cycle (OBS = 5) + * - cycle0 consumer goes first - must wait (buf occupation = 0) + * producer produce 5 bytes (buf occupation = 5) + * - cycle1 producer goes first - produce 5 bytes (buf occupation = 10) + * consumer consumes 5 bytes (buf occupation = 5) + * ===> max buf occupation = 10 + * * * The queue may work in 2 modes * 1) local mode @@ -80,6 +109,9 @@ struct sof_audio_stream_params; struct dp_queue { CORE_CHECK_STRUCT_FIELD; + /* public */ + struct list_item list; /**< fields for connection queues in a list */ + /* public: read only */ struct sof_audio_stream_params audio_stream_params; size_t data_buffer_size; @@ -91,8 +123,8 @@ struct dp_queue { uint32_t _flags; /* DP_QUEUE_MODE_* */ uint8_t __sparse_cache *_data_buffer; - uint32_t _write_offset; /* private: to be modified by data producer using API */ - uint32_t _read_offset; /* private: to be modified by data consumer using API */ + size_t _write_offset; /* private: to be modified by data producer using API */ + size_t _read_offset; /* private: to be modified by data consumer using API */ bool _hw_params_configured; }; @@ -110,12 +142,13 @@ struct dp_queue { struct dp_queue *dp_queue_create(size_t min_available, size_t min_free_space, uint32_t flags); /** - * @brief free dp queue memory + * @brief remove the queue from the list, free dp queue memory */ static inline void dp_queue_free(struct dp_queue *dp_queue) { CORE_CHECK_STRUCT(dp_queue); + list_item_del(&dp_queue->list); rfree((__sparse_force void *)dp_queue->_data_buffer); rfree(dp_queue); } @@ -164,4 +197,28 @@ bool dp_queue_is_shared(struct dp_queue *dp_queue) return !!(dp_queue->_flags & DP_QUEUE_MODE_SHARED); } +/** + * @brief append a dp_queue to the list + */ +static inline void dp_queue_append_to_list(struct dp_queue *item, struct list_item *list) +{ + list_item_append(&item->list, list); +} + +/** + * @brief return a pointer to the first dp_queue on the list + */ +static inline struct dp_queue *dp_queue_get_first_item(struct list_item *list) +{ + return list_first_item(list, struct dp_queue, list); +} + +/** + * @brief return a pointer to the next dp_queue on the list + */ +static inline struct dp_queue *dp_queue_get_next_item(struct dp_queue *item) +{ + return list_next_item(item, list); +} + #endif /* __SOF_DP_QUEUE_H__ */