arduino-emulator
Loading...
Searching...
No Matches
DMAPool.h
1/*
2 This file is part of the Arduino_AdvancedAnalog library.
3 Copyright (c) 2023-2024 Arduino SA. All rights reserved.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20#ifndef __DMA_POOL_H__
21#define __DMA_POOL_H__
22
23#include <atomic>
24#include <memory>
25
26namespace arduino {
27
28#if defined(__DCACHE_PRESENT)
29#define __CACHE_LINE_SIZE__ __SCB_DCACHE_LINE_SIZE
30#elif defined(__cpp_lib_hardware_interference_size)
31#define __CACHE_LINE_SIZE__ std::hardware_constructive_interference_size
32#else // No cache.
33#define __CACHE_LINE_SIZE__ alignof(int)
34#endif
35
36// Single-producer, single-consumer, lock-free bounded Queue.
37template <class T> class SPSCQueue {
38 private:
39 size_t capacity;
40 std::atomic<size_t> head;
41 std::atomic<size_t> tail;
42 std::unique_ptr<T[]> buff;
43
44 public:
45 SPSCQueue(size_t size=0):
46 capacity(0), tail(0), head(0), buff(nullptr) {
47 if (size) {
48 T *mem = new T[size + 1];
49 if (mem) {
50 buff.reset(mem);
51 capacity = size + 1;
52 }
53 }
54 }
55
56 void reset() {
57 tail = head = 0;
58 }
59
60 size_t empty() {
61 return tail == head;
62 }
63
64 operator bool() const {
65 return buff.get() != nullptr;
66 }
67
68 bool push(T data) {
69 size_t curr = head.load(std::memory_order_relaxed);
70 size_t next = (curr + 1) % capacity;
71 if (!buff || (next == tail.load(std::memory_order_acquire))) {
72 return false;
73 }
74 buff[curr] = data;
75 head.store(next, std::memory_order_release);
76 return true;
77 }
78
79 T pop(bool peek=false) {
80 size_t curr = tail.load(std::memory_order_relaxed);
81 if (!buff || (curr == head.load(std::memory_order_acquire))) {
82 return nullptr;
83 }
84 T data = buff[curr];
85 if (!peek) {
86 size_t next = (curr + 1) % capacity;
87 tail.store(next, std::memory_order_release);
88 }
89 return data;
90 }
91};
92
93enum {
94 DMA_BUFFER_READ = (1 << 0),
95 DMA_BUFFER_WRITE = (1 << 1),
96 DMA_BUFFER_DISCONT = (1 << 2),
97 DMA_BUFFER_INTRLVD = (1 << 3),
98} DMABufferFlags;
99
100// Forward declaration of DMAPool class.
101template <class, size_t> class DMAPool;
102
103template <class T, size_t A=__CACHE_LINE_SIZE__> class DMABuffer {
104 private:
105 DMAPool<T, A> *pool;
106 size_t n_samples;
107 size_t n_channels;
108 T *ptr;
109 uint32_t ts;
110 uint32_t flags;
111
112 public:
113 DMABuffer(DMAPool<T, A> *pool=nullptr, size_t samples=0, size_t channels=0, T *mem=nullptr):
114 pool(pool), n_samples(samples), n_channels(channels), ptr(mem), ts(0), flags(0) {
115 }
116
117 T *data() {
118 return ptr;
119 }
120
121 size_t size() {
122 return n_samples * n_channels;
123 }
124
125 size_t bytes() {
126 return n_samples * n_channels * sizeof(T);
127 }
128
129 void flush() {
130 #if __DCACHE_PRESENT
131 if (ptr) {
132 SCB_CleanDCache_by_Addr(data(), bytes());
133 }
134 #endif
135 }
136
137 void invalidate() {
138 #if __DCACHE_PRESENT
139 if (ptr) {
140 SCB_InvalidateDCache_by_Addr(data(), bytes());
141 }
142 #endif
143 }
144
145 uint32_t timestamp() {
146 return ts;
147 }
148
149 void timestamp(uint32_t ts) {
150 this->ts = ts;
151 }
152
153 uint32_t channels() {
154 return n_channels;
155 }
156
157 void release() {
158 if (pool && ptr) {
159 pool->free(this, flags);
160 }
161 }
162
163 void set_flags(uint32_t f) {
164 flags |= f;
165 }
166
167 bool get_flags(uint32_t f=0xFFFFFFFFU) {
168 return flags & f;
169 }
170
171 void clr_flags(uint32_t f=0xFFFFFFFFU) {
172 flags &= (~f);
173 }
174
175 T& operator[](size_t i) {
176 assert(ptr && i < size());
177 return ptr[i];
178 }
179
180 const T& operator[](size_t i) const {
181 assert(ptr && i < size());
182 return ptr[i];
183 }
184
185 operator bool() const {
186 return (ptr != nullptr);
187 }
188};
189
190template <class T, size_t A=__CACHE_LINE_SIZE__> class DMAPool {
191 private:
192 uint8_t *mem;
193 bool managed;
194 SPSCQueue<DMABuffer<T>*> wqueue;
195 SPSCQueue<DMABuffer<T>*> rqueue;
196
197 // Allocates dynamic aligned memory.
198 // Note this memory must be free'd with aligned_free.
199 static void *aligned_malloc(size_t size) {
200 void **ptr, *stashed;
201 size_t offset = A - 1 + sizeof(void *);
202 if ((A % 2) || !((stashed = ::malloc(size + offset)))) {
203 return nullptr;
204 }
205 ptr = (void **) (((uintptr_t) stashed + offset) & ~(A - 1));
206 ptr[-1] = stashed;
207 return ptr;
208 }
209
210 // Frees dynamic aligned memory allocated with aligned_malloc.
211 static void aligned_free(void *ptr) {
212 if (ptr != nullptr) {
213 ::free(((void **) ptr)[-1]);
214 }
215 }
216
217 public:
218 DMAPool(size_t n_samples, size_t n_channels, size_t n_buffers, void *mem_in=nullptr):
219 mem((uint8_t *) mem_in), managed(mem_in==nullptr), wqueue(n_buffers), rqueue(n_buffers) {
220 // Round up to the next multiple of the alignment.
221 size_t bufsize = (((n_samples * n_channels * sizeof(T)) + (A-1)) & ~(A-1));
222 if (bufsize && rqueue && wqueue) {
223 if (mem == nullptr) {
224 // Allocate an aligned memory block for the DMA buffers' memory.
225 mem = (uint8_t *) aligned_malloc(n_buffers * bufsize);
226 if (!mem) {
227 // Failed to allocate memory.
228 return;
229 }
230 }
231 // Allocate the DMA buffers, initialize them using aligned
232 // pointers from the pool, and add them to the write queue.
233 for (size_t i=0; i<n_buffers; i++) {
234 DMABuffer<T> *buf = new DMABuffer<T>(
235 this, n_samples, n_channels, (T *) &mem[i * bufsize]
236 );
237 if (buf == nullptr) {
238 break;
239 }
240 wqueue.push(buf);
241 }
242 }
243 }
244
245 ~DMAPool() {
246 while (readable()) {
247 delete alloc(DMA_BUFFER_READ);
248 }
249
250 while (writable()) {
251 delete alloc(DMA_BUFFER_WRITE);
252 }
253
254 if (mem && managed) {
255 aligned_free(mem);
256 }
257 }
258
259 bool writable() {
260 return !(wqueue.empty());
261 }
262
263 bool readable() {
264 return !(rqueue.empty());
265 }
266
267 void flush() {
268 while (readable()) {
269 DMABuffer<T> *buf = alloc(DMA_BUFFER_READ);
270 if (buf) {
271 buf->release();
272 }
273 }
274 }
275
276 DMABuffer<T> *alloc(uint32_t flags) {
277 DMABuffer<T> *buf = nullptr;
278 if (flags & DMA_BUFFER_READ) {
279 // Get a DMA buffer from the read/ready queue.
280 buf = rqueue.pop();
281 } else {
282 // Get a DMA buffer from the write/free queue.
283 buf = wqueue.pop();
284 }
285 if (buf) {
286 buf->clr_flags(DMA_BUFFER_READ | DMA_BUFFER_WRITE);
287 buf->set_flags(flags);
288 }
289 return buf;
290 }
291
292 void free(DMABuffer<T> *buf, uint32_t flags=0) {
293 if (buf == nullptr) {
294 return;
295 }
296 if (flags == 0) {
297 flags = buf->get_flags();
298 }
299 if (flags & DMA_BUFFER_READ) {
300 // Return the DMA buffer to the write/free queue.
301 buf->clr_flags();
302 wqueue.push(buf);
303 } else {
304 // Return the DMA buffer to the read/ready queue.
305 rqueue.push(buf);
306 }
307 }
308
309};
310
311} // namespace arduino
312
313using arduino::DMAPool;
316#endif //__DMA_POOL_H__
Definition DMAPool.h:103
Definition DMAPool.h:190
Definition DMAPool.h:37
We provide the WiFi class to simulate the Arduino WIFI. In in Linux we can expect that networking is ...
Definition CanMsg.cpp:31