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 <cmath>
2 #include <cstring>
3 #include <type_traits>
4 #include "Fw/Com/ComPacket.hpp"
6 
7 namespace Svc {
8 
9 void FpySequencer::sendSignal(Signal signal) {
10  switch (signal) {
13  break;
14  }
17  break;
18  }
21  break;
22  }
25  break;
26  }
27  default: {
28  FW_ASSERT(0, static_cast<FwAssertArgType>(signal));
29  }
30  }
31 }
32 
33 // utility method for updating telemetry based on a directive error code
34 void FpySequencer::handleDirectiveErrorCode(Fpy::DirectiveId id, DirectiveError err) {
35  this->m_tlm.lastDirectiveError = err;
36  if (err != DirectiveError::NO_ERROR) {
37  this->m_tlm.directiveErrorIndex = this->currentStatementIdx();
38  this->m_tlm.directiveErrorId = id;
39  }
40 }
41 
42 Fw::Success FpySequencer::sendCmd(FwOpcodeType opcode, const U8* argBuf, FwSizeType argBufSize) {
43  Fw::ComBuffer cmdBuf;
44  Fw::SerializeStatus stat =
45  cmdBuf.serializeFrom(static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_COMMAND));
46  // TODO should I assert here? this really shouldn't fail, I should just add a static assert
47  // on com buf size and then assert here
49  return Fw::Success::FAILURE;
50  }
51  stat = cmdBuf.serializeFrom(opcode);
53  return Fw::Success::FAILURE;
54  }
55  stat = cmdBuf.serializeFrom(argBuf, argBufSize, Fw::Serialization::OMIT_LENGTH);
57  return Fw::Success::FAILURE;
58  }
59 
60  // calculate the unique command identifier:
61  // cmd UID is formatted like XXYY, where XX are the first two bytes of the m_sequencesStarted counter
62  // and YY are the first two bytes of the m_statementsDispatched counter.
63  // this way, we know when we get a cmd back A) whether or not it's from this sequence (modulo 2^16) and B)
64  // whether or not it's this specific instance of the cmd in the sequence, and not another one with the same opcode
65  // somewhere else in the file.
66  // if we put this uid in the context we send to the cmdDisp, we will get it back when the cmd returns
67  U32 cmdUid =
68  static_cast<U32>(((this->m_sequencesStarted & 0xFFFF) << 16) | (this->m_statementsDispatched & 0xFFFF));
69 
70  this->cmdOut_out(0, cmdBuf, cmdUid);
71 
72  return Fw::Success::SUCCESS;
73 }
74 
78  this->sendSignal(this->waitRel_directiveHandler(directive, error));
79  handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_REL, error);
80 }
81 
85  this->sendSignal(this->waitAbs_directiveHandler(directive, error));
86  handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_ABS, error);
87 }
88 
92  this->sendSignal(this->goto_directiveHandler(directive, error));
93  handleDirectiveErrorCode(Fpy::DirectiveId::GOTO, error);
94 }
95 
99  this->sendSignal(this->if_directiveHandler(directive, error));
100  handleDirectiveErrorCode(Fpy::DirectiveId::IF, error);
101 }
102 
106  this->sendSignal(this->noOp_directiveHandler(directive, error));
107  handleDirectiveErrorCode(Fpy::DirectiveId::NO_OP, error);
108 }
109 
112  const Svc::FpySequencer_PushTlmValDirective& directive) {
114  this->sendSignal(this->pushTlmVal_directiveHandler(directive, error));
115  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL, error);
116 }
117 
122  this->sendSignal(this->pushTlmValAndTime_directiveHandler(directive, error));
123  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL_AND_TIME, error);
124 }
125 
129  this->sendSignal(this->pushPrm_directiveHandler(directive, error));
130  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_PRM, error);
131 }
132 
136  this->sendSignal(this->constCmd_directiveHandler(directive, error));
137  handleDirectiveErrorCode(Fpy::DirectiveId::CONST_CMD, error);
138 }
139 
143  this->sendSignal(this->stackOp_directiveHandler(directive, error));
144  handleDirectiveErrorCode(directive.get__op(), error);
145 }
146 
150  this->sendSignal(this->exit_directiveHandler(directive, error));
151  handleDirectiveErrorCode(Fpy::DirectiveId::EXIT, error);
152 }
153 
157  this->sendSignal(this->allocate_directiveHandler(directive, error));
158  handleDirectiveErrorCode(Fpy::DirectiveId::ALLOCATE, error);
159 }
160 
165  this->sendSignal(this->storeRelConstOffset_directiveHandler(directive, error));
166  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL_CONST_OFFSET, error);
167 }
168 
172  this->sendSignal(this->pushVal_directiveHandler(directive, error));
173  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_VAL, error);
174 }
175 
179  this->sendSignal(this->loadRel_directiveHandler(directive, error));
180  handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_REL, error);
181 }
182 
186  this->sendSignal(this->discard_directiveHandler(directive, error));
187  handleDirectiveErrorCode(Fpy::DirectiveId::DISCARD, error);
188 }
189 
193  this->sendSignal(this->memCmp_directiveHandler(directive, error));
194  handleDirectiveErrorCode(Fpy::DirectiveId::MEMCMP, error);
195 }
196 
200  this->sendSignal(this->stackCmd_directiveHandler(directive, error));
201  handleDirectiveErrorCode(Fpy::DirectiveId::STACK_CMD, error);
202 }
203 
207  this->sendSignal(this->pushTime_directiveHandler(directive, error));
208  handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TIME, error);
209 }
210 
214  this->sendSignal(this->setFlag_directiveHandler(directive, error));
215  handleDirectiveErrorCode(Fpy::DirectiveId::SET_FLAG, error);
216 }
217 
221  this->sendSignal(this->getFlag_directiveHandler(directive, error));
222  handleDirectiveErrorCode(Fpy::DirectiveId::GET_FLAG, error);
223 }
224 
228  this->sendSignal(this->getField_directiveHandler(directive, error));
229  handleDirectiveErrorCode(Fpy::DirectiveId::GET_FIELD, error);
230 }
231 
235  this->sendSignal(this->peek_directiveHandler(directive, error));
236  handleDirectiveErrorCode(Fpy::DirectiveId::PEEK, error);
237 }
238 
242  this->sendSignal(this->storeRel_directiveHandler(directive, error));
243  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL, error);
244 }
245 
249  this->sendSignal(this->call_directiveHandler(directive, error));
250  handleDirectiveErrorCode(Fpy::DirectiveId::CALL, error);
251 }
252 
256  this->sendSignal(this->return_directiveHandler(directive, error));
257  handleDirectiveErrorCode(Fpy::DirectiveId::RETURN, error);
258 }
259 
263  this->sendSignal(this->loadAbs_directiveHandler(directive, error));
264  handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_ABS, error);
265 }
266 
270  this->sendSignal(this->storeAbs_directiveHandler(directive, error));
271  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS, error);
272 }
273 
278  this->sendSignal(this->storeAbsConstOffset_directiveHandler(directive, error));
279  handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS_CONST_OFFSET, error);
280 }
281 
283 Signal FpySequencer::waitRel_directiveHandler(const FpySequencer_WaitRelDirective& directive, DirectiveError& error) {
284  if (this->m_runtime.stack.size < 8) {
287  }
288 
289  Fw::Time wakeupTime = this->getTime();
290 
291  U32 uSeconds = this->m_runtime.stack.pop<U32>();
292  U32 seconds = this->m_runtime.stack.pop<U32>();
293 
294  wakeupTime.add(seconds, uSeconds);
295  this->m_runtime.wakeupTime = wakeupTime;
297 }
298 
300 Signal FpySequencer::waitAbs_directiveHandler(const FpySequencer_WaitAbsDirective& directive, DirectiveError& error) {
301  if (this->m_runtime.stack.size < 10 + sizeof(FwTimeContextStoreType)) {
304  }
305 
306  U32 uSeconds = this->m_runtime.stack.pop<U32>();
307  U32 seconds = this->m_runtime.stack.pop<U32>();
308  FwTimeContextStoreType ctx = this->m_runtime.stack.pop<FwTimeContextStoreType>();
309  U16 base = this->m_runtime.stack.pop<U16>();
310 
311  this->m_runtime.wakeupTime = Fw::Time(static_cast<TimeBase::T>(base), ctx, seconds, uSeconds);
313 }
314 
316 Signal FpySequencer::goto_directiveHandler(const FpySequencer_GotoDirective& directive, DirectiveError& error) {
317  // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
318  if (directive.get_statementIndex() > m_sequenceObj.get_header().get_statementCount()) {
321  }
322  m_runtime.nextStatementIndex = directive.get_statementIndex();
324 }
325 
327 Signal FpySequencer::if_directiveHandler(const FpySequencer_IfDirective& directive, DirectiveError& error) {
328  if (this->m_runtime.stack.size < 1) {
331  }
332  // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
333  if (directive.get_falseGotoStmtIndex() > m_sequenceObj.get_header().get_statementCount()) {
336  }
337 
338  if (this->m_runtime.stack.pop<U8>() != 0) {
339  // proceed to next instruction
341  }
342 
343  // conditional false case
344  this->m_runtime.nextStatementIndex = directive.get_falseGotoStmtIndex();
346 }
347 
348 Signal FpySequencer::noOp_directiveHandler(const FpySequencer_NoOpDirective& directive, DirectiveError& error) {
350 }
351 
352 Signal FpySequencer::pushTlmVal_directiveHandler(const FpySequencer_PushTlmValDirective& directive,
353  DirectiveError& error) {
354  if (!this->isConnected_getTlmChan_OutputPort(0)) {
357  }
358  Fw::Time tlmTime;
359  Fw::TlmBuffer tlmValue;
360  Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
361 
362  if (valid != Fw::TlmValid::VALID) {
363  // could not find this tlm chan
366  }
367 
368  if (Fpy::MAX_STACK_SIZE - tlmValue.getSize() < this->m_runtime.stack.size) {
371  }
372  this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
374 }
375 
376 Signal FpySequencer::pushTlmValAndTime_directiveHandler(const FpySequencer_PushTlmValAndTimeDirective& directive,
377  DirectiveError& error) {
378  if (!this->isConnected_getTlmChan_OutputPort(0)) {
381  }
382 
383  Fw::Time tlmTime;
384  Fw::TlmBuffer tlmValue;
385  Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
386 
387  if (valid != Fw::TlmValid::VALID) {
388  // could not find this tlm chan
391  }
392 
393  U8 tlmTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
395  Fw::SerializeStatus stat = timeEsb.serializeFrom(tlmTime);
396 
397  // coding error if this failed, we should have enough space
398  FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
399 
400  // check that our stack won't overflow if we put both val and time on it
401  if (Fpy::MAX_STACK_SIZE - tlmValue.getSize() - timeEsb.getSize() < this->m_runtime.stack.size) {
404  }
405 
406  // push tlm to end of stack
407  this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
408  // now push time to end of stack
409  this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
411 }
412 
413 Signal FpySequencer::pushPrm_directiveHandler(const FpySequencer_PushPrmDirective& directive, DirectiveError& error) {
414  if (!this->isConnected_prmGet_OutputPort(0)) {
417  }
418 
419  Fw::ParamBuffer prmValue;
420  Fw::ParamValid valid = this->getParam_out(0, directive.get_prmId(), prmValue);
421 
422  if (valid != Fw::ParamValid::VALID) {
423  // could not find this prm in the DB
426  }
427 
428  if (Fpy::MAX_STACK_SIZE - prmValue.getSize() < this->m_runtime.stack.size) {
431  }
432 
433  this->m_runtime.stack.push(prmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(prmValue.getSize()));
435 }
436 
437 Signal FpySequencer::constCmd_directiveHandler(const FpySequencer_ConstCmdDirective& directive, DirectiveError& error) {
438  if (this->sendCmd(directive.get_opCode(), directive.get_argBuf(), directive.get__argBufSize()) ==
441  } else {
442  // now tell the SM to wait some more until we get the cmd response back
443  // if we've already got the response back this should be harmless
445  }
446 }
447 
448 DirectiveError FpySequencer::op_or() {
449  if (this->m_runtime.stack.size < sizeof(U8) * 2) {
451  }
452  this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() | this->m_runtime.stack.pop<U8>()));
454 }
455 DirectiveError FpySequencer::op_and() {
456  if (this->m_runtime.stack.size < sizeof(U8) * 2) {
458  }
459  this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() & this->m_runtime.stack.pop<U8>()));
461 }
462 DirectiveError FpySequencer::op_ieq() {
463  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
465  }
466  this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() == this->m_runtime.stack.pop<I64>())
467  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
468  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
470 }
471 DirectiveError FpySequencer::op_ine() {
472  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
474  }
475  this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() != this->m_runtime.stack.pop<I64>())
476  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
477  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
479 }
480 DirectiveError FpySequencer::op_ult() {
481  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
483  }
484  U64 rhs = this->m_runtime.stack.pop<U64>();
485  U64 lhs = this->m_runtime.stack.pop<U64>();
486  this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
487  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
489 }
490 DirectiveError FpySequencer::op_ule() {
491  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
493  }
494  U64 rhs = this->m_runtime.stack.pop<U64>();
495  U64 lhs = this->m_runtime.stack.pop<U64>();
496  this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
497  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
499 }
500 DirectiveError FpySequencer::op_ugt() {
501  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
503  }
504  U64 rhs = this->m_runtime.stack.pop<U64>();
505  U64 lhs = this->m_runtime.stack.pop<U64>();
506  this->m_runtime.stack.push(static_cast<U8>((lhs > rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
507  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
509 }
510 DirectiveError FpySequencer::op_uge() {
511  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
513  }
514  U64 rhs = this->m_runtime.stack.pop<U64>();
515  U64 lhs = this->m_runtime.stack.pop<U64>();
516  this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
517  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
519 }
520 DirectiveError FpySequencer::op_slt() {
521  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
523  }
524  I64 rhs = this->m_runtime.stack.pop<I64>();
525  I64 lhs = this->m_runtime.stack.pop<I64>();
526  this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
527  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
529 }
530 DirectiveError FpySequencer::op_sle() {
531  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
533  }
534  I64 rhs = this->m_runtime.stack.pop<I64>();
535  I64 lhs = this->m_runtime.stack.pop<I64>();
536  this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
537  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
539 }
540 DirectiveError FpySequencer::op_sgt() {
541  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
543  }
544  I64 rhs = this->m_runtime.stack.pop<I64>();
545  I64 lhs = this->m_runtime.stack.pop<I64>();
546  this->m_runtime.stack.push(static_cast<U8>((lhs > rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
547  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
549 }
550 DirectiveError FpySequencer::op_sge() {
551  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
553  }
554  I64 rhs = this->m_runtime.stack.pop<I64>();
555  I64 lhs = this->m_runtime.stack.pop<I64>();
556  this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
557  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
559 }
560 DirectiveError FpySequencer::op_feq() {
561  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
563  }
564  F64 rhs = this->m_runtime.stack.pop<F64>();
565  F64 lhs = this->m_runtime.stack.pop<F64>();
566  // eq is true if they are equal and neither is nan
567  this->m_runtime.stack.push(static_cast<U8>((lhs == rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
568  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
570 }
571 DirectiveError FpySequencer::op_fne() {
572  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
574  }
575  F64 rhs = this->m_runtime.stack.pop<F64>();
576  F64 lhs = this->m_runtime.stack.pop<F64>();
577  // ne is true if they are not equal or either is nan
578  this->m_runtime.stack.push(static_cast<U8>((lhs != rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
579  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
581 }
582 DirectiveError FpySequencer::op_flt() {
583  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
585  }
586  F64 rhs = this->m_runtime.stack.pop<F64>();
587  F64 lhs = this->m_runtime.stack.pop<F64>();
588  this->m_runtime.stack.push(static_cast<U8>(std::isless(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
589  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
591 }
592 DirectiveError FpySequencer::op_fle() {
593  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
595  }
596  F64 rhs = this->m_runtime.stack.pop<F64>();
597  F64 lhs = this->m_runtime.stack.pop<F64>();
598  this->m_runtime.stack.push(static_cast<U8>(std::islessequal(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
599  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
601 }
602 DirectiveError FpySequencer::op_fgt() {
603  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
605  }
606  F64 rhs = this->m_runtime.stack.pop<F64>();
607  F64 lhs = this->m_runtime.stack.pop<F64>();
608  this->m_runtime.stack.push(static_cast<U8>(std::isgreater(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
609  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
611 }
612 DirectiveError FpySequencer::op_fge() {
613  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
615  }
616  F64 rhs = this->m_runtime.stack.pop<F64>();
617  F64 lhs = this->m_runtime.stack.pop<F64>();
618  this->m_runtime.stack.push(static_cast<U8>(std::isgreaterequal(lhs, rhs)
619  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
620  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
622 }
623 DirectiveError FpySequencer::op_not() {
624  if (this->m_runtime.stack.size < sizeof(U8)) {
626  }
627  this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<U8>() == 0)
628  ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
629  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
631 }
632 DirectiveError FpySequencer::op_fpext() {
633  // convert F32 to F64
634  if (this->m_runtime.stack.size < sizeof(F32)) {
636  }
637  this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<F32>()));
639 }
640 DirectiveError FpySequencer::op_fptrunc() {
641  // convert F64 to F32
642  if (this->m_runtime.stack.size < sizeof(F64)) {
644  }
645  this->m_runtime.stack.push(static_cast<F32>(this->m_runtime.stack.pop<F64>()));
647 }
648 DirectiveError FpySequencer::op_fptosi() {
649  if (this->m_runtime.stack.size < sizeof(F64)) {
651  }
652  this->m_runtime.stack.push(static_cast<I64>(this->m_runtime.stack.pop<F64>()));
654 }
655 DirectiveError FpySequencer::op_sitofp() {
656  if (this->m_runtime.stack.size < sizeof(I64)) {
658  }
659  this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<I64>()));
661 }
662 DirectiveError FpySequencer::op_fptoui() {
663  if (this->m_runtime.stack.size < sizeof(F64)) {
665  }
666  this->m_runtime.stack.push(static_cast<U64>(this->m_runtime.stack.pop<F64>()));
668 }
669 DirectiveError FpySequencer::op_uitofp() {
670  if (this->m_runtime.stack.size < sizeof(U64)) {
672  }
673  this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<U64>()));
675 }
676 DirectiveError FpySequencer::op_add() {
677  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
679  }
680  I64 rhs = this->m_runtime.stack.pop<I64>();
681  I64 lhs = this->m_runtime.stack.pop<I64>();
682  // Check for overflow and underflow and return the appropriate error code
683  // Overflow can only occur with both operands positive and occurs when one operand is greater than the maximum value
684  // less the other operand. If either operand is negative or zero, overflow cannot occur.
685  if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() - rhs) < lhs)) {
687  }
688  // Underflow can only occur with both operands negative and occurs when one operand is less than the minimum value
689  // minus the other operand. If either operand is positive or zero, underflow cannot occur.
690  else if ((rhs < 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() - rhs) > lhs)) {
692  }
693  this->m_runtime.stack.push(static_cast<I64>(lhs + rhs));
695 }
696 DirectiveError FpySequencer::op_sub() {
697  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
699  }
700  I64 rhs = this->m_runtime.stack.pop<I64>();
701  I64 lhs = this->m_runtime.stack.pop<I64>();
702  // Check for overflow and underflow and return the appropriate error code
703  // Overflow can only occur when the left operand is positive and the right operand is negative. It occurs when the
704  // left (positive) operand is greater than the maximum value plus the other (negative) operand. If the right
705  // operand is positive or zero, overflow cannot occur.
706  if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() + rhs) < lhs)) {
708  }
709  // Underflow can only occur when the left operand is negative and the right operand is positive. It occurs when the
710  // left (negative) operand is less than the minimum value plus the other (positive) operand. If the right operand
711  // is negative or zero, underflow cannot occur.
712  else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() + rhs) > lhs)) {
714  }
715  this->m_runtime.stack.push(static_cast<I64>(lhs - rhs));
717 }
718 DirectiveError FpySequencer::op_mul() {
719  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
721  }
722  I64 rhs = this->m_runtime.stack.pop<I64>();
723  I64 lhs = this->m_runtime.stack.pop<I64>();
724  // Check for overflow and underflow and return the appropriate error code
725  // Overflow can only occur with operands of matching signs and occurs when one operand is greater (or less) than the
726  // maximum value divided by the other operand. Either operand being zero precludes overflow.
727  // Check the both positive case.
728  if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() / rhs) < lhs)) {
730  }
731  // Check the both negative case
732  else if ((rhs < 0) && (lhs < 0) && ((std::numeric_limits<I64>::max() / (-1 * rhs)) < (-1 * lhs))) {
734  }
735  // Underflow can occur with operands of differing signs and occurs when one operand is less than the minimum value
736  // divided by the other operand. Either operand being zero precludes underflow.
737  // Check the case where lhs is positive.
738  else if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::min() / lhs) > rhs)) {
740  }
741  // Check the case where rhs is positive.
742  else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() / rhs) > lhs)) {
744  }
745  this->m_runtime.stack.push(static_cast<I64>(lhs * rhs));
747 }
748 DirectiveError FpySequencer::op_udiv() {
749  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
751  }
752  U64 rhs = this->m_runtime.stack.pop<U64>();
753  U64 lhs = this->m_runtime.stack.pop<U64>();
754  // Prevent division by zero
755  if (rhs == 0) {
757  }
758  this->m_runtime.stack.push(static_cast<U64>(lhs / rhs));
760 }
761 DirectiveError FpySequencer::op_sdiv() {
762  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
764  }
765 
766  I64 rhs = this->m_runtime.stack.pop<I64>();
767  I64 lhs = this->m_runtime.stack.pop<I64>();
768  // Prevent division by zero
769  if (rhs == 0) {
771  }
772  this->m_runtime.stack.push(static_cast<I64>(lhs / rhs));
774 }
775 DirectiveError FpySequencer::op_umod() {
776  if (this->m_runtime.stack.size < sizeof(U64) * 2) {
778  }
779  U64 rhs = this->m_runtime.stack.pop<U64>();
780  if (rhs == 0) {
782  }
783  U64 lhs = this->m_runtime.stack.pop<U64>();
784  this->m_runtime.stack.push(static_cast<U64>(lhs % rhs));
786 }
787 DirectiveError FpySequencer::op_smod() {
788  if (this->m_runtime.stack.size < sizeof(I64) * 2) {
790  }
791  I64 rhs = this->m_runtime.stack.pop<I64>();
792  if (rhs == 0) {
794  }
795  I64 lhs = this->m_runtime.stack.pop<I64>();
796  I64 res = static_cast<I64>(lhs % rhs);
797  // in order to match Python's behavior,
798  // if the signs of the remainder and divisor differ, adjust the result.
799  // this happens when the result should be positive but is negative, or vice-versa.
800  // credit Gemini 2.5 pro
801  if ((res > 0 && rhs < 0) || (res < 0 && rhs > 0)) {
802  res += rhs;
803  }
804  this->m_runtime.stack.push(res);
806 }
807 DirectiveError FpySequencer::op_fadd() {
808  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
810  }
811  F64 rhs = this->m_runtime.stack.pop<F64>();
812  F64 lhs = this->m_runtime.stack.pop<F64>();
813  this->m_runtime.stack.push(static_cast<F64>(lhs + rhs));
815 }
816 DirectiveError FpySequencer::op_fsub() {
817  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
819  }
820  F64 rhs = this->m_runtime.stack.pop<F64>();
821  F64 lhs = this->m_runtime.stack.pop<F64>();
822  this->m_runtime.stack.push(static_cast<F64>(lhs - rhs));
824 }
825 DirectiveError FpySequencer::op_fmul() {
826  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
828  }
829  F64 rhs = this->m_runtime.stack.pop<F64>();
830  F64 lhs = this->m_runtime.stack.pop<F64>();
831  this->m_runtime.stack.push(static_cast<F64>(lhs * rhs));
833 }
834 DirectiveError FpySequencer::op_fdiv() {
835  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
837  }
838  F64 rhs = this->m_runtime.stack.pop<F64>();
839  F64 lhs = this->m_runtime.stack.pop<F64>();
840  this->m_runtime.stack.push(static_cast<F64>(lhs / rhs));
842 }
843 DirectiveError FpySequencer::op_fpow() {
844  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
846  }
847  F64 rhs = this->m_runtime.stack.pop<F64>();
848  F64 lhs = this->m_runtime.stack.pop<F64>();
849  this->m_runtime.stack.push(static_cast<F64>(pow(lhs, rhs)));
851 }
852 DirectiveError FpySequencer::op_flog() {
853  if (this->m_runtime.stack.size < sizeof(F64)) {
855  }
856  F64 val = this->m_runtime.stack.pop<F64>();
857  if (val <= 0.0) {
859  }
860  this->m_runtime.stack.push(static_cast<F64>(log(val)));
862 }
863 DirectiveError FpySequencer::op_fmod() {
864  if (this->m_runtime.stack.size < sizeof(F64) * 2) {
866  }
867  F64 rhs = this->m_runtime.stack.pop<F64>();
868  if (rhs == 0.0) {
870  }
871  F64 lhs = this->m_runtime.stack.pop<F64>();
872  this->m_runtime.stack.push(static_cast<F64>(lhs - rhs * std::floor(lhs / rhs)));
874 }
875 DirectiveError FpySequencer::op_siext_8_64() {
876  if (this->m_runtime.stack.size < sizeof(I8)) {
878  }
879  I8 src = this->m_runtime.stack.pop<I8>();
880  this->m_runtime.stack.push(static_cast<I64>(src));
882 }
883 DirectiveError FpySequencer::op_siext_16_64() {
884  if (this->m_runtime.stack.size < sizeof(I16)) {
886  }
887  I16 src = this->m_runtime.stack.pop<I16>();
888  this->m_runtime.stack.push(static_cast<I64>(src));
890 }
891 DirectiveError FpySequencer::op_siext_32_64() {
892  if (this->m_runtime.stack.size < sizeof(I32)) {
894  }
895  I32 src = this->m_runtime.stack.pop<I32>();
896  this->m_runtime.stack.push(static_cast<I64>(src));
898 }
899 DirectiveError FpySequencer::op_ziext_8_64() {
900  if (this->m_runtime.stack.size < sizeof(U8)) {
902  }
903  U8 src = this->m_runtime.stack.pop<U8>();
904  this->m_runtime.stack.push(static_cast<U64>(src));
906 }
907 DirectiveError FpySequencer::op_ziext_16_64() {
908  if (this->m_runtime.stack.size < sizeof(U16)) {
910  }
911  U16 src = this->m_runtime.stack.pop<U16>();
912  this->m_runtime.stack.push(static_cast<U64>(src));
914 }
915 DirectiveError FpySequencer::op_ziext_32_64() {
916  if (this->m_runtime.stack.size < sizeof(U32)) {
918  }
919  U32 src = this->m_runtime.stack.pop<U32>();
920  this->m_runtime.stack.push(static_cast<U64>(src));
922 }
923 DirectiveError FpySequencer::op_itrunc_64_8() {
924  if (this->m_runtime.stack.size < sizeof(U64)) {
926  }
927  U64 src = this->m_runtime.stack.pop<U64>();
928  this->m_runtime.stack.push(static_cast<U8>(src));
930 }
931 DirectiveError FpySequencer::op_itrunc_64_16() {
932  if (this->m_runtime.stack.size < sizeof(U64)) {
934  }
935  U64 src = this->m_runtime.stack.pop<U64>();
936  this->m_runtime.stack.push(static_cast<U16>(src));
938 }
939 DirectiveError FpySequencer::op_itrunc_64_32() {
940  if (this->m_runtime.stack.size < sizeof(U64)) {
942  }
943  U64 src = this->m_runtime.stack.pop<U64>();
944  this->m_runtime.stack.push(static_cast<U32>(src));
946 }
947 Signal FpySequencer::stackOp_directiveHandler(const FpySequencer_StackOpDirective& directive, DirectiveError& error) {
948  // coding error, should not have gotten to this stack op handler
949  FW_ASSERT(directive.get__op() >= Fpy::DirectiveId::OR && directive.get__op() <= Fpy::DirectiveId::ITRUNC_64_32,
950  static_cast<FwAssertArgType>(directive.get__op()));
951 
952  switch (directive.get__op()) {
954  error = this->op_or();
955  break;
957  error = this->op_and();
958  break;
960  error = this->op_ieq();
961  break;
963  error = this->op_ine();
964  break;
966  error = this->op_ult();
967  break;
969  error = this->op_ule();
970  break;
972  error = this->op_ugt();
973  break;
975  error = this->op_uge();
976  break;
978  error = this->op_slt();
979  break;
981  error = this->op_sle();
982  break;
984  error = this->op_sgt();
985  break;
987  error = this->op_sge();
988  break;
990  error = this->op_feq();
991  break;
993  error = this->op_fne();
994  break;
996  error = this->op_flt();
997  break;
999  error = this->op_fle();
1000  break;
1001  case Fpy::DirectiveId::FGT:
1002  error = this->op_fgt();
1003  break;
1004  case Fpy::DirectiveId::FGE:
1005  error = this->op_fge();
1006  break;
1007  case Fpy::DirectiveId::NOT:
1008  error = this->op_not();
1009  break;
1011  error = this->op_fpext();
1012  break;
1014  error = this->op_fptrunc();
1015  break;
1017  error = this->op_fptosi();
1018  break;
1020  error = this->op_fptoui();
1021  break;
1023  error = this->op_sitofp();
1024  break;
1026  error = this->op_uitofp();
1027  break;
1028  case Fpy::DirectiveId::ADD:
1029  error = this->op_add();
1030  break;
1031  case Fpy::DirectiveId::SUB:
1032  error = this->op_sub();
1033  break;
1034  case Fpy::DirectiveId::MUL:
1035  error = this->op_mul();
1036  break;
1038  error = this->op_udiv();
1039  break;
1041  error = this->op_sdiv();
1042  break;
1044  error = this->op_umod();
1045  break;
1047  error = this->op_smod();
1048  break;
1050  error = this->op_fadd();
1051  break;
1053  error = this->op_fsub();
1054  break;
1056  error = this->op_fmul();
1057  break;
1059  error = this->op_fdiv();
1060  break;
1062  error = this->op_fpow();
1063  break;
1065  error = this->op_flog();
1066  break;
1068  error = this->op_fmod();
1069  break;
1071  error = this->op_siext_8_64();
1072  break;
1074  error = this->op_siext_16_64();
1075  break;
1077  error = this->op_siext_32_64();
1078  break;
1080  error = this->op_ziext_8_64();
1081  break;
1083  error = this->op_ziext_16_64();
1084  break;
1086  error = this->op_ziext_32_64();
1087  break;
1089  error = this->op_itrunc_64_8();
1090  break;
1092  error = this->op_itrunc_64_16();
1093  break;
1095  error = this->op_itrunc_64_32();
1096  break;
1097  default:
1098  FW_ASSERT(0, directive.get__op());
1099  break;
1100  }
1101  if (error != DirectiveError::NO_ERROR) {
1103  }
1105 }
1106 
1107 Signal FpySequencer::exit_directiveHandler(const FpySequencer_ExitDirective& directive, DirectiveError& error) {
1108  if (this->m_runtime.stack.size < 1) {
1111  }
1112  U8 errorCode = this->m_runtime.stack.pop<U8>();
1113  // exit(0), no error
1114  if (errorCode == 0) {
1115  // just goto the end of the sequence
1116  this->m_runtime.nextStatementIndex = this->m_sequenceObj.get_header().get_statementCount();
1118  }
1119  // otherwise, kill the sequence here
1120  // raise the user defined error code as an event
1121  this->log_WARNING_HI_SequenceExitedWithError(this->m_sequenceFilePath, errorCode);
1124 }
1125 
1126 Signal FpySequencer::allocate_directiveHandler(const FpySequencer_AllocateDirective& directive, DirectiveError& error) {
1127  if (directive.get_size() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1130  }
1131  this->m_runtime.stack.pushZeroes(directive.get_size());
1133 }
1134 
1136 Signal FpySequencer::storeHelper(Fpy::StackSizeType destOffset, Fpy::StackSizeType size, DirectiveError& error) {
1137  if (this->m_runtime.stack.size < size) {
1140  }
1141  // After popping the value, would the write go out of bounds?
1142  Fpy::StackSizeType newStackSize = this->m_runtime.stack.size - size;
1143  // Overflow-safe check: destOffset + size > newStackSize
1144  // Rewritten as: check destOffset <= newStackSize first, then size > newStackSize - destOffset
1145  if (destOffset > newStackSize || size > newStackSize - destOffset) {
1148  }
1149  // Copy value to the destination location
1150  this->m_runtime.stack.copy(destOffset, this->m_runtime.stack.size - size, size);
1151  this->m_runtime.stack.size = newStackSize;
1153 }
1154 
1156 Signal FpySequencer::loadHelper(Fpy::StackSizeType srcOffset, Fpy::StackSizeType size, DirectiveError& error) {
1157  if (size > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1160  }
1161  // Overflow-safe check: srcOffset + size > stack.size
1162  // Rewritten as: check srcOffset <= stack.size first, then size > stack.size - srcOffset
1163  if (srcOffset > this->m_runtime.stack.size || size > this->m_runtime.stack.size - srcOffset) {
1166  }
1167  // Copy from source location to top of stack
1168  this->m_runtime.stack.copy(this->m_runtime.stack.size, srcOffset, size);
1169  this->m_runtime.stack.size += size;
1171 }
1172 
1173 Signal FpySequencer::storeRelConstOffset_directiveHandler(const FpySequencer_StoreRelConstOffsetDirective& directive,
1174  DirectiveError& error) {
1175  I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1176  if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1179  }
1180  return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1181 }
1182 
1183 Signal FpySequencer::loadRel_directiveHandler(const FpySequencer_LoadRelDirective& directive, DirectiveError& error) {
1184  I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1185  if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1188  }
1189  return this->loadHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1190 }
1191 
1192 Signal FpySequencer::pushVal_directiveHandler(const FpySequencer_PushValDirective& directive, DirectiveError& error) {
1193  if (directive.get__valSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1196  }
1197  // copy from the bytearray in the directive to the stack, add to stack size.
1198  this->m_runtime.stack.push(const_cast<U8*>(directive.get_val()),
1199  static_cast<Fpy::StackSizeType>(directive.get__valSize()));
1201 }
1202 
1203 Signal FpySequencer::discard_directiveHandler(const FpySequencer_DiscardDirective& directive, DirectiveError& error) {
1204  if (this->m_runtime.stack.size < directive.get_size()) {
1207  }
1208  // drop the specified amount of bytes off the stack. simple as.
1209  this->m_runtime.stack.size -= directive.get_size();
1211 }
1212 
1213 Signal FpySequencer::memCmp_directiveHandler(const FpySequencer_MemCmpDirective& directive, DirectiveError& error) {
1214  // Overflow-safe check: we need size * 2 bytes on the stack
1215  // First check that size * 2 doesn't overflow: size > MAX/2 would overflow
1216  // MAX_STACK_SIZE is the upper bound for stack.size, so if size > MAX_STACK_SIZE/2, we definitely don't have enough
1217  if (directive.get_size() > Fpy::MAX_STACK_SIZE / 2) {
1220  }
1221  // Now safe to compute size * 2
1222  if (this->m_runtime.stack.size < directive.get_size() * 2) {
1225  }
1226 
1227  // find the starting offsets of the two byte arrays
1228  U64 lhsOffset = this->m_runtime.stack.size - directive.get_size() * 2;
1229  U64 rhsOffset = this->m_runtime.stack.size - directive.get_size();
1230 
1231  // "officially" remove them from the stack
1232  // you have to do this before pushing to the stack, otherwise the result would get placed
1233  // after the byte arrays
1234  this->m_runtime.stack.size -= directive.get_size() * 2;
1235 
1236  // memcmp the two byte arrays, push FW_SERIALIZE_TRUE_VALUE if they were equal, FW_SERIALIZE_FALSE_VALUE otherwise
1237  if (memcmp(this->m_runtime.stack.bytes + lhsOffset, this->m_runtime.stack.bytes + rhsOffset,
1238  directive.get_size()) == 0) {
1239  this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_TRUE_VALUE));
1240  } else {
1241  this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_FALSE_VALUE));
1242  }
1244 }
1245 
1246 Signal FpySequencer::stackCmd_directiveHandler(const FpySequencer_StackCmdDirective& directive, DirectiveError& error) {
1247  // Overflow-safe check: need argsSize + sizeof(FwOpcodeType) bytes
1248  // Check stack.size >= sizeof(FwOpcodeType) first, then stack.size - sizeof(FwOpcodeType) >= argsSize
1249  if (this->m_runtime.stack.size < sizeof(FwOpcodeType) ||
1250  this->m_runtime.stack.size - sizeof(FwOpcodeType) < directive.get_argsSize()) {
1253  }
1254 
1255  // pop the opcode of the cmd off the stack
1256  // note this means that, unlike the actual byte array that the dispatcher gets,
1257  // these cmds have opcode after the argument buffer
1258  FwOpcodeType opcode = this->m_runtime.stack.pop<FwOpcodeType>();
1259  U64 argBufOffset = this->m_runtime.stack.size - directive.get_argsSize();
1260 
1261  // update the opcode of the cmd we will await
1262  this->m_runtime.currentCmdOpcode = opcode;
1263 
1264  // also pop the args off the stack
1265  this->m_runtime.stack.size -= directive.get_argsSize();
1266 
1267  if (this->sendCmd(opcode, this->m_runtime.stack.bytes + argBufOffset, directive.get_argsSize()) ==
1270  } else {
1271  // now tell the SM to wait some more until we get the cmd response back
1272  // if we've already got the response back this should be harmless
1274  }
1275 
1277 }
1278 
1279 Signal FpySequencer::pushTime_directiveHandler(const FpySequencer_PushTimeDirective& directive, DirectiveError& error) {
1280  if (Fpy::MAX_STACK_SIZE - Fw::Time::SERIALIZED_SIZE < this->m_runtime.stack.size) {
1283  }
1284 
1285  Fw::Time currentTime = this->getTime();
1286 
1287  U8 currentTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
1288  Fw::ExternalSerializeBuffer timeEsb(currentTimeBuf, Fw::Time::SERIALIZED_SIZE);
1289  Fw::SerializeStatus stat = timeEsb.serializeFrom(currentTime);
1290 
1291  // coding error if this failed, we should have enough space
1292  FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
1293 
1294  // push time to end of stack
1295  this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
1297 }
1298 
1299 Signal FpySequencer::setFlag_directiveHandler(const FpySequencer_SetFlagDirective& directive, DirectiveError& error) {
1300  if (this->m_runtime.stack.size < 1) {
1303  }
1304  if (directive.get_flagIdx() >= Fpy::FLAG_COUNT) {
1307  }
1308 
1309  // 1 if the stack bool is nonzero, 0 otherwise
1310  U8 flagVal = this->m_runtime.stack.pop<U8>() != 0;
1311 
1312  this->m_runtime.flags[directive.get_flagIdx()] = flagVal == 1;
1314 }
1315 
1316 Signal FpySequencer::getFlag_directiveHandler(const FpySequencer_GetFlagDirective& directive, DirectiveError& error) {
1317  if (Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size < 1) {
1320  }
1321  if (directive.get_flagIdx() >= Fpy::FLAG_COUNT) {
1324  }
1325 
1326  bool flagVal = this->m_runtime.flags[directive.get_flagIdx()];
1327  this->m_runtime.stack.push<U8>(flagVal ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
1328  : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE));
1330 }
1331 
1332 Signal FpySequencer::getField_directiveHandler(const FpySequencer_GetFieldDirective& directive, DirectiveError& error) {
1333  // Need sizeof(StackSizeType) for the offset AND parentSize for the parent data
1334  // Check we have enough for the offset first
1335  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
1338  }
1339  // After popping the offset, we need at least parentSize bytes remaining
1340  if (this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < directive.get_parentSize()) {
1343  }
1344 
1345  Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1346 
1347  // Overflow-safe check: offset + memberSize > parentSize
1348  // Rewritten as: check offset <= parentSize first, then memberSize > parentSize - offset
1349  if (offset > directive.get_parentSize() || directive.get_memberSize() > directive.get_parentSize() - offset) {
1350  // i think it's somewhat ambiguous whether this is a stack access out of bounds
1351  // but there isn't really an error code that better reflects this, and i guess
1352  // it's technically true
1355  }
1356 
1357  // the resulting bytes should move to the start of the parent array
1358 
1359  // Calculate the offset of the parent start in the stack
1360  Fpy::StackSizeType parentStartOffset = this->m_runtime.stack.size - directive.get_parentSize();
1361  // Overflow-safe: parentStartOffset + offset cannot overflow since offset <= parentSize
1362  // and parentStartOffset + parentSize == stack.size (which is bounded)
1363  this->m_runtime.stack.move(parentStartOffset, parentStartOffset + offset, directive.get_memberSize());
1364  // adjust stack size by the diff between the member and the parent
1365  this->m_runtime.stack.size -= (directive.get_parentSize() - directive.get_memberSize());
1367 }
1368 
1369 Signal FpySequencer::peek_directiveHandler(const FpySequencer_PeekDirective& directive, DirectiveError& error) {
1370  // must have at least two StackSizeType on stack
1371  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) * 2) {
1374  }
1375 
1376  Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1377  Fpy::StackSizeType byteCount = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1378 
1379  // Check offset doesn't exceed stack size (after both pops)
1380  if (offset > this->m_runtime.stack.size) {
1381  // would access past the bottom of the stack
1382  // note we allow the equals case because the byteCount might be 0
1385  }
1386  if (byteCount > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1387  // we would overflow the stack if we pushed this many bytes to it
1390  }
1391  // Overflow-safe check: byteCount + offset > stack.size
1392  // Rewritten as: check offset <= stack.size (done above), then byteCount > stack.size - offset
1393  if (byteCount > this->m_runtime.stack.size - offset) {
1394  // would access past the bottom of the stack
1397  }
1398  // start copying from the lowest byte of the src array
1399  U8* src = this->m_runtime.stack.top() - offset - byteCount;
1400  this->m_runtime.stack.push(src, byteCount);
1402 }
1403 
1404 Signal FpySequencer::storeRel_directiveHandler(const FpySequencer_StoreRelDirective& directive, DirectiveError& error) {
1405  // Need enough bytes for the value and the offset (SignedStackSizeType = 4 bytes)
1406  // Overflow-safe: check stack.size >= sizeof(SignedStackSizeType) first, then stack.size -
1407  // sizeof(SignedStackSizeType) >= size
1408  if (this->m_runtime.stack.size < sizeof(Fpy::SignedStackSizeType) ||
1409  this->m_runtime.stack.size - sizeof(Fpy::SignedStackSizeType) < directive.get_size()) {
1412  }
1413 
1414  // Pop the signed offset from the stack
1415  Fpy::SignedStackSizeType lvarOffset = this->m_runtime.stack.pop<Fpy::SignedStackSizeType>();
1416 
1417  I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + lvarOffset;
1418  if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1421  }
1422  return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1423 }
1424 
1425 Signal FpySequencer::call_directiveHandler(const FpySequencer_CallDirective& directive, DirectiveError& error) {
1426  // Need at least 4 bytes for the target address
1427  if (this->m_runtime.stack.size < sizeof(U32)) {
1430  }
1431 
1432  // Pop the target directive index from the stack
1433  U32 target = this->m_runtime.stack.pop<U32>();
1434 
1435  // Check if we have space to push return address and saved frame pointer (8 bytes total)
1436  if (this->m_runtime.stack.size + sizeof(Fpy::StackSizeType) + sizeof(U32) > Fpy::MAX_STACK_SIZE) {
1439  }
1440 
1441  // Check target is within bounds (will also be checked at execution time)
1442  if (target > m_sequenceObj.get_header().get_statementCount()) {
1445  }
1446 
1447  // Save the return address (next instruction after CALL)
1448  U32 returnAddr = this->m_runtime.nextStatementIndex;
1449 
1450  // Set the next instruction to the target
1451  this->m_runtime.nextStatementIndex = target;
1452 
1453  // Push the return address to the stack
1454  this->m_runtime.stack.push<U32>(returnAddr);
1455 
1456  // Push the current frame pointer to the stack
1457  this->m_runtime.stack.push<Fpy::StackSizeType>(this->m_runtime.stack.currentFrameStart);
1458 
1459  // Set the new frame pointer to the current top of stack
1460  this->m_runtime.stack.currentFrameStart = this->m_runtime.stack.size;
1461 
1463 }
1464 
1465 Signal FpySequencer::return_directiveHandler(const FpySequencer_ReturnDirective& directive, DirectiveError& error) {
1466  Fpy::StackSizeType returnValSize = directive.get_returnValSize();
1467  Fpy::StackSizeType callArgsSize = directive.get_callArgsSize();
1468 
1469  // Check we have enough bytes for the return value
1470  if (this->m_runtime.stack.size < returnValSize) {
1473  }
1474 
1475  // returnValSize is guaranteed to be less than Fpy::MAX_STACK_SIZE because it's less than the stack.size
1476  // thus the memcpy won't fail
1477 
1478  // Save the return value if there is one
1479  U8 returnValue[Fpy::MAX_STACK_SIZE] = {};
1480  if (returnValSize > 0) {
1481  memcpy(returnValue, this->m_runtime.stack.top() - returnValSize, returnValSize);
1482  }
1483 
1484  // Truncate the stack to stack_frame_start (discard all local variables)
1485  if (this->m_runtime.stack.currentFrameStart > this->m_runtime.stack.size) {
1488  }
1489  this->m_runtime.stack.size = this->m_runtime.stack.currentFrameStart;
1490 
1491  // Check we have enough bytes for saved frame pointer and return address
1492  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) + sizeof(U32)) {
1495  }
1496 
1497  // Pop the saved frame pointer
1498  Fpy::StackSizeType savedFramePtr = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1499 
1500  // Pop the return address
1501  U32 returnAddr = this->m_runtime.stack.pop<U32>();
1502 
1503  // Restore the frame pointer
1504  if (savedFramePtr > this->m_runtime.stack.size) {
1507  }
1508  this->m_runtime.stack.currentFrameStart = savedFramePtr;
1509 
1510  // Validate the return address is within bounds
1511  if (returnAddr > m_sequenceObj.get_header().get_statementCount()) {
1514  }
1515 
1516  // Set the next instruction to the return address
1517  this->m_runtime.nextStatementIndex = returnAddr;
1518 
1519  // Check that we have enough bytes for the call arguments
1520  if (this->m_runtime.stack.size < callArgsSize) {
1523  }
1524  // Discard the function arguments
1525  this->m_runtime.stack.size -= callArgsSize;
1526 
1527  // Push the return value
1528  if (returnValSize > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1531  }
1532  this->m_runtime.stack.push(returnValue, returnValSize);
1533 
1535 }
1536 
1537 Signal FpySequencer::loadAbs_directiveHandler(const FpySequencer_LoadAbsDirective& directive, DirectiveError& error) {
1538  return this->loadHelper(directive.get_globalOffset(), directive.get_size(), error);
1539 }
1540 
1541 Signal FpySequencer::storeAbs_directiveHandler(const FpySequencer_StoreAbsDirective& directive, DirectiveError& error) {
1542  Fpy::StackSizeType size = directive.get_size();
1543 
1544  // Need enough bytes for the value and the offset
1545  // Overflow-safe: check stack.size >= sizeof(StackSizeType) first, then stack.size - sizeof >= size
1546  if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) ||
1547  this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < size) {
1550  }
1551 
1552  // Pop the global offset from the stack
1553  Fpy::StackSizeType globalOffset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1554 
1555  return this->storeHelper(globalOffset, size, error);
1556 }
1557 
1558 Signal FpySequencer::storeAbsConstOffset_directiveHandler(const FpySequencer_StoreAbsConstOffsetDirective& directive,
1559  DirectiveError& error) {
1560  return this->storeHelper(directive.get_globalOffset(), directive.get_size(), error);
1561 }
1562 
1563 } // namespace Svc
void directive_storeRelConstOffset_internalInterfaceHandler(const Svc::FpySequencer_StoreRelConstOffsetDirective &directive) override
Internal interface handler for directive_storeRelConstOffset.
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.
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.
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.
Fw::TlmValid getTlmChan_out(FwIndexType portNum, FwChanIdType id, Fw::Time &timeTag, Fw::TlmBuffer &val)
Invoke output port getTlmChan.
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.
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.
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.
U8 FwTimeContextStoreType
The type used to serialize a time context value.
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 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.
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.
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 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.
void sequencer_sendSignal_stmtResponse_beginSleep()
Send signal stmtResponse_beginSleep to state machine sequencer.
Representing failure.
void directive_getFlag_internalInterfaceHandler(const Svc::FpySequencer_GetFlagDirective &directive) override
Internal interface handler for directive_getFlag.
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
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:138
void sequencer_sendSignal_stmtResponse_keepWaiting()
Send signal stmtResponse_keepWaiting to state machine sequencer.
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)
double F64
64-bit floating point (double). Required for compiler-supplied double promotion.
Definition: BasicTypes.h:85
bool isConnected_prmGet_OutputPort(FwIndexType portNum)
void cmdOut_out(FwIndexType portNum, Fw::ComBuffer &data, U32 context)
Invoke output port cmdOut.
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 directive_storeAbs_internalInterfaceHandler(const Svc::FpySequencer_StoreAbsDirective &directive) override
Internal interface handler for directive_storeAbs.
void directive_setFlag_internalInterfaceHandler(const Svc::FpySequencer_SetFlagDirective &directive) override
Internal interface handler for directive_setFlag.
Fw::ParamValid getParam_out(FwIndexType portNum, FwPrmIdType id, Fw::ParamBuffer &val)
Invoke output port getParam.
pushes the current Fw.Time struct to the stack
pops a bool off the stack, sets a flag with a specific index to that bool
bool isConnected_getTlmChan_OutputPort(FwIndexType portNum)
called when the statement is telling the sequencer to await a later stmt response ...
gets a flag and pushes its value as a U8 to the stack
FpySequencer_SequencerStateMachineStateMachineBase::Signal Signal
#define FW_ASSERT(...)
Definition: Assert.hpp:14
Success/Failure.
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