F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
FpySequencerDirectives.cpp
Go to the documentation of this file.
1 #include <algorithm>
2 #include <cmath>
3 #include <cstring>
4 #include <type_traits>
5 #include "Fw/Com/ComPacket.hpp"
7 
8 namespace Svc {
9 
10 void FpySequencer::sendSignal(Signal signal) {
11  switch (signal) {
14  break;
15  }
18  break;
19  }
22  break;
23  }
26  break;
27  }
28  default: {
29  FW_ASSERT(0, static_cast<FwAssertArgType>(signal));
30  }
31  }
32 }
33 
34 // utility method for updating telemetry based on a directive error code
35 void FpySequencer::handleDirectiveErrorCode(Fpy::DirectiveId id, DirectiveError err) {
36  this->m_tlm.lastDirectiveError = err;
37  if (err != DirectiveError::NO_ERROR) {
38  this->m_tlm.directiveErrorIndex = this->currentStatementIdx();
39  this->m_tlm.directiveErrorId = id;
40  }
41 }
42 
43 Fw::Success FpySequencer::sendCmd(FwOpcodeType opcode, const U8* argBuf, FwSizeType argBufSize) {
44  Fw::ComBuffer cmdBuf;
45  Fw::SerializeStatus stat =
46  cmdBuf.serializeFrom(static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_COMMAND));
47  // TODO should I assert here? this really shouldn't fail, I should just add a static assert
48  // on com buf size and then assert here
50  return Fw::Success::FAILURE;
51  }
52  stat = cmdBuf.serializeFrom(opcode);
54  return Fw::Success::FAILURE;
55  }
56  stat = cmdBuf.serializeFrom(argBuf, argBufSize, Fw::Serialization::OMIT_LENGTH);
58  return Fw::Success::FAILURE;
59  }
60 
61  // calculate the unique command identifier:
62  // cmd UID is formatted like XXYY, where XX are the first two bytes of the m_sequencesStarted counter
63  // and YY are the first two bytes of the m_statementsDispatched counter.
64  // this way, we know when we get a cmd back A) whether or not it's from this sequence (modulo 2^16) and B)
65  // whether or not it's this specific instance of the cmd in the sequence, and not another one with the same opcode
66  // somewhere else in the file.
67  // if we put this uid in the context we send to the cmdDisp, we will get it back when the cmd returns
68  U32 cmdUid =
69  static_cast<U32>(((this->m_sequencesStarted & 0xFFFF) << 16) | (this->m_statementsDispatched & 0xFFFF));
70 
71  this->cmdOut_out(0, cmdBuf, cmdUid);
72 
73  return Fw::Success::SUCCESS;
74 }
75 
79  this->sendSignal(this->waitRel_directiveHandler(directive, error));
80  handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_REL, error);
81 }
82 
86  this->sendSignal(this->waitAbs_directiveHandler(directive, error));
87  handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_ABS, error);
88 }
89 
93  this->sendSignal(this->goto_directiveHandler(directive, error));
94  handleDirectiveErrorCode(Fpy::DirectiveId::GOTO, error);
95 }
96 
100  this->sendSignal(this->if_directiveHandler(directive, error));
101  handleDirectiveErrorCode(Fpy::DirectiveId::IF, error);
102 }
103 
107  this->sendSignal(this->noOp_directiveHandler(directive, error));
108  handleDirectiveErrorCode(Fpy::DirectiveId::NO_OP, error);
109 }
110 
113  const Svc::FpySequencer_PushTlmValDirective& directive) {
115  this->sendSignal(this->pushTlmVal_directiveHandler(directive, error));
116  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL, error);
117 }
118 
123  this->sendSignal(this->pushTlmValAndTime_directiveHandler(directive, error));
124  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL_AND_TIME, error);
125 }
126 
130  this->sendSignal(this->pushPrm_directiveHandler(directive, error));
131  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_PRM, error);
132 }
133 
137  this->sendSignal(this->constCmd_directiveHandler(directive, error));
138  handleDirectiveErrorCode(Fpy::DirectiveId::CONST_CMD, error);
139 }
140 
144  this->sendSignal(this->stackOp_directiveHandler(directive, error));
145  handleDirectiveErrorCode(directive.get__op(), error);
146 }
147 
151  this->sendSignal(this->exit_directiveHandler(directive, error));
152  handleDirectiveErrorCode(Fpy::DirectiveId::EXIT, error);
153 }
154 
158  this->sendSignal(this->allocate_directiveHandler(directive, error));
159  handleDirectiveErrorCode(Fpy::DirectiveId::ALLOCATE, error);
160 }
161 
166  this->sendSignal(this->storeRelConstOffset_directiveHandler(directive, error));
167  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL_CONST_OFFSET, error);
168 }
169 
173  this->sendSignal(this->pushVal_directiveHandler(directive, error));
174  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_VAL, error);
175 }
176 
180  this->sendSignal(this->loadRel_directiveHandler(directive, error));
181  handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_REL, error);
182 }
183 
187  this->sendSignal(this->discard_directiveHandler(directive, error));
188  handleDirectiveErrorCode(Fpy::DirectiveId::DISCARD, error);
189 }
190 
194  this->sendSignal(this->memCmp_directiveHandler(directive, error));
195  handleDirectiveErrorCode(Fpy::DirectiveId::MEMCMP, error);
196 }
197 
201  this->sendSignal(this->stackCmd_directiveHandler(directive, error));
202  handleDirectiveErrorCode(Fpy::DirectiveId::STACK_CMD, error);
203 }
204 
208  this->sendSignal(this->pushTime_directiveHandler(directive, error));
209  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TIME, error);
210 }
211 
215  this->sendSignal(this->setSeed_directiveHandler(directive, error));
216  handleDirectiveErrorCode(Fpy::DirectiveId::SET_SEED, error);
217 }
218 
222  this->sendSignal(this->pushRand_directiveHandler(directive, error));
223  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_RAND, error);
224 }
225 
229  this->sendSignal(this->getField_directiveHandler(directive, error));
230  handleDirectiveErrorCode(Fpy::DirectiveId::GET_FIELD, error);
231 }
232 
236  this->sendSignal(this->peek_directiveHandler(directive, error));
237  handleDirectiveErrorCode(Fpy::DirectiveId::PEEK, error);
238 }
239 
243  this->sendSignal(this->storeRel_directiveHandler(directive, error));
244  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL, error);
245 }
246 
250  this->sendSignal(this->call_directiveHandler(directive, error));
251  handleDirectiveErrorCode(Fpy::DirectiveId::CALL, error);
252 }
253 
257  this->sendSignal(this->return_directiveHandler(directive, error));
258  handleDirectiveErrorCode(Fpy::DirectiveId::RETURN, error);
259 }
260 
264  this->sendSignal(this->loadAbs_directiveHandler(directive, error));
265  handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_ABS, error);
266 }
267 
271  this->sendSignal(this->storeAbs_directiveHandler(directive, error));
272  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS, error);
273 }
274 
279  this->sendSignal(this->storeAbsConstOffset_directiveHandler(directive, error));
280  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS_CONST_OFFSET, error);
281 }
282 
284 Signal FpySequencer::waitRel_directiveHandler(const FpySequencer_WaitRelDirective& directive, DirectiveError& error) {
285  if (this->m_runtime.stack.size < 8) {
288  }
289 
290  Fw::Time wakeupTime = this->getTime();
291 
292  U32 uSeconds = this->m_runtime.stack.pop<U32>();
293  U32 seconds = this->m_runtime.stack.pop<U32>();
294 
295  wakeupTime.add(seconds, uSeconds);
296  this->m_runtime.wakeupTime = wakeupTime;
298 }
299 
301 Signal FpySequencer::waitAbs_directiveHandler(const FpySequencer_WaitAbsDirective& directive, DirectiveError& error) {
302  if (this->m_runtime.stack.size < 2 * sizeof(U32) + sizeof(FwTimeContextStoreType) + sizeof(FwTimeBaseStoreType)) {
305  }
306 
307  U32 uSeconds = this->m_runtime.stack.pop<U32>();
308  U32 seconds = this->m_runtime.stack.pop<U32>();
309  FwTimeContextStoreType ctx = this->m_runtime.stack.pop<FwTimeContextStoreType>();
310  FwTimeBaseStoreType base = this->m_runtime.stack.pop<FwTimeBaseStoreType>();
311 
312  this->m_runtime.wakeupTime = Fw::Time(static_cast<TimeBase::T>(base), ctx, seconds, uSeconds);
314 }
315 
317 Signal FpySequencer::goto_directiveHandler(const FpySequencer_GotoDirective& directive, DirectiveError& error) {
318  // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
319  if (directive.get_statementIndex() > m_sequenceObj.get_header().get_statementCount()) {
322  }
323  m_runtime.nextStatementIndex = directive.get_statementIndex();
325 }
326 
328 Signal FpySequencer::if_directiveHandler(const FpySequencer_IfDirective& directive, DirectiveError& error) {
329  if (this->m_runtime.stack.size < 1) {
332  }
333  // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
334  if (directive.get_falseGotoStmtIndex() > m_sequenceObj.get_header().get_statementCount()) {
337  }
338 
339  if (this->m_runtime.stack.pop<U8>() != 0) {
340  // proceed to next instruction
342  }
343 
344  // conditional false case
345  this->m_runtime.nextStatementIndex = directive.get_falseGotoStmtIndex();
347 }
348 
349 Signal FpySequencer::noOp_directiveHandler(const FpySequencer_NoOpDirective& directive, DirectiveError& error) {
351 }
352 
353 Signal FpySequencer::pushTlmVal_directiveHandler(const FpySequencer_PushTlmValDirective& directive,
354  DirectiveError& error) {
355  if (!this->isConnected_getTlmChan_OutputPort(0)) {
358  }
359  Fw::Time tlmTime;
360  Fw::TlmBuffer tlmValue;
361  Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
362 
363  if (valid != Fw::TlmValid::VALID) {
364  // could not find this tlm chan
367  }
368 
369  if (Fpy::MAX_STACK_SIZE - tlmValue.getSize() < this->m_runtime.stack.size) {
372  }
373  this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
375 }
376 
377 Signal FpySequencer::pushTlmValAndTime_directiveHandler(const FpySequencer_PushTlmValAndTimeDirective& directive,
378  DirectiveError& error) {
379  if (!this->isConnected_getTlmChan_OutputPort(0)) {
382  }
383 
384  Fw::Time tlmTime;
385  Fw::TlmBuffer tlmValue;
386  Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
387 
388  if (valid != Fw::TlmValid::VALID) {
389  // could not find this tlm chan
392  }
393 
394  U8 tlmTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
396  Fw::SerializeStatus stat = timeEsb.serializeFrom(tlmTime);
397 
398  // coding error if this failed, we should have enough space
399  FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
400 
401  // check that our stack won't overflow if we put both val and time on it
402  if (Fpy::MAX_STACK_SIZE - tlmValue.getSize() - timeEsb.getSize() < this->m_runtime.stack.size) {
405  }
406 
407  // push tlm to end of stack
408  this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
409  // now push time to end of stack
410  this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
412 }
413 
414 Signal FpySequencer::pushPrm_directiveHandler(const FpySequencer_PushPrmDirective& directive, DirectiveError& error) {
415  if (!this->isConnected_prmGet_OutputPort(0)) {
418  }
419 
420  Fw::ParamBuffer prmValue;
421  Fw::ParamValid valid = this->getParam_out(0, directive.get_prmId(), prmValue);
422 
423  if (valid != Fw::ParamValid::VALID) {
424  // could not find this prm in the DB
427  }
428 
429  if (Fpy::MAX_STACK_SIZE - prmValue.getSize() < this->m_runtime.stack.size) {
432  }
433 
434  this->m_runtime.stack.push(prmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(prmValue.getSize()));
436 }
437 
438 Signal FpySequencer::constCmd_directiveHandler(const FpySequencer_ConstCmdDirective& directive, DirectiveError& error) {
439  if (this->sendCmd(directive.get_opCode(), directive.get_argBuf(), directive.get__argBufSize()) ==
442  } else {
443  // now tell the SM to wait some more until we get the cmd response back
444  // if we've already got the response back this should be harmless
446  }
447 }
448 
449 DirectiveError FpySequencer::op_or() {
450  if (this->m_runtime.stack.size < sizeof(U8) * 2) {
452  }
453  this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() | this->m_runtime.stack.pop<U8>()));
455 }
456 DirectiveError FpySequencer::op_and() {
457  if (this->m_runtime.stack.size < sizeof(U8) * 2) {
459  }
460  this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() & this->m_runtime.stack.pop<U8>()));
462 }
463 DirectiveError FpySequencer::op_ieq() {
464  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
466  }
467  this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() == this->m_runtime.stack.pop<I64>())
468  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
469  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
471 }
472 DirectiveError FpySequencer::op_ine() {
473  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
475  }
476  this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() != this->m_runtime.stack.pop<I64>())
477  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
478  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
480 }
481 DirectiveError FpySequencer::op_ult() {
482  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
484  }
485  U64 rhs = this->m_runtime.stack.pop<U64>();
486  U64 lhs = this->m_runtime.stack.pop<U64>();
487  this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
488  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
490 }
491 DirectiveError FpySequencer::op_ule() {
492  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
494  }
495  U64 rhs = this->m_runtime.stack.pop<U64>();
496  U64 lhs = this->m_runtime.stack.pop<U64>();
497  this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
498  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
500 }
501 DirectiveError FpySequencer::op_ugt() {
502  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
504  }
505  U64 rhs = this->m_runtime.stack.pop<U64>();
506  U64 lhs = this->m_runtime.stack.pop<U64>();
507  this->m_runtime.stack.push(static_cast<U8>((lhs > rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
508  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
510 }
511 DirectiveError FpySequencer::op_uge() {
512  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
514  }
515  U64 rhs = this->m_runtime.stack.pop<U64>();
516  U64 lhs = this->m_runtime.stack.pop<U64>();
517  this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
518  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
520 }
521 DirectiveError FpySequencer::op_slt() {
522  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
524  }
525  I64 rhs = this->m_runtime.stack.pop<I64>();
526  I64 lhs = this->m_runtime.stack.pop<I64>();
527  this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
528  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
530 }
531 DirectiveError FpySequencer::op_sle() {
532  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
534  }
535  I64 rhs = this->m_runtime.stack.pop<I64>();
536  I64 lhs = this->m_runtime.stack.pop<I64>();
537  this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
538  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
540 }
541 DirectiveError FpySequencer::op_sgt() {
542  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
544  }
545  I64 rhs = this->m_runtime.stack.pop<I64>();
546  I64 lhs = this->m_runtime.stack.pop<I64>();
547  this->m_runtime.stack.push(static_cast<U8>((lhs > rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
548  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
550 }
551 DirectiveError FpySequencer::op_sge() {
552  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
554  }
555  I64 rhs = this->m_runtime.stack.pop<I64>();
556  I64 lhs = this->m_runtime.stack.pop<I64>();
557  this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
558  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
560 }
561 DirectiveError FpySequencer::op_feq() {
562  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
564  }
565  F64 rhs = this->m_runtime.stack.pop<F64>();
566  F64 lhs = this->m_runtime.stack.pop<F64>();
567  // eq is true if they are equal and neither is nan
568  this->m_runtime.stack.push(static_cast<U8>((lhs == rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
569  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
571 }
572 DirectiveError FpySequencer::op_fne() {
573  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
575  }
576  F64 rhs = this->m_runtime.stack.pop<F64>();
577  F64 lhs = this->m_runtime.stack.pop<F64>();
578  // ne is true if they are not equal or either is nan
579  this->m_runtime.stack.push(static_cast<U8>((lhs != rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
580  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
582 }
583 DirectiveError FpySequencer::op_flt() {
584  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
586  }
587  F64 rhs = this->m_runtime.stack.pop<F64>();
588  F64 lhs = this->m_runtime.stack.pop<F64>();
589  this->m_runtime.stack.push(static_cast<U8>(std::isless(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
590  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
592 }
593 DirectiveError FpySequencer::op_fle() {
594  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
596  }
597  F64 rhs = this->m_runtime.stack.pop<F64>();
598  F64 lhs = this->m_runtime.stack.pop<F64>();
599  this->m_runtime.stack.push(static_cast<U8>(std::islessequal(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
600  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
602 }
603 DirectiveError FpySequencer::op_fgt() {
604  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
606  }
607  F64 rhs = this->m_runtime.stack.pop<F64>();
608  F64 lhs = this->m_runtime.stack.pop<F64>();
609  this->m_runtime.stack.push(static_cast<U8>(std::isgreater(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
610  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
612 }
613 DirectiveError FpySequencer::op_fge() {
614  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
616  }
617  F64 rhs = this->m_runtime.stack.pop<F64>();
618  F64 lhs = this->m_runtime.stack.pop<F64>();
619  this->m_runtime.stack.push(static_cast<U8>(std::isgreaterequal(lhs, rhs)
620  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
621  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
623 }
624 DirectiveError FpySequencer::op_not() {
625  if (this->m_runtime.stack.size < sizeof(U8)) {
627  }
628  this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<U8>() == 0)
629  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
630  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
632 }
633 DirectiveError FpySequencer::op_fpext() {
634  // convert F32 to F64
635  if (this->m_runtime.stack.size < sizeof(F32)) {
637  }
638  this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<F32>()));
640 }
641 DirectiveError FpySequencer::op_fptrunc() {
642  // convert F64 to F32
643  if (this->m_runtime.stack.size < sizeof(F64)) {
645  }
646  this->m_runtime.stack.push(static_cast<F32>(this->m_runtime.stack.pop<F64>()));
648 }
649 DirectiveError FpySequencer::op_fptosi() {
650  if (this->m_runtime.stack.size < sizeof(F64)) {
652  }
653  this->m_runtime.stack.push(static_cast<I64>(this->m_runtime.stack.pop<F64>()));
655 }
656 DirectiveError FpySequencer::op_sitofp() {
657  if (this->m_runtime.stack.size < sizeof(I64)) {
659  }
660  this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<I64>()));
662 }
663 DirectiveError FpySequencer::op_fptoui() {
664  if (this->m_runtime.stack.size < sizeof(F64)) {
666  }
667  this->m_runtime.stack.push(static_cast<U64>(this->m_runtime.stack.pop<F64>()));
669 }
670 DirectiveError FpySequencer::op_uitofp() {
671  if (this->m_runtime.stack.size < sizeof(U64)) {
673  }
674  this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<U64>()));
676 }
677 DirectiveError FpySequencer::op_add() {
678  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
680  }
681  I64 rhs = this->m_runtime.stack.pop<I64>();
682  I64 lhs = this->m_runtime.stack.pop<I64>();
683  // Check for overflow and underflow and return the appropriate error code
684  // Overflow can only occur with both operands positive and occurs when one operand is greater than the maximum value
685  // less the other operand. If either operand is negative or zero, overflow cannot occur.
686  if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() - rhs) < lhs)) {
688  }
689  // Underflow can only occur with both operands negative and occurs when one operand is less than the minimum value
690  // minus the other operand. If either operand is positive or zero, underflow cannot occur.
691  else if ((rhs < 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() - rhs) > lhs)) {
693  }
694  this->m_runtime.stack.push(static_cast<I64>(lhs + rhs));
696 }
697 DirectiveError FpySequencer::op_sub() {
698  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
700  }
701  I64 rhs = this->m_runtime.stack.pop<I64>();
702  I64 lhs = this->m_runtime.stack.pop<I64>();
703  // Check for overflow and underflow and return the appropriate error code
704  // Overflow can only occur when the left operand is positive and the right operand is negative. It occurs when the
705  // left (positive) operand is greater than the maximum value plus the other (negative) operand. If the right
706  // operand is positive or zero, overflow cannot occur.
707  if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() + rhs) < lhs)) {
709  }
710  // Underflow can only occur when the left operand is negative and the right operand is positive. It occurs when the
711  // left (negative) operand is less than the minimum value plus the other (positive) operand. If the right operand
712  // is negative or zero, underflow cannot occur.
713  else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() + rhs) > lhs)) {
715  }
716  this->m_runtime.stack.push(static_cast<I64>(lhs - rhs));
718 }
719 DirectiveError FpySequencer::op_mul() {
720  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
722  }
723  I64 rhs = this->m_runtime.stack.pop<I64>();
724  I64 lhs = this->m_runtime.stack.pop<I64>();
725  // Check for overflow and underflow and return the appropriate error code
726  // Overflow can only occur with operands of matching signs and occurs when one operand is greater (or less) than the
727  // maximum value divided by the other operand. Either operand being zero precludes overflow.
728  // Check the both positive case.
729  if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() / rhs) < lhs)) {
731  }
732  // Check the both negative case
733  else if ((rhs < 0) && (lhs < 0) && ((std::numeric_limits<I64>::max() / (-1 * rhs)) < (-1 * lhs))) {
735  }
736  // Underflow can occur with operands of differing signs and occurs when one operand is less than the minimum value
737  // divided by the other operand. Either operand being zero precludes underflow.
738  // Check the case where lhs is positive.
739  else if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::min() / lhs) > rhs)) {
741  }
742  // Check the case where rhs is positive.
743  else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() / rhs) > lhs)) {
745  }
746  this->m_runtime.stack.push(static_cast<I64>(lhs * rhs));
748 }
749 DirectiveError FpySequencer::op_udiv() {
750  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
752  }
753  U64 rhs = this->m_runtime.stack.pop<U64>();
754  U64 lhs = this->m_runtime.stack.pop<U64>();
755  // Prevent division by zero
756  if (rhs == 0) {
758  }
759  this->m_runtime.stack.push(static_cast<U64>(lhs / rhs));
761 }
762 DirectiveError FpySequencer::op_sdiv() {
763  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
765  }
766 
767  I64 rhs = this->m_runtime.stack.pop<I64>();
768  I64 lhs = this->m_runtime.stack.pop<I64>();
769  // Prevent division by zero
770  if (rhs == 0) {
772  }
773  this->m_runtime.stack.push(static_cast<I64>(lhs / rhs));
775 }
776 DirectiveError FpySequencer::op_umod() {
777  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
779  }
780  U64 rhs = this->m_runtime.stack.pop<U64>();
781  if (rhs == 0) {
783  }
784  U64 lhs = this->m_runtime.stack.pop<U64>();
785  this->m_runtime.stack.push(static_cast<U64>(lhs % rhs));
787 }
788 DirectiveError FpySequencer::op_smod() {
789  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
791  }
792  I64 rhs = this->m_runtime.stack.pop<I64>();
793  if (rhs == 0) {
795  }
796  I64 lhs = this->m_runtime.stack.pop<I64>();
797  I64 res = static_cast<I64>(lhs % rhs);
798  // in order to match Python's behavior,
799  // if the signs of the remainder and divisor differ, adjust the result.
800  // this happens when the result should be positive but is negative, or vice-versa.
801  // credit Gemini 2.5 pro
802  if ((res > 0 && rhs < 0) || (res < 0 && rhs > 0)) {
803  res += rhs;
804  }
805  this->m_runtime.stack.push(res);
807 }
808 DirectiveError FpySequencer::op_fadd() {
809  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
811  }
812  F64 rhs = this->m_runtime.stack.pop<F64>();
813  F64 lhs = this->m_runtime.stack.pop<F64>();
814  this->m_runtime.stack.push(static_cast<F64>(lhs + rhs));
816 }
817 DirectiveError FpySequencer::op_fsub() {
818  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
820  }
821  F64 rhs = this->m_runtime.stack.pop<F64>();
822  F64 lhs = this->m_runtime.stack.pop<F64>();
823  this->m_runtime.stack.push(static_cast<F64>(lhs - rhs));
825 }
826 DirectiveError FpySequencer::op_fmul() {
827  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
829  }
830  F64 rhs = this->m_runtime.stack.pop<F64>();
831  F64 lhs = this->m_runtime.stack.pop<F64>();
832  this->m_runtime.stack.push(static_cast<F64>(lhs * rhs));
834 }
835 DirectiveError FpySequencer::op_fdiv() {
836  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
838  }
839  F64 rhs = this->m_runtime.stack.pop<F64>();
840  F64 lhs = this->m_runtime.stack.pop<F64>();
841  this->m_runtime.stack.push(static_cast<F64>(lhs / rhs));
843 }
844 DirectiveError FpySequencer::op_fpow() {
845  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
847  }
848  F64 rhs = this->m_runtime.stack.pop<F64>();
849  F64 lhs = this->m_runtime.stack.pop<F64>();
850  this->m_runtime.stack.push(static_cast<F64>(pow(lhs, rhs)));
852 }
853 DirectiveError FpySequencer::op_flog() {
854  if (this->m_runtime.stack.size < sizeof(F64)) {
856  }
857  F64 val = this->m_runtime.stack.pop<F64>();
858  if (val <= 0.0) {
860  }
861  this->m_runtime.stack.push(static_cast<F64>(log(val)));
863 }
864 DirectiveError FpySequencer::op_fmod() {
865  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
867  }
868  F64 rhs = this->m_runtime.stack.pop<F64>();
869  if (rhs == 0.0) {
871  }
872  F64 lhs = this->m_runtime.stack.pop<F64>();
873  this->m_runtime.stack.push(static_cast<F64>(lhs - rhs * std::floor(lhs / rhs)));
875 }
876 DirectiveError FpySequencer::op_siext_8_64() {
877  if (this->m_runtime.stack.size < sizeof(I8)) {
879  }
880  I8 src = this->m_runtime.stack.pop<I8>();
881  this->m_runtime.stack.push(static_cast<I64>(src));
883 }
884 DirectiveError FpySequencer::op_siext_16_64() {
885  if (this->m_runtime.stack.size < sizeof(I16)) {
887  }
888  I16 src = this->m_runtime.stack.pop<I16>();
889  this->m_runtime.stack.push(static_cast<I64>(src));
891 }
892 DirectiveError FpySequencer::op_siext_32_64() {
893  if (this->m_runtime.stack.size < sizeof(I32)) {
895  }
896  I32 src = this->m_runtime.stack.pop<I32>();
897  this->m_runtime.stack.push(static_cast<I64>(src));
899 }
900 DirectiveError FpySequencer::op_ziext_8_64() {
901  if (this->m_runtime.stack.size < sizeof(U8)) {
903  }
904  U8 src = this->m_runtime.stack.pop<U8>();
905  this->m_runtime.stack.push(static_cast<U64>(src));
907 }
908 DirectiveError FpySequencer::op_ziext_16_64() {
909  if (this->m_runtime.stack.size < sizeof(U16)) {
911  }
912  U16 src = this->m_runtime.stack.pop<U16>();
913  this->m_runtime.stack.push(static_cast<U64>(src));
915 }
916 DirectiveError FpySequencer::op_ziext_32_64() {
917  if (this->m_runtime.stack.size < sizeof(U32)) {
919  }
920  U32 src = this->m_runtime.stack.pop<U32>();
921  this->m_runtime.stack.push(static_cast<U64>(src));
923 }
924 DirectiveError FpySequencer::op_itrunc_64_8() {
925  if (this->m_runtime.stack.size < sizeof(U64)) {
927  }
928  U64 src = this->m_runtime.stack.pop<U64>();
929  this->m_runtime.stack.push(static_cast<U8>(src));
931 }
932 DirectiveError FpySequencer::op_itrunc_64_16() {
933  if (this->m_runtime.stack.size < sizeof(U64)) {
935  }
936  U64 src = this->m_runtime.stack.pop<U64>();
937  this->m_runtime.stack.push(static_cast<U16>(src));
939 }
940 DirectiveError FpySequencer::op_itrunc_64_32() {
941  if (this->m_runtime.stack.size < sizeof(U64)) {
943  }
944  U64 src = this->m_runtime.stack.pop<U64>();
945  this->m_runtime.stack.push(static_cast<U32>(src));
947 }
948 Signal FpySequencer::stackOp_directiveHandler(const FpySequencer_StackOpDirective& directive, DirectiveError& error) {
949  // coding error, should not have gotten to this stack op handler
950  FW_ASSERT(directive.get__op() >= Fpy::DirectiveId::OR && directive.get__op() <= Fpy::DirectiveId::ITRUNC_64_32,
951  static_cast<FwAssertArgType>(directive.get__op()));
952 
953  switch (directive.get__op()) {
955  error = this->op_or();
956  break;
958  error = this->op_and();
959  break;
961  error = this->op_ieq();
962  break;
964  error = this->op_ine();
965  break;
967  error = this->op_ult();
968  break;
970  error = this->op_ule();
971  break;
973  error = this->op_ugt();
974  break;
976  error = this->op_uge();
977  break;
979  error = this->op_slt();
980  break;
982  error = this->op_sle();
983  break;
985  error = this->op_sgt();
986  break;
988  error = this->op_sge();
989  break;
991  error = this->op_feq();
992  break;
994  error = this->op_fne();
995  break;
997  error = this->op_flt();
998  break;
1000  error = this->op_fle();
1001  break;
1002  case Fpy::DirectiveId::FGT:
1003  error = this->op_fgt();
1004  break;
1005  case Fpy::DirectiveId::FGE:
1006  error = this->op_fge();
1007  break;
1008  case Fpy::DirectiveId::NOT:
1009  error = this->op_not();
1010  break;
1012  error = this->op_fpext();
1013  break;
1015  error = this->op_fptrunc();
1016  break;
1018  error = this->op_fptosi();
1019  break;
1021  error = this->op_fptoui();
1022  break;
1024  error = this->op_sitofp();
1025  break;
1027  error = this->op_uitofp();
1028  break;
1029  case Fpy::DirectiveId::ADD:
1030  error = this->op_add();
1031  break;
1032  case Fpy::DirectiveId::SUB:
1033  error = this->op_sub();
1034  break;
1035  case Fpy::DirectiveId::MUL:
1036  error = this->op_mul();
1037  break;
1039  error = this->op_udiv();
1040  break;
1042  error = this->op_sdiv();
1043  break;
1045  error = this->op_umod();
1046  break;
1048  error = this->op_smod();
1049  break;
1051  error = this->op_fadd();
1052  break;
1054  error = this->op_fsub();
1055  break;
1057  error = this->op_fmul();
1058  break;
1060  error = this->op_fdiv();
1061  break;
1063  error = this->op_fpow();
1064  break;
1066  error = this->op_flog();
1067  break;
1069  error = this->op_fmod();
1070  break;
1072  error = this->op_siext_8_64();
1073  break;
1075  error = this->op_siext_16_64();
1076  break;
1078  error = this->op_siext_32_64();
1079  break;
1081  error = this->op_ziext_8_64();
1082  break;
1084  error = this->op_ziext_16_64();
1085  break;
1087  error = this->op_ziext_32_64();
1088  break;
1090  error = this->op_itrunc_64_8();
1091  break;
1093  error = this->op_itrunc_64_16();
1094  break;
1096  error = this->op_itrunc_64_32();
1097  break;
1098  default:
1099  FW_ASSERT(0, directive.get__op());
1100  break;
1101  }
1102  if (error != DirectiveError::NO_ERROR) {
1104  }
1106 }
1107 
1108 Signal FpySequencer::exit_directiveHandler(const FpySequencer_ExitDirective& directive, DirectiveError& error) {
1109  if (this->m_runtime.stack.size < 1) {
1112  }
1113  U8 errorCode = this->m_runtime.stack.pop<U8>();
1114  // exit(0), no error
1115  if (errorCode == 0) {
1116  // just goto the end of the sequence
1117  this->m_runtime.nextStatementIndex = this->m_sequenceObj.get_header().get_statementCount();
1119  }
1120  // otherwise, kill the sequence here
1121  // raise the user defined error code as an event
1122  this->log_WARNING_HI_SequenceExitedWithError(this->m_sequenceFilePath, errorCode);
1125 }
1126 
1127 Signal FpySequencer::allocate_directiveHandler(const FpySequencer_AllocateDirective& directive, DirectiveError& error) {
1128  if (directive.get_size() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1131  }
1132  this->m_runtime.stack.pushZeroes(directive.get_size());
1134 }
1135 
1137 Signal FpySequencer::storeHelper(Fpy::StackSizeType destOffset, Fpy::StackSizeType size, DirectiveError& error) {
1138  if (this->m_runtime.stack.size < size) {
1141  }
1142  // After popping the value, would the write go out of bounds?
1143  Fpy::StackSizeType newStackSize = this->m_runtime.stack.size - size;
1144  // Overflow-safe check: destOffset + size > newStackSize
1145  // Rewritten as: check destOffset <= newStackSize first, then size > newStackSize - destOffset
1146  if (destOffset > newStackSize || size > newStackSize - destOffset) {
1149  }
1150  // Copy value to the destination location
1151  this->m_runtime.stack.copy(destOffset, this->m_runtime.stack.size - size, size);
1152  this->m_runtime.stack.size = newStackSize;
1154 }
1155 
1157 Signal FpySequencer::loadHelper(Fpy::StackSizeType srcOffset, Fpy::StackSizeType size, DirectiveError& error) {
1158  if (size > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1161  }
1162  // Overflow-safe check: srcOffset + size > stack.size
1163  // Rewritten as: check srcOffset <= stack.size first, then size > stack.size - srcOffset
1164  if (srcOffset > this->m_runtime.stack.size || size > this->m_runtime.stack.size - srcOffset) {
1167  }
1168  // Copy from source location to top of stack
1169  this->m_runtime.stack.copy(this->m_runtime.stack.size, srcOffset, size);
1170  this->m_runtime.stack.size += size;
1172 }
1173 
1174 Signal FpySequencer::storeRelConstOffset_directiveHandler(const FpySequencer_StoreRelConstOffsetDirective& directive,
1175  DirectiveError& error) {
1176  I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1177  if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1180  }
1181  return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1182 }
1183 
1184 Signal FpySequencer::loadRel_directiveHandler(const FpySequencer_LoadRelDirective& directive, DirectiveError& error) {
1185  I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1186  if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1189  }
1190  return this->loadHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1191 }
1192 
1193 Signal FpySequencer::pushVal_directiveHandler(const FpySequencer_PushValDirective& directive, DirectiveError& error) {
1194  if (directive.get__valSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1197  }
1198  // copy from the bytearray in the directive to the stack, add to stack size.
1199  this->m_runtime.stack.push(const_cast<U8*>(directive.get_val()),
1200  static_cast<Fpy::StackSizeType>(directive.get__valSize()));
1202 }
1203 
1204 Signal FpySequencer::discard_directiveHandler(const FpySequencer_DiscardDirective& directive, DirectiveError& error) {
1205  if (this->m_runtime.stack.size < directive.get_size()) {
1208  }
1209  // drop the specified amount of bytes off the stack. simple as.
1210  this->m_runtime.stack.size -= directive.get_size();
1212 }
1213 
1214 Signal FpySequencer::memCmp_directiveHandler(const FpySequencer_MemCmpDirective& directive, DirectiveError& error) {
1215  // Overflow-safe check: we need size * 2 bytes on the stack
1216  // First check that size * 2 doesn't overflow: size > MAX/2 would overflow
1217  // MAX_STACK_SIZE is the upper bound for stack.size, so if size > MAX_STACK_SIZE/2, we definitely don't have enough
1218  if (directive.get_size() > Fpy::MAX_STACK_SIZE / 2) {
1221  }
1222  // Now safe to compute size * 2
1223  if (this->m_runtime.stack.size < directive.get_size() * 2) {
1226  }
1227 
1228  // find the starting offsets of the two byte arrays
1229  U64 lhsOffset = this->m_runtime.stack.size - directive.get_size() * 2;
1230  U64 rhsOffset = this->m_runtime.stack.size - directive.get_size();
1231 
1232  // "officially" remove them from the stack
1233  // you have to do this before pushing to the stack, otherwise the result would get placed
1234  // after the byte arrays
1235  this->m_runtime.stack.size -= directive.get_size() * 2;
1236 
1237  // memcmp the two byte arrays, push FW_SERIALIZE_TRUE_VALUE if they were equal, FW_SERIALIZE_FALSE_VALUE otherwise
1238  if (memcmp(this->m_runtime.stack.bytes + lhsOffset, this->m_runtime.stack.bytes + rhsOffset,
1239  directive.get_size()) == 0) {
1240  this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_TRUE_VALUE));
1241  } else {
1242  this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_FALSE_VALUE));
1243  }
1245 }
1246 
1247 Signal FpySequencer::stackCmd_directiveHandler(const FpySequencer_StackCmdDirective& directive, DirectiveError& error) {
1248  // Overflow-safe check: need argsSize + sizeof(FwOpcodeType) bytes
1249  // Check stack.size >= sizeof(FwOpcodeType) first, then stack.size - sizeof(FwOpcodeType) >= argsSize
1250  if (this->m_runtime.stack.size < sizeof(FwOpcodeType) ||
1251  this->m_runtime.stack.size - sizeof(FwOpcodeType) < directive.get_argsSize()) {
1254  }
1255 
1256  // pop the opcode of the cmd off the stack
1257  // note this means that, unlike the actual byte array that the dispatcher gets,
1258  // these cmds have opcode after the argument buffer
1259  FwOpcodeType opcode = this->m_runtime.stack.pop<FwOpcodeType>();
1260  U64 argBufOffset = this->m_runtime.stack.size - directive.get_argsSize();
1261 
1262  // update the opcode of the cmd we will await
1263  this->m_runtime.currentCmdOpcode = opcode;
1264 
1265  // also pop the args off the stack
1266  this->m_runtime.stack.size -= directive.get_argsSize();
1267 
1268  if (this->sendCmd(opcode, this->m_runtime.stack.bytes + argBufOffset, directive.get_argsSize()) ==
1271  } else {
1272  // now tell the SM to wait some more until we get the cmd response back
1273  // if we've already got the response back this should be harmless
1275  }
1276 
1278 }
1279 
1280 Signal FpySequencer::pushTime_directiveHandler(const FpySequencer_PushTimeDirective& directive, DirectiveError& error) {
1281  if (Fpy::MAX_STACK_SIZE - Fw::Time::SERIALIZED_SIZE < this->m_runtime.stack.size) {
1284  }
1285 
1286  Fw::Time currentTime = this->getTime();
1287 
1288  U8 currentTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
1289  Fw::ExternalSerializeBuffer timeEsb(currentTimeBuf, Fw::Time::SERIALIZED_SIZE);
1290  Fw::SerializeStatus stat = timeEsb.serializeFrom(currentTime);
1291 
1292  // coding error if this failed, we should have enough space
1293  FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
1294 
1295  // push time to end of stack
1296  this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
1298 }
1299 
1300 Signal FpySequencer::setSeed_directiveHandler(const FpySequencer_SetSeedDirective& directive, DirectiveError& error) {
1301  if (this->m_runtime.stack.size < sizeof(U32)) {
1304  }
1305 
1306  U32 seed = this->m_runtime.stack.pop<U32>();
1307  this->m_runtime.rng.seed(seed);
1308  this->m_runtime.rngSeeded = true;
1310 }
1311 
1312 Signal FpySequencer::pushRand_directiveHandler(const FpySequencer_PushRandDirective& directive, DirectiveError& error) {
1313  if (Fpy::MAX_STACK_SIZE - sizeof(U32) < this->m_runtime.stack.size) {
1316  }
1317 
1318  if (!this->m_runtime.rngSeeded) {
1319  Fw::Time currentTime = this->getTime();
1320  std::seed_seq seedSeq{static_cast<U32>(currentTime.getTimeBase()), static_cast<U32>(currentTime.getContext()),
1321  currentTime.getSeconds(), currentTime.getUSeconds()};
1322  this->m_runtime.rng.seed(seedSeq);
1323  this->m_runtime.rngSeeded = true;
1324  }
1325 
1326  U32 randVal = static_cast<U32>(this->m_runtime.rng());
1327  this->m_runtime.stack.push(randVal);
1329 }
1330 
1331 Signal FpySequencer::getField_directiveHandler(const FpySequencer_GetFieldDirective& directive, DirectiveError& error) {
1332  // Need sizeof(StackSizeType) for the offset AND parentSize for the parent data
1333  // Check we have enough for the offset first
1334  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
1337  }
1338  // After popping the offset, we need at least parentSize bytes remaining
1339  if (this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < directive.get_parentSize()) {
1342  }
1343 
1344  Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1345 
1346  // Overflow-safe check: offset + memberSize > parentSize
1347  // Rewritten as: check offset <= parentSize first, then memberSize > parentSize - offset
1348  if (offset > directive.get_parentSize() || directive.get_memberSize() > directive.get_parentSize() - offset) {
1349  // i think it's somewhat ambiguous whether this is a stack access out of bounds
1350  // but there isn't really an error code that better reflects this, and i guess
1351  // it's technically true
1354  }
1355 
1356  // the resulting bytes should move to the start of the parent array
1357 
1358  // Calculate the offset of the parent start in the stack
1359  Fpy::StackSizeType parentStartOffset = this->m_runtime.stack.size - directive.get_parentSize();
1360  // Overflow-safe: parentStartOffset + offset cannot overflow since offset <= parentSize
1361  // and parentStartOffset + parentSize == stack.size (which is bounded)
1362  this->m_runtime.stack.move(parentStartOffset, parentStartOffset + offset, directive.get_memberSize());
1363  // adjust stack size by the diff between the member and the parent
1364  this->m_runtime.stack.size -= (directive.get_parentSize() - directive.get_memberSize());
1366 }
1367 
1368 Signal FpySequencer::peek_directiveHandler(const FpySequencer_PeekDirective& directive, DirectiveError& error) {
1369  // must have at least two StackSizeType on stack
1370  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) * 2) {
1373  }
1374 
1375  Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1376  Fpy::StackSizeType byteCount = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1377 
1378  // Check offset doesn't exceed stack size (after both pops)
1379  if (offset > this->m_runtime.stack.size) {
1380  // would access past the bottom of the stack
1381  // note we allow the equals case because the byteCount might be 0
1384  }
1385  if (byteCount > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1386  // we would overflow the stack if we pushed this many bytes to it
1389  }
1390  // Overflow-safe check: byteCount + offset > stack.size
1391  // Rewritten as: check offset <= stack.size (done above), then byteCount > stack.size - offset
1392  if (byteCount > this->m_runtime.stack.size - offset) {
1393  // would access past the bottom of the stack
1396  }
1397  // start copying from the lowest byte of the src array
1398  U8* src = this->m_runtime.stack.top() - offset - byteCount;
1399  this->m_runtime.stack.push(src, byteCount);
1401 }
1402 
1403 Signal FpySequencer::storeRel_directiveHandler(const FpySequencer_StoreRelDirective& directive, DirectiveError& error) {
1404  // Need enough bytes for the value and the offset (SignedStackSizeType = 4 bytes)
1405  // Overflow-safe: check stack.size >= sizeof(SignedStackSizeType) first, then stack.size -
1406  // sizeof(SignedStackSizeType) >= size
1407  if (this->m_runtime.stack.size < sizeof(Fpy::SignedStackSizeType) ||
1408  this->m_runtime.stack.size - sizeof(Fpy::SignedStackSizeType) < directive.get_size()) {
1411  }
1412 
1413  // Pop the signed offset from the stack
1414  Fpy::SignedStackSizeType lvarOffset = this->m_runtime.stack.pop<Fpy::SignedStackSizeType>();
1415 
1416  I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + lvarOffset;
1417  if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1420  }
1421  return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1422 }
1423 
1424 Signal FpySequencer::call_directiveHandler(const FpySequencer_CallDirective& directive, DirectiveError& error) {
1425  // Need at least 4 bytes for the target address
1426  if (this->m_runtime.stack.size < sizeof(U32)) {
1429  }
1430 
1431  // Pop the target directive index from the stack
1432  U32 target = this->m_runtime.stack.pop<U32>();
1433 
1434  // Check if we have space to push return address and saved frame pointer (8 bytes total)
1435  if (this->m_runtime.stack.size + sizeof(Fpy::StackSizeType) + sizeof(U32) > Fpy::MAX_STACK_SIZE) {
1438  }
1439 
1440  // Check target is within bounds (will also be checked at execution time)
1441  if (target > m_sequenceObj.get_header().get_statementCount()) {
1444  }
1445 
1446  // Save the return address (next instruction after CALL)
1447  U32 returnAddr = this->m_runtime.nextStatementIndex;
1448 
1449  // Set the next instruction to the target
1450  this->m_runtime.nextStatementIndex = target;
1451 
1452  // Push the return address to the stack
1453  this->m_runtime.stack.push<U32>(returnAddr);
1454 
1455  // Push the current frame pointer to the stack
1456  this->m_runtime.stack.push<Fpy::StackSizeType>(this->m_runtime.stack.currentFrameStart);
1457 
1458  // Set the new frame pointer to the current top of stack
1459  this->m_runtime.stack.currentFrameStart = this->m_runtime.stack.size;
1460 
1462 }
1463 
1464 Signal FpySequencer::return_directiveHandler(const FpySequencer_ReturnDirective& directive, DirectiveError& error) {
1465  Fpy::StackSizeType returnValSize = directive.get_returnValSize();
1466  Fpy::StackSizeType callArgsSize = directive.get_callArgsSize();
1467 
1468  // Check we have enough bytes for the return value
1469  if (this->m_runtime.stack.size < returnValSize) {
1472  }
1473 
1474  // returnValSize is guaranteed to be less than Fpy::MAX_STACK_SIZE because it's less than the stack.size
1475  // thus the memcpy won't fail
1476 
1477  // Save the return value if there is one
1478  U8 returnValue[Fpy::MAX_STACK_SIZE] = {};
1479  if (returnValSize > 0) {
1480  memcpy(returnValue, this->m_runtime.stack.top() - returnValSize, returnValSize);
1481  }
1482 
1483  // Truncate the stack to stack_frame_start (discard all local variables)
1484  if (this->m_runtime.stack.currentFrameStart > this->m_runtime.stack.size) {
1487  }
1488  this->m_runtime.stack.size = this->m_runtime.stack.currentFrameStart;
1489 
1490  // Check we have enough bytes for saved frame pointer and return address
1491  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) + sizeof(U32)) {
1494  }
1495 
1496  // Pop the saved frame pointer
1497  Fpy::StackSizeType savedFramePtr = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1498 
1499  // Pop the return address
1500  U32 returnAddr = this->m_runtime.stack.pop<U32>();
1501 
1502  // Restore the frame pointer
1503  if (savedFramePtr > this->m_runtime.stack.size) {
1506  }
1507  this->m_runtime.stack.currentFrameStart = savedFramePtr;
1508 
1509  // Validate the return address is within bounds
1510  if (returnAddr > m_sequenceObj.get_header().get_statementCount()) {
1513  }
1514 
1515  // Set the next instruction to the return address
1516  this->m_runtime.nextStatementIndex = returnAddr;
1517 
1518  // Check that we have enough bytes for the call arguments
1519  if (this->m_runtime.stack.size < callArgsSize) {
1522  }
1523  // Discard the function arguments
1524  this->m_runtime.stack.size -= callArgsSize;
1525 
1526  // Push the return value
1527  if (returnValSize > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1530  }
1531  this->m_runtime.stack.push(returnValue, returnValSize);
1532 
1534 }
1535 
1536 Signal FpySequencer::loadAbs_directiveHandler(const FpySequencer_LoadAbsDirective& directive, DirectiveError& error) {
1537  return this->loadHelper(directive.get_globalOffset(), directive.get_size(), error);
1538 }
1539 
1540 Signal FpySequencer::storeAbs_directiveHandler(const FpySequencer_StoreAbsDirective& directive, DirectiveError& error) {
1541  Fpy::StackSizeType size = directive.get_size();
1542 
1543  // Need enough bytes for the value and the offset
1544  // Overflow-safe: check stack.size >= sizeof(StackSizeType) first, then stack.size - sizeof >= size
1545  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) ||
1546  this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < size) {
1549  }
1550 
1551  // Pop the global offset from the stack
1552  Fpy::StackSizeType globalOffset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1553 
1554  return this->storeHelper(globalOffset, size, error);
1555 }
1556 
1557 Signal FpySequencer::storeAbsConstOffset_directiveHandler(const FpySequencer_StoreAbsConstOffsetDirective& directive,
1558  DirectiveError& error) {
1559  return this->storeHelper(directive.get_globalOffset(), directive.get_size(), error);
1560 }
1561 
1565  this->sendSignal(this->popEvent_directiveHandler(directive, error));
1566  handleDirectiveErrorCode(Fpy::DirectiveId::POP_EVENT, error);
1567 }
1568 
1569 Signal FpySequencer::popEvent_directiveHandler(const FpySequencer_PopEventDirective& directive, DirectiveError& error) {
1570  // Pop messageSize from the stack
1571  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
1574  }
1575  Fpy::StackSizeType messageSize = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1576 
1577  // Need message_size bytes + sizeof(LogSeverity serial type) for severity
1578  if (this->m_runtime.stack.size < messageSize + sizeof(Fw::LogSeverity::SerialType)) {
1581  }
1582 
1583  // Pop message bytes first
1584  U8 messageBuf[FW_LOG_STRING_MAX_SIZE] = {};
1585  // don't read in more than (log string size) - 1 bytes
1586  Fpy::StackSizeType clampedSize = std::min(messageSize, static_cast<Fpy::StackSizeType>(FW_LOG_STRING_MAX_SIZE - 1));
1587  // If message is larger than buffer, discard the excess bytes first (from top of stack, which is the end of the
1588  // message)
1589  if (messageSize > clampedSize) {
1590  Fpy::StackSizeType excess = messageSize - clampedSize;
1591  this->m_runtime.stack.size -= excess;
1592  }
1593  this->m_runtime.stack.pop(messageBuf, clampedSize);
1594  messageBuf[clampedSize] = '\0';
1595 
1596  // Pop severity
1597  Fw::LogSeverity::SerialType severity = this->m_runtime.stack.pop<Fw::LogSeverity::SerialType>();
1598 
1599  // Construct the message string
1600  Fw::String messageStr(reinterpret_cast<const char*>(messageBuf));
1601 
1602  // Emit the appropriate event based on severity
1603  switch (severity) {
1605  this->log_FATAL_LogFatal(this->m_sequenceFilePath, messageStr);
1606  break;
1608  this->log_WARNING_HI_LogWarningHi(this->m_sequenceFilePath, messageStr);
1609  break;
1611  this->log_WARNING_LO_LogWarningLo(this->m_sequenceFilePath, messageStr);
1612  break;
1614  this->log_COMMAND_LogCommand(this->m_sequenceFilePath, messageStr);
1615  break;
1617  this->log_ACTIVITY_HI_LogActivityHi(this->m_sequenceFilePath, messageStr);
1618  break;
1620  this->log_ACTIVITY_LO_LogActivityLo(this->m_sequenceFilePath, messageStr);
1621  break;
1623  this->log_DIAGNOSTIC_LogDiagnostic(this->m_sequenceFilePath, messageStr);
1624  break;
1625  default:
1628  }
1629 
1631 }
1632 
1633 } // namespace Svc
void directive_storeRelConstOffset_internalInterfaceHandler(const Svc::FpySequencer_StoreRelConstOffsetDirective &directive) override
Internal interface handler for directive_storeRelConstOffset.
void cmdOut_out(FwIndexType portNum, Fw::ComBuffer &data, U32 context) const
Invoke output port cmdOut.
void directive_storeRel_internalInterfaceHandler(const Svc::FpySequencer_StoreRelDirective &directive) override
Internal interface handler for directive_storeRel.
Serialization/Deserialization operation was successful.
void directive_return_internalInterfaceHandler(const Svc::FpySequencer_ReturnDirective &directive) override
Internal interface handler for directive_return.
sets the index of the next directive to execute
U8 * getBuffAddr()
Get buffer address for data filling (non-const version)
Definition: PrmBuffer.cpp:42
void directive_call_internalInterfaceHandler(const Svc::FpySequencer_CallDirective &directive) override
Internal interface handler for directive_call.
bool isConnected_getTlmChan_OutputPort(FwIndexType portNum) const
FwIdType FwOpcodeType
The type of a command opcode.
SerializeStatus serializeFrom(U8 val, Endianness mode=Endianness::BIG) override
Serialize an 8-bit unsigned integer value.
U16 get_statementCount() const
Get member statementCount.
branches based off of the top byte of the stack
void directive_if_internalInterfaceHandler(const Svc::FpySequencer_IfDirective &directive) override
Internal interface handler for directive_if.
Representing success.
void directive_setSeed_internalInterfaceHandler(const Svc::FpySequencer_SetSeedDirective &directive) override
Internal interface handler for directive_setSeed.
PlatformSizeType FwSizeType
stores a value to an absolute address in the stack (for global variables), offset from stack ...
void directive_loadAbs_internalInterfaceHandler(const Svc::FpySequencer_LoadAbsDirective &directive) override
Internal interface handler for directive_loadAbs.
void directive_stackOp_internalInterfaceHandler(const Svc::FpySequencer_StackOpDirective &directive) override
Internal interface handler for directive_stackOp.
called when statement successfully executed. only raised in the RUNNING.AWAITING_CMD_RESPONSE state ...
void directive_getField_internalInterfaceHandler(const Svc::FpySequencer_GetFieldDirective &directive) override
Internal interface handler for directive_getField.
I32 SignedStackSizeType
signed version of StackSizeType, used for relative offsets that can be negative
Serializable::SizeType getSize() const override
Get current buffer size.
void log_WARNING_HI_SequenceExitedWithError(const Fw::StringBase &filePath, U8 errorCode) const
Log event SequenceExitedWithError.
void directive_pushTlmValAndTime_internalInterfaceHandler(const Svc::FpySequencer_PushTlmValAndTimeDirective &directive) override
Internal interface handler for directive_pushTlmValAndTime.
int8_t I8
8-bit signed integer
Definition: BasicTypes.h:50
stores a value to a local variable at a compile-time-known offset relative to the current stack frame...
void directive_constCmd_internalInterfaceHandler(const Svc::FpySequencer_ConstCmdDirective &directive) override
Internal interface handler for directive_constCmd.
Fw::ParamValid getParam_out(FwIndexType portNum, FwPrmIdType id, Fw::ParamBuffer &val) const
Invoke output port getParam.
bool isConnected_prmGet_OutputPort(FwIndexType portNum) const
pop an opcode and arg buf off the stack, send to cmd dispatcher and await response ...
void directive_peek_internalInterfaceHandler(const Svc::FpySequencer_PeekDirective &directive) override
Internal interface handler for directive_peek.
TimeBase getTimeBase() const
Definition: Time.cpp:126
void sequencer_sendSignal_stmtResponse_success()
Send signal stmtResponse_success to state machine sequencer.
void directive_loadRel_internalInterfaceHandler(const Svc::FpySequencer_LoadRelDirective &directive) override
Internal interface handler for directive_loadRel.
void directive_memCmp_internalInterfaceHandler(const Svc::FpySequencer_MemCmpDirective &directive) override
Internal interface handler for directive_memCmp.
void directive_pushVal_internalInterfaceHandler(const Svc::FpySequencer_PushValDirective &directive) override
Internal interface handler for directive_pushVal.
void directive_pushPrm_internalInterfaceHandler(const Svc::FpySequencer_PushPrmDirective &directive) override
Internal interface handler for directive_pushPrm.
pops bytes off the top of the stack and does nothing with them
void directive_pushTime_internalInterfaceHandler(const Svc::FpySequencer_PushTimeDirective &directive) override
Internal interface handler for directive_pushTime.
void log_DIAGNOSTIC_LogDiagnostic(const Fw::StringBase &filePath, const Fw::StringBase &message) const
Log event LogDiagnostic.
void log_COMMAND_LogCommand(const Fw::StringBase &filePath, const Fw::StringBase &message) const
Log event LogCommand.
U8 FwTimeContextStoreType
The type used to serialize a time context value.
Software diagnostic events.
void directive_pushRand_internalInterfaceHandler(const Svc::FpySequencer_PushRandDirective &directive) override
Internal interface handler for directive_pushRand.
void log_ACTIVITY_LO_LogActivityLo(const Fw::StringBase &filePath, const Fw::StringBase &message) const
Log event LogActivityLo.
SerializeStatus
forward declaration for string
float F32
32-bit floating point
Definition: BasicTypes.h:83
Fpy::DirectiveErrorCode DirectiveError
void directive_allocate_internalInterfaceHandler(const Svc::FpySequencer_AllocateDirective &directive) override
Internal interface handler for directive_allocate.
void directive_waitRel_internalInterfaceHandler(const FpySequencer_WaitRelDirective &directive) override
Internal interface handler for directive_waitRel.
void directive_noOp_internalInterfaceHandler(const Svc::FpySequencer_NoOpDirective &directive) override
Internal interface handler for directive_noOp.
loads a value from a local variable at a compile-time-known offset relative to the current stack fram...
void directive_stackCmd_internalInterfaceHandler(const Svc::FpySequencer_StackCmdDirective &directive) override
Internal interface handler for directive_stackCmd.
void log_ACTIVITY_HI_LogActivityHi(const Fw::StringBase &filePath, const Fw::StringBase &message) const
Log event LogActivityHi.
void directive_discard_internalInterfaceHandler(const Svc::FpySequencer_DiscardDirective &directive) override
Internal interface handler for directive_discard.
pop two byte arrays off the top of the stack, call memcmp, push 1 if they were equal, 0 otherwise
Svc::Fpy::Header & get_header()
Get member header.
Less important informational events.
pops a severity and message from the stack and emits an F Prime event
An activity related to commanding.
A less serious but recoverable event.
Omit length from serialization.
void directive_exit_internalInterfaceHandler(const Svc::FpySequencer_ExitDirective &directive) override
Internal interface handler for directive_exit.
stores a value to an absolute address in the stack (for global variables), const offset ...
External serialize buffer with no copy semantics.
U32 getSeconds() const
Definition: Time.cpp:118
Svc::Fpy::DirectiveId::T get__op() const
Get member _op.
void directive_waitAbs_internalInterfaceHandler(const FpySequencer_WaitAbsDirective &directive) override
Internal interface handler for directive_waitAbs.
U8 * getBuffAddr()
Get buffer address for data filling (non-const version)
Definition: TlmBuffer.cpp:42
void log_WARNING_HI_LogWarningHi(const Fw::StringBase &filePath, const Fw::StringBase &message) const
Log event LogWarningHi.
void directive_goto_internalInterfaceHandler(const Svc::FpySequencer_GotoDirective &directive) override
Internal interface handler for directive_goto.
peeks at N bytes from the stack, starting from an offset relative to the top of the stack ...
void sequencer_sendSignal_stmtResponse_failure()
Send signal stmtResponse_failure to state machine sequencer.
A serious but recoverable event.
void sequencer_sendSignal_stmtResponse_beginSleep()
Send signal stmtResponse_beginSleep to state machine sequencer.
Representing failure.
void directive_storeAbsConstOffset_internalInterfaceHandler(const Svc::FpySequencer_StoreAbsConstOffsetDirective &directive) override
Internal interface handler for directive_storeAbsConstOffset.
sleeps for a relative duration from the current time
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:53
void directive_popEvent_internalInterfaceHandler(const Svc::FpySequencer_PopEventDirective &directive) override
Internal interface handler for directive_popEvent.
Important informational events.
static U32 min(const U32 a, const U32 b)
Definition: Checksum.cpp:16
static Time add(const Time &a, const Time &b)
Definition: Time.cpp:164
void sequencer_sendSignal_stmtResponse_keepWaiting()
Send signal stmtResponse_keepWaiting to state machine sequencer.
FwTimeContextStoreType getContext() const
Definition: Time.cpp:130
U32 getUSeconds() const
Definition: Time.cpp:122
called when the statement unsuccessfully executed. only raised in the RUNNING.AWAITING_CMD_RESPONSE s...
loads a value from an absolute address in the stack (for global variables)
pops a U32 from the stack and uses it to seed the RNG used by PushRandDirective
double F64
64-bit floating point (double). Required for compiler-supplied double promotion.
Definition: BasicTypes.h:85
A fatal non-recoverable event.
void log_WARNING_LO_LogWarningLo(const Fw::StringBase &filePath, const Fw::StringBase &message) const
Log event LogWarningLo.
stores a value to a local variable at a runtime-determined offset relative to the current stack frame...
RateGroupDivider component implementation.
Enum representing parameter validity.
void log_FATAL_LogFatal(const Fw::StringBase &filePath, const Fw::StringBase &message) const
Log event LogFatal.
void directive_storeAbs_internalInterfaceHandler(const Svc::FpySequencer_StoreAbsDirective &directive) override
Internal interface handler for directive_storeAbs.
U16 FwTimeBaseStoreType
The type used to serialize a time base value.
pushes the current Fw.Time struct to the stack
called when the statement is telling the sequencer to await a later stmt response ...
FpySequencer_SequencerStateMachineStateMachineBase::Signal Signal
Fw::TlmValid getTlmChan_out(FwIndexType portNum, FwChanIdType id, Fw::Time &timeTag, Fw::TlmBuffer &val) const
Invoke output port getTlmChan.
#define FW_ASSERT(...)
Definition: Assert.hpp:14
Success/Failure.
U8 SerialType
The serial representation type.
PlatformAssertArgType FwAssertArgType
The type of arguments to assert functions.
U32 StackSizeType
the type which everything referencing a size or offset on the stack is represented in ...
void directive_pushTlmVal_internalInterfaceHandler(const Svc::FpySequencer_PushTlmValDirective &directive) override
Internal interface handler for directive_pushTlmVal.
#define U64(C)
Definition: sha.h:181