Dawn Framework 1.0
Universal data acquisition framework for embedded systems
sequencer.cxx
1// dawn/src/prog/sequencer.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/prog/sequencer.hxx"
7
8#include <cstring>
9#include <new>
10#include <unistd.h>
11
12#include "dawn/debug.hxx"
13#include "dawn/io/common.hxx"
14#include "dawn/io/ddata.hxx"
15
16using namespace dawn;
17
18int CProgSequencer::configureDesc(const CDescObject &desc)
19{
20 const SObjectCfg::SObjectCfgItem *item = nullptr;
21 size_t offset = 0;
22 bool hasTargets = false;
23 bool hasStates = false;
24 size_t statesCount = 0;
25
26 for (size_t i = 0; i < desc.getSize(); i++)
27 {
28 item = desc.objectCfgItemNext(offset);
29
31 {
32 DAWNERR("sequencer: unsupported cfg 0x%08" PRIx32 "\n", item->cfgid.v);
33 return -EINVAL;
34 }
35
36 switch (item->cfgid.s.id)
37 {
38 case PROG_SEQUENCER_CFG_TARGET:
39 {
40 const SObjectId::UObjectId *ids;
41 size_t ntargets;
42 size_t j;
43 int ret;
44
45 if (item->cfgid.s.size == 0)
46 {
47 DAWNERR("sequencer: no targets configured\n");
48 return -EINVAL;
49 }
50
51 hasTargets = true;
52 ntargets = item->cfgid.s.size;
53 targetIds.reserve(ntargets);
54 ids = reinterpret_cast<const SObjectId::UObjectId *>(item->data);
55
56 for (j = 0; j < ntargets; j++)
57 {
58 ret = allocObject(ids[j].v);
59 if (ret != OK)
60 {
61 return ret;
62 }
63 }
64
65 break;
66 }
67
68 case PROG_SEQUENCER_CFG_STATE:
69 {
70 if (item->cfgid.s.size == 0 || item->cfgid.s.size % 2 != 0)
71 {
72 DAWNERR("sequencer: invalid state table size %d\n", item->cfgid.s.size);
73 return -EINVAL;
74 }
75
76 hasStates = true;
77 statesCount = item->cfgid.s.size / 2;
78 break;
79 }
80
81 case PROG_SEQUENCER_CFG_START:
82 {
83 if (item->cfgid.s.size != 1)
84 {
85 DAWNERR("sequencer: invalid START size %d\n", item->cfgid.s.size);
86 return -EINVAL;
87 }
88
89 break;
90 }
91
92 default:
93 {
94 DAWNERR("sequencer: unsupported cfg 0x%08" PRIx32 "\n", item->cfgid.v);
95 return -EINVAL;
96 }
97 }
98 }
99
100 if (!hasTargets || targetIds.empty())
101 {
102 DAWNERR("sequencer: target list is empty\n");
103 return -EINVAL;
104 }
105
106 if (!hasStates || statesCount == 0)
107 {
108 DAWNERR("sequencer: state table is empty\n");
109 return -EINVAL;
110 }
111
112 states.resize(statesCount);
113
114 return reloadRuntimeConfig(true);
115}
116
117int CProgSequencer::allocObject(SObjectId::ObjectId targetId)
118{
119 DAWNINFO("allocate sequencer target 0x%" PRIx32 "\n", targetId);
120
121 targetIds.push_back(targetId);
122 setObjectMapItem(targetId, nullptr);
123
124 return OK;
125}
126
127int CProgSequencer::reloadRuntimeConfig(bool resetCurrent,
128 SObjectCfg::ObjectCfgId overrideCfg,
129 const uint32_t *overrideData,
130 size_t overrideLen)
131{
132 CDescObject &desc = getDesc();
133 const SObjectCfg::SObjectCfgItem *item = nullptr;
134 const uint32_t *data = nullptr;
135 size_t dataWords = 0;
136 size_t offset = 0;
137 size_t statesCount = 0;
138 size_t newStartIndex = startIndex;
139 size_t parsedStates = 0;
140 size_t i;
141 bool hasState = false;
142 bool hasStartItem = false;
143
144 for (size_t itemIdx = 0; itemIdx < desc.getSize(); itemIdx++)
145 {
146 item = desc.objectCfgItemNext(offset);
147 data = reinterpret_cast<const uint32_t *>(item->data);
148 dataWords = item->cfgid.s.size;
149
150 if (overrideCfg != 0 && item->cfgid.v == overrideCfg)
151 {
152 if (!overrideData)
153 {
154 DAWNERR("sequencer: null override data\n");
155 return -EINVAL;
156 }
157
158 data = overrideData;
159 dataWords = overrideLen;
160 }
161
163 {
164 DAWNERR("sequencer: invalid runtime cfg class\n");
165 return -EINVAL;
166 }
167
168 switch (item->cfgid.s.id)
169 {
170 case PROG_SEQUENCER_CFG_STATE:
171 {
172 hasState = true;
173 statesCount = dataWords / 2;
174 if (dataWords == 0 || dataWords % 2 != 0 || states.empty() ||
175 statesCount != states.size())
176 {
177 DAWNERR("sequencer: invalid state table size %d\n", (int)dataWords);
178 return -EINVAL;
179 }
180
181 parsedStates = statesCount;
182 break;
183 }
184
185 case PROG_SEQUENCER_CFG_START:
186 {
187 if (dataWords != 1)
188 {
189 DAWNERR("sequencer: invalid START size %d\n", (int)dataWords);
190 return -EINVAL;
191 }
192
193 hasStartItem = true;
194 if (overrideCfg == 0 || item->cfgid.v == overrideCfg)
195 {
196 newStartIndex = static_cast<size_t>(*data);
197 }
198 break;
199 }
200
201 default:
202 break;
203 }
204 }
205
206 if (!hasState || parsedStates == 0)
207 {
208 DAWNERR("sequencer: state table is empty\n");
209 return -EINVAL;
210 }
211
212 if (hasStartItem && newStartIndex >= parsedStates)
213 {
214 DAWNERR("sequencer: start index %zu out of range (%zu)\n", newStartIndex, parsedStates);
215 return -EINVAL;
216 }
217
218 offset = 0;
219 stateLock.lock();
220
221 for (size_t itemIdx = 0; itemIdx < desc.getSize(); itemIdx++)
222 {
223 item = desc.objectCfgItemNext(offset);
224 data = reinterpret_cast<const uint32_t *>(item->data);
225 dataWords = item->cfgid.s.size;
226
227 if (overrideCfg != 0 && item->cfgid.v == overrideCfg)
228 {
229 data = overrideData;
230 dataWords = overrideLen;
231 }
232
233 if (item->cfgid.s.id == PROG_SEQUENCER_CFG_STATE)
234 {
235 parsedStates = 0;
236 for (i = 0; i < dataWords; i += 2)
237 {
238 states[parsedStates].value = data[i];
239 states[parsedStates].dwellUs = data[i + 1];
240 if (states[parsedStates].dwellUs == 0)
241 {
242 DAWNERR("sequencer: state[%zu] dwell_us must be > 0\n", parsedStates);
243 stateLock.unlock();
244 return -EINVAL;
245 }
246
247 parsedStates++;
248 }
249 }
250 }
251
252 startIndex = newStartIndex;
253
254 if (resetCurrent)
255 {
256 currentIndex = startIndex;
257 }
258
259 stateLock.unlock();
260 return OK;
261}
262
263int CProgSequencer::onSetObjConfig(SObjectCfg::ObjectCfgId objcfg, uint32_t *data, size_t len)
264{
265 int ret;
266 uint8_t id;
267 bool resetCurrent;
268
270 {
271 return OK;
272 }
273
275 {
276 return OK;
277 }
278
279 id = SObjectCfg::objectCfgGetId(objcfg);
280 if (id != PROG_SEQUENCER_CFG_STATE && id != PROG_SEQUENCER_CFG_START)
281 {
282 return OK;
283 }
284
285 resetCurrent = id == PROG_SEQUENCER_CFG_START || getState() != CObject::STATE_RUNNING;
286
287 ret = reloadRuntimeConfig(resetCurrent, objcfg, data, len);
288 if (ret != OK)
289 {
290 return ret;
291 }
292
294 {
295 ret = applyState(startIndex);
296 if (ret != OK)
297 {
298 return ret;
299 }
300 }
301 else if (getState() == CObject::STATE_RUNNING)
302 {
303 size_t index;
304
305 stateLock.lock();
306 index = currentIndex;
307 stateLock.unlock();
308
309 ret = applyState(index);
310 if (ret != OK)
311 {
312 return ret;
313 }
314 }
315
316 return OK;
317}
318
319int CProgSequencer::applyState(size_t index)
320{
321 uint32_t value;
322 int ret;
323 size_t i;
324
325 if (index >= states.size() || !iodata || targetSize == 0 || targetSize > 4)
326 {
327 return -EINVAL;
328 }
329
330 value = states[index].value;
331 std::memset(iodata->getDataPtr(), 0, iodata->getDataSize());
332 std::memcpy(iodata->getDataPtr(), &value, targetSize);
333
334 ret = OK;
335 for (i = 0; i < targets.size(); i++)
336 {
337 int r;
338 r = targets[i]->setData(*iodata);
339 if (r != OK)
340 {
341 DAWNERR("sequencer: setData failed for target[%zu] (%d)\n", i, r);
342 ret = r;
343 }
344 }
345
346 return ret;
347}
348
349void CProgSequencer::thread()
350{
351 size_t idx;
352 uint32_t dwell;
353
354 DAWNINFO("start sequencer thread\n");
355
356 do
357 {
358 stateLock.lock();
359 idx = currentIndex;
360 dwell = states[idx].dwellUs;
361 stateLock.unlock();
362
363 applyState(idx);
364 usleep(dwell);
365
366 stateLock.lock();
367 currentIndex = (idx + 1) % states.size();
368 stateLock.unlock();
369 }
370 while (!threadCtl.shouldQuit());
371}
372
373CProgSequencer::~CProgSequencer()
374{
375 deinit();
376}
377
379{
380 int ret;
381
382 ret = configureDesc(getDesc());
383 if (ret != OK)
384 {
385 DAWNERR("sequencer configure failed: %d\n", ret);
386 return ret;
387 }
388
389 return OK;
390}
391
393{
394 CIOCommon *io;
395 size_t i;
396 int ret;
397
398 targets.reserve(targetIds.size());
399
400 for (i = 0; i < targetIds.size(); i++)
401 {
402 io = getIO(targetIds[i]);
403 if (!io)
404 {
405 DAWNERR("sequencer: target 0x%" PRIx32 " not found\n", targetIds[i]);
406 return -EIO;
407 }
408
409 ret = prepareWritableTarget(io, 1, true);
410 if (ret != OK)
411 {
412 DAWNERR("sequencer: target prepare failed %d\n", ret);
413 return ret;
414 }
415
416 if (io->getDataDim() != 1)
417 {
418 DAWNERR("sequencer: target 0x%" PRIx32 " must be scalar output\n", targetIds[i]);
419 return -EINVAL;
420 }
421
422 if (io->getDataSize() == 0 || io->getDataSize() > sizeof(uint32_t))
423 {
424 DAWNERR("sequencer: target 0x%" PRIx32 " unsupported data size %zu (expected 1..4)\n",
425 targetIds[i],
426 io->getDataSize());
427 return -EINVAL;
428 }
429
430 if (targets.empty())
431 {
432 targetSize = io->getDataSize();
433 targetDtype = io->getDtype();
434 }
435 else if (targetSize != io->getDataSize() || targetDtype != io->getDtype())
436 {
437 DAWNERR("sequencer: target 0x%" PRIx32 " type mismatch with first target\n",
438 targetIds[i]);
439 return -EINVAL;
440 }
441
442 targets.push_back(io);
443 }
444
445 // Target ID list is no longer needed after targets are resolved.
446 std::vector<SObjectId::ObjectId>().swap(targetIds);
447
448 iodata = new (std::nothrow) io_ddata_t(targetSize, 1, 1, targetDtype);
449 if (iodata == nullptr || !iodata->isAllocated())
450 {
451 delete iodata;
452 iodata = nullptr;
453 DAWNERR("sequencer: iodata allocation failed\n");
454 return -ENOMEM;
455 }
456
457 return OK;
458}
459
461{
462 doStop();
463 delete iodata;
464 iodata = nullptr;
465 std::vector<CIOCommon *>().swap(targets);
466 std::vector<SObjectId::ObjectId>().swap(targetIds);
467 std::vector<SState>().swap(states);
468 targetSize = 0;
469 targetDtype = SObjectId::DTYPE_ANY;
470 return OK;
471}
472
474{
475 int ret;
476
477 ret = reloadRuntimeConfig(true);
478 if (ret != OK)
479 {
480 return ret;
481 }
482
483 // The worker thread applies the start state on its first iteration.
484 // Not writing here avoids a stale value when another PROG (e.g.
485 // Switch) immediately stops this sequencer before the thread runs.
486
487 threadCtl.setThreadFunc([this]() { thread(); });
488 return threadCtl.threadStart();
489}
490
492{
493 return threadCtl.threadStop();
494}
495
497{
498 return threadCtl.isRunning();
499}
500
502{
503 int ret;
504
505 if (cmd != CMD_RESET)
506 {
507 return -ENOTSUP;
508 }
509
510 ret = reloadRuntimeConfig(true);
511 if (ret != OK)
512 {
513 return ret;
514 }
515
516 ret = applyState(startIndex);
517 return ret;
518}
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.
Base class for all I/O objects.
Definition common.hxx:27
virtual size_t getDataDim() const =0
Get data vector dimension.
virtual size_t getDataSize() const =0
Get data size in bytes.
@ STATE_STOPPED
Object is stopped.
Definition object.hxx:34
@ 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
uint8_t getDtype() const
Get data type field.
Definition object.cxx:175
@ CMD_RESET
Reset object internal state.
Definition object.hxx:42
@ PROG_CLASS_SEQUENCER
Periodic state sequencer.
Definition common.hxx:107
bool hasThread() const
Check if a background thread is active.
int init()
One-time initialize object after bindings are resolved.
int configure()
Configure object from descriptor data.
int deinit()
De-initialize object.
int doStop()
Stop implementation hook.
int doStart()
Start implementation hook.
int trigger(uint8_t cmd)
Execute a trigger command.
bool isRunning() const
Check if the worker thread is running.
Definition thread.cxx:256
int threadStop()
Stop the worker thread.
Definition thread.cxx:240
int threadStart()
Start the worker thread.
Definition thread.cxx:166
void setThreadFunc(Func &&func)
Assign the function executed by threadStart().
Definition thread.hxx:100
uint32_t ObjectCfgId
ConfigID type - single 32-bit value.
Definition objectcfg.hxx:60
static uint16_t objectCfgGetCls(const ObjectCfgId objcfg)
Extract object class from ConfigID.
static uint8_t objectCfgGetId(const ObjectCfgId objcfg)
Extract configuration identifier from ConfigID.
static uint8_t objectCfgGetType(const ObjectCfgId objcfg)
Extract object type from ConfigID.
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).
@ OBJTYPE_PROG
Program/algorithm object type.
Definition objectid.hxx:202
@ DTYPE_ANY
Wildcard data type (matches any actual type).
Definition objectid.hxx:68
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
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.
32-bit encoded object identifier (union with bit field).
Definition objectid.hxx:218