forked from thesofproject/sof
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathring_buffer.h
More file actions
131 lines (122 loc) · 4.96 KB
/
Copy pathring_buffer.h
File metadata and controls
131 lines (122 loc) · 4.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright(c) 2023 Intel Corporation. All rights reserved.
*
*/
#ifndef __SOF_RING_BUFFER_H__
#define __SOF_RING_BUFFER_H__
#include <sof/audio/sink_api.h>
#include <sof/audio/source_api.h>
#include <sof/audio/audio_stream.h>
#include <sof/audio/audio_buffer.h>
#include <rtos/bit.h>
#include <sof/common.h>
#include <ipc/topology.h>
#include <sof/coherent.h>
/**
* ring_buffer is a lockless async circular buffer
* providing safe consumer/producer cached operations cross cores that may be write/read
* at any time
*
* prerequisites:
* 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 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 consumer thread will start now - asyn to producer 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
* in case both receiver and sender are located on the same core and cache coherency
* does not matter. ring_buffer structure is located in cached memory
*
* 2) shared mode
* In this case we need to writeback cache when new data arrive and invalidate cache on
* secondary core. ring_buffer structure is located in shared memory
*
*
* ring_buffer is a lockless consumer/producer safe buffer. It is achieved by having only 2 shared
* variables:
* _write_offset - can be modified by data producer only
* _read_offset - can be modified by data consumer only
*
* as 32 bit operations are atomic, it is multi-thread and multi-core save
*
* There some explanation needed how free_space and available_data are calculated
*
* number of avail data in circular buffer may be calculated as:
* data_avail = _write_offset - _read_offset
* and check for wrap around
* if (data_avail < 0) data_avail = buffer_size - data_avail
*
* The problem is when _write_offset == _read_offset,
* !!! it may mean either that the buffer is empty or the buffer is completely filled !!!
*
* To solve the above issue having only 2 variables mentioned before:
* - allow both offsets to point from 0 to DOUBLE buffer_size
* - when calculating pointers to data, use: data_bufer[offset % buffer_size]
* - use double buffer size in wrap around check when calculating available data
*
* And now:
* - _write_offset == _read_offset
* always means "buffer empty"
* - _write_offset == _read_offset + buffer_size
* always means "buffer full"
*/
struct comp_dev;
struct ring_buffer;
struct sof_audio_stream_params;
/* the ring_buffer structure */
struct ring_buffer {
/* public: read only */
struct sof_audio_buffer audio_buffer;
size_t data_buffer_size;
uint8_t __sparse_cache *_data_buffer;
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 */
};
/**
*
* @param dev pointer to the DP module device structure
* @param min_available minimum data available in queue required by the module using
* ring_buffer's source api
* @param min_free_space minimum buffer space in queue required by the module using
* ring_buffer's sink api
* @param is_shared indicates if the buffer will be shared between cores
* @param id a stream ID, accessible later by sink_get_id/source_get_id
*
*/
struct ring_buffer *ring_buffer_create(struct comp_dev *dev, size_t min_available,
size_t min_free_space, bool is_shared,
uint32_t id);
#endif /* __SOF_RING_BUFFER_H__ */