Skip to content

Commit 0691812

Browse files
Merge tag '1.5.1' into feature/fromUtf8ToEAscii-method-in-String
2 parents 4b150cc + 331cdd9 commit 0691812

File tree

2 files changed

+318
-2
lines changed

2 files changed

+318
-2
lines changed

api/ArduinoAPI.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
#ifndef ARDUINO_API_H
2121
#define ARDUINO_API_H
2222

23-
// version 1.5.0
24-
#define ARDUINO_API_VERSION 10500
23+
// version 1.5.1
24+
#define ARDUINO_API_VERSION 10501
2525

2626
#include "Binary.h"
2727

api/DMAPool.h

+316
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
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+
26+
namespace 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.
37+
template <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+
93+
enum {
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.
101+
template <class, size_t> class DMAPool;
102+
103+
template <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+
190+
template <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+
313+
using arduino::DMAPool;
314+
using arduino::DMABuffer;
315+
using arduino::SPSCQueue;
316+
#endif //__DMA_POOL_H__

0 commit comments

Comments
 (0)