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::outGetCb(CIOVirt *io, void *priv)
39{
40 (void)io;
41 CProgBuffer *self = static_cast<CProgBuffer *>(priv);
42 if (self == nullptr)
43 {
44 return;
45 }
46
47 int ret = self->updateSelected();
48 if (ret != OK)
49 {
50 DAWNERR("buffer: out get update failed: %d\n", ret);
51 }
52}
53
54void CProgBuffer::selSetCb(CIOVirt *io, void *priv)
55{
56 CProgBuffer *self = static_cast<CProgBuffer *>(priv);
57 if (self == nullptr)
58 {
59 return;
60 }
61
62 SBufferBind *bind = self->bind;
63 int ret = io->getVal(bind->selData->getDataPtr(), bind->selData->getDataSize());
64 if (ret != OK)
65 {
66 DAWNERR("buffer: failed to read selector value: %d\n", ret);
67 return;
68 }
69
70 bind->selectedOffset = bind->selData->get<uint32_t>(0);
71 ret = self->updateSelected();
72 if (ret != OK)
73 {
74 DAWNERR("buffer: selector apply failed: %d\n", ret);
75 }
76}
77
78void CProgBuffer::statGetCb(CIOVirt *io, void *priv)
79{
80 (void)io;
81 CProgBuffer *self = static_cast<CProgBuffer *>(priv);
82 if (self == nullptr)
83 {
84 return;
85 }
86
87 int ret = self->updateStat();
88 if (ret != OK)
89 {
90 DAWNERR("buffer: stat update failed: %d\n", ret);
91 }
92}
93
94int CProgBuffer::allocBind(SObjectId::ObjectId src,
98{
99 bind = new (std::nothrow) SBufferBind();
100 if (bind == nullptr)
101 {
102 return -ENOMEM;
103 }
104
105 bind->srcId = src;
106 bind->outId = out;
107 bind->selId = sel;
108 bind->statId = stat;
109 bind->src = nullptr;
110 bind->out = nullptr;
111 bind->sel = nullptr;
112 bind->stat = nullptr;
113 bind->ring = nullptr;
114 bind->outData = nullptr;
115 bind->selData = nullptr;
116 bind->statData = nullptr;
117 bind->head = 0;
118 bind->count = 0;
119 bind->overflow = 0;
120 bind->selectedOffset = 0;
121 bind->snapshotSeq = 0;
122 bind->captureActive = false;
123
124 setObjectMapItem(src, nullptr);
125 setObjectMapItem(out, nullptr);
126 setObjectMapItem(sel, nullptr);
127 setObjectMapItem(stat, nullptr);
128
129 return OK;
130}
131
132int CProgBuffer::configureDesc(const CDescObject &desc)
133{
134 const SObjectCfg::SObjectCfgItem *item = nullptr;
135 size_t offset = 0;
136
137 for (size_t i = 0; i < desc.getSize(); i++)
138 {
139 item = desc.objectCfgItemNext(offset);
140
142 {
143 DAWNERR("buffer: unsupported cfg 0x%08" PRIx32 "\n", item->cfgid.v);
144 return -EINVAL;
145 }
146
147 switch (item->cfgid.s.id)
148 {
150 {
151 const size_t wpe = sizeof(SProgBufferIOBind) / 4;
152 int ret;
153
154 if (item->cfgid.s.size != wpe)
155 {
156 DAWNERR("buffer: invalid IOBIND size %d\n", item->cfgid.s.size);
157 return -EINVAL;
158 }
159
160 if (bind != nullptr)
161 {
162 DAWNERR("buffer: only one IOBIND per instance is supported\n");
163 return -EINVAL;
164 }
165
166 const SProgBufferIOBind *cfg =
167 reinterpret_cast<const SProgBufferIOBind *>(item->data);
168
169 ret = allocBind(cfg->src.v, cfg->out.v, cfg->sel.v, cfg->stat.v);
170 if (ret != OK)
171 {
172 return ret;
173 }
174
175 break;
176 }
177
179 {
180 const SObjectCfg::ObjectCfgData_t *data;
181
182 if (item->cfgid.s.size != 1)
183 {
184 DAWNERR("buffer: invalid DEPTH size %d\n", item->cfgid.s.size);
185 return -EINVAL;
186 }
187
188 data = reinterpret_cast<const SObjectCfg::ObjectCfgData_t *>(item->data);
189 depth = SObjectCfg::cfgToU32(data[0]);
190 break;
191 }
192
194 {
195 const SObjectCfg::ObjectCfgData_t *data;
196
197 if (item->cfgid.s.size != 1)
198 {
199 DAWNERR("buffer: invalid FLAGS size %d\n", item->cfgid.s.size);
200 return -EINVAL;
201 }
202
203 data = reinterpret_cast<const SObjectCfg::ObjectCfgData_t *>(item->data);
204 flags = SObjectCfg::cfgToU32(data[0]);
205 break;
206 }
207
209 {
210 const SObjectCfg::ObjectCfgData_t *data;
211
212 if (item->cfgid.s.size != 1)
213 {
214 DAWNERR("buffer: invalid CHUNK_SIZE size %d\n", item->cfgid.s.size);
215 return -EINVAL;
216 }
217
218 data = reinterpret_cast<const SObjectCfg::ObjectCfgData_t *>(item->data);
219 chunkSize = SObjectCfg::cfgToU32(data[0]);
220 break;
221 }
222
223 default:
224 {
225 DAWNERR("buffer: unsupported cfg 0x%08" PRIx32 "\n", item->cfgid.v);
226 return -EINVAL;
227 }
228 }
229 }
230
231 return OK;
232}
233
234int CProgBuffer::updateStat()
235{
236 uint32_t runtimeFlags = 0;
237 bool full = (bind->count == depth);
238 bool rangeError = (bind->count > 0 && bind->selectedOffset >= bind->count);
239
240 if (getState() == STATE_RUNNING)
241 {
242 runtimeFlags |= RUNTIME_RUNNING;
243 }
244
245 if (bind->captureActive)
246 {
247 runtimeFlags |= RUNTIME_CAPTURE_ACTIVE;
248 }
249
250 if (full)
251 {
252 runtimeFlags |= RUNTIME_FULL;
253 }
254
255 if (rangeError)
256 {
257 runtimeFlags |= RUNTIME_ERROR_RANGE;
258 }
259
260 bind->statData->get<uint32_t>(STAT_COUNT) = bind->count;
261 bind->statData->get<uint32_t>(STAT_DEPTH) = depth;
262 bind->statData->get<uint32_t>(STAT_HEAD) = bind->head;
263 bind->statData->get<uint32_t>(STAT_OVERFLOW) = bind->overflow;
264 bind->statData->get<uint32_t>(STAT_SNAPSHOT_SEQ) = bind->snapshotSeq;
265 bind->statData->get<uint32_t>(STAT_RUNTIME_FLAGS) = runtimeFlags;
266 bind->statData->get<uint32_t>(STAT_SELECTED_OFFSET) = bind->selectedOffset;
267 bind->statData->get<uint32_t>(STAT_RESERVED) = 0;
268
269 return bind->stat->setVal(bind->statData->getDataPtr(), bind->statData->getDataSize());
270}
271
272int CProgBuffer::updateSelected()
273{
274 int ret;
275 size_t sampleSize = bind->src->getDataSize();
276 uint8_t *out = static_cast<uint8_t *>(bind->outData->getDataPtr());
277
278 std::memset(bind->outData->getDataPtr(), 0, bind->outData->getDataSize());
279
280 if (bind->count == 0)
281 {
282 bind->snapshotSeq++;
283 ret = bind->out->setVal(bind->outData->getDataPtr(), bind->outData->getDataSize());
284 if (ret != OK)
285 {
286 return ret;
287 }
288
289 return updateStat();
290 }
291
292 if (bind->selectedOffset >= bind->count)
293 {
294 updateStat();
295 return -ERANGE;
296 }
297
298 for (uint32_t i = 0; i < chunkSize; i++)
299 {
300 uint32_t sel = bind->selectedOffset + i;
301 if (sel >= bind->count)
302 {
303 break;
304 }
305
306 uint32_t idx = resolveRingIndex(bind, sel, depth);
307 std::memcpy(out + (i * sampleSize), bind->ring->getDataPtr(idx), sampleSize);
308 }
309
310 bind->snapshotSeq++;
311
312 ret = bind->out->setVal(bind->outData->getDataPtr(), bind->outData->getDataSize());
313 if (ret != OK)
314 {
315 return ret;
316 }
317
318 return updateStat();
319}
320
321int CProgBuffer::captureBind(io_ddata_t *data)
322{
323 if (!bind->captureActive)
324 {
325 return OK;
326 }
327
328 if ((flags & FLAG_MODE_ONESHOT) && bind->count >= depth)
329 {
330 bind->captureActive = false;
331 return updateStat();
332 }
333
334 std::memcpy(bind->ring->getDataPtr(bind->head), data->getDataPtr(), data->getDataSize());
335 bind->ring->getTs(bind->head) = data->getTs(0);
336
337 bind->head = (bind->head + 1u) % depth;
338 if (bind->count < depth)
339 {
340 bind->count++;
341 }
342 else
343 {
344 bind->overflow++;
345 }
346
347 return updateSelected();
348}
349
350void CProgBuffer::clearBind()
351{
352 bind->head = 0;
353 bind->count = 0;
354 bind->overflow = 0;
355 bind->selectedOffset = 0;
356 bind->snapshotSeq = 0;
357
358 std::memset(bind->outData->getDataPtr(), 0, bind->outData->getDataSize());
359}
360
361int CProgBuffer::validateBind()
362{
363 bind->src = getIO(bind->srcId);
364 if (bind->src == nullptr)
365 {
366 DAWNERR("buffer: src 0x%" PRIx32 " not found\n", bind->srcId);
367 return -EIO;
368 }
369
370 bind->out = reinterpret_cast<CIOVirt *>(getIO(bind->outId));
371 if (bind->out == nullptr)
372 {
373 DAWNERR("buffer: out 0x%" PRIx32 " not found\n", bind->outId);
374 return -EIO;
375 }
376
377 bind->sel = reinterpret_cast<CIOVirt *>(getIO(bind->selId));
378 if (bind->sel == nullptr)
379 {
380 DAWNERR("buffer: sel 0x%" PRIx32 " not found\n", bind->selId);
381 return -EIO;
382 }
383
384 bind->stat = reinterpret_cast<CIOVirt *>(getIO(bind->statId));
385 if (bind->stat == nullptr)
386 {
387 DAWNERR("buffer: stat 0x%" PRIx32 " not found\n", bind->statId);
388 return -EIO;
389 }
390
391 if (!bind->src->isNotify())
392 {
393 DAWNERR("buffer: src 0x%" PRIx32 " has no notify support\n", bind->src->getIdV());
394 return -EINVAL;
395 }
396
397 if (bind->out->getDtype() != bind->src->getDtype())
398 {
399 DAWNERR("buffer: out IO incompatible with src 0x%" PRIx32 "\n", bind->src->getIdV());
400 return -EINVAL;
401 }
402
403 if (bind->sel->getDtype() != SObjectId::DTYPE_UINT32)
404 {
405 DAWNERR("buffer: sel IO must use uint32 dtype\n");
406 return -EINVAL;
407 }
408
409 if (bind->stat->getDtype() != SObjectId::DTYPE_UINT32)
410 {
411 DAWNERR("buffer: stat IO must use uint32 dtype\n");
412 return -EINVAL;
413 }
414
415 return OK;
416}
417
418int CProgBuffer::allocateBind()
419{
420 int ret;
421 const size_t srcDim = bind->src->getDataDim();
422 size_t outDim;
423
424 if (srcDim == 0)
425 {
426 DAWNERR("buffer: src dimension must be > 0\n");
427 return -EINVAL;
428 }
429
430 outDim = srcDim * chunkSize;
431 if (outDim == 0)
432 {
433 return -EINVAL;
434 }
435
436 if (bind->out->getCls() == CIOCommon::IO_CLASS_VIRT)
437 {
438 ret = bind->out->initialize(outDim, 1, true);
439 if (ret != OK)
440 {
441 DAWNERR("buffer: out initialize failed: %d\n", ret);
442 return ret;
443 }
444 }
445
446 ret = prepareWritableTarget(bind->sel, 1, false);
447 if (ret != OK)
448 {
449 DAWNERR("buffer: sel initialize failed: %d\n", ret);
450 return ret;
451 }
452
453 ret = prepareWritableTarget(bind->stat, STAT_WORDS, false);
454 if (ret != OK)
455 {
456 DAWNERR("buffer: stat initialize failed: %d\n", ret);
457 return ret;
458 }
459
460 bind->ring = new (std::nothrow) io_ddata_t(
461 bind->src->getDtypeSize(), srcDim, depth, bind->src->getDtype(), bind->src->isTimestamp());
462 if (bind->ring == nullptr || !bind->ring->isAllocated())
463 {
464 delete bind->ring;
465 bind->ring = nullptr;
466 return -ENOMEM;
467 }
468
469 bind->outData = bind->out->ddata_alloc(1);
470 bind->selData = bind->sel->ddata_alloc(1);
471 bind->statData = bind->stat->ddata_alloc(1);
472
473 if (bind->outData == nullptr || bind->selData == nullptr || bind->statData == nullptr)
474 {
475 return -ENOMEM;
476 }
477
478 return OK;
479}
480
481CProgBuffer::~CProgBuffer()
482{
483 deinit();
484}
485
487{
488 int ret = configureDesc(getDesc());
489 if (ret != OK)
490 {
491 DAWNERR("buffer: configure failed: %d\n", ret);
492 return ret;
493 }
494
495 if (bind == nullptr)
496 {
497 DAWNERR("buffer: no binding configured\n");
498 return -EINVAL;
499 }
500
501 if (depth == 0)
502 {
503 DAWNERR("buffer: depth must be > 0\n");
504 return -EINVAL;
505 }
506
507 if (chunkSize == 0)
508 {
509 DAWNERR("buffer: chunk_size must be > 0\n");
510 return -EINVAL;
511 }
512
513 if (chunkSize > depth)
514 {
515 DAWNERR("buffer: chunk_size must be <= depth\n");
516 return -EINVAL;
517 }
518
519 uint32_t validFlags = FLAG_AUTO_START | FLAG_MODE_ONESHOT | FLAG_KEEP_DATA_ON_STOP;
520 if ((flags & ~validFlags) != 0)
521 {
522 DAWNERR("buffer: unsupported FLAGS bits 0x%" PRIx32 "\n", flags & ~validFlags);
523 return -EINVAL;
524 }
525
526 return OK;
527}
528
530{
531 int ret;
532
533 ret = validateBind();
534 if (ret != OK)
535 {
536 return ret;
537 }
538
539 ret = allocateBind();
540 if (ret != OK)
541 {
542 return ret;
543 }
544
545 ret = bind->src->setNotifier(srcNotifyCb, 0, this);
546 if (ret != OK)
547 {
548 DAWNERR("buffer: set notifier failed for 0x%" PRIx32 ": %d\n", bind->src->getIdV(), ret);
549 return ret;
550 }
551
552 return updateSelected();
553}
554
556{
557 doStop();
558
559 if (bind != nullptr)
560 {
561 delete bind->ring;
562 delete bind->outData;
563 delete bind->selData;
564 delete bind->statData;
565 delete bind;
566 bind = nullptr;
567 }
568
569 return OK;
570}
571
573{
574 bind->captureActive = (flags & FLAG_AUTO_START) != 0;
575
576 if ((flags & FLAG_MODE_ONESHOT) && bind->count >= depth)
577 {
578 bind->captureActive = false;
579 }
580
581 bind->out->setCallbackGet(outGetCb, this);
582 bind->sel->setCallbackSet(selSetCb, this);
583 bind->stat->setCallbackGet(statGetCb, this);
584 updateStat();
585
586 return OK;
587}
588
590{
591 bool keep = (flags & FLAG_KEEP_DATA_ON_STOP) != 0;
592
593 if (bind == nullptr)
594 {
595 return OK;
596 }
597
598 bind->captureActive = false;
599
600 if (bind->out != nullptr)
601 {
602 bind->out->setCallbackGet(nullptr, nullptr);
603 }
604
605 if (bind->sel != nullptr)
606 {
607 bind->sel->setCallbackSet(nullptr, nullptr);
608 }
609
610 if (bind->stat != nullptr)
611 {
612 bind->stat->setCallbackGet(nullptr, nullptr);
613 }
614
615 if (!keep && bind->ring != nullptr && bind->outData != nullptr)
616 {
617 clearBind();
618 }
619
620 if (bind->stat != nullptr && bind->statData != nullptr)
621 {
622 updateStat();
623 }
624
625 return OK;
626}
627
629{
630 return false;
631}
632
633void CProgBuffer::cmdReset()
634{
635 if (bind->ring != nullptr && bind->outData != nullptr)
636 {
637 clearBind();
638 updateSelected();
639 }
640 if (bind->stat != nullptr && bind->statData != nullptr)
641 {
642 updateStat();
643 }
644}
645
646void CProgBuffer::cmdStartCapture()
647{
648 if ((flags & FLAG_MODE_ONESHOT) && bind->count >= depth)
649 {
650 bind->captureActive = false;
651 }
652 else
653 {
654 bind->captureActive = true;
655 }
656
657 if (bind->stat != nullptr && bind->statData != nullptr)
658 {
659 updateStat();
660 }
661}
662
663void CProgBuffer::cmdStopCapture()
664{
665 bind->captureActive = false;
666 if (bind->stat != nullptr && bind->statData != nullptr)
667 {
668 updateStat();
669 }
670}
671
672int CProgBuffer::trigger(uint8_t cmd)
673{
674 switch (cmd)
675 {
676 case CMD_RESET:
677 cmdReset();
678 return OK;
679
680 case CMD_TRIGGER1:
681 cmdStartCapture();
682 return OK;
683
684 case CMD_TRIGGER2:
685 cmdStopCapture();
686 return OK;
687
688 default:
689 return -ENOTSUP;
690 }
691}
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:188
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:572
int deinit()
De-initialize object.
Definition buffer.cxx:555
int init()
One-time initialize object after bindings are resolved.
Definition buffer.cxx:529
int trigger(uint8_t cmd)
Execute a trigger command.
Definition buffer.cxx:672
@ 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:486
int doStop()
Stop implementation hook.
Definition buffer.cxx:589
bool hasThread() const
Check if a background thread is active.
Definition buffer.cxx:628
@ 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.