Dawn Framework 1.0
Universal data acquisition framework for embedded systems
buffer.cxx
1// dawn/src/prog/buffer.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/prog/buffer.hxx"
7
8#include <cstring>
9#include <new>
10
11#include "dawn/debug.hxx"
12#include "dawn/io/common.hxx"
13#include "dawn/io/ddata.hxx"
14#include "dawn/io/virt.hxx"
15
16using namespace dawn;
17
18uint32_t CProgBuffer::resolveRingIndex(const SBufferBind *bind, uint32_t sel, uint32_t depth)
19{
20 DAWNASSERT(bind != nullptr, "nullptr bind");
21 DAWNASSERT(bind->count > 0, "count must be > 0");
22 DAWNASSERT(depth > 0, "depth must be > 0");
23
24 return (bind->head + depth - 1u - sel) % depth;
25}
26
27int CProgBuffer::srcNotifyCb(void *priv, io_ddata_t *data)
28{
29 CProgBuffer *self = static_cast<CProgBuffer *>(priv);
30 if (self == nullptr || !self->bind->captureActive)
31 {
32 return OK;
33 }
34
35 return self->captureBind(data);
36}
37
38void CProgBuffer::selSetCb(CIOVirt *io, void *priv)
39{
40 CProgBuffer *self = static_cast<CProgBuffer *>(priv);
41 if (self == nullptr)
42 {
43 return;
44 }
45
46 SBufferBind *bind = self->bind;
47 int ret = io->getVal(bind->selData->getDataPtr(), bind->selData->getDataSize());
48 if (ret != OK)
49 {
50 DAWNERR("buffer: failed to read selector value: %d\n", ret);
51 return;
52 }
53
54 bind->selectedOffset = bind->selData->get<uint32_t>(0);
55 ret = self->updateSelected();
56 if (ret != OK)
57 {
58 DAWNERR("buffer: selector apply failed: %d\n", ret);
59 }
60}
61
62void CProgBuffer::statGetCb(CIOVirt *io, void *priv)
63{
64 (void)io;
65 CProgBuffer *self = static_cast<CProgBuffer *>(priv);
66 if (self == nullptr)
67 {
68 return;
69 }
70
71 int ret = self->updateStat();
72 if (ret != OK)
73 {
74 DAWNERR("buffer: stat update failed: %d\n", ret);
75 }
76}
77
78int CProgBuffer::allocBind(SObjectId::ObjectId src,
82{
83 bind = new (std::nothrow) SBufferBind();
84 if (bind == nullptr)
85 {
86 return -ENOMEM;
87 }
88
89 bind->srcId = src;
90 bind->outId = out;
91 bind->selId = sel;
92 bind->statId = stat;
93 bind->src = nullptr;
94 bind->out = nullptr;
95 bind->sel = nullptr;
96 bind->stat = nullptr;
97 bind->ring = nullptr;
98 bind->outData = nullptr;
99 bind->selData = nullptr;
100 bind->statData = nullptr;
101 bind->head = 0;
102 bind->count = 0;
103 bind->overflow = 0;
104 bind->selectedOffset = 0;
105 bind->snapshotSeq = 0;
106 bind->captureActive = false;
107
108 setObjectMapItem(src, nullptr);
109 setObjectMapItem(out, nullptr);
110 setObjectMapItem(sel, nullptr);
111 setObjectMapItem(stat, nullptr);
112
113 return OK;
114}
115
116int CProgBuffer::configureDesc(const CDescObject &desc)
117{
118 const SObjectCfg::SObjectCfgItem *item = nullptr;
119 size_t offset = 0;
120
121 for (size_t i = 0; i < desc.getSize(); i++)
122 {
123 item = desc.objectCfgItemNext(offset);
124
126 {
127 DAWNERR("buffer: unsupported cfg 0x%08" PRIx32 "\n", item->cfgid.v);
128 return -EINVAL;
129 }
130
131 switch (item->cfgid.s.id)
132 {
134 {
135 const size_t wpe = sizeof(SProgBufferIOBind) / 4;
136 int ret;
137
138 if (item->cfgid.s.size != wpe)
139 {
140 DAWNERR("buffer: invalid IOBIND size %d\n", item->cfgid.s.size);
141 return -EINVAL;
142 }
143
144 if (bind != nullptr)
145 {
146 DAWNERR("buffer: only one IOBIND per instance is supported\n");
147 return -EINVAL;
148 }
149
150 const SProgBufferIOBind *cfg =
151 reinterpret_cast<const SProgBufferIOBind *>(item->data);
152
153 ret = allocBind(cfg->src.v, cfg->out.v, cfg->sel.v, cfg->stat.v);
154 if (ret != OK)
155 {
156 return ret;
157 }
158
159 break;
160 }
161
163 {
164 const SObjectCfg::ObjectCfgData_t *data;
165
166 if (item->cfgid.s.size != 1)
167 {
168 DAWNERR("buffer: invalid DEPTH size %d\n", item->cfgid.s.size);
169 return -EINVAL;
170 }
171
172 data = reinterpret_cast<const SObjectCfg::ObjectCfgData_t *>(item->data);
173 depth = SObjectCfg::cfgToU32(data[0]);
174 break;
175 }
176
178 {
179 const SObjectCfg::ObjectCfgData_t *data;
180
181 if (item->cfgid.s.size != 1)
182 {
183 DAWNERR("buffer: invalid FLAGS size %d\n", item->cfgid.s.size);
184 return -EINVAL;
185 }
186
187 data = reinterpret_cast<const SObjectCfg::ObjectCfgData_t *>(item->data);
188 flags = SObjectCfg::cfgToU32(data[0]);
189 break;
190 }
191
193 {
194 const SObjectCfg::ObjectCfgData_t *data;
195
196 if (item->cfgid.s.size != 1)
197 {
198 DAWNERR("buffer: invalid CHUNK_SIZE size %d\n", item->cfgid.s.size);
199 return -EINVAL;
200 }
201
202 data = reinterpret_cast<const SObjectCfg::ObjectCfgData_t *>(item->data);
203 chunkSize = SObjectCfg::cfgToU32(data[0]);
204 break;
205 }
206
207 default:
208 {
209 DAWNERR("buffer: unsupported cfg 0x%08" PRIx32 "\n", item->cfgid.v);
210 return -EINVAL;
211 }
212 }
213 }
214
215 return OK;
216}
217
218int CProgBuffer::updateStat()
219{
220 uint32_t runtimeFlags = 0;
221 bool full = (bind->count == depth);
222 bool rangeError = (bind->count > 0 && bind->selectedOffset >= bind->count);
223
224 if (getState() == STATE_RUNNING)
225 {
226 runtimeFlags |= RUNTIME_RUNNING;
227 }
228
229 if (bind->captureActive)
230 {
231 runtimeFlags |= RUNTIME_CAPTURE_ACTIVE;
232 }
233
234 if (full)
235 {
236 runtimeFlags |= RUNTIME_FULL;
237 }
238
239 if (rangeError)
240 {
241 runtimeFlags |= RUNTIME_ERROR_RANGE;
242 }
243
244 bind->statData->get<uint32_t>(STAT_COUNT) = bind->count;
245 bind->statData->get<uint32_t>(STAT_DEPTH) = depth;
246 bind->statData->get<uint32_t>(STAT_HEAD) = bind->head;
247 bind->statData->get<uint32_t>(STAT_OVERFLOW) = bind->overflow;
248 bind->statData->get<uint32_t>(STAT_SNAPSHOT_SEQ) = bind->snapshotSeq;
249 bind->statData->get<uint32_t>(STAT_RUNTIME_FLAGS) = runtimeFlags;
250 bind->statData->get<uint32_t>(STAT_SELECTED_OFFSET) = bind->selectedOffset;
251 bind->statData->get<uint32_t>(STAT_RESERVED) = 0;
252
253 return bind->stat->setVal(bind->statData->getDataPtr(), bind->statData->getDataSize());
254}
255
256int CProgBuffer::updateSelected()
257{
258 int ret;
259 size_t sampleSize = bind->src->getDataSize();
260 uint8_t *out = static_cast<uint8_t *>(bind->outData->getDataPtr());
261
262 std::memset(bind->outData->getDataPtr(), 0, bind->outData->getDataSize());
263
264 if (bind->count == 0)
265 {
266 bind->snapshotSeq++;
267 ret = bind->out->setVal(bind->outData->getDataPtr(), bind->outData->getDataSize());
268 if (ret != OK)
269 {
270 return ret;
271 }
272
273 return updateStat();
274 }
275
276 if (bind->selectedOffset >= bind->count)
277 {
278 updateStat();
279 return -ERANGE;
280 }
281
282 for (uint32_t i = 0; i < chunkSize; i++)
283 {
284 uint32_t sel = bind->selectedOffset + i;
285 if (sel >= bind->count)
286 {
287 break;
288 }
289
290 uint32_t idx = resolveRingIndex(bind, sel, depth);
291 std::memcpy(out + (i * sampleSize), bind->ring->getDataPtr(idx), sampleSize);
292 }
293
294 bind->snapshotSeq++;
295
296 ret = bind->out->setVal(bind->outData->getDataPtr(), bind->outData->getDataSize());
297 if (ret != OK)
298 {
299 return ret;
300 }
301
302 return updateStat();
303}
304
305int CProgBuffer::captureBind(io_ddata_t *data)
306{
307 if (!bind->captureActive)
308 {
309 return OK;
310 }
311
312 if ((flags & FLAG_MODE_ONESHOT) && bind->count >= depth)
313 {
314 bind->captureActive = false;
315 return updateStat();
316 }
317
318 std::memcpy(bind->ring->getDataPtr(bind->head), data->getDataPtr(), data->getDataSize());
319 bind->ring->getTs(bind->head) = data->getTs(0);
320
321 bind->head = (bind->head + 1u) % depth;
322 if (bind->count < depth)
323 {
324 bind->count++;
325 }
326 else
327 {
328 bind->overflow++;
329 }
330
331 return updateSelected();
332}
333
334void CProgBuffer::clearBind()
335{
336 bind->head = 0;
337 bind->count = 0;
338 bind->overflow = 0;
339 bind->selectedOffset = 0;
340 bind->snapshotSeq = 0;
341
342 std::memset(bind->outData->getDataPtr(), 0, bind->outData->getDataSize());
343}
344
345int CProgBuffer::validateBind()
346{
347 bind->src = getIO(bind->srcId);
348 if (bind->src == nullptr)
349 {
350 DAWNERR("buffer: src 0x%" PRIx32 " not found\n", bind->srcId);
351 return -EIO;
352 }
353
354 bind->out = reinterpret_cast<CIOVirt *>(getIO(bind->outId));
355 if (bind->out == nullptr)
356 {
357 DAWNERR("buffer: out 0x%" PRIx32 " not found\n", bind->outId);
358 return -EIO;
359 }
360
361 bind->sel = reinterpret_cast<CIOVirt *>(getIO(bind->selId));
362 if (bind->sel == nullptr)
363 {
364 DAWNERR("buffer: sel 0x%" PRIx32 " not found\n", bind->selId);
365 return -EIO;
366 }
367
368 bind->stat = reinterpret_cast<CIOVirt *>(getIO(bind->statId));
369 if (bind->stat == nullptr)
370 {
371 DAWNERR("buffer: stat 0x%" PRIx32 " not found\n", bind->statId);
372 return -EIO;
373 }
374
375 if (!bind->src->isNotify())
376 {
377 DAWNERR("buffer: src 0x%" PRIx32 " has no notify support\n", bind->src->getIdV());
378 return -EINVAL;
379 }
380
381 if (bind->out->getDtype() != bind->src->getDtype())
382 {
383 DAWNERR("buffer: out IO incompatible with src 0x%" PRIx32 "\n", bind->src->getIdV());
384 return -EINVAL;
385 }
386
387 if (bind->sel->getDtype() != SObjectId::DTYPE_UINT32)
388 {
389 DAWNERR("buffer: sel IO must use uint32 dtype\n");
390 return -EINVAL;
391 }
392
393 if (bind->stat->getDtype() != SObjectId::DTYPE_UINT32)
394 {
395 DAWNERR("buffer: stat IO must use uint32 dtype\n");
396 return -EINVAL;
397 }
398
399 return OK;
400}
401
402int CProgBuffer::allocateBind()
403{
404 int ret;
405 const size_t srcDim = bind->src->getDataDim();
406 size_t outDim;
407
408 if (srcDim == 0)
409 {
410 DAWNERR("buffer: src dimension must be > 0\n");
411 return -EINVAL;
412 }
413
414 outDim = srcDim * chunkSize;
415 if (outDim == 0)
416 {
417 return -EINVAL;
418 }
419
420 if (bind->out->getCls() == CIOCommon::IO_CLASS_VIRT)
421 {
422 ret = bind->out->initialize(outDim, 1, true);
423 if (ret != OK)
424 {
425 DAWNERR("buffer: out initialize failed: %d\n", ret);
426 return ret;
427 }
428 }
429
430 ret = prepareWritableTarget(bind->sel, 1, false);
431 if (ret != OK)
432 {
433 DAWNERR("buffer: sel initialize failed: %d\n", ret);
434 return ret;
435 }
436
437 ret = prepareWritableTarget(bind->stat, STAT_WORDS, false);
438 if (ret != OK)
439 {
440 DAWNERR("buffer: stat initialize failed: %d\n", ret);
441 return ret;
442 }
443
444 bind->ring = new (std::nothrow) io_ddata_t(
445 bind->src->getDtypeSize(), srcDim, depth, bind->src->getDtype(), bind->src->isTimestamp());
446 if (bind->ring == nullptr || !bind->ring->isAllocated())
447 {
448 delete bind->ring;
449 bind->ring = nullptr;
450 return -ENOMEM;
451 }
452
453 bind->outData = bind->out->ddata_alloc(1);
454 bind->selData = bind->sel->ddata_alloc(1);
455 bind->statData = bind->stat->ddata_alloc(1);
456
457 if (bind->outData == nullptr || bind->selData == nullptr || bind->statData == nullptr)
458 {
459 return -ENOMEM;
460 }
461
462 return OK;
463}
464
465CProgBuffer::~CProgBuffer()
466{
467 deinit();
468}
469
471{
472 int ret = configureDesc(getDesc());
473 if (ret != OK)
474 {
475 DAWNERR("buffer: configure failed: %d\n", ret);
476 return ret;
477 }
478
479 if (bind == nullptr)
480 {
481 DAWNERR("buffer: no binding configured\n");
482 return -EINVAL;
483 }
484
485 if (depth == 0)
486 {
487 DAWNERR("buffer: depth must be > 0\n");
488 return -EINVAL;
489 }
490
491 if (chunkSize == 0)
492 {
493 DAWNERR("buffer: chunk_size must be > 0\n");
494 return -EINVAL;
495 }
496
497 if (chunkSize > depth)
498 {
499 DAWNERR("buffer: chunk_size must be <= depth\n");
500 return -EINVAL;
501 }
502
503 uint32_t validFlags = FLAG_AUTO_START | FLAG_MODE_ONESHOT | FLAG_KEEP_DATA_ON_STOP;
504 if ((flags & ~validFlags) != 0)
505 {
506 DAWNERR("buffer: unsupported FLAGS bits 0x%" PRIx32 "\n", flags & ~validFlags);
507 return -EINVAL;
508 }
509
510 return OK;
511}
512
514{
515 int ret;
516
517 ret = validateBind();
518 if (ret != OK)
519 {
520 return ret;
521 }
522
523 ret = allocateBind();
524 if (ret != OK)
525 {
526 return ret;
527 }
528
529 ret = bind->src->setNotifier(srcNotifyCb, 0, this);
530 if (ret != OK)
531 {
532 DAWNERR("buffer: set notifier failed for 0x%" PRIx32 ": %d\n", bind->src->getIdV(), ret);
533 return ret;
534 }
535
536 return updateSelected();
537}
538
540{
541 doStop();
542
543 if (bind != nullptr)
544 {
545 delete bind->ring;
546 delete bind->outData;
547 delete bind->selData;
548 delete bind->statData;
549 delete bind;
550 bind = nullptr;
551 }
552
553 return OK;
554}
555
557{
558 bind->captureActive = (flags & FLAG_AUTO_START) != 0;
559
560 if ((flags & FLAG_MODE_ONESHOT) && bind->count >= depth)
561 {
562 bind->captureActive = false;
563 }
564
565 // NOTE: deliberately no get-callback on `out`. The out value is already kept
566 // current by captureBind() and selSetCb(), so a get-callback would only
567 // re-run updateSelected()->setVal(), which emits a notification. A read must
568 // not generate a notification; worse, a transport that reads the value in
569 // order to send a notification would then self-trigger an unbounded loop.
570 bind->sel->setCallbackSet(selSetCb, this);
571 bind->stat->setCallbackGet(statGetCb, this);
572 updateStat();
573
574 return OK;
575}
576
578{
579 bool keep = (flags & FLAG_KEEP_DATA_ON_STOP) != 0;
580
581 if (bind == nullptr)
582 {
583 return OK;
584 }
585
586 bind->captureActive = false;
587
588 if (bind->sel != nullptr)
589 {
590 bind->sel->setCallbackSet(nullptr, nullptr);
591 }
592
593 if (bind->stat != nullptr)
594 {
595 bind->stat->setCallbackGet(nullptr, nullptr);
596 }
597
598 if (!keep && bind->ring != nullptr && bind->outData != nullptr)
599 {
600 clearBind();
601 }
602
603 if (bind->stat != nullptr && bind->statData != nullptr)
604 {
605 updateStat();
606 }
607
608 return OK;
609}
610
612{
613 return false;
614}
615
616void CProgBuffer::cmdReset()
617{
618 if (bind->ring != nullptr && bind->outData != nullptr)
619 {
620 clearBind();
621 updateSelected();
622 }
623 if (bind->stat != nullptr && bind->statData != nullptr)
624 {
625 updateStat();
626 }
627}
628
629void CProgBuffer::cmdStartCapture()
630{
631 if ((flags & FLAG_MODE_ONESHOT) && bind->count >= depth)
632 {
633 bind->captureActive = false;
634 }
635 else
636 {
637 bind->captureActive = true;
638 }
639
640 if (bind->stat != nullptr && bind->statData != nullptr)
641 {
642 updateStat();
643 }
644}
645
646void CProgBuffer::cmdStopCapture()
647{
648 bind->captureActive = false;
649 if (bind->stat != nullptr && bind->statData != nullptr)
650 {
651 updateStat();
652 }
653}
654
655int CProgBuffer::trigger(uint8_t cmd)
656{
657 switch (cmd)
658 {
659 case CMD_RESET:
660 cmdReset();
661 return OK;
662
663 case CMD_TRIGGER1:
664 cmdStartCapture();
665 return OK;
666
667 case CMD_TRIGGER2:
668 cmdStopCapture();
669 return OK;
670
671 default:
672 return -ENOTSUP;
673 }
674}
CIOCommon * getIO(SObjectId::ObjectId id)
Get an I/O object by ID.
Definition bindable.cxx:41
void setObjectMapItem(SObjectId::ObjectId id, CObject *obj)
Set an item in the object map.
Definition bindable.cxx:23
Descriptor wrapper for individual object configuration.
size_t getSize() const
Get number of configuration items for this object.
SObjectCfg::SObjectCfgItem * objectCfgItemNext(size_t &offset) const
Get config item at current offset and advance past it.
@ IO_CLASS_VIRT
Virtual I/O.
Definition common.hxx:210
Virtual I/O type for user-provided data and callbacks.
Definition virt.hxx:26
@ STATE_RUNNING
Object is running.
Definition object.hxx:35
virtual EObjectState getState() const
Get current operational state.
Definition object.hxx:183
CDescObject & getDesc()
Get descriptor object for this object.
Definition object.cxx:190
@ CMD_RESET
Reset object internal state.
Definition object.hxx:42
@ CMD_TRIGGER2
Object-specific trigger slot 2.
Definition object.hxx:44
@ CMD_TRIGGER1
Object-specific trigger slot 1.
Definition object.hxx:43
Notify-driven history buffer Program.
Definition buffer.hxx:27
int doStart()
Start implementation hook.
Definition buffer.cxx:556
int deinit()
De-initialize object.
Definition buffer.cxx:539
int init()
One-time initialize object after bindings are resolved.
Definition buffer.cxx:513
int trigger(uint8_t cmd)
Execute a trigger command.
Definition buffer.cxx:655
@ PROG_BUFFER_CFG_IOBIND
I/O binding configuration.
Definition buffer.hxx:34
@ PROG_BUFFER_CFG_CHUNK_SIZE
Output samples per read.
Definition buffer.hxx:37
@ PROG_BUFFER_CFG_FLAGS
Config flags.
Definition buffer.hxx:36
@ PROG_BUFFER_CFG_DEPTH
Buffer depth.
Definition buffer.hxx:35
int configure()
Configure object from descriptor data.
Definition buffer.cxx:470
int doStop()
Stop implementation hook.
Definition buffer.cxx:577
bool hasThread() const
Check if a background thread is active.
Definition buffer.cxx:611
@ PROG_CLASS_BUFFER
Notify-driven history capture buffer.
Definition common.hxx:103
uint32_t ObjectCfgData_t
Configuration data element - single 32-bit word.
Definition objectcfg.hxx:69
static uint32_t cfgToU32(ObjectCfgData_t x)
Convert ObjectCfgData_t to uint32_t.
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
Single configuration item within object.
ObjectCfgData_t data[]
Configuration data array (flexible, size from cfgid.s.size).
UObjectCfgId cfgid
Configuration ID header (type, class, id, size, rw, dtype).
@ DTYPE_UINT32
Unsigned 32-bit integer (0 to 4294967295).
Definition objectid.hxx:96
uint32_t ObjectId
ObjectID type - single 32-bit value.
Definition objectid.hxx:44
Heap-allocated dynamic I/O data buffer.
Definition ddata.hxx:21
size_t getDataSize()
Get data size in bytes.
Definition ddata.hxx:153
void * getDataPtr(size_t batch=0)
Get pointer to data only (skips timestamp if present).
Definition ddata.hxx:180
io_ts_t & getTs(size_t batch=0)
Get timestamp reference for batch.
Definition ddata.hxx:203
ObjectCfgId v
Raw 32-bit ConfigID value (for storage, comparison).
Definition objectcfg.hxx:82
uint32_t cls
Object class (bits 21-29, max 511).
uint32_t id
Configuration identifier (bits 0-4, max 31).
Definition objectcfg.hxx:94
uint32_t size
Configuration data size in 32-bit words (bits 5-14, max 1023).
struct dawn::SObjectCfg::UObjectCfgId::@10 s
Bit-field structure for named member access.